LavaUI docs GitHub

protocol View

Swift

protocol View

A declarative UI description.

Sources/LavaUI/View.swift:31

Properties

@ViewBuilder var body: Body { get }

The content of this view, built from other views.

Evaluated whenever the view's state or inputs change, so keep it free of side effects. Primitive views (PrimitiveView) do not use it.

Sources/LavaUI/View.swift:39

var body: Never { get }

Not documented.

Sources/LavaUI/View.swift:98

Methods

func agentId(_ id: String) -> AgentIdentifiedView<Self>

Stable automation id for agents (layout_tree → sid, click --sid, …).

Prefer short kebab-case names (theme-toggle, sidebar-nav). When set, this wins over the structural path fallback.

Sources/LavaUI/AgentId.swift:83

func backdropBlur(radius: Float = 8, adaptation: Float = 1) -> ModifiedView<Self>

Frosted-glass backdrop under this view's layout rect.

Captures UI already painted behind the rect, blurs it, composites the result, then draws this view's fill and children sharp on top. Typical use: .background(Color(...).opacity(0.15)).backdropBlur(radius: 6) on a panel or overlay so chrome reads as glass.

adaptation keeps the glass visible over a bright backdrop. Frost over a white page blurs to white, and the panel disappears into it; at 1 the renderer pulls the brightest part of the backdrop down toward light grey, pixel by pixel from the blurred sample, so a panel straddling a white window and a dark one adapts on each side. Mid-tones and darks are left alone. 0 is plain frost.

Sources/LavaUI/Modifiers.swift:675

func background(_ color: Color) -> ModifiedView<Self>

Fills the view's box with color, behind its content.

Sources/LavaUI/Modifiers.swift:523

func background(_ gradient: Gradient) -> ModifiedView<Self>

Fills the box with a two-stop linear ramp instead of a flat colour.

The start colour is also recorded as the flat fill, so anything that reads a background as one colour — and a renderer too old to know about gradients — still has an answer.

Sources/LavaUI/Modifiers.swift:532

func blur(radius: Float = 8) -> ModifiedView<Self>

Softens this view and its children.

The subtree is drawn into an offscreen target of its own, blurred, and composited back with its own alpha — so a blurred view has a genuinely soft edge and whatever sits behind it shows through unchanged. This is the SwiftUI meaning of .blur(); for frosted glass, where what is behind the view is what should soften, use backdropBlur(radius:).

Sources/LavaUI/Modifiers.swift:658

func border(_ color: Color, width: Float = 1) -> ModifiedView<Self>

Strokes this view's edge, width pixels thick, drawn over its content.

Text("Hi").padding(8).border(.purple, width: 4)

The stroke lies inside the layout rect, as in SwiftUI and CSS: the view still occupies exactly the space it was laid out in, and a border never nudges a sibling. It also follows .cornerRadius() — the two compose, and the border's inner edge is the true inward offset of the rounded shape, so the corners stay concentric.

Order matters the way it does in SwiftUI: .border() before .padding() frames the content, after it frames the padded box.

Sources/LavaUI/Modifiers.swift:571

func clipped() -> ModifiedView<Self>

Scissor this view's paint (and its children's) to its layout rect.

Yoga still sizes children freely; only drawing is clipped. That is what makes .frame(width:).clipped() on a Text work: the glyphs keep their intrinsic width, and the extra ink is cut off rather than drawn over the next view. Use on fixed chrome such as a menubar so hover fills cannot spill into content.

Sources/LavaUI/Modifiers.swift:737

func cornerRadius(_ radius: Float) -> ModifiedView<Self>

Rounds the corners of the view's background, hover fill and border.

Sources/LavaUI/Modifiers.swift:553

func cursor(_ shape: CursorShape?) -> ModifiedView<Self>

The pointer image while the pointer is inside this view.

grabBar.cursor(.resizeUpDown)

The innermost view under the pointer that states one wins; ancestors answer for the gaps, so a whole panel can ask for .text and a button inside it ask for .pointer without either knowing about the other.

Stating a cursor makes the view hit-testable — the renderer has to report the pointer entering it before anything can act on this, which is the same node a hover fill needs. That is a scene node per such view, so it is not free; it is the same cost .hoverBackground already pays.

