Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion gui-easy/gui/easy/scribblings/escape-hatches.scrbl
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
racket/gui/easy
racket/gui/easy/operator))

; Example links cited within this section
@(define example-link-close-window
(link "https://github.com/Bogdanp/racket-gui-easy/blob/master/examples/close-window.rkt"
@filepath{examples/close-window.rkt}))

@title{Escape Hatches}

Some views take a @racket[#:mixin] argument that can be used to alter
Expand All @@ -14,5 +19,5 @@ 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 a example of using a mixin to
See @|example-link-close-window| for an example of using a mixin to
programmatically toggle a window's visibility.
4 changes: 2 additions & 2 deletions gui-easy/gui/easy/scribblings/gui-easy.scrbl
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
@title{@tt{gui-easy}: Declarative GUIs}
@author[(author+email "Bogdan Popa" "[email protected]")]

This library provides a declarative API on top of
@racketmodname[racket/gui].
The goal of GUI Easy is to simplify user interface construction in Racket
by wrapping the existing imperative API (@racketmodname[racket/gui]) in a functional shell.

@(define-runtime-path youtubestub.tex "youtubestub.tex")
@(define embed-style
Expand Down
187 changes: 106 additions & 81 deletions gui-easy/gui/easy/scribblings/quickstart.scrbl
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,27 @@

@title{Quickstart}

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.

@section{Hello, World!}

@centered[@image[(build-path media "1.1-hello-world.png") #:scale 0.5]]

@codeblock|{
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these codeblock reindents were probably not intended

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No :/ DrRacket's Ctrl - I is addictively useful but does not do good all the time.

#lang racket/base
#lang racket/base

(require racket/gui/easy)
(require racket/gui/easy)

(window
(text "Hello, World!"))
(window
(text "Hello, World!"))
}|

The code above describes a view hierarchy rooted in a window that
Expand All @@ -28,13 +38,13 @@ but you can take it and pass it to @racket[render] to convert it into
a native GUI:

@codeblock|{
#lang racket/base
#lang racket/base

(require racket/gui/easy)
(require racket/gui/easy)

(render
(window
(text "Hello, World!")))
(render
(window
(text "Hello, World!")))
}|

@section{Counter}
Expand All @@ -44,18 +54,18 @@ a native GUI:
State in @tt{gui-easy} is held by @tech{observables}.

@codeblock|{
#lang racket/base

(require racket/gui/easy
racket/gui/easy/operator)

(define @count (@ 0))
(render
(window
(hpanel
(button "-" (λ () (@count . <~ . sub1)))
(text (@count . ~> . number->string))
(button "+" (λ () (@count . <~ . add1))))))
#lang racket/base

(require racket/gui/easy
racket/gui/easy/operator)

(define @count (@ 0))
(render
(window
(hpanel
(button "-" (λ () (@count . <~ . sub1)))
(text (@count . ~> . number->string))
(button "+" (λ () (@count . <~ . add1))))))
}|

Here we define an observable called @racket[|@count|] that holds the
Expand All @@ -72,25 +82,25 @@ Since views are at their core just descriptions of a GUI, it's easy to
abstract over them and make them reusable.

@codeblock|{
#lang racket/base
#lang racket/base

(require racket/gui/easy
racket/gui/easy/operator)
(require racket/gui/easy
racket/gui/easy/operator)

(define (counter @count action)
(hpanel
(button "-" (λ () (action sub1)))
(text (@count . ~> . number->string))
(button "+" (λ () (action add1)))))
(define (counter @count action)
(hpanel
(button "-" (λ () (action sub1)))
(text (@count . ~> . number->string))
(button "+" (λ () (action add1)))))

(define @counter-1 (@ 0))
(define @counter-2 (@ 0))
(define @counter-1 (@ 0))
(define @counter-2 (@ 0))

(render
(window
(vpanel
(counter @counter-1 (λ (proc) (@counter-1 . <~ . proc)))
(counter @counter-2 (λ (proc) (@counter-2 . <~ . proc))))))
(render
(window
(vpanel
(counter @counter-1 (λ (proc) (@counter-1 . <~ . proc)))
(counter @counter-2 (λ (proc) (@counter-2 . <~ . proc))))))
}|

@section{Dynamic Counters}
Expand All @@ -101,46 +111,46 @@ Taking the previous example further, we can render a dynamic list of
counters.

@codeblock|{
#lang racket/base

(require racket/gui/easy
racket/gui/easy/operator)

(define @counters (@ '((0 . 0))))

(define (append-counter counts)
(define next-id (add1 (apply max (map car counts))))
(append counts `((,next-id . 0))))

(define (update-count counts k proc)
(for/list ([entry (in-list counts)])
(if (eq? (car entry) k)
(cons k (proc (cdr entry)))
entry)))

(define (counter @count action)
(hpanel
#:stretch '(#t #f)
(button "-" (λ () (action sub1)))
(text (@count . ~> . number->string))
(button "+" (λ () (action add1)))))

(render
(window
#:size '(#f 200)
(vpanel
(hpanel
#:alignment '(center top)
#:stretch '(#t #f)
(button "Add counter" (λ () (@counters . <~ . append-counter))))
(list-view
@counters
#:key car
(λ (k @entry)
(counter
(@entry . ~> . cdr)
(λ (proc)
(@counters . <~ . (λ (counts) (update-count counts k proc))))))))))
#lang racket/base

(require racket/gui/easy
racket/gui/easy/operator)

(define @counters (@ '((0 . 0))))

(define (append-counter counts)
(define next-id (add1 (apply max (map car counts))))
(append counts `((,next-id . 0))))

(define (update-count counts k proc)
(for/list ([entry (in-list counts)])
(if (eq? (car entry) k)
(cons k (proc (cdr entry)))
entry)))

(define (counter @count action)
(hpanel
#:stretch '(#t #f)
(button "-" (λ () (action sub1)))
(text (@count . ~> . number->string))
(button "+" (λ () (action add1)))))

(render
(window
#:size '(#f 200)
(vpanel
(hpanel
#:alignment '(center top)
#:stretch '(#t #f)
(button "Add counter" (λ () (@counters . <~ . append-counter))))
(list-view
@counters
#:key car
(λ (k @entry)
(counter
(@entry . ~> . cdr)
(λ (proc)
(@counters . <~ . (λ (counts) (update-count counts k proc))))))))))
}|

Here the @racket[|@counters|] observable holds a list of pairs where
Expand All @@ -151,8 +161,23 @@ individual counter by passing in a derived observable to its
@racket[make-view] argument.

@section{More}

@(define repo-link
(link "https://github.com/Bogdanp/racket-gui-easy" "Git repository"))

For more examples, see the "examples" directory in the @|repo-link|.

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


@(bibliography
@(bib-entry #:key "knoblepopa23"
#:title "Functional Shell and Reusable Components for Easy GUIs"
#:author "D. B. Knoble and B. Popa"
#:location "FUNARCH 2023: Proceedings of the 1st ACM SIGPLAN International Workshop on
Functional Software Architecture"
#:date "31 August 2023 "
#:url "https://arxiv.org/abs/2308.16024")
@(bib-entry #:key "repo-link"
#:title "Declarative GUIs in Racket"
#:author "B. Popa"
#:location "Online"
#:date "Accessed: 28 July 2025"
#:url "https://github.com/Bogdanp/racket-gui-easy"))
30 changes: 27 additions & 3 deletions gui-easy/gui/easy/scribblings/reference.scrbl
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,17 @@
racket/gui/easy/operator
(prefix-in gui: racket/gui)))

; Example links cited within this section
@(define example-link-tabs
(link "https://github.com/Bogdanp/racket-gui-easy/blob/master/examples/tabs.rkt"
@filepath{examples/tabs.rkt}))
@(define example-link-list
(link "https://github.com/Bogdanp/racket-gui-easy/blob/master/examples/list.rkt"
@filepath{examples/list.rkt}))




@title{Reference}
@defmodule[racket/gui/easy]

Expand Down Expand Up @@ -64,6 +75,19 @@

@section{Views}

@deftech{View}s are functions that return a @racket[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 @tech{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 @racketmodname[racket/gui] widgets are already wrapped by GUI Easy, but programmers can
implement the @racket[view<%>] interface themselves in order to integrate arbitrary widgets, such as
those from 3rd-party packages in the Racket ecosystem, into their projects.

@subsection[#:tag "windows&dialogs"]{Windows & Dialogs}

@defproc[(window [#:title title (maybe-obs/c string?) "Untitled"]
Expand Down Expand Up @@ -285,7 +309,7 @@
selection changes to an adjacent tab). When tabs are reordered, the
choices provided to the action represent the new tab order.

See @filepath{examples/tabs.rkt} for an example.
See @|example-link-tabs| for an example.

@history[
#:changed "0.3" @elem{Added the @racket[#:choice=?] argument.}
Expand Down Expand Up @@ -354,7 +378,7 @@
procedure must return a unique value for each entry in the list, as
compared using @racket[equal?].

See @filepath{examples/list.rkt} for an example.
See @|example-link-list| for an example.
}

@defproc[(observable-view [data (obs/c any/c)]
Expand Down Expand Up @@ -818,7 +842,7 @@

@section{Observables}

@deftech{Observables} are containers for values that may change over
@deftech{Observable}s are containers for values that may change over
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I missed this this morning, but deftech works fine with plurals (so you can @deftech{Views} and then use both @tech{view} and @tech{views} to refer to something). I'll fix before merging.

time. Their changes may be observed by arbitrary functions.

@; Require the private module to avoid requiring racket/gui/base.
Expand Down