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 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