A program is a model, an update and a view. A view is a Bjolang value; an event finds its message in the view its control was last given, and the next view is compared with the last so only what differs is changed. Controls are made with plain constructors and changed with SetValue, so nothing is subclassed, and a program never names an Avalonia type. Text, button, text input, checkbox, column and row. tests/app.bjo drives an app through its real controls with no window. 🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8) Co-Authored-By: eca-agent <git@eca.dev>
3.2 KiB
bjogui
Desktop programs in Bjolang, written as a model, an update and a view, and
shown with Avalonia.
(import (bjogui view))
(import (bjogui app))
(type (: Msg (Union Inc Dec)))
(: update (-> Msg int int))
(defun (update msg n)
(match msg (Inc (+ n 1)) (Dec (- n 1))))
(: view (-> int (View Msg)))
(defun (view n)
(row #:spacing 8.0 #:margin 16.0
(button "-" Dec)
(text #"${n}")
(button "+" Inc)))
(defun (main)
(run-app 0 update view #:title "Counter"))
A program never names an Avalonia type. A view is a Bjolang value, an event 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
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.
Children are matched by their place in the list. A child whose kind changes gets a new control.
A control that is changed raises events of its own: a text input whose text is set says that its text changed. Those events come while the controls are being changed, and they are dropped, so only the user's events are messages.
The views: (bjogui view)
| Constructor | Control | Sends |
|---|---|---|
(text s) |
TextBlock |
nothing |
(button label msg) |
Button |
msg on a click |
(text-input value on-input) |
TextBox |
(on-input text) on a change |
(checkbox label checked? on-toggle) |
CheckBox |
(on-toggle checked) |
(column child ...), (row child ...) |
StackPanel |
nothing |
They take keywords where they apply: #:size (the font size of a text),
#:width, #:height, #:margin, #:padding, #:spacing (between the
children of a column or a row), #:enabled and #:placeholder. A size of
zero, the default, leaves the size to the control.
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 giveson-rootthe control to show.app-dispatch!sends a message,app-modelreads the model, andapp-control-atfinds a control by its path of child indices.tests/app.bjouses these to drive an app through its real controls.
Requirements
- Avalonia 11.3, which
bjorestores from NuGet. On Linux it needs X11 or XWayland. - A Bjolang with
(std interop), which makes the delegates that Avalonia's events take.
Tests
bjo run tests/app.bjo # prints "bjogui ok"
bjo run examples/counter.bjo
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.