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
|
2026-10-25 17:55:19 +01:00
|
|
|
menu-bar submenu menu-item menu-separator
|
Switches, radios, numbers, dates, completion, expanders, tooltips, flows, docks
A number input's numbers are decimals in Avalonia, so they are converted
where they are set. A date is "yyyy-MM-dd" both ways. An expander and a
completion box raise their own events from the parts of their templates, so
they listen to the property instead.
The cases of Prop and On are now PropLabel, OnClick and so on. They are
exported with their types, and as Label, Value, Selected and Click they
took names that a program's own messages want.
The panel is flow and not wrap, which is CML's.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 18:05:12 +01:00
|
|
|
toggle-switch radio number-input date-picker auto-complete expander
|
|
|
|
|
column row grid scroll border flow dock
|
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 18:08:34 +01:00
|
|
|
keyed at paint with-context-menu tooltip at-top at-bottom at-left at-right classed
|
2026-10-25 17:55:19 +01:00
|
|
|
|
|
|
|
|
;; Keyboard shortcuts.
|
|
|
|
|
Key key key-gesture key-message
|
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
|
2026-10-25 17:55:19 +01:00
|
|
|
MenuBarKind MenuItemKind SeparatorKind ContextKind ContextMenuKind
|
Switches, radios, numbers, dates, completion, expanders, tooltips, flows, docks
A number input's numbers are decimals in Avalonia, so they are converted
where they are set. A date is "yyyy-MM-dd" both ways. An expander and a
completion box raise their own events from the parts of their templates, so
they listen to the property instead.
The cases of Prop and On are now PropLabel, OnClick and so on. They are
exported with their types, and as Label, Value, Selected and Click they
took names that a program's own messages want.
The panel is flow and not wrap, which is CML's.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 18:05:12 +01:00
|
|
|
ToggleSwitchKind RadioKind NumberKind DateKind AutoCompleteKind
|
|
|
|
|
ExpanderKind WrapKind DockKind
|
2026-10-25 17:21:40 +01:00
|
|
|
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.
|
Switches, radios, numbers, dates, completion, expanders, tooltips, flows, docks
A number input's numbers are decimals in Avalonia, so they are converted
where they are set. A date is "yyyy-MM-dd" both ways. An expander and a
completion box raise their own events from the parts of their templates, so
they listen to the property instead.
The cases of Prop and On are now PropLabel, OnClick and so on. They are
exported with their types, and as Label, Value, Selected and Click they
took names that a program's own messages want.
The panel is flow and not wrap, which is CML's.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 18:05:12 +01:00
|
|
|
(type (: Prop (Union (: PropLabel string) ; the text of a text, a button or a checkbox
|
|
|
|
|
(: PropValue string) ; the text in a text input
|
|
|
|
|
(: PropPlaceholder string) ; shown in an empty text input
|
|
|
|
|
(: PropChecked bool)
|
|
|
|
|
(: PropEnabled bool)
|
|
|
|
|
(: PropFontSize double)
|
|
|
|
|
(: PropWidth double)
|
|
|
|
|
(: PropHeight double)
|
|
|
|
|
(: PropMargin double)
|
|
|
|
|
(: PropPadding double)
|
|
|
|
|
(: PropSpacing double)
|
|
|
|
|
(: PropWrap bool) ; a text breaks its lines at the width
|
|
|
|
|
(: PropMinimum double) ; of a slider or a progress bar
|
|
|
|
|
(: PropMaximum double)
|
|
|
|
|
(: PropAmount double) ; where a slider or a progress bar is
|
|
|
|
|
(: PropStep double) ; what an arrow key moves a slider by
|
|
|
|
|
(: PropItems (Vec string)) ; what a dropdown offers
|
|
|
|
|
(: PropSelected int) ; which item is chosen; -1 for none
|
|
|
|
|
(: PropSource string) ; the file an image shows
|
|
|
|
|
(: PropColumns string) ; a grid's columns, as "Auto,*,2*"
|
|
|
|
|
(: PropRows string)
|
|
|
|
|
(: PropRow int) ; where a view is in its grid
|
|
|
|
|
(: PropColumn int)
|
|
|
|
|
(: PropRowSpan int)
|
|
|
|
|
(: PropColumnSpan int)
|
|
|
|
|
(: PropBackground string) ; a colour: "red", "#08f", "#80ff8800"
|
|
|
|
|
(: PropForeground string) ; the colour of the text
|
|
|
|
|
(: PropBorderColour string)
|
|
|
|
|
(: PropBorderWidth double)
|
|
|
|
|
(: PropRadius double) ; how round a border's corners are
|
|
|
|
|
(: PropShortcut string) ; a menu item's keys, as "Ctrl+S"
|
|
|
|
|
(: PropTip string) ; shown when the pointer rests on a view
|
|
|
|
|
(: PropExpanded bool) ; whether an expander shows its content
|
|
|
|
|
(: PropDate string) ; a date picker's date, as "2026-10-25"
|
2026-10-25 18:08:34 +01:00
|
|
|
(: PropDockSide int) ; where a view is in its dock: 0 to 3
|
|
|
|
|
(: PropClasses (Vec string))))) ; the classes that styles match
|
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.
|
Switches, radios, numbers, dates, completion, expanders, tooltips, flows, docks
A number input's numbers are decimals in Avalonia, so they are converted
where they are set. A date is "yyyy-MM-dd" both ways. An expander and a
completion box raise their own events from the parts of their templates, so
they listen to the property instead.
The cases of Prop and On are now PropLabel, OnClick and so on. They are
exported with their types, and as Label, Value, Selected and Click they
took names that a program's own messages want.
The panel is flow and not wrap, which is CML's.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 18:05:12 +01:00
|
|
|
(type (: (On %m) (Union (: OnClick %m)
|
|
|
|
|
(: OnInput (-> string %m))
|
|
|
|
|
(: OnToggle (-> bool %m))
|
|
|
|
|
(: OnSlide (-> double %m))
|
|
|
|
|
(: OnSelect (-> 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))))
|
Switches, radios, numbers, dates, completion, expanders, tooltips, flows, docks
A number input's numbers are decimals in Avalonia, so they are converted
where they are set. A date is "yyyy-MM-dd" both ways. An expander and a
completion box raise their own events from the parts of their templates, so
they listen to the property instead.
The cases of Prop and On are now PropLabel, OnClick and so on. They are
exported with their types, and as Label, Value, Selected and Click they
took names that a program's own messages want.
The panel is flow and not wrap, which is CML's.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 18:05:12 +01:00
|
|
|
(record-set v (props (with (with (.props v) PropForeground colour) PropBackground background))))
|
2026-10-25 17:21:40 +01:00
|
|
|
|
2026-10-25 17:55:19 +01:00
|
|
|
;; The view, with a menu that opens when it is clicked with the right button.
|
|
|
|
|
;; The items are made by `menu-item`, `submenu` and `menu-separator`.
|
|
|
|
|
(: with-context-menu (-> (View %m) #:rest (View %m) (View %m)))
|
|
|
|
|
(defun (with-context-menu v #:rest items)
|
|
|
|
|
(container ContextKind [] [v (container ContextMenuKind [] (array->vec items))]))
|
|
|
|
|
|
2026-10-25 18:08:34 +01:00
|
|
|
;; The view, in the classes `names`, which the styles given to `run-app`
|
|
|
|
|
;; match with `#:class`.
|
|
|
|
|
(: classed (-> (View %m) #:rest string (View %m)))
|
|
|
|
|
(defun (classed v #:rest names)
|
|
|
|
|
(record-set v (props (vec-add (.props v) (PropClasses (array->vec names))))))
|
|
|
|
|
|
Switches, radios, numbers, dates, completion, expanders, tooltips, flows, docks
A number input's numbers are decimals in Avalonia, so they are converted
where they are set. A date is "yyyy-MM-dd" both ways. An expander and a
completion box raise their own events from the parts of their templates, so
they listen to the property instead.
The cases of Prop and On are now PropLabel, OnClick and so on. They are
exported with their types, and as Label, Value, Selected and Click they
took names that a program's own messages want.
The panel is flow and not wrap, which is CML's.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 18:05:12 +01:00
|
|
|
;; The view, with `text` shown when the pointer rests on it.
|
|
|
|
|
(: tooltip (-> (View %m) string (View %m)))
|
|
|
|
|
(defun (tooltip v text)
|
|
|
|
|
(record-set v (props (vec-add (.props v) (PropTip text)))))
|
|
|
|
|
|
|
|
|
|
(: (-> int (View %m) (View %m)))
|
|
|
|
|
(defun (docked side v)
|
|
|
|
|
(record-set v (props (vec-add (.props v) (PropDockSide side)))))
|
|
|
|
|
|
|
|
|
|
;; The view, at an edge of its dock. The last child of a dock that is put at
|
|
|
|
|
;; no edge fills what is left.
|
|
|
|
|
(: at-left (-> (View %m) (View %m)))
|
|
|
|
|
(defun (at-left v) (docked 0 v))
|
|
|
|
|
|
|
|
|
|
(: at-top (-> (View %m) (View %m)))
|
|
|
|
|
(defun (at-top v) (docked 1 v))
|
|
|
|
|
|
|
|
|
|
(: at-right (-> (View %m) (View %m)))
|
|
|
|
|
(defun (at-right v) (docked 2 v))
|
|
|
|
|
|
|
|
|
|
(: at-bottom (-> (View %m) (View %m)))
|
|
|
|
|
(defun (at-bottom v) (docked 3 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
|
|
|
;; 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)
|
Switches, radios, numbers, dates, completion, expanders, tooltips, flows, docks
A number input's numbers are decimals in Avalonia, so they are converted
where they are set. A date is "yyyy-MM-dd" both ways. An expander and a
completion box raise their own events from the parts of their templates, so
they listen to the property instead.
The cases of Prop and On are now PropLabel, OnClick and so on. They are
exported with their types, and as Label, Value, Selected and Click they
took names that a program's own messages want.
The panel is flow and not wrap, which is CML's.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 18:05:12 +01:00
|
|
|
(record-set v (props (vec-add (vec-add (vec-add (vec-add (.props v) (PropRow row)) (PropColumn column))
|
|
|
|
|
(PropRowSpan row-span))
|
|
|
|
|
(PropColumnSpan column-span)))))
|
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, 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)
|
Switches, radios, numbers, dates, completion, expanders, tooltips, flows, docks
A number input's numbers are decimals in Avalonia, so they are converted
where they are set. A date is "yyyy-MM-dd" both ways. An expander and a
completion box raise their own events from the parts of their templates, so
they listen to the property instead.
The cases of Prop and On are now PropLabel, OnClick and so on. They are
exported with their types, and as Label, Value, Selected and Click they
took names that a program's own messages want.
The panel is flow and not wrap, which is CML's.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 18:05:12 +01:00
|
|
|
(with-size (with-size (with-size (with-size props PropWidth width) PropHeight height)
|
|
|
|
|
PropMargin margin)
|
|
|
|
|
PropPadding padding))
|
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 (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
|
Switches, radios, numbers, dates, completion, expanders, tooltips, flows, docks
A number input's numbers are decimals in Avalonia, so they are converted
where they are set. A date is "yyyy-MM-dd" both ways. An expander and a
completion box raise their own events from the parts of their templates, so
they listen to the property instead.
The cases of Prop and On are now PropLabel, OnClick and so on. They are
exported with their types, and as Label, Value, Selected and Click they
took names that a program's own messages want.
The panel is flow and not wrap, which is CML's.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 18:05:12 +01:00
|
|
|
(with-layout (with-size [(PropLabel s) (PropWrap wrap)] PropFontSize 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
|
Switches, radios, numbers, dates, completion, expanders, tooltips, flows, docks
A number input's numbers are decimals in Avalonia, so they are converted
where they are set. A date is "yyyy-MM-dd" both ways. An expander and a
completion box raise their own events from the parts of their templates, so
they listen to the property instead.
The cases of Prop and On are now PropLabel, OnClick and so on. They are
exported with their types, and as Label, Value, Selected and Click they
took names that a program's own messages want.
The panel is flow and not wrap, which is CML's.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 18:05:12 +01:00
|
|
|
(with-layout [(PropLabel label) (PropEnabled enabled)] width height margin padding)
|
|
|
|
|
[(OnClick msg)]))
|
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 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
|
Switches, radios, numbers, dates, completion, expanders, tooltips, flows, docks
A number input's numbers are decimals in Avalonia, so they are converted
where they are set. A date is "yyyy-MM-dd" both ways. An expander and a
completion box raise their own events from the parts of their templates, so
they listen to the property instead.
The cases of Prop and On are now PropLabel, OnClick and so on. They are
exported with their types, and as Label, Value, Selected and Click they
took names that a program's own messages want.
The panel is flow and not wrap, which is CML's.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 18:05:12 +01:00
|
|
|
(with-layout [(PropValue value) (PropPlaceholder placeholder) (PropEnabled enabled)]
|
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
|
|
|
width 0.0 margin 0.0)
|
Switches, radios, numbers, dates, completion, expanders, tooltips, flows, docks
A number input's numbers are decimals in Avalonia, so they are converted
where they are set. A date is "yyyy-MM-dd" both ways. An expander and a
completion box raise their own events from the parts of their templates, so
they listen to the property instead.
The cases of Prop and On are now PropLabel, OnClick and so on. They are
exported with their types, and as Label, Value, Selected and Click they
took names that a program's own messages want.
The panel is flow and not wrap, which is CML's.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 18:05:12 +01:00
|
|
|
[(OnInput on-input)]))
|
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
|
|
|
|
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
|
Switches, radios, numbers, dates, completion, expanders, tooltips, flows, docks
A number input's numbers are decimals in Avalonia, so they are converted
where they are set. A date is "yyyy-MM-dd" both ways. An expander and a
completion box raise their own events from the parts of their templates, so
they listen to the property instead.
The cases of Prop and On are now PropLabel, OnClick and so on. They are
exported with their types, and as Label, Value, Selected and Click they
took names that a program's own messages want.
The panel is flow and not wrap, which is CML's.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 18:05:12 +01:00
|
|
|
(with-layout [(PropValue value) (PropPlaceholder placeholder) (PropEnabled enabled)]
|
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
|
|
|
width height margin 0.0)
|
Switches, radios, numbers, dates, completion, expanders, tooltips, flows, docks
A number input's numbers are decimals in Avalonia, so they are converted
where they are set. A date is "yyyy-MM-dd" both ways. An expander and a
completion box raise their own events from the parts of their templates, so
they listen to the property instead.
The cases of Prop and On are now PropLabel, OnClick and so on. They are
exported with their types, and as Label, Value, Selected and Click they
took names that a program's own messages want.
The panel is flow and not wrap, which is CML's.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 18:05:12 +01:00
|
|
|
[(OnInput 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 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
|
Switches, radios, numbers, dates, completion, expanders, tooltips, flows, docks
A number input's numbers are decimals in Avalonia, so they are converted
where they are set. A date is "yyyy-MM-dd" both ways. An expander and a
completion box raise their own events from the parts of their templates, so
they listen to the property instead.
The cases of Prop and On are now PropLabel, OnClick and so on. They are
exported with their types, and as Label, Value, Selected and Click they
took names that a program's own messages want.
The panel is flow and not wrap, which is CML's.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 18:05:12 +01:00
|
|
|
(with-layout [(PropMinimum minimum) (PropMaximum maximum) (PropAmount value) (PropStep step)
|
|
|
|
|
(PropEnabled enabled)]
|
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
|
|
|
width 0.0 margin 0.0)
|
Switches, radios, numbers, dates, completion, expanders, tooltips, flows, docks
A number input's numbers are decimals in Avalonia, so they are converted
where they are set. A date is "yyyy-MM-dd" both ways. An expander and a
completion box raise their own events from the parts of their templates, so
they listen to the property instead.
The cases of Prop and On are now PropLabel, OnClick and so on. They are
exported with their types, and as Label, Value, Selected and Click they
took names that a program's own messages want.
The panel is flow and not wrap, which is CML's.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 18:05:12 +01:00
|
|
|
[(OnSlide on-change)]))
|
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 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
|
Switches, radios, numbers, dates, completion, expanders, tooltips, flows, docks
A number input's numbers are decimals in Avalonia, so they are converted
where they are set. A date is "yyyy-MM-dd" both ways. An expander and a
completion box raise their own events from the parts of their templates, so
they listen to the property instead.
The cases of Prop and On are now PropLabel, OnClick and so on. They are
exported with their types, and as Label, Value, Selected and Click they
took names that a program's own messages want.
The panel is flow and not wrap, which is CML's.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 18:05:12 +01:00
|
|
|
(with-layout [(PropMinimum 0.0) (PropMaximum maximum) (PropAmount value)] width 0.0 margin 0.0)
|
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 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
|
Switches, radios, numbers, dates, completion, expanders, tooltips, flows, docks
A number input's numbers are decimals in Avalonia, so they are converted
where they are set. A date is "yyyy-MM-dd" both ways. An expander and a
completion box raise their own events from the parts of their templates, so
they listen to the property instead.
The cases of Prop and On are now PropLabel, OnClick and so on. They are
exported with their types, and as Label, Value, Selected and Click they
took names that a program's own messages want.
The panel is flow and not wrap, which is CML's.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 18:05:12 +01:00
|
|
|
(with-layout [(PropItems options) (PropSelected selected) (PropEnabled enabled)]
|
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
|
|
|
width 0.0 margin 0.0)
|
Switches, radios, numbers, dates, completion, expanders, tooltips, flows, docks
A number input's numbers are decimals in Avalonia, so they are converted
where they are set. A date is "yyyy-MM-dd" both ways. An expander and a
completion box raise their own events from the parts of their templates, so
they listen to the property instead.
The cases of Prop and On are now PropLabel, OnClick and so on. They are
exported with their types, and as Label, Value, Selected and Click they
took names that a program's own messages want.
The panel is flow and not wrap, which is CML's.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 18:05:12 +01:00
|
|
|
[(OnSelect on-select)]))
|
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 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)
|
Switches, radios, numbers, dates, completion, expanders, tooltips, flows, docks
A number input's numbers are decimals in Avalonia, so they are converted
where they are set. A date is "yyyy-MM-dd" both ways. An expander and a
completion box raise their own events from the parts of their templates, so
they listen to the property instead.
The cases of Prop and On are now PropLabel, OnClick and so on. They are
exported with their types, and as Label, Value, Selected and Click they
took names that a program's own messages want.
The panel is flow and not wrap, which is CML's.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 18:05:12 +01:00
|
|
|
(leaf ImageKind (with-layout [(PropSource path)] width height margin 0.0) []))
|
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
|
|
|
|
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)
|
Switches, radios, numbers, dates, completion, expanders, tooltips, flows, docks
A number input's numbers are decimals in Avalonia, so they are converted
where they are set. A date is "yyyy-MM-dd" both ways. An expander and a
completion box raise their own events from the parts of their templates, so
they listen to the property instead.
The cases of Prop and On are now PropLabel, OnClick and so on. They are
exported with their types, and as Label, Value, Selected and Click they
took names that a program's own messages want.
The panel is flow and not wrap, which is CML's.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 18:05:12 +01:00
|
|
|
(props (with-layout [(PropSelected selected)] width height margin 0.0))
|
|
|
|
|
(events [(OnSelect on-select)])
|
2026-10-25 17:21:40 +01:00
|
|
|
(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)
|
Switches, radios, numbers, dates, completion, expanders, tooltips, flows, docks
A number input's numbers are decimals in Avalonia, so they are converted
where they are set. A date is "yyyy-MM-dd" both ways. An expander and a
completion box raise their own events from the parts of their templates, so
they listen to the property instead.
The cases of Prop and On are now PropLabel, OnClick and so on. They are
exported with their types, and as Label, Value, Selected and Click they
took names that a program's own messages want.
The panel is flow and not wrap, which is CML's.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 18:05:12 +01:00
|
|
|
(props (with-layout [(PropSelected selected)] 0.0 0.0 margin 0.0))
|
|
|
|
|
(events [(OnSelect on-select)])
|
2026-10-25 17:21:40 +01:00
|
|
|
(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)
|
Switches, radios, numbers, dates, completion, expanders, tooltips, flows, docks
A number input's numbers are decimals in Avalonia, so they are converted
where they are set. A date is "yyyy-MM-dd" both ways. An expander and a
completion box raise their own events from the parts of their templates, so
they listen to the property instead.
The cases of Prop and On are now PropLabel, OnClick and so on. They are
exported with their types, and as Label, Value, Selected and Click they
took names that a program's own messages want.
The panel is flow and not wrap, which is CML's.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 18:05:12 +01:00
|
|
|
(View (kind TabKind) (props [(PropLabel header)]) (events []) (children [content]) (key None)))
|
2026-10-25 17:21:40 +01:00
|
|
|
|
2026-10-25 17:55:19 +01:00
|
|
|
;; A bar of menus, made by `submenu`, for the top of the window.
|
|
|
|
|
(: (-> #:rest (View %m) (View %m)))
|
|
|
|
|
(defun (menu-bar #:rest menus)
|
|
|
|
|
(container MenuBarKind [] (array->vec menus)))
|
|
|
|
|
|
|
|
|
|
;; A menu of items, in a menu bar or in another menu.
|
|
|
|
|
(: (-> string #:rest (View %m) (View %m)))
|
|
|
|
|
(defun (submenu label #:rest items)
|
Switches, radios, numbers, dates, completion, expanders, tooltips, flows, docks
A number input's numbers are decimals in Avalonia, so they are converted
where they are set. A date is "yyyy-MM-dd" both ways. An expander and a
completion box raise their own events from the parts of their templates, so
they listen to the property instead.
The cases of Prop and On are now PropLabel, OnClick and so on. They are
exported with their types, and as Label, Value, Selected and Click they
took names that a program's own messages want.
The panel is flow and not wrap, which is CML's.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 18:05:12 +01:00
|
|
|
(View (kind MenuItemKind) (props [(PropLabel label)]) (events []) (children (array->vec items))
|
2026-10-25 17:55:19 +01:00
|
|
|
(key None)))
|
|
|
|
|
|
|
|
|
|
;; An item that sends `msg` when it is chosen. `#:shortcut`, such as
|
|
|
|
|
;; "Ctrl+S", is shown beside it, and the keys send `msg` too while the item
|
|
|
|
|
;; is in the view and enabled.
|
|
|
|
|
(: (-> string %m (#:shortcut string) (#:enabled bool) (View %m)))
|
|
|
|
|
(defun (menu-item label msg #:shortcut "" #:enabled #t)
|
|
|
|
|
(leaf MenuItemKind
|
|
|
|
|
(if (string-empty? shortcut)
|
Switches, radios, numbers, dates, completion, expanders, tooltips, flows, docks
A number input's numbers are decimals in Avalonia, so they are converted
where they are set. A date is "yyyy-MM-dd" both ways. An expander and a
completion box raise their own events from the parts of their templates, so
they listen to the property instead.
The cases of Prop and On are now PropLabel, OnClick and so on. They are
exported with their types, and as Label, Value, Selected and Click they
took names that a program's own messages want.
The panel is flow and not wrap, which is CML's.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 18:05:12 +01:00
|
|
|
[(PropLabel label) (PropEnabled enabled)]
|
|
|
|
|
[(PropLabel label) (PropEnabled enabled) (PropShortcut shortcut)])
|
|
|
|
|
[(OnClick msg)]))
|
2026-10-25 17:55:19 +01:00
|
|
|
|
|
|
|
|
;; A line between items of a menu.
|
|
|
|
|
(: (-> (View %m)))
|
|
|
|
|
(defun (menu-separator)
|
|
|
|
|
(leaf SeparatorKind [] []))
|
|
|
|
|
|
Switches, radios, numbers, dates, completion, expanders, tooltips, flows, docks
A number input's numbers are decimals in Avalonia, so they are converted
where they are set. A date is "yyyy-MM-dd" both ways. An expander and a
completion box raise their own events from the parts of their templates, so
they listen to the property instead.
The cases of Prop and On are now PropLabel, OnClick and so on. They are
exported with their types, and as Label, Value, Selected and Click they
took names that a program's own messages want.
The panel is flow and not wrap, which is CML's.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 18:05:12 +01:00
|
|
|
;; A switch that is on when `on?`. A flip sends `(on-toggle on)`.
|
|
|
|
|
(: (-> string bool (-> bool %m) (#:enabled bool) (#:margin double) (View %m)))
|
|
|
|
|
(defun (toggle-switch label on? on-toggle #:enabled #t #:margin 0.0)
|
|
|
|
|
(leaf ToggleSwitchKind
|
|
|
|
|
(with-layout [(PropLabel label) (PropChecked on?) (PropEnabled enabled)] 0.0 0.0 margin 0.0)
|
|
|
|
|
[(OnToggle on-toggle)]))
|
|
|
|
|
|
|
|
|
|
;; One of a set of choices, chosen when `chosen?`. Choosing it sends `msg`.
|
|
|
|
|
;; The model says which is chosen, so give one of a set `#t` and the others
|
|
|
|
|
;; `#f`.
|
|
|
|
|
(: (-> string bool %m (#:enabled bool) (#:margin double) (View %m)))
|
|
|
|
|
(defun (radio label chosen? msg #:enabled #t #:margin 0.0)
|
|
|
|
|
(leaf RadioKind
|
|
|
|
|
(with-layout [(PropLabel label) (PropChecked chosen?) (PropEnabled enabled)] 0.0 0.0 margin 0.0)
|
|
|
|
|
[(OnClick msg)]))
|
|
|
|
|
|
|
|
|
|
;; A number to type, or to step with arrows by `#:step`. A change sends
|
|
|
|
|
;; `(on-change value)`.
|
|
|
|
|
(: (-> double (-> double %m) (#:minimum double) (#:maximum double) (#:step double)
|
|
|
|
|
(#:enabled bool) (#:width double) (#:margin double)
|
|
|
|
|
(View %m)))
|
|
|
|
|
(defun (number-input value on-change #:minimum -1e9 #:maximum 1e9 #:step 1.0 #:enabled #t
|
|
|
|
|
#:width 0.0 #:margin 0.0)
|
|
|
|
|
(leaf NumberKind
|
|
|
|
|
(with-layout [(PropMinimum minimum) (PropMaximum maximum) (PropStep step) (PropAmount value)
|
|
|
|
|
(PropEnabled enabled)]
|
|
|
|
|
width 0.0 margin 0.0)
|
|
|
|
|
[(OnSlide on-change)]))
|
|
|
|
|
|
|
|
|
|
;; A date to pick, as "2026-10-25", or "" for none. A pick sends
|
|
|
|
|
;; `(on-change date)`.
|
|
|
|
|
(: (-> string (-> string %m) (#:enabled bool) (#:margin double) (View %m)))
|
|
|
|
|
(defun (date-picker date on-change #:enabled #t #:margin 0.0)
|
|
|
|
|
(leaf DateKind (with-layout [(PropDate date) (PropEnabled enabled)] 0.0 0.0 margin 0.0)
|
|
|
|
|
[(OnInput on-change)]))
|
|
|
|
|
|
|
|
|
|
;; A text input that offers the `options` that match what is typed. Each
|
|
|
|
|
;; change sends `(on-input text)`.
|
|
|
|
|
(: (-> string (Vec string) (-> string %m) (#:placeholder string) (#:width double)
|
|
|
|
|
(#:margin double)
|
|
|
|
|
(View %m)))
|
|
|
|
|
(defun (auto-complete value options on-input #:placeholder "" #:width 0.0 #:margin 0.0)
|
|
|
|
|
(leaf AutoCompleteKind
|
|
|
|
|
(with-layout [(PropItems options) (PropValue value) (PropPlaceholder placeholder)] width 0.0 margin 0.0)
|
|
|
|
|
[(OnInput on-input)]))
|
|
|
|
|
|
|
|
|
|
;; A header that shows `child` below it when `expanded?`. A click on the
|
|
|
|
|
;; header sends `(on-toggle expanded)`.
|
|
|
|
|
(: (-> string bool (-> bool %m) (View %m) (#:margin double) (View %m)))
|
|
|
|
|
(defun (expander header expanded? on-toggle child #:margin 0.0)
|
|
|
|
|
(View (kind ExpanderKind)
|
|
|
|
|
(props (with-layout [(PropLabel header) (PropExpanded expanded?)] 0.0 0.0 margin 0.0))
|
|
|
|
|
(events [(OnToggle on-toggle)])
|
|
|
|
|
(children [child])
|
|
|
|
|
(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
|
Switches, radios, numbers, dates, completion, expanders, tooltips, flows, docks
A number input's numbers are decimals in Avalonia, so they are converted
where they are set. A date is "yyyy-MM-dd" both ways. An expander and a
completion box raise their own events from the parts of their templates, so
they listen to the property instead.
The cases of Prop and On are now PropLabel, OnClick and so on. They are
exported with their types, and as Label, Value, Selected and Click they
took names that a program's own messages want.
The panel is flow and not wrap, which is CML's.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 18:05:12 +01:00
|
|
|
(with-layout [(PropLabel label) (PropChecked checked?) (PropEnabled enabled)] 0.0 0.0 margin 0.0)
|
|
|
|
|
[(OnToggle on-toggle)]))
|
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
|
|
|
|
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)
|
Switches, radios, numbers, dates, completion, expanders, tooltips, flows, docks
A number input's numbers are decimals in Avalonia, so they are converted
where they are set. A date is "yyyy-MM-dd" both ways. An expander and a
completion box raise their own events from the parts of their templates, so
they listen to the property instead.
The cases of Prop and On are now PropLabel, OnClick and so on. They are
exported with their types, and as Label, Value, Selected and Click they
took names that a program's own messages want.
The panel is flow and not wrap, which is CML's.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 18:05:12 +01:00
|
|
|
(container kind (with-layout (with-size [] PropSpacing spacing) 0.0 0.0 margin 0.0)
|
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
|
|
|
(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
|
Switches, radios, numbers, dates, completion, expanders, tooltips, flows, docks
A number input's numbers are decimals in Avalonia, so they are converted
where they are set. A date is "yyyy-MM-dd" both ways. An expander and a
completion box raise their own events from the parts of their templates, so
they listen to the property instead.
The cases of Prop and On are now PropLabel, OnClick and so on. They are
exported with their types, and as Label, Value, Selected and Click they
took names that a program's own messages want.
The panel is flow and not wrap, which is CML's.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 18:05:12 +01:00
|
|
|
(with-layout (with-size [(PropColumns columns) (PropRows rows)] PropSpacing spacing)
|
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
|
|
|
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)
|
Switches, radios, numbers, dates, completion, expanders, tooltips, flows, docks
A number input's numbers are decimals in Avalonia, so they are converted
where they are set. A date is "yyyy-MM-dd" both ways. An expander and a
completion box raise their own events from the parts of their templates, so
they listen to the property instead.
The cases of Prop and On are now PropLabel, OnClick and so on. They are
exported with their types, and as Label, Value, Selected and Click they
took names that a program's own messages want.
The panel is flow and not wrap, which is CML's.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 18:05:12 +01:00
|
|
|
PropBorderWidth thickness)
|
|
|
|
|
PropRadius radius)
|
|
|
|
|
PropBorderColour colour)
|
|
|
|
|
PropBackground background)
|
2026-10-25 17:21:40 +01:00
|
|
|
[child]))
|
|
|
|
|
|
Switches, radios, numbers, dates, completion, expanders, tooltips, flows, docks
A number input's numbers are decimals in Avalonia, so they are converted
where they are set. A date is "yyyy-MM-dd" both ways. An expander and a
completion box raise their own events from the parts of their templates, so
they listen to the property instead.
The cases of Prop and On are now PropLabel, OnClick and so on. They are
exported with their types, and as Label, Value, Selected and Click they
took names that a program's own messages want.
The panel is flow and not wrap, which is CML's.
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)
Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 18:05:12 +01:00
|
|
|
;; Views side by side, on as many lines as they need.
|
|
|
|
|
(: (-> (#:spacing double) (#:margin double) #:rest (View %m) (View %m)))
|
|
|
|
|
(defun (flow #:spacing 0.0 #:margin 0.0 #:rest children)
|
|
|
|
|
(stack WrapKind spacing margin children))
|
|
|
|
|
|
|
|
|
|
;; Views at the edges, put there with `at-top`, `at-bottom`, `at-left` and
|
|
|
|
|
;; `at-right`, in order from the outside in. The last child, at no edge,
|
|
|
|
|
;; fills the middle.
|
|
|
|
|
(: (-> (#:margin double) #:rest (View %m) (View %m)))
|
|
|
|
|
(defun (dock #:margin 0.0 #:rest children)
|
|
|
|
|
(stack DockKind 0.0 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
|
|
|
;; 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]))
|
2026-10-25 17:55:19 +01:00
|
|
|
|
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
|
;; Keyboard shortcuts
|
|
|
|
|
;; ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
;; Keys, such as "Ctrl+N" or "F5", and the message they send.
|
|
|
|
|
(type (: (Key %m) (Record (: gesture string) (: msg %m))))
|
|
|
|
|
|
|
|
|
|
;; `msg` is sent when `gesture` is pressed in the window, and no control that
|
|
|
|
|
;; has the keyboard takes the keys itself.
|
|
|
|
|
(: key (-> string %m (Key %m)))
|
|
|
|
|
(defun (key gesture msg) (Key (gesture gesture) (msg msg)))
|
|
|
|
|
|
|
|
|
|
(: key-gesture (-> (Key %m) string))
|
|
|
|
|
(defun (key-gesture k) (.gesture k))
|
|
|
|
|
|
|
|
|
|
(: key-message (-> (Key %m) %m))
|
|
|
|
|
(defun (key-message k) (.msg k))
|