#+TITLE: bjogui Desktop programs in Bjolang, written as a model, an ~update~ and a ~view~, and shown with [[https://avaloniaui.net][Avalonia]]. #+BEGIN_SRC scheme (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")) #+END_SRC 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 #+BEGIN_SRC sh bjo run tests/app.bjo # prints "bjogui ok" bjo run examples/counter.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.