89 lines
3.2 KiB
Org Mode
89 lines
3.2 KiB
Org Mode
|
|
#+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.
|