Skip to main content

WidgetSpec

Enum WidgetSpec 

Source
pub enum WidgetSpec {
    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>,
    },
    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,
        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>,
    },
}
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
§

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.

§

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

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