facet_appkit
The AppKit renderer backend for facet. Where facet
describes a UI as a platform-free Node tree, facet_appkit is the half that
turns that tree into native Cocoa / AppKit objects on
macOS. It walks identity into native — the opposite direction from
agent_appkit, which walks a live native tree back
into an introspectable surface.
This is the FACET.1 discovery spike: the scope is the same three element kinds
facet ships — label, button, and a vertical stack — mapped to
NSTextField, NSButton, and NSStackView.
Two functions
The package surface is just two free functions.
render(np: *facet::Node) -> *u8walks one node into a native view and returns its raw*u8pointer. Alabelbecomes a non-editable, non-bezeledNSTextField(a true static label), abuttonbecomes anNSButtonwhose title is set and whosefacetclick handler is wired up, and astackbecomes a verticalNSStackViewwhose children are rendered recursively and added as arranged subviews.run(root: facet::Node)opens the application and a 480x320 window, rendersroot, mounts it as the window's content view, and runs the event loop. It blocks until the app exits.
import "facet/facet" as facet;
import "facet_appkit/facet_appkit" as ui;
fn on_save(_w: *u8) { return; }
fn main() -> i32 {
let tree = @facet {
label("Untitled document")
button("Save")
.on_click(on_save)
};
ui::run(tree); // opens the window, mounts the tree, blocks
return 0;
}
The @facet { ... } block is the builder block that
facet provides: bare leaf names resolve against the package and a leading-dot
line like .on_click(on_save) calls the modifier on the item above it. The
result is an ordinary facet::Node — pure data, no native types — which
facet_appkit then renders.
Click handlers are plain function pointers (fn(*u8)), in keeping with C+
having no closures; render hands a button's handler to the appkit runtime,
which stashes it on the sender so the native control can fire it.
Ownership note
This is a discovery spike, and its ownership model is deliberately simple: each
view is created with raw ObjC alloc/init and handed to its parent (which
retains it) without a balancing release, so every view leaks its initial +1.
That sidesteps the retained-tree ownership work and never crashes; the proper
+1 handoff that the appkit bindings use is a later
slice. Do not treat the spike as a leak-free production renderer.
Platform notes
macOS only — it links against AppKit through the appkit bindings. Build it on
a macos target; the same facet::Node tree is meant to be
rendered by other backends on other platforms, but only the AppKit backend
exists at FACET.1.
To drive a rendered AppKit app from an external agent rather than render one, see the agent surface and agent_appkit.