1.2Counter
1.5More
5.2Views
5.2.3Containers
5.2.5Controls
5.2.7Interfaces
5.2.7.1view<%>
5.2.7.4context<%>
obs
@
:=
<~
~>
~#>
8.18
top
← prev up next →

gui-easy: Declarative GUIsπŸ”— i

Bogdan Popa <bogdan@defn.io>

The goal of GUI Easy is to simplify user interface construction in Racket by wrapping the existing imperative API (racket/gui) in a functional shell.

[フレーム]

1QuickstartπŸ”— i

gui-easy can be broadly split up into two parts: observables and views.

Observables contain values and notify subscribed observers of changes to their contents. Views are representations of racket/gui widget trees that, when rendered, produce concrete instances of those trees and handle the details of wiring state and widgets together.

The core abstractions of observables and views correspond to a model-view-controller (MVC) architecture for graphical applications as popularized by Smalltalk-80.

1.1Hello, World!πŸ”— i

 
(require racket/gui/easy)
 
(text "Hello,World!"))

The code above describes a view hierarchy rooted in a window that contains the text “Hello, World!”. By itself, it doesn’t do much, but you can take it and pass it to render to convert it into a native GUI:

 
(require racket/gui/easy)
 
(text "Hello,World!")))

1.2CounterπŸ”— i

State in gui-easy is held by observables.

 
(require racket/gui/easy
racket/gui/easy/operator)
 
(define @count(@ 0))
(button "-"(λ ()(@count.<~ .sub1 )))
(text (@count.~> .number->string ))
(button "+"(λ ()(@count.<~ .add1 ))))))

Here we define an observable called @count that holds the current value of a counter. The two button s change the value of the counter when clicked and the text view displays its current string value via a derived observable. The three widgets are laid out horizontally by the hpanel .

1.3CountersπŸ”— i

Since views are at their core just descriptions of a GUI, it’s easy to abstract over them and make them reusable.

 
(require racket/gui/easy
racket/gui/easy/operator)
 
(define (counter@countaction)
(button "-"(λ ()(actionsub1 )))
(text (@count.~> .number->string ))
(button "+"(λ ()(actionadd1 )))))
 
(define @counter-1(@ 0))
(define @counter-2(@ 0))
 
(counter@counter-1(λ (proc)(@counter-1.<~ .proc)))
(counter@counter-2(λ (proc)(@counter-2.<~ .proc))))))

1.4Dynamic CountersπŸ”— i

Taking the previous example further, we can render a dynamic list of counters.

 
(require racket/gui/easy
racket/gui/easy/operator)
 
