How to: creating custom classes for widgets

August 29, 2009

One common way of building GUI code is to have one class for one type of widget. Slots (fields) of these classes are child widgets or some data related to the state of the widget. This approach might not be the best approach or adequate at all cases, but it is simple and quite common to many programmers.

To use this approach, we create the class as a subclass of desired widget:

(defclass custom-window (gtk-window)
  ()
  (:metaclass gobject-class))

In CL-GTK2, GObject classes and their subclasses must have gobject-class as their metaclass. The absence of :g-type-name class option specifies that this class is mapped to GObject class GtkWindow. This means that (make-instance 'custom-window) creates the GObject class GtkWindow and returns the custom-window instance as its wrapper. CL-GTK2 maintains references to created object instances, so you will always get back the instance of custom-window from all Gtk+ functions, methods, properties. This means that this works as you would expect it work.

Often, we want to customize the creation of widgets (by specifying their properties at construction time). This is possible with :default-initargs class option:

(defclass custom-window (gtk-window)
  ()
  (:metaclass gobject-class)
  (:default-initargs :title "Custom window"
                     :default-width 320
                     :default-height 240))

Specified default-initargs will be passed into GtkWindow‘s constructor.

Also it is possible to specify the :after method for initialize-instance that will initialize the the widget.

For example, we will have a GtkLabel and a GtkButton inside the custom-window packed into GtkVBox.

(defclass custom-window (gtk-window)
  ((label :initform (make-instance 'label :label "A label text")
          :reader custom-window-label)
   (button :initform (make-instance 'button :label "Click me!")
           :reader custom-window-button))
  (:metaclass gobject-class)
  (:default-initargs :title "Custom window"
                     :default-width 320
                     :default-height 240))

(defmethod initialize-instance :after
    ((window custom-window) &key &allow-other-keys)
  (connect-signal (custom-window-button window) "clicked"
                  (lambda (button)
                    (declare (ignore button))
                    (format t "Button clicked~%")))
  (let ((box (make-instance 'v-box)))
    (container-add window box)
    (box-pack-start box (custom-window-label window))
    (box-pack-start box (custom-window-button window) :expand nil)))

Now we can use the custom-window as a composite widget:

(within-main-loop
  (let ((w (make-instance 'custom-window)))
    (widget-show w)))

Screenshot

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: