Skip to main content

Runtime And Updates

MountAsync

MountAsync walks the element tree, allocates NodeHandle values, writes CreateElement, attributes, styles, text, listeners, and the final SetRoot opcode, then sends the frame to the WebKitGTK renderer.

await using var host = RenderHostFactory.CreateWebKitGtk();
var app = App.Create(host);

var root = new Div();
root.Children.Add(new Button("Save"));

await app.MountAsync(root, cancellationToken);
await host.RunAsync(cancellationToken);

Update

Update and UpdateAsync submit imperative DOM update transactions. A transaction reuses mounted element handles and writes text, attribute, style, child, or listener changes into one frame.

await app.UpdateAsync(tx => tx
.SetText(button, "Saved")
.SetAttr(button, "class", "primary")
.SetStyle(button, StylePropId.FontSize, 16), cancellationToken);

HTML / CSS Load

The WebKitGTK host can load HTML and CSS directly:

await using var host = RenderHostFactory.CreateWebKitGtk();

await host.LoadHtmlAsync("<main><h1>Hello</h1></main>");
await host.LoadCssAsync("h1 { color: rgb(1, 2, 3); }");

LoadHtmlAsync does not allow JavaScript from the HTML by default. Before insertion, the browser DOM parser parses the document and strips <script>, iframe, object, embed, meta[http-equiv], inline event handlers, srcdoc, and javascript: URLs, then replaces the current document head/body. The framework shim remains available.

LoadCssAsync(css) writes the default stylesheet. LoadCssAsync(css, styleId) writes or replaces a named stylesheet, which is useful for themes and hot style updates.

Child transactions can create new subtrees, insert, move, detach, and destroy nodes:

var item = new Li("New");

app.Update(tx => tx
.InsertBefore(list, item, before)
.MoveBefore(list, item, null)
.RemoveChild(list, oldItem)
.RemoveNode(item));

RemoveChild only detaches the subtree from its parent, so the node handle can be inserted again later. RemoveNode destroys the renderer node, releases the arena rows, and clears listener bindings for that subtree.

Event Listeners

Listeners are mapped to wire tokens through ListenerRegistry. The renderer only sends the token and event payload back; C# confirms the binding by token, event id, and node id before parsing the payload and invoking the matching delegate.

Action handler = () => Console.WriteLine("clicked");

app.Update(tx => tx.AddListener(button, eventId: 1, handler));
app.Update(tx => tx.RemoveListener(button, eventId: 1, handler));

RemoveListener requires the same delegate identity that was previously registered. Removing an unknown listener throws instead of sending a meaningless token to the renderer.

The same delegate can be bound to multiple nodes. Removing the listener from one node does not affect the remaining node bindings. Destroying a node or disposing the app clears the C# registry binding; the renderer shim also clears its listener table when RemoveNode is applied.

Dispose

UltimateUiApp implements IAsyncDisposable. Disposal removes inbound event subscriptions, clears the listener registry, and disposes the bound IRenderHost or IDomBackend.