#lang scribble/manual @(require scribble/eval (for-label racket)) @title{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: @racketblock[ (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))))))))) ] @scheme[g] is actually of a subclass of @scheme[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. @section{Usage} @defform[ #:literals (guiml |@|) (guiml (class% [id] (|@| (class-parameter val) ...) (child-class% [child-id] (|@| ...) [grandchildren] ...) [more-children] ...))] The @scheme[|@|] subform contains all the arguments required by that object's @scheme[new] operator, except the @scheme[parent], which is added automatically. This subform is required even if it's empty. Within the context of the above @scheme[guiml] form, this: @racketblock[ (button% (|@| (label "Close") (style '(border)) (callback (λ ignored-args (sendmsg g show #f))))) ] is equivalent to this: @racketblock[ (new button% (parent the-vertical-panel-above-this-in-the-tree) (label "Close") (style '(border)) (callback (λ ignored-args (sendmsg g show #f)))) ] The @scheme[|@|] subform is followed by all the object's children. @section{Retrieving Objects From the Tree} To give an object in the tree a name, you put the identifier in front of the @scheme[|@|] subform: @racketblock[ (guiml frame% ... (editor-canvas% 'ecanvas (|@|))) ] The identifier can be of any type, and by default will be compared with @scheme[eq?] Then the same object can be retrieved from the tree using @scheme[get-widget-by-id]: @racketblock[ (get-widget-by-id g 'ecanvas) ] You can specify a comparator if you used an identifier that can't be compared with @scheme[eq?]: @racketblock[ (get-widget-by-id g "ecanvas" string=?) ] You can also retrieve all the objects of a given class from the tree: @racketblock[ (get-widgets-by-type g button%) ] @section{Adding and Removing Children} GUIML provides syntax to add existing or newly-created children to an existing GUIML object: @defform[ #:literals (add-children |@|) (add-children parent (child-class% [id] (|@| (class-parameter val) ...) (grandchild-class% [child-id] (|@| ...) [great-grandchildren] ...) [more-children] ...) ...)] All the @scheme[child-class%] definitions are instantiated as children of the @scheme[parent]. Instead of a @scheme[child-class%] definition, you can use any form that evaluates to an object that can be used as a child. There is also the @scheme[delete-children] procedure: @racketblock[ (delete-children object [id]) ] If the optional @scheme[id] argument is provided, then only the child with that tag will be deleted, otherwise all children will be deleted. @section{Miscellaneous} Each object in the tree has its own semaphore, which can be obtained like this: @racketblock[ (send g get-semaphore) ] The following syntax is provided for backward compatibility: @racketblock[ (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 @scheme[sendmsg] form allowed you to somewhat still be able to treat this structure like a class.