Sources/LavaUI/Cursor.swift:48

func defaultStructureLines(indent: Int) -> [String]

The structure dump every composite view gets: its own type name, then its body one level deeper. Call it from a custom structureLines(indent:) that wants to add to the default rather than replace it.

Sources/LavaUI/View.swift:62

func dumpStructure(indent: Int = 0)

Prints structureLines(indent:) to standard error.

Sources/LavaUI/View.swift:49

func flexGrow(_ value: Float = 1) -> ModifiedView<Self>

Lets the view take a share of the parent's leftover main-axis space. With every sibling at 1, the space is split evenly.

Sources/LavaUI/Modifiers.swift:641

func flexShrink(_ value: Float) -> ModifiedView<Self>

How much the view gives up when its parent overflows along the main axis. 0 keeps its size; larger values give up proportionally more.

Sources/LavaUI/Modifiers.swift:647

func focusable(enabled: Bool = true, onKey: @escaping (KeyEvent) -> Bool, onChar: ((Character) -> Bool)? = nil) -> BoxRegistrationView<Self>

Takes keyboard focus when pressed anywhere inside, and sends keys to onKey — and typed characters to onChar — while it holds it. Each returns whether it used the input; what it does not use falls through to the window's shortcuts.

Focus stays until something else takes it — a text field, another focusable view — or the view leaves the tree.

enabled: false is a view that never takes focus, and a press in it leaves focus where it was — a results list under a search field, whose field keeps the keyboard. Prefer it to applying the modifier conditionally, which remounts the view whenever the condition changes.

Sources/LavaUI/Focusable.swift:76

func focused(_ isFocused: Binding<Bool>) -> BoxRegistrationView<Self>

Mirrors whether this view — or the first field or .focusable view inside it — holds keyboard focus, both ways.

Read it to know; set it to true to give the view the keyboard, and to false to take it away. It is set back to false when focus goes elsewhere — a click into another field, another .focused taking it — so it never claims focus the view has lost.

TextField(text: $path).focused($session.editingPath)
// Escape: session.editingPath = false — the caret goes, keys go back

Sources/LavaUI/Focusable.swift:208

func font(_ value: UIFont) -> EnvironmentModifiedView<Self>

Overrides the font for this subtree only.

Sources/LavaUI/Environment.swift:104

func frame(width: Dimension? = nil, height: Dimension? = nil, minWidth: Float? = nil, minHeight: Float? = nil, alignment: Alignment? = nil) -> ModifiedView<Self>

Fix this view's size, and optionally say where its content sits inside it.

Without alignment the size lands on the view's own node: a Text given .frame(width: 34) becomes a 34pt-wide text, and its glyphs start at the left edge because that is where a text's glyphs start. That is the historical behaviour of this modifier and every existing caller depends on it — a VStack given a width is expected to be that width and lay its children out across it, not to sit at its natural width inside a wider box.

With alignment it is SwiftUI's frame instead: a box of the stated size with the content placed inside at its natural size. That needs a node the content does not own, so stating an alignment always adds one. This is the version to reach for when a label has to sit in the middle of a fixed cell — a calendar day, a keypad key, a grid of counts:

Text("24").frame(width: .pt(34), height: .pt(28), alignment: .center)

The added node is where the click is not. Everything stated after this modifier — background, hoverBackground, cursor — lands on the wrapper, because that is now the outermost node. But whatever made the content interactive stays on the content: a Text(_:onClick:) keeps its click on the text, at the text's own size. The result is a control that fills 44pt, highlights across 44pt and shows a pointer over 44pt, while only the 27pt of glyphs in the middle actually respond — and nothing about the call site says so.

So do not reach for this to centre a clickable label. Text has an align: of its own, taking this same Alignment, that moves the pen inside the node that already exists and adds none:

Text("Fit", align: .center, onClick: …).frame(width: .pt(44))

Prefer it for any text, clickable or not — a grid of cells is a wrapper per cell, and TextAlignmentEquivalenceTests pins the two spellings to the same glyph positions. This modifier remains the right one for centring content that is not a text at all.

The default is nil and not .center on purpose. SwiftUI centres by default because its frame has only ever meant the second thing; here the two meanings share a modifier, so the default has to be the one that leaves existing layouts alone. nil is "never asked", which is not the same answer as .center.

