No description
Find a file
Linus Björnstam 2f4c99ba62 Views, events as messages, and a patch, over plain Avalonia
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>
2026-10-25 16:18:09 +01:00
examples Views, events as messages, and a patch, over plain Avalonia 2026-10-25 16:18:09 +01:00
src Views, events as messages, and a patch, over plain Avalonia 2026-10-25 16:18:09 +01:00
tests Views, events as messages, and a patch, over plain Avalonia 2026-10-25 16:18:09 +01:00
.gitignore Views, events as messages, and a patch, over plain Avalonia 2026-10-25 16:18:09 +01:00
LICENSE Views, events as messages, and a patch, over plain Avalonia 2026-10-25 16:18:09 +01:00
manifest.bjodat Views, events as messages, and a patch, over plain Avalonia 2026-10-25 16:18:09 +01:00
packages.lock.json Views, events as messages, and a patch, over plain Avalonia 2026-10-25 16:18:09 +01:00
Readme.org Views, events as messages, and a patch, over plain Avalonia 2026-10-25 16:18:09 +01:00

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 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 its real controls.

Requirements

  • Avalonia 11.3, which bjo restores 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.