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 actually of a subclass of frame%. The subclass is generated on the fly, and allows all the widgets in the tree to be tagged with an optional identifier, which can be used to retrieve a widget from the tree even if it’s not bound to a variable.
(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, and by default will be compared with eq?
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%)
GUIML provides syntax to add existing or newly-created children to an existing GUIML object:
(add-children parent (child-class% [id] (@ (class-parameter val) ...) (grandchild-class% [child-id] (@ ...) [great-grandchildren] ...) [more-children] ...) ...)
All the child-class% definitions are instantiated as children of the parent. Instead of a child-class% definition, you can use any form that evaluates to an object that can be used as a child.
There is also the delete-children procedure:
(delete-children object [id])
If the optional id argument is provided, then only the child with that tag will be deleted, otherwise all children will be deleted.
Each object in the tree has its own semaphore, which can be obtained like this:
(send g get-semaphore)
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.