Skip to main content
render records hit regions. Your Wayland loop passes pointer and keyboard events through dispatch, or you can call hitTest yourself.

Hit regions

Each interactive node with hit set appends a HitRegion:
After render + finish:
hitTest picks the topmost enabled region containing the point (higher z wins).

dispatch event router

UiState.dispatch updates input snapshot and returns a DispatchResult:
Feed the same input snapshot back into the next frame’s FrameOptions.input so controls render hovered/pressed/focused states.

InputSnapshot

Focus traversal

Set focus_scope = true on modal panels. keyboard_focus events with FocusMove (.next, .previous, .first, .last) walk focusable hits inside that scope. Focusable kinds include buttons, text inputs, toggles, sliders, selects, and tabs.

Overlays

Popups that must float above the main tree go through queueOverlay, not stack.
finish() renders overlays after the root tree. Overlay hits set HitFlags.overlay = true. Dismiss when clicking outside:
Placement .below flips to .above automatically when the popup would clip past the viewport bottom.

Tooltips

Empty text is skipped.

UniformList (virtualized rows)

Fixed-height lists without allocating per-row nodes. Compute which indices are visible, build only those list_row nodes each frame.
Helpers: UniformGrid adds column-aware scrolling and hit testing for grid layouts.

Scrollbars

  1. Keep scroll offset in app state.
  2. Pass ui.ScrollState with content and viewport heights into ScrollbarSpec.
  3. On dispatch .scroll over the list area, adjust offset.
  4. Optionally add a scrollbar node bound to the same ScrollState.
ui_state.scrollState(id) returns a persistent slot keyed by SurfaceId.

Text input and IME

  1. text_input node with focused = true when input.focused matches.
  2. On text_input_focus dispatch, enable Wayland text-input-v3 on that surface.
  3. Pipe committed text and preedit into InputBuffer.
  4. Pass buffer contents back into TextInputSpec each frame.
See otter-settings and otter-launcher for full IME wiring.

Damage and partial redraw

After rasterize, merge damage from:
  • DamageTracker for surface commits
  • overlayDamageRect() when overlays animate open/close

Debug inspector

Ctrl+Shift+I toggles element rects and semantics. findElement(id) returns the laid-out Element after render.

Next