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
|
|
|
;; This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
|
|
|
|
|
|
;; bjogui/src/view.bjo — the module (bjogui view).
|
|
|
|
|
;;
|
|
|
|
|
;; A view is a value: what the window shows for one model. `view` functions
|
|
|
|
|
;; build it with the constructors here, and (bjogui app) makes the controls
|
|
|
|
|
;; for it and changes them when the next view differs. Nothing here knows
|
|
|
|
|
;; about Avalonia, so a view can be built and compared in a test with no
|
|
|
|
|
;; window.
|
|
|
|
|
;;
|
|
|
|
|
;; An event is a message, or a function that makes one from what the control
|
|
|
|
|
;; holds. The app gives the message to `update`.
|
|
|
|
|
|
|
|
|
|
(export
|
|
|
|
|
;; Types.
|
|
|
|
|
View Kind Prop On
|
|
|
|
|
|
|
|
|
|
;; Constructors.
|
Keyed children, and eight more controls
A child is now known by its key, or by its place among the children with no
key. One whose identity was there before keeps its control, and the
controls are put in the new order by moving only those out of place. So a
list that is sorted, or has an item put in front, keeps its controls.
Text areas, sliders, progress bars, dropdowns, images, grids with cells and
spans, and scroll views. A grid's sizes are .NET properties and not
Avalonia's, so they are set by a setter of their own. A text is centred in
its height, so a label lines up with the control beside it.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 17:13:35 +01:00
|
|
|
text button text-input text-area checkbox slider progress-bar dropdown image
|
2026-10-25 17:21:40 +01:00
|
|
|
list-box tabs tab
|
|
|
|
|
column row grid scroll border
|
Keyed children, and eight more controls
A child is now known by its key, or by its place among the children with no
key. One whose identity was there before keeps its control, and the
controls are put in the new order by moving only those out of place. So a
list that is sorted, or has an item put in front, keeps its controls.
Text areas, sliders, progress bars, dropdowns, images, grids with cells and
spans, and scroll views. A grid's sizes are .NET properties and not
Avalonia's, so they are set by a setter of their own. A text is centred in
its height, so a label lines up with the control beside it.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 17:13:35 +01:00
|
|
|
;; Wrappers.
|
2026-10-25 17:21:40 +01:00
|
|
|
keyed at paint
|
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
|
|
|
|
|
|
|
|
;; Reading one.
|
Keyed children, and eight more controls
A child is now known by its key, or by its place among the children with no
key. One whose identity was there before keeps its control, and the
controls are put in the new order by moving only those out of place. So a
list that is sorted, or has an item put in front, keeps its controls.
Text areas, sliders, progress bars, dropdowns, images, grids with cells and
spans, and scroll views. A grid's sizes are .NET properties and not
Avalonia's, so they are set by a setter of their own. A text is centred in
its height, so a label lines up with the control beside it.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 17:13:35 +01:00
|
|
|
view-kind view-props view-events view-children view-key)
|
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
|
|
|
|
|
|
|
|
(import (std prelude))
|
|
|
|
|
|
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
|
;; The types
|
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
;; What control a view is.
|
Keyed children, and eight more controls
A child is now known by its key, or by its place among the children with no
key. One whose identity was there before keeps its control, and the
controls are put in the new order by moving only those out of place. So a
list that is sorted, or has an item put in front, keeps its controls.
Text areas, sliders, progress bars, dropdowns, images, grids with cells and
spans, and scroll views. A grid's sizes are .NET properties and not
Avalonia's, so they are set by a setter of their own. A text is centred in
its height, so a label lines up with the control beside it.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 17:13:35 +01:00
|
|
|
(type (: Kind (Union TextKind ButtonKind TextInputKind TextAreaKind CheckBoxKind
|
|
|
|
|
SliderKind ProgressKind DropdownKind ImageKind
|
2026-10-25 17:21:40 +01:00
|
|
|
ListBoxKind TabsKind TabKind
|
|
|
|
|
ColumnKind RowKind GridKind ScrollKind BorderKind)))
|
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
|
|
|
|
|
|
|
|
;; A property of a control. A kind takes only some of them; the app ignores
|
|
|
|
|
;; the others.
|
|
|
|
|
(type (: Prop (Union (: Label string) ; the text of a text, a button or a checkbox
|
|
|
|
|
(: Value string) ; the text in a text input
|
|
|
|
|
(: Placeholder string) ; shown in an empty text input
|
|
|
|
|
(: Checked bool)
|
|
|
|
|
(: Enabled bool)
|
|
|
|
|
(: FontSize double)
|
|
|
|
|
(: Width double)
|
|
|
|
|
(: Height double)
|
|
|
|
|
(: Margin double)
|
|
|
|
|
(: Padding double)
|
Keyed children, and eight more controls
A child is now known by its key, or by its place among the children with no
key. One whose identity was there before keeps its control, and the
controls are put in the new order by moving only those out of place. So a
list that is sorted, or has an item put in front, keeps its controls.
Text areas, sliders, progress bars, dropdowns, images, grids with cells and
spans, and scroll views. A grid's sizes are .NET properties and not
Avalonia's, so they are set by a setter of their own. A text is centred in
its height, so a label lines up with the control beside it.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 17:13:35 +01:00
|
|
|
(: Spacing double)
|
|
|
|
|
(: Wrap bool) ; a text breaks its lines at the width
|
|
|
|
|
(: Minimum double) ; of a slider or a progress bar
|
|
|
|
|
(: Maximum double)
|
|
|
|
|
(: Amount double) ; where a slider or a progress bar is
|
|
|
|
|
(: Step double) ; what an arrow key moves a slider by
|
|
|
|
|
(: Items (Vec string)) ; what a dropdown offers
|
2026-10-25 17:21:40 +01:00
|
|
|
(: Selected int) ; which item is chosen; -1 for none
|
Keyed children, and eight more controls
A child is now known by its key, or by its place among the children with no
key. One whose identity was there before keeps its control, and the
controls are put in the new order by moving only those out of place. So a
list that is sorted, or has an item put in front, keeps its controls.
Text areas, sliders, progress bars, dropdowns, images, grids with cells and
spans, and scroll views. A grid's sizes are .NET properties and not
Avalonia's, so they are set by a setter of their own. A text is centred in
its height, so a label lines up with the control beside it.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 17:13:35 +01:00
|
|
|
(: Source string) ; the file an image shows
|
|
|
|
|
(: Columns string) ; a grid's columns, as "Auto,*,2*"
|
|
|
|
|
(: Rows string)
|
|
|
|
|
(: Row int) ; where a view is in its grid
|
|
|
|
|
(: Column int)
|
|
|
|
|
(: RowSpan int)
|
2026-10-25 17:21:40 +01:00
|
|
|
(: ColumnSpan int)
|
|
|
|
|
(: Background string) ; a colour: "red", "#08f", "#80ff8800"
|
|
|
|
|
(: Foreground string) ; the colour of the text
|
|
|
|
|
(: BorderColour string)
|
|
|
|
|
(: BorderWidth double)
|
|
|
|
|
(: Radius double)))) ; how round a border's corners are
|
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
|
|
|
|
|
|
|
|
;; An event, and the message it sends.
|
|
|
|
|
(type (: (On %m) (Union (: Click %m)
|
|
|
|
|
(: Input (-> string %m))
|
Keyed children, and eight more controls
A child is now known by its key, or by its place among the children with no
key. One whose identity was there before keeps its control, and the
controls are put in the new order by moving only those out of place. So a
list that is sorted, or has an item put in front, keeps its controls.
Text areas, sliders, progress bars, dropdowns, images, grids with cells and
spans, and scroll views. A grid's sizes are .NET properties and not
Avalonia's, so they are set by a setter of their own. A text is centred in
its height, so a label lines up with the control beside it.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 17:13:35 +01:00
|
|
|
(: Toggle (-> bool %m))
|
|
|
|
|
(: Slide (-> double %m))
|
|
|
|
|
(: Select (-> int %m)))))
|
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
|
|
|
|
|
|
|
|
;; One control and what is in it.
|
Keyed children, and eight more controls
A child is now known by its key, or by its place among the children with no
key. One whose identity was there before keeps its control, and the
controls are put in the new order by moving only those out of place. So a
list that is sorted, or has an item put in front, keeps its controls.
Text areas, sliders, progress bars, dropdowns, images, grids with cells and
spans, and scroll views. A grid's sizes are .NET properties and not
Avalonia's, so they are set by a setter of their own. A text is centred in
its height, so a label lines up with the control beside it.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 17:13:35 +01:00
|
|
|
;;
|
|
|
|
|
;; `key` says which child of its parent a view is. Children with keys are
|
|
|
|
|
;; matched by key when the next view is compared with the last one, so a
|
|
|
|
|
;; child that moves keeps its control. Children without one are matched by
|
|
|
|
|
;; their place among the others without one.
|
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
|
|
|
(type (: (View %m) (Record (: kind Kind)
|
|
|
|
|
(: props (Vec Prop))
|
|
|
|
|
(: events (Vec (On %m)))
|
Keyed children, and eight more controls
A child is now known by its key, or by its place among the children with no
key. One whose identity was there before keeps its control, and the
controls are put in the new order by moving only those out of place. So a
list that is sorted, or has an item put in front, keeps its controls.
Text areas, sliders, progress bars, dropdowns, images, grids with cells and
spans, and scroll views. A grid's sizes are .NET properties and not
Avalonia's, so they are set by a setter of their own. A text is centred in
its height, so a label lines up with the control beside it.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 17:13:35 +01:00
|
|
|
(: children (Vec (View %m)))
|
|
|
|
|
(: key (Option string)))))
|
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
|
|
|
|
|
|
|
|
(: view-kind (-> (View %m) Kind))
|
|
|
|
|
(defun (view-kind v) (.kind v))
|
|
|
|
|
|
|
|
|
|
(: view-props (-> (View %m) (Vec Prop)))
|
|
|
|
|
(defun (view-props v) (.props v))
|
|
|
|
|
|
|
|
|
|
(: view-events (-> (View %m) (Vec (On %m))))
|
|
|
|
|
(defun (view-events v) (.events v))
|
|
|
|
|
|
|
|
|
|
(: view-children (-> (View %m) (Vec (View %m))))
|
|
|
|
|
(defun (view-children v) (.children v))
|
|
|
|
|
|
Keyed children, and eight more controls
A child is now known by its key, or by its place among the children with no
key. One whose identity was there before keeps its control, and the
controls are put in the new order by moving only those out of place. So a
list that is sorted, or has an item put in front, keeps its controls.
Text areas, sliders, progress bars, dropdowns, images, grids with cells and
spans, and scroll views. A grid's sizes are .NET properties and not
Avalonia's, so they are set by a setter of their own. A text is centred in
its height, so a label lines up with the control beside it.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 17:13:35 +01:00
|
|
|
(: view-key (-> (View %m) (Option string)))
|
|
|
|
|
(defun (view-key v) (.key v))
|
|
|
|
|
|
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
|
;; Wrappers
|
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
;; The view, with a key that says which child it is. Give each item of a list
|
|
|
|
|
;; that can change order a key of its own, such as its id.
|
|
|
|
|
(: keyed (-> string (View %m) (View %m)))
|
|
|
|
|
(defun (keyed key v)
|
|
|
|
|
(record-set v (key (Some key))))
|
|
|
|
|
|
2026-10-25 17:21:40 +01:00
|
|
|
;; The view, in colours. A colour is a name, such as "red", or "#rgb",
|
|
|
|
|
;; "#rrggbb" or "#aarrggbb". The empty string, the default, leaves the colour
|
|
|
|
|
;; to the theme, and so does a colour that cannot be read.
|
|
|
|
|
(: paint (-> (View %m) (#:colour string) (#:background string) (View %m)))
|
|
|
|
|
(defun (paint v #:colour "" #:background "")
|
|
|
|
|
(defun (with props make c) (if (string-empty? c) props (vec-add props (make c))))
|
|
|
|
|
(record-set v (props (with (with (.props v) Foreground colour) Background background))))
|
|
|
|
|
|
Keyed children, and eight more controls
A child is now known by its key, or by its place among the children with no
key. One whose identity was there before keeps its control, and the
controls are put in the new order by moving only those out of place. So a
list that is sorted, or has an item put in front, keeps its controls.
Text areas, sliders, progress bars, dropdowns, images, grids with cells and
spans, and scroll views. A grid's sizes are .NET properties and not
Avalonia's, so they are set by a setter of their own. A text is centred in
its height, so a label lines up with the control beside it.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 17:13:35 +01:00
|
|
|
;; The view, in a cell of its grid. Rows and columns count from 0.
|
|
|
|
|
(: at (-> int int (View %m) (#:row-span int) (#:column-span int) (View %m)))
|
|
|
|
|
(defun (at row column v #:row-span 1 #:column-span 1)
|
|
|
|
|
(record-set v (props (vec-add (vec-add (vec-add (vec-add (.props v) (Row row)) (Column column))
|
|
|
|
|
(RowSpan row-span))
|
|
|
|
|
(ColumnSpan column-span)))))
|
|
|
|
|
|
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
|
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
|
;; Constructors
|
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
;; A size of zero or less means: the size the control asks for. The
|
|
|
|
|
;; constructors leave such a size out of the props, so the control keeps its
|
|
|
|
|
;; own.
|
|
|
|
|
(: (-> (Vec Prop) (-> double Prop) double (Vec Prop)))
|
|
|
|
|
(defun (with-size props make size)
|
|
|
|
|
(if (> size 0.0) (vec-add props (make size)) props))
|
|
|
|
|
|
|
|
|
|
(: (-> (Vec Prop) double double double double (Vec Prop)))
|
|
|
|
|
(defun (with-layout props width height margin padding)
|
|
|
|
|
(with-size (with-size (with-size (with-size props Width width) Height height)
|
|
|
|
|
Margin margin)
|
|
|
|
|
Padding padding))
|
|
|
|
|
|
|
|
|
|
(: (-> Kind (Vec Prop) (Vec (On %m)) (View %m)))
|
|
|
|
|
(defun (leaf kind props events)
|
Keyed children, and eight more controls
A child is now known by its key, or by its place among the children with no
key. One whose identity was there before keeps its control, and the
controls are put in the new order by moving only those out of place. So a
list that is sorted, or has an item put in front, keeps its controls.
Text areas, sliders, progress bars, dropdowns, images, grids with cells and
spans, and scroll views. A grid's sizes are .NET properties and not
Avalonia's, so they are set by a setter of their own. A text is centred in
its height, so a label lines up with the control beside it.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 17:13:35 +01:00
|
|
|
(View (kind kind) (props props) (events events) (children []) (key None)))
|
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
|
|
|
|
|
|
|
|
;; A text.
|
Keyed children, and eight more controls
A child is now known by its key, or by its place among the children with no
key. One whose identity was there before keeps its control, and the
controls are put in the new order by moving only those out of place. So a
list that is sorted, or has an item put in front, keeps its controls.
Text areas, sliders, progress bars, dropdowns, images, grids with cells and
spans, and scroll views. A grid's sizes are .NET properties and not
Avalonia's, so they are set by a setter of their own. A text is centred in
its height, so a label lines up with the control beside it.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 17:13:35 +01:00
|
|
|
(: (-> string (#:size double) (#:wrap bool) (#:width double) (#:height double)
|
|
|
|
|
(#:margin double)
|
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
|
|
|
(View %m)))
|
Keyed children, and eight more controls
A child is now known by its key, or by its place among the children with no
key. One whose identity was there before keeps its control, and the
controls are put in the new order by moving only those out of place. So a
list that is sorted, or has an item put in front, keeps its controls.
Text areas, sliders, progress bars, dropdowns, images, grids with cells and
spans, and scroll views. A grid's sizes are .NET properties and not
Avalonia's, so they are set by a setter of their own. A text is centred in
its height, so a label lines up with the control beside it.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 17:13:35 +01:00
|
|
|
(defun (text s #:size 0.0 #:wrap #f #:width 0.0 #:height 0.0 #:margin 0.0)
|
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
|
|
|
(leaf TextKind
|
Keyed children, and eight more controls
A child is now known by its key, or by its place among the children with no
key. One whose identity was there before keeps its control, and the
controls are put in the new order by moving only those out of place. So a
list that is sorted, or has an item put in front, keeps its controls.
Text areas, sliders, progress bars, dropdowns, images, grids with cells and
spans, and scroll views. A grid's sizes are .NET properties and not
Avalonia's, so they are set by a setter of their own. A text is centred in
its height, so a label lines up with the control beside it.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 17:13:35 +01:00
|
|
|
(with-layout (with-size [(Label s) (Wrap wrap)] FontSize size) width height margin 0.0)
|
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
|
|
|
[]))
|
|
|
|
|
|
|
|
|
|
;; A button that sends `msg` when it is clicked.
|
|
|
|
|
(: (-> string %m (#:enabled bool) (#:width double) (#:height double)
|
|
|
|
|
(#:margin double) (#:padding double)
|
|
|
|
|
(View %m)))
|
|
|
|
|
(defun (button label msg #:enabled #t #:width 0.0 #:height 0.0 #:margin 0.0 #:padding 0.0)
|
|
|
|
|
(leaf ButtonKind
|
|
|
|
|
(with-layout [(Label label) (Enabled enabled)] width height margin padding)
|
|
|
|
|
[(Click msg)]))
|
|
|
|
|
|
|
|
|
|
;; A one-line text input that holds `value`. Each change sends `(on-input
|
|
|
|
|
;; text)`, with the whole text.
|
|
|
|
|
(: (-> string (-> string %m) (#:placeholder string) (#:enabled bool)
|
|
|
|
|
(#:width double) (#:margin double)
|
|
|
|
|
(View %m)))
|
|
|
|
|
(defun (text-input value on-input #:placeholder "" #:enabled #t #:width 0.0 #:margin 0.0)
|
|
|
|
|
(leaf TextInputKind
|
|
|
|
|
(with-layout [(Value value) (Placeholder placeholder) (Enabled enabled)]
|
|
|
|
|
width 0.0 margin 0.0)
|
|
|
|
|
[(Input on-input)]))
|
|
|
|
|
|
Keyed children, and eight more controls
A child is now known by its key, or by its place among the children with no
key. One whose identity was there before keeps its control, and the
controls are put in the new order by moving only those out of place. So a
list that is sorted, or has an item put in front, keeps its controls.
Text areas, sliders, progress bars, dropdowns, images, grids with cells and
spans, and scroll views. A grid's sizes are .NET properties and not
Avalonia's, so they are set by a setter of their own. A text is centred in
its height, so a label lines up with the control beside it.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 17:13:35 +01:00
|
|
|
;; A text input of several lines, that breaks its lines at its width. Each
|
|
|
|
|
;; change sends `(on-input text)`, with the whole text.
|
|
|
|
|
(: (-> string (-> string %m) (#:placeholder string) (#:enabled bool)
|
|
|
|
|
(#:width double) (#:height double) (#:margin double)
|
|
|
|
|
(View %m)))
|
|
|
|
|
(defun (text-area value on-input #:placeholder "" #:enabled #t #:width 0.0 #:height 0.0
|
|
|
|
|
#:margin 0.0)
|
|
|
|
|
(leaf TextAreaKind
|
|
|
|
|
(with-layout [(Value value) (Placeholder placeholder) (Enabled enabled)]
|
|
|
|
|
width height margin 0.0)
|
|
|
|
|
[(Input on-input)]))
|
|
|
|
|
|
|
|
|
|
;; A slider at `value`, from `minimum` to `maximum`. A move sends `(on-change
|
|
|
|
|
;; value)`.
|
|
|
|
|
(: (-> double double double (-> double %m) (#:step double) (#:enabled bool)
|
|
|
|
|
(#:width double) (#:margin double)
|
|
|
|
|
(View %m)))
|
|
|
|
|
(defun (slider value minimum maximum on-change #:step 1.0 #:enabled #t #:width 0.0
|
|
|
|
|
#:margin 0.0)
|
|
|
|
|
;; The bounds come before the value, because a value outside the bounds
|
|
|
|
|
;; that are there is moved inside them.
|
|
|
|
|
(leaf SliderKind
|
|
|
|
|
(with-layout [(Minimum minimum) (Maximum maximum) (Amount value) (Step step)
|
|
|
|
|
(Enabled enabled)]
|
|
|
|
|
width 0.0 margin 0.0)
|
|
|
|
|
[(Slide on-change)]))
|
|
|
|
|
|
|
|
|
|
;; A bar that is `value` of the way from 0 to `#:maximum`, 1 unless told
|
|
|
|
|
;; otherwise.
|
|
|
|
|
(: (-> double (#:maximum double) (#:width double) (#:margin double) (View %m)))
|
|
|
|
|
(defun (progress-bar value #:maximum 1.0 #:width 0.0 #:margin 0.0)
|
|
|
|
|
(leaf ProgressKind
|
|
|
|
|
(with-layout [(Minimum 0.0) (Maximum maximum) (Amount value)] width 0.0 margin 0.0)
|
|
|
|
|
[]))
|
|
|
|
|
|
|
|
|
|
;; A list to choose one of `options` from, with the one at `selected` chosen,
|
|
|
|
|
;; or none for -1. A choice sends `(on-select index)`.
|
|
|
|
|
(: (-> (Vec string) int (-> int %m) (#:enabled bool) (#:width double) (#:margin double)
|
|
|
|
|
(View %m)))
|
|
|
|
|
(defun (dropdown options selected on-select #:enabled #t #:width 0.0 #:margin 0.0)
|
|
|
|
|
;; The items come before the choice, because the choice is an index into
|
|
|
|
|
;; them.
|
|
|
|
|
(leaf DropdownKind
|
|
|
|
|
(with-layout [(Items options) (Selected selected) (Enabled enabled)]
|
|
|
|
|
width 0.0 margin 0.0)
|
|
|
|
|
[(Select on-select)]))
|
|
|
|
|
|
|
|
|
|
;; The picture in the file at `path`. A file that cannot be read shows
|
|
|
|
|
;; nothing.
|
|
|
|
|
(: (-> string (#:width double) (#:height double) (#:margin double) (View %m)))
|
|
|
|
|
(defun (image path #:width 0.0 #:height 0.0 #:margin 0.0)
|
|
|
|
|
(leaf ImageKind (with-layout [(Source path)] width height margin 0.0) []))
|
|
|
|
|
|
2026-10-25 17:21:40 +01:00
|
|
|
;; A list of views to choose one of, with the one at `selected` chosen, or
|
|
|
|
|
;; none for -1. A choice sends `(on-select index)`. Give the items keys if
|
|
|
|
|
;; the list can change order.
|
|
|
|
|
(: (-> (Vec (View %m)) int (-> int %m) (#:width double) (#:height double) (#:margin double)
|
|
|
|
|
(View %m)))
|
|
|
|
|
(defun (list-box items selected on-select #:width 0.0 #:height 0.0 #:margin 0.0)
|
|
|
|
|
(View (kind ListBoxKind)
|
|
|
|
|
(props (with-layout [(Selected selected)] width height margin 0.0))
|
|
|
|
|
(events [(Select on-select)])
|
|
|
|
|
(children items)
|
|
|
|
|
(key None)))
|
|
|
|
|
|
|
|
|
|
;; Tabs, with the one at `selected` shown. A click on another tab sends
|
|
|
|
|
;; `(on-select index)`. Each tab is made by `tab`.
|
|
|
|
|
(: (-> int (-> int %m) (#:margin double) #:rest (View %m) (View %m)))
|
|
|
|
|
(defun (tabs selected on-select #:margin 0.0 #:rest tabs)
|
|
|
|
|
(View (kind TabsKind)
|
|
|
|
|
(props (with-layout [(Selected selected)] 0.0 0.0 margin 0.0))
|
|
|
|
|
(events [(Select on-select)])
|
|
|
|
|
(children (array->vec tabs))
|
|
|
|
|
(key None)))
|
|
|
|
|
|
|
|
|
|
;; A tab of `tabs`: its header, and what it shows when it is chosen.
|
|
|
|
|
(: (-> string (View %m) (View %m)))
|
|
|
|
|
(defun (tab header content)
|
|
|
|
|
(View (kind TabKind) (props [(Label header)]) (events []) (children [content]) (key None)))
|
|
|
|
|
|
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
|
|
|
;; A checkbox that is `checked?`. A click sends `(on-toggle checked)`, with
|
|
|
|
|
;; the new state.
|
|
|
|
|
(: (-> string bool (-> bool %m) (#:enabled bool) (#:margin double) (View %m)))
|
|
|
|
|
(defun (checkbox label checked? on-toggle #:enabled #t #:margin 0.0)
|
|
|
|
|
(leaf CheckBoxKind
|
|
|
|
|
(with-layout [(Label label) (Checked checked?) (Enabled enabled)] 0.0 0.0 margin 0.0)
|
|
|
|
|
[(Toggle on-toggle)]))
|
|
|
|
|
|
Keyed children, and eight more controls
A child is now known by its key, or by its place among the children with no
key. One whose identity was there before keeps its control, and the
controls are put in the new order by moving only those out of place. So a
list that is sorted, or has an item put in front, keeps its controls.
Text areas, sliders, progress bars, dropdowns, images, grids with cells and
spans, and scroll views. A grid's sizes are .NET properties and not
Avalonia's, so they are set by a setter of their own. A text is centred in
its height, so a label lines up with the control beside it.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 17:13:35 +01:00
|
|
|
(: (-> Kind (Vec Prop) (Vec (View %m)) (View %m)))
|
|
|
|
|
(defun (container kind props children)
|
|
|
|
|
(View (kind kind) (props props) (events []) (children children) (key None)))
|
|
|
|
|
|
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
|
|
|
(: (-> Kind double double (Array (View %m)) (View %m)))
|
|
|
|
|
(defun (stack kind spacing margin children)
|
Keyed children, and eight more controls
A child is now known by its key, or by its place among the children with no
key. One whose identity was there before keeps its control, and the
controls are put in the new order by moving only those out of place. So a
list that is sorted, or has an item put in front, keeps its controls.
Text areas, sliders, progress bars, dropdowns, images, grids with cells and
spans, and scroll views. A grid's sizes are .NET properties and not
Avalonia's, so they are set by a setter of their own. A text is centred in
its height, so a label lines up with the control beside it.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 17:13:35 +01:00
|
|
|
(container kind (with-layout (with-size [] Spacing spacing) 0.0 0.0 margin 0.0)
|
|
|
|
|
(array->vec children)))
|
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
|
|
|
|
|
|
|
|
;; Views one above the other.
|
|
|
|
|
(: (-> (#:spacing double) (#:margin double) #:rest (View %m) (View %m)))
|
|
|
|
|
(defun (column #:spacing 0.0 #:margin 0.0 #:rest children)
|
|
|
|
|
(stack ColumnKind spacing margin children))
|
|
|
|
|
|
|
|
|
|
;; Views side by side.
|
|
|
|
|
(: (-> (#:spacing double) (#:margin double) #:rest (View %m) (View %m)))
|
|
|
|
|
(defun (row #:spacing 0.0 #:margin 0.0 #:rest children)
|
|
|
|
|
(stack RowKind spacing margin children))
|
Keyed children, and eight more controls
A child is now known by its key, or by its place among the children with no
key. One whose identity was there before keeps its control, and the
controls are put in the new order by moving only those out of place. So a
list that is sorted, or has an item put in front, keeps its controls.
Text areas, sliders, progress bars, dropdowns, images, grids with cells and
spans, and scroll views. A grid's sizes are .NET properties and not
Avalonia's, so they are set by a setter of their own. A text is centred in
its height, so a label lines up with the control beside it.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 17:13:35 +01:00
|
|
|
|
|
|
|
|
;; Views in the cells of a grid. `#:columns` and `#:rows` are the sizes, as
|
|
|
|
|
;; in Avalonia: "Auto" fits the content, "*" shares what is left, "2*" takes
|
|
|
|
|
;; two shares, and a number is pixels. Put a view in a cell with `at`; one
|
|
|
|
|
;; that is not put anywhere is in the first.
|
|
|
|
|
(: (-> (#:columns string) (#:rows string) (#:spacing double) (#:margin double)
|
|
|
|
|
#:rest (View %m) (View %m)))
|
|
|
|
|
(defun (grid #:columns "*" #:rows "*" #:spacing 0.0 #:margin 0.0 #:rest children)
|
|
|
|
|
(container GridKind
|
|
|
|
|
(with-layout (with-size [(Columns columns) (Rows rows)] Spacing spacing)
|
|
|
|
|
0.0 0.0 margin 0.0)
|
|
|
|
|
(array->vec children)))
|
|
|
|
|
|
2026-10-25 17:21:40 +01:00
|
|
|
;; A view in a frame: a line of `#:colour` and `#:thickness` around it,
|
|
|
|
|
;; corners as round as `#:radius`, `#:background` behind it, and `#:padding`
|
|
|
|
|
;; between the line and the view.
|
|
|
|
|
(: (-> (View %m) (#:colour string) (#:thickness double) (#:radius double)
|
|
|
|
|
(#:background string) (#:padding double) (#:margin double)
|
|
|
|
|
(View %m)))
|
|
|
|
|
(defun (border child #:colour "" #:thickness 1.0 #:radius 0.0 #:background ""
|
|
|
|
|
#:padding 0.0 #:margin 0.0)
|
|
|
|
|
(defun (with props make c) (if (string-empty? c) props (vec-add props (make c))))
|
|
|
|
|
(container BorderKind
|
|
|
|
|
(with (with (with-size (with-size (with-layout [] 0.0 0.0 margin padding)
|
|
|
|
|
BorderWidth thickness)
|
|
|
|
|
Radius radius)
|
|
|
|
|
BorderColour colour)
|
|
|
|
|
Background background)
|
|
|
|
|
[child]))
|
|
|
|
|
|
Keyed children, and eight more controls
A child is now known by its key, or by its place among the children with no
key. One whose identity was there before keeps its control, and the
controls are put in the new order by moving only those out of place. So a
list that is sorted, or has an item put in front, keeps its controls.
Text areas, sliders, progress bars, dropdowns, images, grids with cells and
spans, and scroll views. A grid's sizes are .NET properties and not
Avalonia's, so they are set by a setter of their own. A text is centred in
its height, so a label lines up with the control beside it.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 17:13:35 +01:00
|
|
|
;; A view that scrolls when it is taller than the space it has.
|
|
|
|
|
(: (-> (View %m) (#:width double) (#:height double) (#:margin double) (View %m)))
|
|
|
|
|
(defun (scroll child #:width 0.0 #:height 0.0 #:margin 0.0)
|
|
|
|
|
(container ScrollKind (with-layout [] width height margin 0.0) [child]))
|