Sources/LavaUI/Modifiers.swift:623

func glassEffect(refraction: Float = 20, rim: Float = 1, frost: Float = 0, magnification: Float = 1) -> ModifiedView<Self>

Makes this view a pane of glass.

What is behind the view is bent at its rim, as if seen through a lens whose edge curves away, with a little colour fringing where it bends hardest; and the rim catches a light from the top left, drawn over the view's own fill so a tint does not bury it.

Blur is a separate choice. Alone, this bends a sharp copy of what is behind; with .backdropBlur(radius:) as well, it bends the frost. On an image it bends the picture itself, and cuts it to the view's corner radius. Children are not glass — a glass panel's buttons are drawn sharp on top of it.

magnification makes the glass a lens: what is behind it is drawn that many times larger about the view's centre, under the rim's bend — a search field over a wall of icons enlarges the ones passing under it. A lens is read through, so pair it with little or no blur.

refraction is how far, in pixels, the very edge bends what is under it (the bend fades to nothing 20 px in); rim is the strength of the light, 0…1. frost, 0…1, gives what is behind the surface of frosted glass: a fine grain that catches the light and shifts what is under it by a pixel or two — on top of a blur it is what makes the pane read as etched glass rather than as a blur. It has no effect on an image.

In a window whose backdrop is the desktop (WindowBackdrop .showsDesktop), with .backdropBlur as well, the bend and the frost apply to the compositor's plate of the desktop under the view too.

On a view that fills its window — a popup's root — the blur and the glass are the window's own plate of the desktop, cut to the window's corner: .backdropBlur(radius:).glassEffect(…) on the root is the whole of a frosted popup. The plate needs the blur; glass alone has no plate to bend and draws only the rim.

Sources/LavaUI/Modifiers.swift:718

func hidden(_ isHidden: Bool = true) -> ModifiedView<Self>

Takes this view out of layout and drawing, without unmounting it.

The difference from if condition { view } is identity. An if that goes false destroys the subtree, and everything keyed to it goes with it — most visibly the scroll position, which the renderer holds against a node id that no longer exists when the view comes back. A hidden view keeps its nodes, so it comes back where it was.

That is what this is for: alternating panes that should each remember their own place. It is not a cheaper if — the subtree stays mounted, its state stays alive, and its body still recomputes when what it reads changes. Yoga skips it (display: none, so it occupies nothing rather than occupying nothing visibly) and the draw walk steps over it.

Long enough away and the renderer forgets anyway: it drops a node's state after SceneNodeIdentity.retentionPasses emit passes without seeing it, which is what stops a hidden pane pinning that state forever.

Sources/LavaUI/Modifiers.swift:758

func hoverBackground(_ color: Color) -> ModifiedView<Self>

Fills the view's box with color while the pointer is over it. The renderer draws the fill, so hovering costs the app no frame.

Sources/LavaUI/Modifiers.swift:538

func hoverSnap(_ snap: Bool = true) -> ModifiedView<Self>

When true, the hover chip vanishes the frame the pointer leaves.

Default hover eases out, which is right for a button and wrong for a dense list: twenty full-width rows still fading is a trail. File lists and menu items opt out. It is not .transition(), which animates a view arriving or leaving, not the pointer moving across one.

Sources/LavaUI/Modifiers.swift:548

func onAnyPress(perform: @escaping (_ button: Int32) -> Void) -> BoxRegistrationView<Self>

Runs perform with the button for every press inside this view, before the press reaches what it landed on, and without taking it.

For a container that needs to know it was used — a pane becoming the active one — while every row and button in it keeps its own clicks. Before, so that the handler of what was pressed already sees the consequence.

Sources/LavaUI/DragGesture.swift:241

func onDragGesture(minimumDistance: Float = 6, perform: @escaping (DragGestureValue) -> Void) -> BoxRegistrationView<Self>

Runs perform as a left press on this view travels: .began once it has moved minimumDistance pixels, .changed on every move after — wherever the pointer goes, including out of the view — and .ended on release.

The press still goes to whatever is under it first, so a tab that selects on press and moves on drag is two modifiers, not a gesture that has to remember to select. It is resolved outwards through the hit chain, so a label inside the view does not hide it. A press something else captures — a slider inside it — never becomes this gesture.

