GUIML
6.1

GUIML

This is a DSL for describing GUIs. It is inspired by SXML notation and the HTMLPrag library. "GUIML" is a thin wrapper around Racket’s standard GUI library that allows a window and all its initial contents to be specified in a single expression, instead of having to specify each child widget’s parent by name.

Here is a simple GUI:

(define g (guiml (frame% (@ (label "Foobar")
                            (width 640)
                            (height 150))
                         (vertical-panel% (@)
                             (message% (@ (label "Hello, world!")))
                             (editor-canvas% 'ecanvas (@))
                             (button% (@ (label "Close")
                                         (style '(border))
                                         (callback (lambda ignored-args
                                                     (sendmsg g show #f)))))))))

g is a frame% object.

Usage:

syntax

(guiml
   (class% [id] (@ (class-parameter val) ...)
       (child-class% [child-id] (@ ...) [grandchildren] ...)
       [more-children] ...))

The @ subform contains all the arguments required by that object’s new operator, except the parent, which is added automatically. This subform is required even if it’s empty.

Within the context of the above guiml form, this:

(button% (@ (label "Close")
            (style '(border))
            (callback (λ ignored-args
                         (sendmsg g show #f)))))

is equivalent to this:

(new button% (parent the-vertical-panel-above-this-in-the-tree)
             (label "Close")
             (style '(border))
             (callback (λ ignored-args
                          (sendmsg g show #f))))

The @ subform is followed by all the object’s children.

To give an object in the tree a name, you put the identifier in front of the @ subform:

(guiml frame% ...
 (editor-canvas% 'ecanvas (@)))

The identifier can be of any type as long as it’s not #f, and by default will be compared with eq?

When you use an identifier, a subclass is generated on the fly instead of using the original class. This is done in order to provide a place to put the identifier. The subclass also adds a semaphore to the object, which can be retrieved like this:

(send tagged-object get-semaphore)

Then the same object can be retrieved from the tree using get-widget-by-id:

(get-widget-by-id g 'ecanvas)

You can specify a comparator if you used an identifier that can’t be compared with eq?:

(get-widget-by-id g "ecanvas" string=?)

You can also retrieve all the objects of a given class from the tree:

(get-widgets-by-type g button%)

The following syntax is provided for backward compatibility:

(sendmsg g show #t)

In older versions of GUIML, the GUI objects were wrapped in a bundle of structs and lists, and that was how they were tagged with identifiers and arranged in a tree that could be traversed. The sendmsg form allowed you to somewhat still be able to treat this structure like a class.