Skip to main content

WidgetSpec

Enum WidgetSpec 

Source
pub enum WidgetSpec {
Show 13 variants Row { children: Vec<WidgetSpec>, key: Option<String>, }, Col { children: Vec<WidgetSpec>, key: Option<String>, }, HintBar { entries: Vec<HintEntry>, key: Option<String>, }, Toggle { checked: bool, label: String, focused: bool, key: Option<String>, }, Button { label: String, focused: bool, intent: ButtonKind, key: Option<String>, disabled: bool, }, Spacer { cols: u32, flex: bool, key: Option<String>, }, List { items: Vec<TextPropertyEntry>, item_keys: Vec<String>, selected_index: i32, visible_rows: u32, focusable: bool, key: Option<String>, }, Tree { nodes: Vec<TreeNode>, item_keys: Vec<String>, selected_index: i32, visible_rows: u32, expanded_keys: Vec<String>, checkable: bool, key: Option<String>, }, Text { value: String, cursor_byte: i32, focused: bool, label: String, placeholder: Option<String>, rows: u32, field_width: u32, max_visible_chars: u32, full_width: bool, completions: Vec<CompletionItem>, completions_visible_rows: u32, key: Option<String>, }, LabeledSection { label: String, child: Box<WidgetSpec>, width_pct: Option<u32>, key: Option<String>, }, WindowEmbed { window_id: u32, rows: u32, key: Option<String>, }, Raw { entries: Vec<TextPropertyEntry>, key: Option<String>, }, Overlay { child: Box<WidgetSpec>, key: Option<String>, },
}
Expand description

Declarative widget tree. Each variant is one node; nested composition is via Row { children } / Col { children }.

key is the stable identifier used by the reconciler to match a node across MountWidgetPanel / UpdateWidgetPanel calls — when the plugin re-emits a Spec, instance state (cursor offset, scroll, expanded keys, hover) is preserved on nodes whose key matches. Plugins should provide stable keys for any widget that owns instance state; stateless widgets (HintBar, Toggle, Button, Spacer) can omit it.

Variants§

§

Row

Horizontal layout: children laid out left-to-right.

Fields

§children: Vec<WidgetSpec>
§

Col

Vertical layout: children stacked top-to-bottom.

Fields

§children: Vec<WidgetSpec>
§

HintBar

Keyboard-hint footer (one row, comma-separated <keys> <label> items).

Fields

§entries: Vec<HintEntry>
§

Toggle

Boolean toggle, rendered as [v] label / [ ] label. The focused flag controls the focus-styling overlay; the host will own focus once the keymap layer is wired (today the plugin passes it explicitly per render).

Fields

§checked: bool
§label: String
§focused: bool
§

Button

Action button, rendered as [ Label ] (or [ Label ] with emphasized styling for Primary/Danger). Focused buttons flip foreground/background using the active menu theme keys.

intent is the button’s visual role (Normal / Primary / Danger); the field is named intent rather than kind because kind is the discriminator for the outer WidgetSpec tag.

Fields

§label: String
§focused: bool
§intent: ButtonKind
§disabled: bool

When true, the button renders in a muted style, is dropped from the Tab cycle, and clicks on it are ignored. Use for actions that aren’t currently available against the surrounding state (e.g. “Archive” on the base session). The button still occupies its layout cell so the surrounding row doesn’t reshuffle when the disabled flag flips.

§

Spacer

Horizontal whitespace eater. In a Row, produces cols spaces (or fills remaining width if flex: true); in a Col, produces cols blank lines (flex is ignored).

flex: true distributes the row’s leftover width — panel width - sum(non-flex child widths) — across flex spacers. With multiple flex spacers in one row the leftover splits evenly. With no leftover (children already exceed panel width), the flex spacer collapses to zero.

Fields

§cols: u32
§flex: bool
§

List

Vertical list of pre-rendered rows with host-managed selection styling, click routing, and virtual scrolling.

The plugin passes the full dataset of items + a visible_rows count (typically the panel’s available height). The host owns the scroll offset as widget instance state, keyed by the spec’s key — so a key is required for any List that should preserve scroll across re-renders. The scroll offset auto-clamps to keep selected_index in view; plugins never compute scroll math.

Each item is one rendered row (TextPropertyEntry). item_keys is a parallel array of stable per-item identifiers the plugin uses to map a click event back to its model (e.g. "file:5/match:23"); the array length must match items.len(). Missing keys default to empty string.