Sources/LavaUI/DragGesture.swift:218

func onDrop(targeted: ((Bool) -> Void)? = nil, springLoaded: (() -> Void)? = nil, perform: @escaping ([URL]) -> Void) -> DropTargetView<Self>

Runs perform with the dropped file paths (as URLs) when the user releases an OS drag over this view.

While a drag is still over it: targeted hears true as the drag becomes aimed at this view and false as it leaves, is dropped, or ends elsewhere — the place to light a target up. springLoaded runs once the drag has rested here for DropRouter.springDelay, the way a tab or a folder opens under a file being carried to it. Both need a compositor to say where a drag is; a windowed app gets the drop alone.

Nested targets resolve innermost first, like the drop itself: a folder row inside a list is its own target, a file row is not and the list behind it answers.

Sources/LavaUI/DropTarget.swift:129

func onFileDrag<Chip>(offsetX: Float = 16, offsetY: Float = 12, paths: @escaping () -> [String], @ViewBuilder chip: @escaping () -> Chip) -> FileDragSourceView<Self, Chip> where Chip : View

Lets files be dragged out of this view — onto another window, into another app, anywhere a text/uri-list drop is understood.

paths is asked when the drag starts, not when the view is built, so it can answer with whatever is selected by then. chip is what follows the pointer: ordinary views, laid out at their natural size and drawn once by the compositor, which then only moves the result (see DragChipImage). It hangs offsetX,offsetY from the pointer — below and to the right by default, clear of what is being aimed at.

The left button only, after FileDrag.threshold of travel, and only where a compositor can carry a drag (DragBridge). The press is delivered as a press either way, so a row selects itself first.

Sources/LavaUI/DragSource.swift:184

func onFileDrag(paths: @escaping () -> [String]) -> FileDragSourceView<Self, EmptyView>

The same, with nothing but the cursor to show for it.

Sources/LavaUI/DragSource.swift:197

func onFrame(perform: @escaping (CanvasFrame) -> Void) -> BoxRegistrationView<Self>

Runs perform with this view's rectangle, in window coordinates, after each layout pass that places it.

For aiming at views from somewhere that is not their own handler: a tab dragged across a window needs to know which pane is under the pointer, and the panes are not what is receiving the moves.

Called from inside layout. Store the frame; do not change what the tree shows from here — that is a second layout the frame loop never asked for. Not called while the view is hidden.

Sources/LavaUI/DragGesture.swift:261

func onHover(_ action: @escaping (Bool) -> Void) -> ModifiedView<Self>

Calls action(true) when the pointer comes over this view or anything inside it, and action(false) when it leaves all of them.

Not called again while the pointer moves between the view's children: crossing from one button to the next inside a bar is not leaving it. Both run modes — the compositor's hover events and a window's own resolve it the same way.

ControlBar()
    .onHover { inside in session.pointerOnBar = inside }

Sources/LavaUI/HoverRegion.swift:28

func overlay<OverlayContent>(alignment: OverlayAnchor, inset: Float = 0, style: OverlayStyle? = nil, @ViewBuilder content: () -> OverlayContent) -> ComposedOverlayView<Self, OverlayContent> where OverlayContent : View

Draws content over this view, taking no layout space.

The composition overlay, as distinct from overlay(isPresented:), which is a modal popup. Interaction underneath is unaffected: this imposes no input priority and no outside-click dismissal, so it suits a floating button, a badge, or an in-canvas control that is simply always there.

inset insets from the anchored edges; a centred axis ignores it.

style draws a panel under the content — fill, outline, corner radius, padding and frosted backdrop, the same OverlayStyle the presented overlay takes. Omitted, the content floats bare, which is what a badge or an already-styled button wants; the ordinary modifiers still work on whatever is inside the closure, so this is convenience rather than the only way to paint one.

Sources/LavaUI/ComposedOverlay.swift:291

func overlay<OverlayContent>(isPresented: Binding<Bool>, alignment: OverlayAlignment = .below, style: OverlayStyle = OverlayStyle(), @ViewBuilder content: () -> OverlayContent) -> OverlayView<Self, OverlayContent> where OverlayContent : View

