Next: , Previous: Direct Layer, Up: Top


3 Widgets

When developing a graphics user interface based application, it is convinient to assemble it from simple and elastic components, such as buttons, text areas, draggable icons, canvases, menus, windows and so on. The common name for these components is “widget”. SLAYER is shipped with its own framework for creating and using widgets.

In order to use the widget framework, one shall (use-modules (widgets base)). It creates and exports the main widget under the symbol *stage*, and sets the appropriate display and resize procedures, as well as handlers for mouse1 and mouse2 press and release events and mousemove event.

It also contains a definition of a <widget> class, which is the base class for all widgets. Here's a simplified definition of <widget> class (in fact, all event handler slots are initialized to noop, and their init keywords correspond strictly to their names):

     (define-class <widget> ()
       (parent #:init-value #f #:init-keyword #:parent)
       (children #:init-value '() #:init-keyword #:children)
     
       ;; event handlers (initialized to noop)
       left-mouse-down
       left-mouse-up
       right-mouse-down
       right-mouse-up
       mouse-over
       mouse-out
       drag
       update!
       activate
       deactivate
       resize
     
       (x #:init-value 0 #:init-keyword #:x)
       (y #:init-value 0 #:init-keyword #:y)
       (w #:init-value 0 #:init-keyword #:w)
       (h #:init-value 0 #:init-keyword #:h))

As you can see, the widget structure is hierarchical: all widgets (except *stage*) have their parent, and some can have their children. The biggest part of the definition consists of some event handlers, which are called in certain situations (like clicking, dragging and so on). Furthermore, all widgets have rectangular shape, and the rectangle should be big enough to fit all of the widget's children (otherwise they can become unreachable).

In order to use the widget framework, it is enough to load the desired widget modules and add their instances to *stage* using the (add-child! /instance-of-a-widget/ #;to *stage*). The simplest program that uses widgets – a draggable rectangle – could look like this:

     (use-modules (slayer) (slayer image)
     	     (widgets base) (widgets sprite)
     	     (oop goops))
     (keydn 'esc quit)
     
     (define-method (dragger (w <widget>))
       (lambda (x y xrel yrel)
         (slot-set! w 'x (+ (slot-ref w 'x) xrel))
         (slot-set! w 'y (+ (slot-ref w 'y) yrel))))
     
     (let* ((rect (rectangle #;w 50 #;h 50 #;color #xcc33dd))
            (img (make-image rect #;x 50 #;y 50)))
       (slot-set! img 'drag (dragger img))
       (add-child! img #;to *stage*))

There are a few widgets already bundled with slayer, among which there is <text-area> widget (which still requires some work) and <3d-view> widget.

Check the slayer.scm demo to see how to use them.

Note also, that although the direct layer described in the previous chapter is rather stable and will only be extended rather than modified, the widget layer is more likely to change.