(define @counters(@ ' ((0.0))))
 
(define (append-countercounts)
(define next-id(add1 (apply max (map car counts))))
(append counts` ((, next-id.0))))
 
(define (update-countcountskproc)
(for/list ([entry(in-list counts)])
(if (eq? (car entry)k)
(cons k(proc(cdr entry)))
entry)))
 
(define (counter@countaction)
#:stretch' (#t#f)
(button "-"(λ ()(actionsub1 )))
(text (@count.~> .number->string ))
(button "+"(λ ()(actionadd1 )))))
 
#:size' (#f200)
#:alignment' (centertop)
#:stretch' (#t#f)
(button "Addcounter"(λ ()(@counters.<~ .append-counter))))
@counters
#:keycar
(λ (k@entry)
(counter
(@entry.~> .cdr )
(λ (proc)
(@counters.<~ .(λ (counts)(update-countcountskproc))))))))))

Here the @counters observable holds a list of pairs where the first element of a pair is the id of each counter and the second is its count. When the “Add counter” button is clicked, a new counter is added to the list. The list-view renders each individual counter by passing in a derived observable to its make-view argument.

1.5MoreπŸ”— i

For more information about the core concepts of gui-easy and its design, see [knoblepopa23]. For more examples, see the "examples" directory in the [repo-link].

2Geometry ManagementπŸ”— i

See Geometry Management in the racket/gui docs for details on how views get laid out.

Containers, Windows & Dialogs take optional keyword arguments that allow you to control the #:spacing and #:alignment of their children and their own #:min-size, #:stretch and #:margin. All of these arguments can be passed as either regular values or as observables, in which case the properties they control will vary with changes to the observables.

3Custom ViewsπŸ”— i

You can create your own views by implementing the view<%> interface.

As an example, let’s wrap Jeffrey Massung’s canvas-list<%>. I find it helps to work backwards from the API you’d like to end up with. In this case, that would be:

(canvas-list
@entries
(λ (itemstatedcwh)
(draw-item... ))
(λ (item)
(printf "double-clicked ~s~n"item)))

A canvas-list takes an observable of a list of entries, a function that knows how to draw each entry to a gui:dc<%> and a callback for when the user double-clicks an entry. The canvas-list function should then look something like this:

(define (canvas-list@entriesdraw[actionvoid ])
(new canvas-list-view%
[@entries@entries]
[drawdraw]
[actionaction]))

All it needs to do is abstract over the instantiation of the underlying view<%> . Next, we can define a skeleton implementation of canvas-list-view%:

(define canvas-list-view%
(init-field @entriesdrawaction)
(define/public (dependencies)
(error 'create"not implemented"))
(define/public (createparent)
(error 'create"not implemented"))
(define/public (updatevwhatval)
(error 'update"not implemented"))
(define/public (destroyv)
(error 'destroy"not implemented"))))

Views must communicate what observables they depend on to their parents. Since the only dependency a canvas list has is its set of entries. That’s straightforward:

(define canvas-list-view%
...
(define/public (dependencies)
(list @entries))
... ))

When a view is rendered, its parent is in charge of calling its create method. That method must instantiate a GUI object, associate it with the passed-in parent, perform any initialization steps and then return it. In our case:

(define canvas-list-view%
...
(define/public (createparent)
(new canvas-list%
[parentparent]
[items(obs-peek @entries)]
[paint-item-callback(λ (selfentrystatedcwh)
(drawentrystatedcwh))]
[action-callback(λ (selfitemevent)
(actionitem))]))
... ))

When the observables the view depends on change, its parent will call its update method with the GUI object that the view returned from its create method, the observable that changed and the observable’s value when it changed. The view is then in charge of modifying its GUI object appropriately.

(define canvas-list-view%
...
(define/public (updatevwhatval)
(case/dep what
[@entries(send vset-itemsval)]))
... ))

Windows are a special case: the resources they manage only get disposed of when renderer-destroy is called, or when the program exits.

Finally, when a view is no longer visible, its destroy method is called to dispose of the GUI object and perform any teardown actions. In our case, there’s nothing to tear down so we can let garbage collection take care of destroying the canvas-list% object:

(define canvas-list-view%
...
(define/public (destroyv)
(void ))))

When the view becomes visible again, its create method will be called again and the whole cycle will repeat itself.

That’s all there is to it when it comes to custom controls. See the "examples/hn.rkt" example for a program that uses a custom view.

3.1Custom ContainersπŸ”— i

Containers are slightly more complicated to implement than controls. They must collect all their children’s unique dependencies and list them in their dependencies method. Additionally, their update method is in charge of dispatching updates to their children.

See "gui-easy-lib/gui/easy/private/view/panel.rkt" for an example.

4Escape HatchesπŸ”— i

Some views take a #:mixin argument that can be used to alter the behavior of the underlying widget. These are intended to be used as “escape hatches” when the library doesn’t provide a piece of functionality you need, but that functionality is available on the native widget.

See "examples/close-window.rkt" for an example of using a mixin to programmatically toggle a window’s visibility.

5ReferenceπŸ”— i

5.1RenderersπŸ”— i

Renderers convert view definitions to GUI elements.

procedure

( renderer? v)boolean?

v:any/c
Returns #t if v is a renderer.

procedure

( embed parentview)renderer?

parent:(is-a?/c gui:area<%> )
view:(is-a?/c view<%> )
Renders the view hierarchy represented by view as a child of parent.

Use this function when you need to embed one or more view<%> s within an existing racket/gui application. Otherwise, use render .

procedure

( render view[parent])renderer?

parent:(or/c #frenderer? )=#f
Renders the view hierarchy represented by view.

When a parent renderer is provided, renders the view as a child of the root view of parent. This is useful when you need to render a modal dialog on top of an existing window.

Renders the popup menu represented by view as a child of parent.

procedure

( renderer-root r)any/c

Returns the root widget of r. This function is handy when you need to embed a gui:top-level-window<%> . The embed function won’t show the embedded window, so you’ll need to get it and send it a show message.

procedure

( renderer-destroy r)void?

Destroys the render tree managed by r.

5.2ViewsπŸ”— i

Views are functions that return a view<%> instance.

Views might wrap a specific GUI widget, like a text message or button, or they might construct a tree of smaller views, forming a larger component.

Views are typically Observable-aware in ways that make sense for each individual view. For instance the text view takes as input an observable string and the rendered text label updates with changes to that observable.

Many racket/gui widgets are already wrapped by GUI Easy, but programmers can implement the view<%> interface themselves in order to integrate arbitrary widgets, such as those from 3rd-party packages in the Racket ecosystem, into their projects.

5.2.1Windows & DialogsπŸ”— i

procedure

( window [ #:titletitle
#:sizesize
#:alignmentalignment
#:positionposition
#:min-sizemin-size
#:stretchstretch
#:stylestyle
#:mixinmix]
child...+)(is-a?/c window-view<%> )
title:(maybe-obs/c string? )="Untitled"
size:(maybe-obs/c size/c )='(#f#f)
alignment:(maybe-obs/c alignment/c )='(centertop)
position:(maybe-obs/c position/c )='center
min-size:(maybe-obs/c size/c )='(#f#f)
stretch:(maybe-obs/c stretch/c )='(#t#t)
style :
(listof (or/c 'no-resize-border'no-caption
'no-system-menu'hide-menu-bar
'toolbar-button'float'metal
'fullscreen-button'fullscreen-aux))
= null
child:(is-a?/c view<%> )
Returns a representation of a top-level window.

procedure

( dialog [ #:titletitle
#:sizesize
#:alignmentalignment
#:positionposition
#:min-sizemin-size
#:stretchstretch
#:stylestyle
#:mixinmix]
child...+)(is-a?/c window-view<%> )
title:(maybe-obs/c string? )="Untitled"
size:(maybe-obs/c size/c )='(#f#f)
alignment:(maybe-obs/c alignment/c )='(centertop)
position:(maybe-obs/c position/c )='center
min-size:(maybe-obs/c size/c )='(#f#f)
stretch:(maybe-obs/c stretch/c )='(#t#t)
style : (listof (or/c 'no-caption'no-sheet'resize-border'close-button))
= '(close-button)
child:(is-a?/c view<%> )
Returns a representation of a dialog.

5.2.2Menus & Menu ItemsπŸ”— i

procedure

( popup-menu menu-or-item...)(is-a?/c popup-menu-view<%> )

menu-or-item:(is-a?/c view<%> )
Returns a representation of a popup menu. Popup menus are rendered using render-popup-menu .

(menu
"File"
(menu-item "Open...")
(menu-item "Save"))
(menu-item "Quit"))

procedure

( menu-bar [ #:enabled?enabled?]
menu-or-item...)(is-a?/c view<%> )
enabled?:(maybe-obs/c any/c )=#t
menu-or-item:(is-a?/c view<%> )
Returns a representation of a menu-bar menu.

(menu
"File"
(menu-item "Open...")
(menu-item "Save")
(menu-item "Quit"))
(menu
"Help"
(menu-item "Getting Started")))

Changed in version 0.15 of package gui-easy-lib: The #:enabled? argument.

procedure

( menu label
[ #:enabled?enabled?
#:helphelp-text]
item...)(is-a?/c view<%> )
enabled?:(maybe-obs/c any/c )=#t
help-text:(maybe-obs/c (or/c #fstring? ))=#f
item:(is-a?/c view<%> )
Returns a representation of a menu with items as children.

Changed in version 0.15 of package gui-easy-lib: The #:enabled? and #:help arguments.

procedure

( menu-item label
[ action
#:enabled?enabled?
#:helphelp-text
#:shortcutshortcut])(is-a?/c view<%> )
action:(-> any )=void
enabled?:(maybe-obs/c any/c )=#t
help-text:(maybe-obs/c (or/c #fstring? ))=#f
shortcut :
(or/c 'alt'cmd'meta'ctl'shift'option)
(or/c 'alt'cmd'meta'ctl'shift'option)
= #f
Returns a representation of a menu item that calls action when clicked.

Changed in version 0.15 of package gui-easy-lib: The #:enabled?, #:help and #:shortcut arguments.

procedure

[ action
#:checked?checked?
#:enabled?enabled?
#:helphelp-text
#:shortcutshortcut])(is-a?/c view<%> )
action:(-> boolean? any )=void
checked?:(maybe-obs/c any/c )=#f
enabled?:(maybe-obs/c any/c )=#t
help-text:(maybe-obs/c (or/c #fstring? ))=#f
shortcut :
(or/c 'alt'cmd'meta'ctl'shift'option)
(or/c 'alt'cmd'meta'ctl'shift'option)
= #f
Returns a representation of a menu item with a checkbox. The action callback is called with the current checked state when the menu item is clicked. Use #:checked? to set or update the checkbox programmatically.

Added in version 0.18 of package gui-easy-lib.

Returns a representation of a menu item separator.

5.2.3ContainersπŸ”— i

procedure

( hpanel [ #:alignmentalignment
#:stylestyle
#:enabled?enabled?
#:spacingspacing
#:marginmargin
#:min-sizemin-size
#:stretchstretch
#:mixinmix]
child...+)(is-a?/c view<%> )
alignment:(maybe-obs/c alignment/c )='(leftcenter)
style :
(listof (or/c 'border'deleted
'hscroll'auto-hscroll'hide-hscroll
'vscroll'auto-vscroll'hide-vscroll))
= null
enabled?:(maybe-obs/c boolean? )=#t
spacing:(maybe-obs/c spacing/c )=0
margin:(maybe-obs/c margin/c )='(00)
min-size:(maybe-obs/c size/c )='(#f#f)
stretch:(maybe-obs/c stretch/c )='(#t#t)
child:(is-a?/c view<%> )
Returns a representation of a panel that lays out its children horizontally.

Changed in version 0.13 of package gui-easy-lib: Added the #:mixin argument.

procedure

( vpanel [ #:alignmentalignment
#:stylestyle
#:enabled?enabled?
#:spacingspacing
#:marginmargin
#:min-sizemin-size
#:stretchstretch
#:mixinmix]
child...+)(is-a?/c view<%> )
alignment:(maybe-obs/c alignment/c )='(centertop)
style :
(listof (or/c 'border'deleted
'hscroll'auto-hscroll'hide-hscroll
'vscroll'auto-vscroll'hide-vscroll))
= null
enabled?:(maybe-obs/c boolean? )=#t
spacing:(maybe-obs/c spacing/c )=0
margin:(maybe-obs/c margin/c )='(00)
min-size:(maybe-obs/c size/c )='(#f#f)
stretch:(maybe-obs/c stretch/c )='(#t#t)
child:(is-a?/c view<%> )
Returns a representation of a panel that lays out its children vertically.

Changed in version 0.13 of package gui-easy-lib: Added the #:mixin argument.

procedure

( group label
[ #:alignmentalignment
#:stylestyle
#:enabled?enabled?
#:spacingspacing
#:marginmargin
#:min-sizemin-size
#:stretchstretch
#:mixinmix]
child...+)(is-a?/c view<%> )
alignment:(maybe-obs/c alignment/c )='(centertop)
style:(listof (or/c 'deleted))=null
enabled?:(maybe-obs/c boolean? )=#t
spacing:(maybe-obs/c spacing/c )=0
margin:(maybe-obs/c margin/c )='(00)
min-size:(maybe-obs/c size/c )='(#f#f)
stretch:(maybe-obs/c stretch/c )='(#t#t)
child:(is-a?/c view<%> )
Returns a representation of a labeled vertical panel.

Changed in version 0.13 of package gui-easy-lib: Added the #:mixin argument.

procedure

( tabs choices
action
child...
[ #:choice->labelchoice->label
#:choice=?choice=?
#:selectionselection
#:alignmentalignment
#:enabled?enabled?
#:stylestyle
#:spacingspacing
#:marginmargin
#:min-sizemin-size
#:stretchstretch])(is-a?/c view<%> )
choices:(maybe-obs/c (listof any/c ))
action :
(-> (or/c 'new'close'reorder'select)
(or/c #fany/c )
any )
child:(is-a?/c view<%> )
choice->label:(-> any/c gui:label-string? )=values
choice=?:(-> any/c any/c boolean? )=equal?
selection:(maybe-obs/c (or/c #fany/c ))=#f
alignment:(maybe-obs/c alignment/c )='(leftcenter)
enabled?:(maybe-obs/c boolean? )=#t
style :
(listof (or/c 'no-border
'can-reorder'can-close'new-button
'flat-portable'deleted))
= null
spacing:(maybe-obs/c spacing/c )=0
margin:(maybe-obs/c margin/c )=0
min-size:(maybe-obs/c size/c )='(#f#f)
stretch:(maybe-obs/c stretch/c )='(#t#t)
Returns a representation of a tab panel.

The #:choice->label argument controls how each choice is displayed and the #:choice=? argument controls how the current #:selection is compared against the list of choices to determine the currently selected tab.

On user interaction, action is called with a symbol representing the event, the set of choices at the moment the action occurred and the current selection. The selection may be adjusted depending on the event (eg. when the current tab is closed, the selection changes to an adjacent tab). When tabs are reordered, the choices provided to the action represent the new tab order.

See "examples/tabs.rkt" for an example.

Changed in version 0.3 of package gui-easy-lib: Added the #:choice=? argument.
Changed in version 0.3: The selection is now a value in the set of choices instead of an index.

syntax

( if-view cond-ethen-eelse-e)

cond-e : (maybe-obs/c any/c )
then-e : (is-a?/c view<%> )
else-e : (is-a?/c view<%> )
Returns a representation of a panel that renders then-e when the current-value of cond-e is truthy and else-e otherwise.

Changed in version 0.4 of package gui-easy-lib: The if-view form was converted from a procedure into a syntactic form.

syntax

[cond-eview-e]...+
[else view-e])
cond-e : (maybe-obs/c any/c )
view-e : (is-a?/c view<%> )
Returns a representation of a panel that renders the first view-e for which the associated cond-e’s current value is truthy.

syntax

[(case-lit...+)view-e]...+
[else view-e])
e : (obs/c any/c )
view-e : (is-a?/c view<%> )
Returns a representation of a panel that renders the first view-e where one of the case-lits is equal? to e’s current value.

procedure

( list-view entries
make-view
[ #:keykey
#:alignmentalignment
#:enabled?enabled?
#:stylestyle
#:spacingspacing
#:marginmargin
#:min-sizemin-size
#:stretchstretch
#:mixinmix])(is-a?/c view<%> )
entries:(maybe-obs/c list? )
make-view:(-> any/c any/c (is-a?/c view<%> ))
key:(-> any/c any/c )=values
alignment:(maybe-obs/c alignment/c )='(centertop)
enabled?:(maybe-obs/c boolean? )=#t
style :
(listof (or/c 'horizontal'vertical'border'deleted
'hscroll'auto-hscroll'hide-hscroll
'vscroll'auto-vscroll'hide-vscroll))
= '(verticalauto-vscroll)
spacing:(maybe-obs/c spacing/c )=0
margin:(maybe-obs/c margin/c )='(00)
min-size:(maybe-obs/c size/c )='(#f#f)
stretch:(maybe-obs/c stretch/c )='(#t#t)
Returns a representation of a panel that renders the entries by passing each one as a derived observable to make-view. The make-view procedure is called with the key and the derived observable of each entry. The #:key procedure must return a unique value for each entry in the list, as compared using equal? .

See "examples/list.rkt" for an example.

procedure

[ make-view
#:equal?equal?-proc])(is-a?/c view<%> )
data:(obs/c any/c )
make-view:(-> any/c (is-a?/c view<%> ))=values
equal?-proc:(-> any/c any/c boolean? )=equal?
Returns a representation of a pane whose content is the result of applying make-view to the value of data. The content of the pane changes every time data changes and its current value is not equal (according to equal?-proc) to the previous value. The pane automatically adjusts its area properties when its child’s area properties change to match.

Added in version 0.9 of package gui-easy-lib.

5.2.4Canvases & SnipsπŸ”— i

procedure

( canvas data
draw
[ #:labellabel
#:enabled?enabled?
#:stylestyle
#:marginmargin
#:min-sizemin-size
#:stretchstretch
#:mixinmix])(is-a?/c view<%> )
enabled?:(maybe-obs/c boolean? )=#t
style :
(listof (or/c 'border'control-border'combo
'vscroll'hscroll'resize-corner
'gl'no-autoclear'transparent
'no-focus'deleted))
= null
margin:(maybe-obs/c margin/c )='(00)
min-size:(maybe-obs/c size/c )='(#f#f)
stretch:(maybe-obs/c stretch/c )='(#t#t)
Returns a representation of a canvas that is redrawn using draw whenever data changes.

procedure

( pict-canvas data
make-pict
[ #:labellabel
#:enabled?enabled?
#:stylestyle
#:marginmargin
#:min-sizemin-size
#:stretchstretch
#:mixinmix])(is-a?/c view<%> )
make-pict:(-> any/c pict? )
enabled?:(maybe-obs/c boolean? )=#t
style :
(listof (or/c 'border'control-border'combo
'vscroll'hscroll'resize-corner
'gl'no-autoclear'transparent
'no-focus'deleted))
= null
margin:(maybe-obs/c margin/c )='(00)
min-size:(maybe-obs/c size/c )='(#f#f)
stretch:(maybe-obs/c stretch/c )='(#t#t)
Returns a representation of a canvas that is redrawn using the result of make-pict whenever data changes.

procedure

( snip-canvas data
make-snip
[ #:labellabel
#:enabled?enabled?
#:stylestyle
#:marginmargin
#:min-sizemin-size
#:stretchstretch
#:mixinmix])(is-a?/c view<%> )
enabled?:(maybe-obs/c boolean? )=#t
style :
(listof (or/c 'border'control-border'combo
'vscroll'hscroll'resize-corner
'gl'no-autoclear'transparent
'no-focus'deleted))
= null
margin:(maybe-obs/c margin/c )='(00)
min-size:(maybe-obs/c size/c )='(#f#f)
stretch:(maybe-obs/c stretch/c )='(#t#t)
Returns a representation of a canvas that is redrawn using the result of make-snip whenever data changes. The snip is converted to a bitmap before being drawn to the canvas so it is non-interactive. Use this view when you want to efficiently update plots. For interactive snips, see snip .

procedure

( snip data
make-snip
[ update-snip
#:labellabel
#:enabled?enabled?
#:stylestyle
#:marginmargin
#:min-sizemin-size
#:stretchstretch
#:mixinmix])(is-a?/c view<%> )
update-snip:(-> (is-a?/c gui:snip% )any/c any )=void
enabled?:(maybe-obs/c boolean? )=#t
style :
(listof (or/c 'no-border'control-border'combo
'resize-corner'no-focus'deleted
'transparent))
= null
margin:(maybe-obs/c margin/c )='(00)
min-size:(maybe-obs/c size/c )='(#f#f)
stretch:(maybe-obs/c stretch/c )='(#t#t)
Returns the representation of an editor that holds a snip generated via make-snip. The snip may be updated whenever data changes via update-snip.

5.2.5ControlsπŸ”— i

procedure

( button label
action
[ #:enabled?enabled?
#:stylestyle
#:fontfont
#:marginmargin
#:min-sizemin-size
#:stretchstretch])(is-a?/c view<%> )
action:(-> any )
enabled?:(maybe-obs/c boolean? )=#t
style:(listof (or/c 'border'multi-line'deleted))=null
margin:(maybe-obs/c margin/c )='(00)
min-size:(maybe-obs/c size/c )='(#f#f)
stretch:(maybe-obs/c stretch/c )='(#t#t)
Returns a representation of a button that calls action when clicked.

procedure

( checkbox action
[ #:labellabel
#:checked?checked?
#:enabled?enabled?])(is-a?/c view<%> )
action:(-> boolean? any )
checked?:(maybe-obs/c boolean? )=#f
enabled?:(maybe-obs/c boolean? )=#f
Returns a representation of a checkbox that calls action when toggled.

procedure

( choice choices
action
[ #:choice->labelchoice->label
#:choice=?choice=?
#:selectionselection
#:labellabel
#:stylestyle
#:enabled?enabled?
#:min-sizemin-size
#:stretchstretch])(is-a?/c view<%> )
choices:(maybe-obs/c (listof any/c ))
action:(-> (or/c #fany/c )any )
choice->label:(-> any/c gui:label-string? )=values
choice=?:(-> any/c any/c boolean? )=equal?
selection:(maybe-obs/c any/c )=#f
style : (listof (or/c 'horizontal-label'vertical-label'deleted))
= null
enabled?:(maybe-obs/c boolean? )=#t
min-size:(maybe-obs/c size/c )='(#f#f)
stretch:(maybe-obs/c stretch/c )='(#t#t)
Returns a representation of a choice widget that calls action whenever the current selection changes.

The #:choice->label argument controls how each choice is displayed and the #:choice=? argument controls how the current #:selection is compared against the list of choices to determine the selection index.

procedure

( image path-or-bitmap
[ #:sizesize
#:modemode])(is-a?/c view<%> )
size:(maybe-obs/c size/c )='(#f#f)
mode:(maybe-obs/c (or/c 'fit'fill))='fit
Returns a representation of an image.

The #:mode argument controls how the image stretches to fill its container. If the mode is 'fit, then the image will preserve its aspect ratio, otherwise it will stretch to fill the container.

Changed in version 0.11.1 of package gui-easy-lib: The canvas background is now 'transparent. Now passes #t to the #:try-@2x? argument of gui:read-bitmap .
Changed in version 0.17: The first argument may now be a gui:bitmap% .

procedure

( input value
[ action
#:labellabel
#:enabled?enabled?
#:background-colorbackground-color
#:stylestyle
#:fontfont
#:keymapkeymap
#:marginmargin
#:min-sizemin-size
#:stretchstretch
#:mixinmix
#:value=?value=?
#:value->textvalue->text])(is-a?/c view<%> )
value:(maybe-obs/c any/c )
action : (-> (or/c 'input'return'has-focus'lost-focus)string? any )
= void
enabled?:(maybe-obs/c boolean? )=#t
background-color : (maybe-obs/c (or/c #f(is-a?/c gui:color% )))
= #f
style :
(listof (or/c 'single'multiple'hscroll'password
'vertical-label'horizontal-label
'deleted))
= '(single)
margin:(maybe-obs/c margin/c )='(00)
min-size:(maybe-obs/c size/c )='(#f#f)
stretch:(maybe-obs/c stretch/c )='(#t#t)
value->text:(-> any/c string? )=values
Returns a representation of a text field that calls action on change. The first argument to the action is the type of event that the control is responding to and the second is the contents of the text field.

The #:value=? argument controls when changes to the input data are reflected in the contents of the field. The contents of the input field only change when the new value of the underlying observable is not value=? to the previous one. The only exception to this is when the textual value (via #:value->text) of the observable is the empty string, in which case the input is cleared regardless of the value of the underlying observable.

The #:value->text argument controls how the input values are rendered to strings. If not provided, value must be either a string? or an observable of strings.

Changed in version 0.21 of package gui-easy-lib: input also responds to the 'has-focus and 'lost-focus events.

procedure

( progress value
[ #:labellabel
#:enabled?enabled?
#:stylestyle
#:rangerange
#:min-sizemin-size
#:stretchstretch])(is-a?/c view<%> )
enabled?:(maybe-obs/c boolean? )=#t
style :
(listof (or/c 'horizontal'vertical'plain
'vertical-label'horizontal-label
'deleted))
= '(horizontal)
min-size:(maybe-obs/c size/c )='(#f#f)
stretch : (maybe-obs/c stretch/c )
=
(list (memq 'horizontalstyle)
(memq 'verticalstyle))
Returns a representation of a progress bar.

procedure

( radios choices
action
[ #:choice->labelchoice->label
#:choice=?choice=?
#:selectionselection
#:labellabel
#:stylestyle
#:enabled?enabled?
#:min-sizemin-size
#:stretchstretch])(is-a?/c view<%> )
choices:(listof any/c )
action:(-> (or/c #fany/c )any )
choice->label:(-> any/c gui:label-string? )=values
choice=?:(-> any/c any/c boolean? )=equal?
selection:(maybe-obs/c any/c )=#f
style : (listof (or/c 'horizontal-label'vertical-label'deleted))
= null
enabled?:(maybe-obs/c boolean? )=#t
min-size:(maybe-obs/c size/c )='(#f#f)
stretch:(maybe-obs/c stretch/c )='(#t#t)
Returns a representation of a radio box widget that calls action whenever the current selection changes.

The #:choice->label argument controls how each choice is displayed and the #:choice=? argument controls how the current #:selection is compared against the list of choices to determine the selection index.

Unlike choice , the set of choices cannot be changed.

procedure

( slider value
action
[ #:labellabel
#:enabled?enabled?
#:stylestyle
#:min-valuemin-value
#:max-valuemax-value
#:min-sizemin-size
#:stretchstretch])(is-a?/c view<%> )
enabled?:(maybe-obs/c boolean? )=#t
style :
(listof (or/c 'horizontal'vertical'plain
'vertical-label'horizontal-label
'deleted))
= '(horizontal)
min-value:gui:position-integer? =0
max-value:gui:position-integer? =100
min-size:(maybe-obs/c size/c )='(#f#f)
stretch : (maybe-obs/c stretch/c )
=
(list (memq 'horizontalstyle)
(memq 'verticalstyle))
Returns a representation of a slider that calls the action on change.

procedure

( spacer )(is-a?/c view<%> )

Returns a representation of a spacer. Spacers extend to fill the space of their parents.

procedure

( table columns
entries
[ action
#:entry->rowentry->row
#:selectionselection
#:labellabel
#:enabled?enabled?
#:stylestyle
#:fontfont
#:marginmargin
#:min-sizemin-size
#:stretchstretch
#:column-widthscolumn-widths
#:mixinmix])(is-a?/c view<%> )
entries:(maybe-obs/c vector? )
action :
(-> (or/c 'select'dclick'column)
(or/c #f
any )
= void
entry->row:(-> any/c vector? )=values
enabled?:(maybe-obs/c boolean? )=#t
style :
(listof (or/c 'single'multiple'extended
'vertical-label'horizontal-label
'variable-columns'column-headers
'clickable-headers'reorderable-headers
'deleted))
= '(singlecolumnn-headersclickable-headersreorderable-headers)
margin:(maybe-obs/c margin/c )='(00)
min-size:(maybe-obs/c size/c )='(#f#f)
stretch:(maybe-obs/c stretch/c )='(#t#t)
Returns a representation of a table that calls action when the selection changes or when one of its columns is clicked (if the 'clickable-headers style is set). The action callback is called with the type of event that occurred, the set of entries at the time of the event and the current selection, if any. The current selection can either be a single index in the set of entries or a list of indices in the case of a 'multiple selection table.

The #:entry->row argument converts each row in the input data for display in the table.

The #:column-widths argument controls the widths of the columns. Column lengths can be specified either as a list of the column index (starting from 0) and the default width or a list of the column index, the column width, the minimum width and the maximum width.

Changed in version 0.13 of package gui-easy-lib: Added the #:mixin argument.

procedure

( text s[#:colorcolor#:fontfont])(is-a?/c view<%> )

= #f
Returns a representation of a textual label.

5.2.6CombinatorsπŸ”— i

procedure

( add-hooks [ #:on-createcreate-proc
#:on-destroydestroy-proc]
v)(is-a?/c view<%> )
create-proc:(-> any )=void
destroy-proc:(-> any )=void
Returns a proxy of v that calls create-proc and destroy-proc when a GUI widget is created and destroyed, respectively, from the view.

Added in version 0.14 of package gui-easy-lib.

5.2.7InterfacesπŸ”— i

5.2.7.1view<%>πŸ”— i

interface

view<%> :interface?

A view<%> object is a wrapper around a GUI object that knows what its data dependecies are and how to respond to their changes.
A single view<%> object may be used to manage multiple GUI widgets. Consequently, when implementing custom views, it’s best not to store any state within the view object itself. Instead, associate any internal state with the GUI widgets returned by create, possibly via context-mixin .

method

(send a-view dependencies )(listof obs? )

Returns the set of observers that this view depends on.

method

(send a-view create parent)(is-a?/c gui:area<%> )

Instantiates the underlying GUI object, associates it with parent and returns it so that the parent of this view<%> can manage it.

method

(send a-view update vdepval)void?

dep:obs?
val:any/c
Responds to a change to the contents of dep. The val argument is the most recent value of dep and the v argument is the GUI object created by create.

method

(send a-view destroy v)void?

Destroys the GUI object v and performs any necessary cleanup.

5.2.7.2window-view<%>πŸ”— i

implements: view<%>
A window-view<%> is like a regular view<%> but its create method has additional constraints placed on it.

method

(send a-window-view create parent)

Returns a new gui:top-level-window<%> belonging to parent.

method

(send a-window-view is-dialog? )boolean?

Returns #t if this view is a dialog.

5.2.7.3popup-menu-view<%>πŸ”— i

implements: view<%>
A popup-menu-view<%> is like a regular view<%> but its create method has additional constraints placed on it.

method

(send a-popup-menu-view create parent)

parent:#f
Returns a new gui:popup-menu% .

5.2.7.4context<%>πŸ”— i

mixin

context-mixin :(class? . -> .class? )

argument extends/implements: view<%>
result implements: context<%>
Specializes a class to implement the context<%> interface. Compares keys using eq? .

Examples:
> (send obset-context'a42)
> (send obget-context'a)

42

interface

context<%> :interface?

A context<%> object allows the user of an object to associate arbitrary values with it. Many of the view<%> s implemented by this library wrap their underlying GUI widgets using context-mixin in order to associate internal state with them.

method

(send a-context set-context kv)void?

k:any/c
v:any/c
Stores v under k within the context, overriding any existing values.

method

(send a-context set-context* kv......)void?

k:any/c
v:any/c
Stores each v under each k within the context.

method

(send a-context get-context k[default])any/c

k:any/c
default : any/c
= (λ ()(error 'get-context"no entry for ~a"k))
Returns the value stored under k from the context. If there is no value, the result is determined by default:

  • If default is a procedure? , it is called with no arguments to produce a result.

  • Otherwise, default is returned unchanged.

method

(send a-context get-context! kdefault)any/c

k:any/c
default:any/c
Like get-context, but if there is no value stored under k, the default value is computed as in get-context, stored in the context under k and then returned.

method

(send a-context remove-context k)void?

k:any/c
Removes the value stored under k from the context.

method

(send a-context clear-context )void?

Removes all stored values from the context.

5.3ObservablesπŸ”— i

Observables are containers for values that may change over time. Their changes may be observed by arbitrary functions.

Examples:
> (define @ints(obs 0))
> (obs-observe! @ints(λ (v)(printf "observer 1 got ~s~n"v)))
> (obs-observe! @ints(λ (v)(printf "observer 2 got ~s~n"v)))
> (obs-update! @intsadd1 )

observer 1 got 1

observer 2 got 1

1

Derived observables are observables whose values depend on other observables. Derived observables cannot be updated using obs-update! .

Examples:
> (define @strs(obs-map @intsnumber->string ))
> @strs

(obs "1" #:name 'anon #:derived? #t)

> (obs-update! @strsadd1 )

obs-update!: contract violation

expected: (not/c obs-derived?)

given: (obs "1" #:name 'anon #:derived? #t)

Internally, every observable has a unique handle and two observables are equal? when their handles are eq? . This means that equality (via equal? ) is preserved for impersonated observables, such as those guarded by obs/c .

procedure

( obs? v)boolean?

v:any/c
Returns #t when v is an observable.

procedure

( obs v[#:namename#:derived?derived?])obs?

v:any/c
name:symbol? ='anon
derived?:boolean? =#f
Returns a new observable, whose initial value is v.

The #:name of an observable is visible when the observable is printed so using a custom name can come in handy while debugging code.

The #:derived? argument controls whether or not the observable may be updated.

procedure

( obs-name o)symbol?

o:obs?
Returns the name of o.

Observables implement prop:object-name , so the name of an observable is also accessible generically via object-name .

Added in version 0.20 of package gui-easy-lib.

procedure

( obs-rename oname)obs?

o:obs?
name:symbol?
Returns an impersonator of o whose name is changed to name.

procedure

( obs-observe! of)void?

o:obs?
f:(-> any/c any/c )
Registers f as an observer of o, applying it to the value contained by o every time it changes.

procedure

( obs-unobserve! of)void?

o:obs?
f:(-> any/c any/c )
Removes f from o’s set of observers.

procedure

( obs-update! of)any/c

o:obs?
f:(-> any/c any/c )
Updates the value within o by applying f to it and storing the result. Returns the new value. If o is a derived observable, raises an exn:fail:contract? error.

procedure

( obs-set! ov)void?

o:obs?
v:any/c
Sets the value of o to v. Equivalent to (void (obs-update! o(λ (old-v)v))).

Added in version 0.16 of package gui-easy-lib.

procedure

( obs-peek o)any/c

o:obs?
Returns the current value contained within o.

procedure

( obs-map of)obs?

o:obs?
f:(-> any/c any/c )
Returns a new derived observable whose value changes every time o’s value does. The values held by the new observable are mapped via f.

procedure

( obs-filter-map op[d])obs?

o:obs?
p:(-> any/c any/c )
d:any/c =#f
Returns a new derived observable that applies p to every new value of o. The derived observable updates when the result of applying p to the new value of o is not #f. The initial value of the derived observable is (or (p(obs-peek o))d).

Added in version 0.11 of package gui-easy-lib.

procedure

( obs-filter op[d])obs?

o:obs?
p:(-> any/c any/c )
d:any/c =#f
Equivalent to (obs-filter-map o(λ (v)(and (pv)v))d).

Added in version 0.11 of package gui-easy-lib.

procedure

( obs-combine fo...+)obs?

f:(-> any/c ...+any/c )
o:obs?
Returns a new derived observable whose value changes every time one of the os change. The values held by the new observable are the values of the os combined via f.

This combinator retains a strong reference to each of the last values of the respective observables that are being combined until they change.

procedure

( obs-debounce o[#:durationduration-ms])obs?

o:obs?
duration-ms:exact-nonnegative-integer? =200
Returns a new derived observable based on o, whose value changes when there is at least a duration-ms millisecond pause in changes to o.

procedure

( obs-throttle o[#:durationduration-ms])obs?

o:obs?
duration-ms:exact-nonnegative-integer? =200
Returns a new derived observable based on o, whose values change at most once every duration-ms milliseconds.

procedure

[ #:refref-proc
#:setset-proc])obs?
o:obs?
ref-proc:(or/c #f(-> obs? any/c any/c ))=#f
set-proc:(or/c #f(-> obs? any/c any/c ))=#f

procedure

[ #:refref-proc
#:setset-proc])obs?
o:obs?
ref-proc:(or/c #f(-> obs? any/c any/c ))=#f
set-proc:(or/c #f(-> obs? any/c any/c ))=#f
Returns an impersonator or chaperone of an observable where wrappers optionally redirect access and update of the observable’s value, analogous to impersonate-box and chaperone-box . In the case of chaperone-obs , the value produced by a wrapper procedure must be a chaperone of the wrapper’s second argument.

Added in version 0.19 of package gui-easy-lib.

5.4View HelpersπŸ”— i

syntax

( case/dep what-expr
[dep-exprbody...]...+)
what-expr : obs?
dep-expr : obs?
Executes the body of the first clause body whose dep-expr is equal? to what-expr. Logs the dep-expr that matched to the 'gui-easy topic. Use this form to implement update methods.

5.5Observable OperatorsπŸ”— i

syntax

( define/obs nameinit-expr)

Binds name to an observable whose initial value is init-expr and whose name is 'name. If init-expr is already an observable, then it is locally renamed to 'name then bound to name.

procedure

( @ v)obs?

v:any/c
Converts v into an observable. If v is already an observable, it is returned unchanged.

procedure

( := ov)any/c

o:obs?
v:any/c
Changes the value of o to v.

procedure

(( λ:= o[f])v)any/c

o:obs?
v:any/c
Changes the value of o to the result of (fv).

procedure

( <~ of)any/c

o:obs?
f:(-> any/c any/c )
An alias for obs-update! .

procedure

( ~> of)obs?

o:obs?
f:(-> any/c any/c )
An alias for obs-map .

procedure

( ~#> op[d])obs?

o:obs?
p:(-> any/c any/c )
d:any/c =#f
An alias for obs-filter .

Added in version 0.11 of package gui-easy-lib.

procedure

( λ<~ of)(-> any/c )

o:obs?
f:(-> any/c any/c )
Returns a function that updates o using f when applied.

5.6ContractsπŸ”— i

value

alignment/c :
(list/c (or/c 'left'center'right)
(or/c 'top'center'bottom))
The contract for container child alignment. Represents the horizontal and vertical alignment, respectively.

The contract for margins. Represents the horizontal and vertical margin, respectively.

The contract for optional labels.

The contract for positions. The first places window s and dialog s in the center of the screen.

The contract for sizes. Represents the width and height, respectively. If either value is false, the view is allowed to stretch in that direction.

The contract for spacing.

The contract for stretch values. Represents whether or not a view can stretch horizontally and vertically, respectively.

procedure

( obs/c c)contract?

Returns a contract that accepts an obs? whose values conform to c. Checks the initial value of the observable as well as all subsequent updated values.

procedure

( maybe-obs/c c)contract?

A shorthand for (or/c c(obs/c c)).

BibliographyπŸ”— i

[knoblepopa23] D. B. Knoble and B. Popa, “Functional Shell and Reusable Components for Easy GUIs,” FUNARCH 2023: Proceedings of the 1st ACM SIGPLAN International Workshop on Functional Software Architecture, 31 August 2023 . https://arxiv.org/abs/2308.16024
[repo-link] B. Popa, “Declarative GUIs in Racket,” Online, Accessed: 28 July 2025. https://github.com/Bogdanp/racket-gui-easy

top
← prev up next →

AltStyle γ«γ‚ˆγ£γ¦ε€‰ζ›γ•γ‚ŒγŸγƒšγƒΌγ‚Έ (->γ‚ͺγƒͺγ‚ΈγƒŠγƒ«) /