LavaUI docs GitHub

enum LavaApp

Swift

enum LavaApp

Reusable window/input/invalidation/render loop, so a second LavaUI executable is a main.swift of a dozen lines instead of a copy of HelloWorldApp's ~350.

Split into open + run because asset loading a specific app owns (an app icon, a brand image) has to happen once, against an already-open Editor, before the first makeRoot() — folding it into run's hot path would either re-load it every rebuild or force every caller to thread a cache through their own view. Two calls keeps that ordering explicit:

guard let editor = LavaApp.open(title: "My App") else { exit(1) }
let icon = ImageStore.loadAsset(
    named: "icon.png", bundle: .module, into: editor)
LavaApp.run(editor: editor) { MyRootView(icon: icon) }

Sources/LavaUI/LavaApp.swift:20

Properties

static var currentWindow: WindowID { get }

The window whose frame is being built or whose handler is running.

Ambient rather than passed down, for the reason WindowScope gives at length: a view genuinely does not know which window it is in, and every view in a tree would have to thread the answer through to the one place that wants it. Outside a frame this is the first window.

Sources/LavaUI/LavaApp.swift:554

static var mainLayoutHost: LayoutHost? { get }

The layout host of the window the app started with, once run has brought it up. Nil before that, and after teardown.

For the app that has to know how big the tree it just laid out actually is — agentFrame(sid:) on a committed layout answers it. A context menu is the case this exists for: it has to tell the compositor its size before the compositor will place it, and the only honest source for that number is the layout pass, not a second pile of font arithmetic that agrees with Yoga until a face changes.

Everything else about a window's geometry is told to the app. This is the one question that runs the other way.

Sources/LavaUI/LavaApp.swift:261

static var windowCount: Int { get }

How many windows are open, counting ones opened this frame that are not yet on screen.

Sources/LavaUI/LavaApp.swift:237

Methods

static func closeCurrentWindow()

Closes the window this handler is running in. What a "Close" button in a secondary window wants, without the window having to know its own id.

Sources/LavaUI/LavaApp.swift:558

static func closeWindow(_ id: WindowID)

Closes a window this app opened. Closing the window the app started with ends run, the same as its titlebar X.

Takes effect at the end of the current frame, so it is safe from a handler running inside that window's own tree.

Sources/LavaUI/LavaApp.swift:523

static func isWindowOpen(_ id: WindowID) -> Bool

Whether id is still open.

Sources/LavaUI/LavaApp.swift:544

static func layoutHost(for id: WindowID) -> LayoutHost?

The layout host of one open window, once run has brought it up.

A fly-out measures itself the same way the root menu does — the plate's frame after a layout pass — and that frame lives on the fly-out's own host, not the window the process started with.

Sources/LavaUI/LavaApp.swift:244

static func open(title: String, assetsRoot: String? = nil, width: Float = 1280, height: Float = 800) -> Editor?

Opens the window and does one-time framework setup: default font bootstrap, clipboard bridge. Logs and returns nil if the window failed to open — the caller should exit(1).

assetsRoot
Engine resource root (directory with shaders/). Default is the SwiftPM CanvasResources bundle. Fonts always load from LavaUI's own resource bundle.

Sources/LavaUI/LavaApp.swift:28

@discardableResult static func openMenuPlate<V>(width: Float, height: Float, onClose: (() -> Void)? = nil, makeRoot: @escaping () -> V) -> WindowID? where V : View

A menu fly-out: its own window, hidden until the compositor places it.

Not openPopup. A popup hangs below an anchor the panel named, and this hangs beside a row of a menu the compositor is already showing. Where that is — and which side of the row still fits on the work area — is decided when the plate has been measured, not here.

Frosted the way a popup is, by its root view (see openPopup); the plate comes up with the fly-out, since the compositor captures when it places it, not before.

Sources/LavaUI/LavaApp.swift:453

@discardableResult static func openPopup<V>(title: String, width: Float = 280, height: Float = 240, anchor: SurfaceAnchor, onClose: (() -> Void)? = nil, makeRoot: @escaping () -> V) -> WindowID? where V : View

A borderless popup of the window the app started with, placed just below anchor (a rectangle in that window) and dismissed by a press that misses it.

The anchor is this window's own coordinates. Where that window sits on the screen is the compositor's, and so is the press that lands somewhere else — a client cannot hit-test a foreign window. Under a window manager that is not ours this is an ordinary second window; the placement and the dismiss are compositor work.

The frost behind it is the root view's to state, like everything else it draws:

LavaApp.openPopup(title: "Network", anchor: anchor) {
    NetworkWindow()
        .backdropBlur(radius: 12)
        .glassEffect(refraction: 20, frost: 1)
}

On a view that fills the window those two are the window's plate of the desktop, cut to the window's own corner radius — the same mask as the popup, so the two are one outline. The blur is what makes a plate; glass without it draws only the rim, because there is no sharp plate. A windowed app frosts nothing: nothing there can see the desktop behind the window.

Views inside can frost rectangles of their own instead: in a window whose backdrop is the desktop (WindowBackdrop.showsDesktop), .backdropBlur(radius:) on a smaller view asks the compositor to frost that view's rectangle, cut to its corner radius, and .glassEffect bends that plate alone — so two siblings can be a flat frost and a bent one, with desktop between them.

Sources/LavaUI/LavaApp.swift:418

@discardableResult static func openWindow<V>(title: String, width: Float = 800, height: Float = 600, onClose: (() -> Void)? = nil, makeRoot: @escaping () -> V) -> WindowID? where V : View

Opens a second window running its own view tree, sharing this process's GPU, font atlas, texture cache and frame loop.

Under a compositor this is a second surface, not a second process: its own arena and its own input stream, closed without ending the app. That only happens when ClientSurfaceBridge is installed, which LavaClient.run does. A client that calls Editor.openWindow directly gets the arena and the queue and nothing on screen.

Safe to call from a button handler: the window is created immediately but brought up at the end of the current frame, so it never draws against a half-updated tree, and the caller's own window still presents the frame the click belongs to.

The new window gets its own focus, invalidation and hover state (see WindowScope) and the app-level onRawKey from run. It does not get the menubar: a second menubar strip is a decision for the app, not a default, and the DBus global menu is registered per process.

Returns nil if the engine could not open it, or if called before run.

Sources/LavaUI/LavaApp.swift:356

static func resolveAssetsRoot(_ override: String? = nil) -> String

Back-compat alias — engine root only. App images should use ImageStore.loadAsset(named:bundle:into:) with the app bundle.

Sources/LavaUI/LavaApp.swift:82

static func resolveEngineAssetsRoot(_ override: String? = nil) -> String

Engine assets root: override, else CANVAS_ASSETS_ROOT, else the CanvasResources SwiftPM bundle (checked-in SPIR-V under shaders/).

Sources/LavaUI/LavaApp.swift:75

static func run<V>(editor: Editor, menu: (() -> MenuBar)? = nil, onRawKey: ((InputEvent) -> Bool)? = nil, makeRoot: @escaping () -> V) where V : View

Runs the full input/invalidation/render/agent-server loop until the window closes.

Drives every open window, not just this one: openWindow adds a second tree to the same loop, and one GLFW wait serves all of them, so N windows cost N draw arenas rather than N event loops. The app ends when the window opened here closes; secondary windows come and go under it. See LavaWindow for the per-window half and WindowScope for what each window owns privately.

  • makeRoot: rebuilds the view tree from scratch. Called on the first frame and on any invalidation that can't be satisfied per-node (see ViewInvalidation.consumeDirtyBodyNodes) — keep it cheap, an app's own one-time setup belongs before this call, against the Editor open already returned.
  • menu: optional application menubar. When a global-menu registrar is available (Vala Panel / Plasma appmenu / …), the tree is exported via DBusMenu and no in-window strip is drawn. Otherwise MenuChromeRoot draws it with Vulkan. Override with LAVA_MENU=vulkan or dbus. Rebuilt on every full body pass so labels/isEnabled stay in sync. Menu shortcuts are matched after onRawKey (both backends).
  • onRawKey: first look at every key event, ahead of focus/overlay/ content-scale handling. Return true to consume it.

Sources/LavaUI/LavaApp.swift:109

Types