Presents content above everything, anchored to this view.

Sources/LavaUI/Overlay.swift:453

func overlay<OverlayContent>(isPresented: Binding<Bool>, placement: OverlayPlacement, style: OverlayStyle = OverlayStyle(), @ViewBuilder content: () -> OverlayContent) -> OverlayView<Self, OverlayContent> where OverlayContent : View

Presents content above everything using a caller-defined frame.

Sources/LavaUI/Overlay.swift:470

func overlayLayer<Layer>(@ViewBuilder _ layer: () -> Layer) -> LayeredView<Self, Layer> where Layer : View

Draws layer over this view, covering its box and taking no layout space or input — presses go through it to what is underneath. See LayeredView.

Sources/LavaUI/Underlay.swift:166

func padding(_ amount: Float) -> ModifiedView<Self>

Uniform inset on every edge.

Sources/LavaUI/Modifiers.swift:503

func padding(_ edges: Edge, _ amount: Float) -> ModifiedView<Self>

Inset only the listed edges by amount.

content.padding(.horizontal, 12)
content.padding([.top, .leading], 4)

Sources/LavaUI/Modifiers.swift:513

func padding(_ insets: EdgeInsets) -> ModifiedView<Self>

Fully specified per-edge inset.

Sources/LavaUI/Modifiers.swift:518

func scrollIntoView(when active: Bool) -> BoxRegistrationView<Self>

Scrolls the nearest enclosing ScrollView so this view is in sight, each time active becomes true — the selected tab in a strip that overflows, say. Only as far as it takes: a view already in sight does not move anything.

Asks once per change rather than holding the view in place, so the container can still be scrolled away from it afterwards.

Sources/LavaUI/ScrollReveal.swift:84

func structureLines(indent: Int) -> [String]

A text rendering of this view's structure, one line per view, for debugging.

indent
Nesting depth of this view; each level indents two spaces.

Sources/LavaUI/View.swift:44

func structureLines(indent: Int = 0) -> [String]

Not documented.

Sources/LavaUI/View.swift:55

func structureLines(indent: Int = 0) -> [String]

Not documented.

Sources/LavaUI/View.swift:104

func theme(_ value: Theme) -> EnvironmentModifiedView<Self>

Overrides the theme for this subtree only; Theme.current (and every other subtree) is unaffected.

Sources/LavaUI/Environment.swift:99

func tooltip(_ text: String, placement: TooltipPlacement = .below) -> ModifiedView<Self>

Shows text beside this view after the pointer has rested on it.

For a control whose meaning is a picture — an icon button. Hidden again by a press, a key or a wheel: whoever is acting already knows what the control is.

IconButton("folder-plus") { session.startNewFolder() }
    .tooltip("New Folder")

placement is the side it prefers; it takes the opposite one when the window has no room there. A tooltip is drawn inside its window, so in a window as short as a panel's strip neither below nor above fits — .leading or .trailing does, level with the view.

Sources/LavaUI/Tooltip.swift:33

func transition(_ transition: Transition = .opacity) -> TransitionView<Self>

Animates this view appearing and disappearing.

Only meaningful where a reconciler can actually insert or remove it: an if, an optional, or a ForEach row. On a view that is always present it animates in once, on first mount.

Sources/LavaUI/Transition.swift:171

func underlay<Layer>(@ViewBuilder _ layer: () -> Layer) -> LayeredView<Self, Layer> where Layer : View

Draws layer behind this view, covering its box and taking no layout space or input. See LayeredView.

Sources/LavaUI/Underlay.swift:157

func windowChrome() -> some View
  

Hidden while the window is maximized — the client-side half of dropping a compositor title bar. Wrap the VStack / strip that holds WindowControls(), not the whole window.

Sources/LavaUI/WindowControls.swift:501

func windowDrag(doubleClickMaximizes: Bool = true) -> WindowDragArea<Self>

Lets the window be dragged by this view's own empty space, and maximized by double-clicking it — what a title bar did, minus the bar.

A no-op in a windowed app, where the window manager's frame is still the thing that moves the window.

Sources/LavaUI/WindowControls.swift:494

Type aliases

associatedtype Body : View

The view type body returns. Primitive views, which draw themselves, use Never.

Sources/LavaUI/View.swift:33