Commit graph

15 commits

Author SHA256 Message Date
5127fbac13 Windows besides the main one, and dialogs of the program's own
run-app's #:windows gives the windows a model has. The app opens a window
when it is first in the list, patches its view and title while it stays,
and closes it when it is gone. A click on its close button sends its
on-close message, and the model closes it. A #:modal window is a dialog
over the main window, so a dialog of the program's own is a window whose
state is in the model.

Windows wait for the main window to be shown, because a dialog cannot
belong to a hidden one, and the program ends when the main window closes.
Key shortcuts and commands such as focus reach the other windows too, and
#:screenshot saves a picture of each one beside the main window's.

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

Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 21:21:09 +01:00
708256d0b3 Commands for focus, scrolling and the clipboard, and pictures from bytes
focus, scroll-to-end, scroll-to-start and scroll-into-view find their
control by the key of its view, and run once the update's controls are
laid out. copy-text puts text on the clipboard, and paste-text sends what
it holds. A command that sends no message ends its fiber when its work is
done, so none is left waiting until the app stops.

image-data shows a picture from the bytes of a file, read again only when
it is given another array.

start-headless! now adds the Fluent theme, as run-app does, so controls
are templated and laid out as in a window.

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

Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 21:15:12 +01:00
52ebd0ace8 Controls that bjogui does not wrap, with (bjogui native)
A native is made with a function that makes the control and hooks its
events, and one that puts a value on it. hosted puts it in a view: the
control is made once for its place, given a value only when the view's
value changes, and what it sends while it is given one is dropped. Its
outputs become the app's messages through on-output and view-map, and
may be sent from any thread.

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

Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 21:02:17 +01:00
5237bf3273 Controls of the program's own, drawn with a pen
A component draws itself with (bjogui draw), a pen over Avalonia's
DrawingContext, and hears the pointer, the wheel, the keys and the focus.
It keeps a state of its own between inputs, and sends outputs that drawn
makes the app's messages. It is drawn again only when its value, its state
or its size changes, and no Avalonia class is subclassed: the picture is a
DrawingImage on a canvas.

start-headless! sets Avalonia up with no screen, drawing with Skia, so
tests/components.bjo draws components and reads their pixels. The mixer
example has knobs and a timeline.

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

Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 20:34:19 +01:00
5379269f43 The canvas test's position helpers are generic again
The compiler now casts from a type parameter through object, so the
helpers no longer need their parameter declared as a Control.

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

Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 19:40:54 +01:00
ff9db1cc5b Components with view-map, and canvases with shapes and the pointer
view-map gives a view's every message to a function first, so a part with a
message type, an update and a view of its own goes in a larger view with
the case of the larger message that carries its own.

canvas is a surface for rect, ellipse, line and path, placed by their own
numbers, and other views by at-point. on-press, on-move and on-release send
the place on the canvas. They come from whatever is drawn on it too, so
they are taken from any source, and a press takes the pointer for the drag.
examples/sketch.bjo draws with it, and docs/sketch.png is its picture.

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

Co-Authored-By: eca-agent <git@eca.dev>
2026-10-25 19:26:37 +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
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