Commands and subscriptions, and gui-update
gui-update is a plain function, so it cannot wait. It returns the work to do as values. A command is an (Event %m), synced in a fiber of its own, whose answer is the next message. A subscription is a source of messages that the model asks for: after each update the app starts the new ones and cancels the ones that are gone, by key. every sends a message on a timer, and subscription takes a source of one's own. The fibers belong to the app's scope and post their messages to the UI thread, so gui-update and view only run there. Closing the window cancels them. The prelude's path macros already use the name update, hence gui-update. 🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8) Co-Authored-By: eca-agent <git@eca.dev>
This commit is contained in:
parent
2f4c99ba62
commit
4f48282314
6 changed files with 460 additions and 67 deletions
76
Readme.org
76
Readme.org
|
|
@ -1,7 +1,7 @@
|
|||
#+TITLE: bjogui
|
||||
|
||||
Desktop programs in Bjolang, written as a model, an ~update~ and a ~view~, and
|
||||
shown with [[https://avaloniaui.net][Avalonia]].
|
||||
Desktop programs in Bjolang, written as a model, a ~gui-update~ and a ~view~,
|
||||
and shown with [[https://avaloniaui.net][Avalonia]].
|
||||
|
||||
#+BEGIN_SRC scheme
|
||||
(import (bjogui view))
|
||||
|
|
@ -9,9 +9,9 @@ shown with [[https://avaloniaui.net][Avalonia]].
|
|||
|
||||
(type (: Msg (Union Inc Dec)))
|
||||
|
||||
(: update (-> Msg int int))
|
||||
(defun (update msg n)
|
||||
(match msg (Inc (+ n 1)) (Dec (- n 1))))
|
||||
(: gui-update (-> Msg int (Tuple int (List (Event Msg)))))
|
||||
(defun (gui-update msg n)
|
||||
(next (match msg (Inc (+ n 1)) (Dec (- n 1)))))
|
||||
|
||||
(: view (-> int (View Msg)))
|
||||
(defun (view n)
|
||||
|
|
@ -21,7 +21,7 @@ shown with [[https://avaloniaui.net][Avalonia]].
|
|||
(button "+" Inc)))
|
||||
|
||||
(defun (main)
|
||||
(run-app 0 update view #:title "Counter"))
|
||||
(run-app 0 gui-update view #:title "Counter"))
|
||||
#+END_SRC
|
||||
|
||||
A program never names an Avalonia type. A view is a Bjolang value, an event
|
||||
|
|
@ -30,7 +30,7 @@ is a message, and the controls are made and changed by the package.
|
|||
* How it works
|
||||
|
||||
An event finds its message in the view its control was last given.
|
||||
~update~ makes the next model from the message, and ~view~ makes the next
|
||||
~gui-update~ makes the next model from the message, and ~view~ makes the next
|
||||
view. The next view is compared with the last one, control by control, and
|
||||
only what differs is changed. So a control keeps its focus, its caret and its
|
||||
scroll position when the model changes.
|
||||
|
|
@ -57,14 +57,59 @@ They take keywords where they apply: ~#:size~ (the font size of a text),
|
|||
children of a column or a row), ~#:enabled~ and ~#:placeholder~. A size of
|
||||
zero, the default, leaves the size to the control.
|
||||
|
||||
* Work: commands and subscriptions
|
||||
|
||||
~gui-update~ is a plain function, so it cannot wait, and the compiler says so
|
||||
if it tries. Work is a value it returns, and the app runs it.
|
||||
|
||||
A *command* is an ~(Event %m)~: an event whose answer is a message. ~(next m
|
||||
command ...)~ is the next model and the commands to start; ~(next m)~ is the
|
||||
model alone. The app syncs each command in a fiber of its own and gives its
|
||||
message to ~gui-update~. Any event will do: a ~timeout~, a ~blocking~ call, a
|
||||
channel, a ~choose~ of several.
|
||||
|
||||
#+BEGIN_SRC scheme
|
||||
(Load (next (record-set m (loading #t))
|
||||
(wrap (blocking (fun () (file-read-text path))) Loaded)))
|
||||
#+END_SRC
|
||||
|
||||
A *subscription* is a source of messages that runs while the model asks for
|
||||
it. ~subscriptions~ gives the list for a model. After each update the app
|
||||
starts those that are new and cancels those that are gone; the others keep
|
||||
running. A subscription is known by its key.
|
||||
|
||||
- ~(every duration msg)~ sends ~msg~ each time ~duration~ has passed.
|
||||
- ~(subscription key next-event)~ is a source of your own: ~next-event~ gives
|
||||
the event of the next message, and is called again after each one.
|
||||
~(subscription "jobs" (fun () (wrap (chan-recv jobs) Got)))~ sends what
|
||||
comes on a channel.
|
||||
|
||||
#+BEGIN_SRC scheme
|
||||
(: subscriptions (-> Model (List (Sub Msg))))
|
||||
(defun (subscriptions m)
|
||||
(if (.running m) (list (every (milliseconds 100) Tick)) '()))
|
||||
#+END_SRC
|
||||
|
||||
The fibers belong to the app. They post their messages to the UI thread, so
|
||||
~gui-update~ and ~view~ always run there. When the window is closed they are
|
||||
cancelled, and the program ends.
|
||||
|
||||
* Running: (bjogui app)
|
||||
|
||||
- ~(run-app init update view #:title #:width #:height)~ opens a window and
|
||||
runs until it is closed.
|
||||
- ~(start-app init update view on-root)~ makes the controls with no window,
|
||||
and gives ~on-root~ the control to show. ~app-dispatch!~ sends a message,
|
||||
~app-model~ reads the model, and ~app-control-at~ finds a control by its
|
||||
path of child indices. ~tests/app.bjo~ uses these to drive an app through
|
||||
- ~(run-app init gui-update view #:subscriptions #:title #:width #:height)~
|
||||
opens a window and runs until it is closed.
|
||||
- ~(start-app init gui-update view on-root #:subscriptions)~ makes the controls
|
||||
with no window, and gives ~on-root~ the control to show. Its fibers belong
|
||||
to the scope it is started in.
|
||||
- ~app-dispatch!~ gives a message on the UI thread, and ~app-send!~ from any
|
||||
thread.
|
||||
- ~app-run-pending!~ runs what fibers have posted, where no loop of
|
||||
Avalonia's does.
|
||||
- ~app-stop!~ cancels the subscriptions and drops later messages.
|
||||
- ~app-model~ reads the model, and ~app-control-at~ finds a control by its
|
||||
path of child indices.
|
||||
|
||||
~tests/app.bjo~ and ~tests/effects.bjo~ use these to drive an app through
|
||||
its real controls.
|
||||
|
||||
* Requirements
|
||||
|
|
@ -77,12 +122,13 @@ zero, the default, leaves the size to the control.
|
|||
* Tests
|
||||
|
||||
#+BEGIN_SRC sh
|
||||
bjo run tests/app.bjo # prints "bjogui ok"
|
||||
bjo run tests/app.bjo # prints "bjogui ok"
|
||||
bjo run tests/effects.bjo # prints "bjogui effects ok"
|
||||
bjo run examples/counter.bjo
|
||||
bjo run examples/stopwatch.bjo
|
||||
#+END_SRC
|
||||
|
||||
* Not yet
|
||||
|
||||
- Keyed children, for lists whose items move.
|
||||
- Messages from fibers and timers, for work that is not a click.
|
||||
- More controls: lists, grids, images, scroll views, menus.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue