Commit graph

12 commits

Author SHA256 Message Date
f53877e7a1 Dialogs await their Task through a generic #:async import
Bjolang now resolves a generic method imported #:async, so
Dispatcher.InvokeAsync(Func<Task<T>>) is imported as it is: it runs the
dialog on the UI thread and is awaited for what the dialog answers. The
detour through the non-generic Task.WhenAll and the Result read by
reflection are gone.

🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)

Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 18:36:53 +01:00
8941425f05 Tree views, a styles example, and the Readme for all of it
A tree node is a TreeViewItem: a header and items, patched by key as a
list's items are. The chosen node is chosen by its key, once the nodes are
there, and a click sends the key. SelectedItem is a direct property, which
a clear does not reset, so no choice is set as null.

The Readme now covers menus and keys, dialogs, styles and themes, and the
new controls. docs/styles.png is examples/styles.bjo.

🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)

Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 18:10:53 +01:00
e3c54c4348 Themes, classes and styles
run-app's #:theme gives "light", "dark" or the system's for a model, and is
set again when it changes. classed puts a view in classes, and #:styles
are rules of style: a kind and a class, and colours, a font size, a
padding and a radius. A button's rule may also give the colours while the
pointer is over it and while it is pressed; those are set on the presenter
in its template, as the Fluent theme sets its own.

Avalonia's Selectors.Class adds a class to a type-and-class selector it is
given rather than making a new one, so each style's selector is made anew.

🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)

Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 18:08:34 +01:00
56a01efe76 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
cfcb1851b7 Dialogs, file pickers, and closing by the program's leave
(bjogui dialogs) has alert, confirm and prompt, the open, save and folder
pickers, and quit, each a command whose answer comes back as a message. A
dialog is a window, which only the UI thread may make, so the command posts
it there and awaits its Task in a fiber. Avalonia's dialogs take no
CancellationToken, so they cannot be task->event; the Task is awaited
through the non-generic Task.WhenAll, and its result read by reflection,
because Bjolang reads no Task's Result.

With no window, as under start-app, each answers as if cancelled.

run-app's #:on-close turns the close button into a message, and quit
closes the windows by program, which is not asked again.

🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)

Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 18:01:27 +01:00
be288ac03e Menus, context menus and keyboard shortcuts
A menu bar, submenus and menu items are views. A menu item's items are an
ItemsControl's, so they are patched as a list's. A context menu wraps a
view: the view and its menu are two places of the wrapper.

Keys are matched in the window's KeyDown, after the focused control had its
chance: an enabled menu item in the view whose shortcut it is, or else the
app's #:keys. Avalonia's own key bindings take an ICommand, which a Bjolang
program cannot make. app-press! presses keys for a test.

🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)

Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 17:55:19 +01:00
1e46202eef Show the todo picture in the Readme, and say how it was taken
🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)

Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 17:24:45 +01:00
5a80685397 A todo example, and its picture in the Readme
run-app takes #:screenshot, a path: the window saves a PNG of itself there
once it is shown and laid out, at twice its size, and closes. It defaults to
the environment variable BJOGUI_SCREENSHOT, so any program can be pictured
with no change. docs/todo.png is examples/todo.bjo, taken that way.

🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)

Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 17:24:32 +01:00
e773bc424e List boxes, tabs, colours and borders
A list box's items and a tab control's tabs are views, patched by key as a
column's children are: Panel.Children and ItemsControl.Items are both an
IList, so one patch serves both. A list can choose an item only once its
items are there, so the choice is set after the children.

A routed event bubbles, so each handler now takes only its own control's
events. Before, a dropdown in a list item would have been heard as the
list's choice too.

paint gives any view a text and a background colour, and border a frame. A
colour that cannot be read leaves the theme's.

🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)

Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 17:21:40 +01:00
95b6707cc1 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
4f48282314 Commands and subscriptions, and gui-update
gui-update is a plain function, so it cannot wait. It returns the work to do
as values. A command is an (Event %m), synced in a fiber of its own, whose
answer is the next message. A subscription is a source of messages that the
model asks for: after each update the app starts the new ones and cancels the
ones that are gone, by key. every sends a message on a timer, and
subscription takes a source of one's own.

The fibers belong to the app's scope and post their messages to the UI
thread, so gui-update and view only run there. Closing the window cancels
them. The prelude's path macros already use the name update, hence
gui-update.

🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)

Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 17:02:02 +01:00
2f4c99ba62 Views, events as messages, and a patch, over plain Avalonia
A program is a model, an update and a view. A view is a Bjolang value; an
event finds its message in the view its control was last given, and the next
view is compared with the last so only what differs is changed. Controls are
made with plain constructors and changed with SetValue, so nothing is
subclassed, and a program never names an Avalonia type.

Text, button, text input, checkbox, column and row. tests/app.bjo drives an
app through its real controls with no window.

🤖 Generated with [ECA](https://eca.dev) (anthropic/claude-opus-4-8)

Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 16:18:09 +01:00