selected_index is the absolute index into items (-1 for no selection); the host paints the selected row with ui.menu_active_bg extended to line end. Clicks fire widget_event { event_type: "select", payload: { index, key } } where index is the absolute (not visible-window) index.

Fields

§item_keys: Vec<String>
§selected_index: i32
§visible_rows: u32

Number of rows of the panel’s available height the list should occupy. Plugin computes from its viewport. The host shows up to this many items per render.

§focusable: bool

Whether Tab / Shift+Tab will land focus on this list. Defaults to true (lists are normal tabbable widgets). Picker-style usage typically sets this to false so Tab moves between the filter input and the action buttons, while Up/Down on the focused filter still forwards to the list via host smart-key dispatch.

§

Tree

Hierarchical list with host-managed expand/collapse, selection styling, click routing, and virtual scrolling.

The plugin emits its tree as a depth-first flat list of TreeNodes (each carrying a depth and has_children flag) plus a parallel item_keys array. The host filters out descendants of collapsed nodes when rendering the visible window, so the plugin always emits the full tree — toggling expansion is host-owned (instance state) rather than the plugin re-emitting on every / press.

expanded_keys is initial-only (seeded into instance state on first render); subsequent expansion changes flow through WidgetCommand::Key (Right/Left) or click on the disclosure glyph — neither requires the plugin to re-emit. Plugins that need to react to expansion changes listen for widget_event { event_type: "expand" }.

selected_index is the absolute index into nodes (initial-only; instance state takes over). Click on a row fires widget_event { event_type: "select", payload: { index, key } }; click on the disclosure column fires widget_event { event_type: "expand", payload: { index, key, expanded } }. Enter/Space on the focused tree fires widget_event { event_type: "activate", payload: { index, key } }.

Fields

§nodes: Vec<TreeNode>
§item_keys: Vec<String>
§selected_index: i32
§visible_rows: u32
§expanded_keys: Vec<String>

Initial-only set of expanded item keys. Once the widget has rendered, the host’s instance-state expanded_keys is authoritative; updating this field on subsequent specs has no effect (use WidgetMutation::SetExpandedKeys to override host state).

§checkable: bool

When true, every node with checked: Some(_) renders a [v] / [ ] glyph and emits a toggle hit area over the glyph. Click on the glyph fires widget_event { event_type: "toggle", payload: { key, checked: <new> } }; the plugin updates its model and pushes the new state back via WidgetMutation::SetCheckedKeys.

§

Text

Single-line text input, rendered as [value] with a cursor highlight at the byte position given by cursor_byte (when cursor_byte >= 0). When value is empty and the input is not focused, placeholder (if set) is shown instead.

v1 is a render-only widget: the host owns visual cursor styling and theme-keyed focus, but the plugin still owns the value string and cursor position. Keystrokes (Backspace, arrows, character input) flow through the plugin’s existing defineMode + mode_text_input plumbing; the plugin re-emits the spec on every change. The keymap-routing layer (host claims widget keys before the plugin sees them) lands in a later commit. Text input — single-line (rows == 1, default) or multi-line (rows > 1). The host owns value and cursor_byte as instance state once the widget renders for the first time; the spec’s values are initial-only.

Single-line vs multi-line behaviour is selected by rows:

  • rows == 1 — renders as [value] with the cursor pinned to a constant field_width (head-truncate when the value exceeds it). Enter advances focus (form-like UX). Up/Down are no-ops. Home/End jump to the start / end of the whole value.
  • rows > 1 — renders as rows lines tall (padded with blanks when value is shorter). Enter inserts a newline at the cursor. Up/Down move between lines (clamped to each line’s column count). Home/End jump within the current line. The host auto-scrolls vertically to keep the cursor’s line visible.

Smart-key dispatch (WidgetCommand::Key) selects the right behaviour from rows. Plugins that want a different Enter binding intercept the key in their own mode binding before dispatching it through the smart-key router.

label (when non-empty) renders inline before [ for single-line, and as a row above the editing region for multi-line. placeholder shows when value is empty and the field is unfocused (first row only for multi-line). field_width controls visible column width: 0 = auto-fit (single-line) or panel width (multi-line). max_visible_chars is a single-line soft cap applied after the field-width pad (0 = no cap; ignored when rows > 1).

Fields

§value: String

Initial text. Spec value is read at first render only; instance state takes over thereafter.

§cursor_byte: i32

Initial byte-offset cursor within value. Negative (encoded as i32 in JSON) means “no cursor” — clamped to [0, value.len()] host-side.

§focused: bool

Whether this widget has visual focus.

§label: String

Optional label rendered before / above the editing region. Empty = omitted.

§placeholder: Option<String>

Placeholder shown when unfocused and value is empty.

§rows: u32

Number of visible rows of editing region. 0 falls back to 1 (single-line). 1 = single-line behaviour; >= 2 = multi-line behaviour. See the type-level doc for the per-mode semantics.

§field_width: u32

Visible column width. 0 = auto-fit (single-line) or panel width (multi-line). When set, single-line head-truncates with and multi-line tail-truncates per-line.

§max_visible_chars: u32

Single-line soft cap on visible chars after the field_width pad. 0 = no cap. Ignored when rows > 1.

§full_width: bool

Stretch the visible field to fill the available width of the enclosing container. Overrides field_width when set: the renderer computes panel_width - label_overhead - bracket_overhead as the effective visible width. Multi-line widgets already fill the panel width by default; this flag is most useful for single-line inputs inside a LabeledSection or a flexible row.

§completions: Vec<CompletionItem>

Optional completion candidates. When non-empty AND label is non-empty (the chrome trigger), the renderer paints a popup directly under the input, inside a unified box: the input’s normal ╰─...─╯ bottom border becomes a dimmed separator, the labeled section’s side borders extend down through the candidate rows, and a single ╰─...─╯ bottom closes the whole block. Candidates render left- aligned with the input’s text (the position right after [), with the host-managed selected index highlighted.

Smart-key dispatch on a focused Text-with-completions: Up/Down moves selection (host-internal, no event), Tab fires completion_accept with the selected candidate, Enter / Escape fire completion_dismiss (the dispatcher’s normal “Enter focus-advance / Esc close panel” only runs once the popup is closed).

Plugins push candidates in response to the text widget’s change event via WidgetMutation::SetCompletions. An empty items closes the popup.

§completions_visible_rows: u32

How many candidate rows the popup paints at once when it opens. Excess candidates stay reachable via Up/Down (host auto-scrolls to keep selection in view) or the mouse wheel; a thumb glyph paints in the right edge of the popup whenever there’s more to scroll. 0 (default) falls back to 5.

§

LabeledSection

Visual grouping container: renders a rounded thin border around a single child widget, with label printed as a top-left legend overlapping the border (HTML <fieldset> semantics).

Layout (border drawn with ╭─╮│╰─╯):

╭─ Label ──────────────────╮
│ <child rendered content> │
╰──────────────────────────╯

Width: the section always occupies the full panel_width passed down by its parent container. The child is rendered with panel_width - 4 (two border columns + two padding columns) so widgets that honour full_width size themselves to the inner area.

The child can be any single WidgetSpec — typically a Text input, but a Toggle/Button/nested Col also works. Focus, hit areas and cursor positions bubble up from the child unchanged, shifted by the section’s border offset (1 row down, 2 columns in).

Fields

§label: String

Legend text printed in the top border. Empty = no legend (the top border becomes one unbroken line).

§child: Box<WidgetSpec>

The single wrapped widget. Boxed because WidgetSpec is recursive.

§width_pct: Option<u32>

When this section is a Block child of a Row, request width_pct percent of the row’s panel_width instead of the equal-split default. Multiple siblings with width_pct set sum to ≤ 100; the remainder splits equally among siblings without an explicit width. Out-of-range values (0 or > 100) fall back to the equal-split path.

§

WindowEmbed

Reserve a rectangle in the widget layout for the host to natively paint the editor Window identified by window_id. The widget itself renders only blank lines so subsequent passes (split tree, terminal grids, syntax highlighting, decorations) can be drawn into the reserved cells by the existing per-window render path.

rows controls the embed’s height. Width is whatever the parent container allocates (panel_width for a direct Col child; the block’s column_width inside a Row’s horizontal-zip path). Used by Orchestrator’s open dialog so the preview pane shows a live render of the highlighted session.

Fields

§window_id: u32

Numeric editor-window id, matching WindowId(N).0. 0 (or any unknown id) renders empty placeholder rows without dispatching the per-window render. u32 rather than u64 to keep the TS binding a plain number; window ids never exceed 4B in practice.

§rows: u32

Number of visible rows the embed should occupy.

§

Raw

Imperative-virtual-buffer escape hatch. The plugin supplies TextPropertyEntry[] exactly as it would for setVirtualBufferContent; the host inlines those entries into the rendered panel without further interpretation. Used during migration to wrap existing hand-rolled rendering inside a new widget panel.

Fields

§

Overlay

Float child over the rest of the layout instead of consuming vertical space. Placed inside a Col, the overlay anchors at the row it would have occupied if it were a regular child — but the rows below it DO NOT shift down. At paint time the overlay is drawn last, over whatever’s beneath it, like a tooltip / popup.

Use case: dropdown completions, hover popups, transient hints that should appear right next to the focused widget without reflowing the rest of the panel each time they show / hide.

Hit testing: overlays paint on top, so clicks inside an overlay’s region go to the overlay (not whatever’s underneath). Tab cycle: the host’s collect_tabbable walks into the overlay’s child like any other widget; give the child a key if you want it focusable, or leave it keyless to keep it out of the cycle.

Fields

Implementations§

Source§

impl WidgetSpec

Source

pub fn children(&self) -> Box<dyn Iterator<Item = &WidgetSpec> + '_>

Iterate this widget’s immediate child specs in declaration order. Container kinds (Row, Col, LabeledSection) return their nested children; leaf kinds return an empty iterator.

Generic tree walkers (focus dispatch, hit-area lookup, scrollable-widget detection, instance-state mutation) call this instead of pattern-matching every container variant by hand, so adding a new container kind is a single update here rather than touching every walker. The box is the price for returning an iterator whose type depends on the variant; the allocation is single-digit-byte and dwarfed by everything else in the dispatch path.

Source

pub fn children_mut(&mut self) -> Box<dyn Iterator<Item = &mut WidgetSpec> + '_>

Mutable counterpart of [children]. Same set of container kinds, same semantics — the iterator yields exclusive references so walkers that mutate (e.g. set_*_in_spec) can recurse generically.

Trait Implementations§

Source§

impl Clone for WidgetSpec

Source§

fn clone(&self) -> WidgetSpec

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for WidgetSpec

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for WidgetSpec

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Serialize for WidgetSpec

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl TS for WidgetSpec

Source§

type WithoutGenerics = WidgetSpec

If this type does not have generic parameters, then WithoutGenerics should just be Self. If the type does have generic parameters, then all generic parameters must be replaced with a dummy type, e.g ts_rs::Dummy or ().
The only requirement for these dummy types is that EXPORT_TO must be None. Read more
Source§

type OptionInnerType = WidgetSpec

If the implementing type is std::option::Option<T>, then this associated type is set to T. All other implementations of TS should set this type to Self instead.
Source§

fn ident(cfg: &Config) -> String

Identifier of this type, excluding generic parameters.
Source§

fn docs() -> Option<String>

JSDoc comment to describe this type in TypeScript - when TS is derived, docs are automatically read from your doc comments or #[doc = ".."] attributes
Source§

fn name(cfg: &Config) -> String

Name of this type in TypeScript, including generic parameters
Source§

fn decl_concrete(cfg: &Config) -> String

Declaration of this type using the supplied generic arguments. The resulting TypeScript definition will not be generic. For that, see TS::decl(). If this type is not generic, then this function is equivalent to TS::decl().
Source§

fn decl(cfg: &Config) -> String

Declaration of this type, e.g. type User = { user_id: number, ... }. This function will panic if the type has no declaration. Read more
Source§

fn inline(cfg: &Config) -> String

Formats this types definition in TypeScript, e.g { user_id: number }. This function will panic if the type cannot be inlined.
Source§

fn inline_flattened(cfg: &Config) -> String

Flatten a type declaration. This function will panic if the type cannot be flattened.
Source§

fn visit_generics(v: &mut impl TypeVisitor)
where Self: 'static,

Iterates over all type parameters of this type.
Source§

fn output_path() -> Option<PathBuf>

Returns the output path to where T should be exported, relative to the output directory. The returned path does not include any base directory. Read more
Source§

fn visit_dependencies(v: &mut impl TypeVisitor)
where Self: 'static,

Iterates over all dependency of this type.
Source§

fn dependencies(cfg: &Config) -> Vec<Dependency>
where Self: 'static,

Resolves all dependencies of this type recursively.
Source§

fn export(cfg: &Config) -> Result<(), ExportError>
where Self: 'static,

Manually export this type to the filesystem. To export this type together with all of its dependencies, use TS::export_all. Read more
Source§

fn export_all(cfg: &Config) -> Result<(), ExportError>
where Self: 'static,

Manually export this type to the filesystem, together with all of its dependencies. To export only this type, without its dependencies, use TS::export. Read more
Source§

fn export_to_string(cfg: &Config) -> Result<String, ExportError>
where Self: 'static,

Manually generate bindings for this type, returning a String. This function does not format the output, even if the format feature is enabled. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,