Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Field (family)

Layer: molecule · Path: src/molecules/field.rs · Exports: field::{Field, FieldGroup, FieldOrientation, FieldSeparator, FieldSet}

The form-layout family. Field wraps any control with a label and a hint/error line in one of three orientations. FieldGroup stacks fields with a standard gap; FieldSet groups them semantically under a legend; FieldSeparator divides groups with an optional inline label. FieldOrientation selects vertical / horizontal / responsive layout. Modeled on the shadcn Field primitives.

Design

  • Purpose / when to use — Build consistent forms: every labeled control through Field, grouped by FieldGroup/FieldSet, divided by FieldSeparator.
  • Anatomy (Field) — A label row (Text::label + an optional * in theme.destructive when required) and a control closure, with a hint or error caption below. Layout is vertical or horizontal per orientation.
  • Tokens / layout consumedcore::SPACE_4 (horizontal label↔control gap; FieldGroup item gap; FieldSet pad), SPACE_2, SPACE_1; layout::FIELD_HORIZONTAL_MIN (= 480.0) for responsive switching. See tokens and layout.
  • Accessibility — required state is marked with a destructive-colored asterisk; error text uses theme.error and replaces the hint when present.

Field

A labeled form field. show runs the control closure and lays out the label + hint/error around it.

API

MethodEffect
Field::new(label: impl Into<String>) -> SelfConstruct with a label; orientation Vertical.
.required() -> SelfAppend a destructive * after the label.
.hint(hint: impl Into<String>) -> SelfMuted caption below the control (suppressed if an error is set).
.error(error: impl Into<String>) -> SelfError caption below the control (takes priority over hint).
.orientation(orientation: FieldOrientation) -> SelfSet layout mode.
.horizontal() -> SelfSugar for FieldOrientation::Horizontal.
.responsive() -> SelfSugar for FieldOrientation::Responsive.
.show(self, ui: &mut Ui, control: impl FnOnce(&mut Ui) -> Response) -> ResponseRender; returns the control’s Response (not a wrapper).

Note the closure signature: control must return a Response (e.g. the return of an Input/Switch/Slider .show(ui)).

Usage

#![allow(unused)]
fn main() {
use ouroboros_ui::molecules::Field;
use ouroboros_ui::atoms::Input;

// minimal — vertical
Field::new("Email").show(ui, |ui| {
    Input::new(&mut email).placeholder("[email protected]").show(ui)
});
}
#![allow(unused)]
fn main() {
use ouroboros_ui::molecules::Field;
use ouroboros_ui::atoms::{Input, Switch};

// required + hint, then error state
Field::new("Email")
    .required()
    .hint("We never share it")
    .show(ui, |ui| Input::new(&mut email).show(ui));

Field::new("Username")
    .error("Already taken")
    .show(ui, |ui| Input::new(&mut user).error(true).show(ui));

// horizontal — label ↔ control (good for switches)
Field::new("Vsync")
    .horizontal()
    .show(ui, |ui| Switch::new(&mut vsync).show(ui));
}

FieldOrientation

Field layout mode.

VariantBehavior
Vertical (default)Label above, control below.
HorizontalLabel and control side by side (SPACE_4 gap), hint/error under the control.
ResponsiveHorizontal when ui.available_width() >= layout::FIELD_HORIZONTAL_MIN (480.0), else vertical.

Responsive evaluates the available width at show time each frame, so a field re-stacks as its container resizes. See layout.


FieldGroup

A zero-config stacker: runs a content closure inside a vertical layout with item_spacing.y = SPACE_4, so consecutive fields get uniform spacing.

API

MethodEffect
FieldGroup::new() -> SelfConstruct. (Default also available.)
.show(self, ui: &mut Ui, content: impl FnOnce(&mut Ui)) -> ResponseRender the stacked content. Returns the vertical layout Response.

Usage

#![allow(unused)]
fn main() {
use ouroboros_ui::molecules::{Field, FieldGroup};

FieldGroup::new().show(ui, |ui| {
    Field::new("Name").show(ui, |ui| Input::new(&mut name).show(ui));
    Field::new("Email").show(ui, |ui| Input::new(&mut email).show(ui));
});
}

FieldSet

A semantic group with an optional legend, rendered inside a Surface with SurfaceFill::None (padding only, no fill).

API

MethodEffect
FieldSet::new() -> SelfConstruct. (Default also available.)
.legend(legend: impl Into<String>) -> SelfOptional heading label above the content (Text::label).
.show(self, ui: &mut Ui, content: impl FnOnce(&mut Ui)) -> ResponseRender the legend + content. Returns the surface Response.

Usage

#![allow(unused)]
fn main() {
use ouroboros_ui::molecules::{FieldSet, RadioGroup};

FieldSet::new().legend("Display").show(ui, |ui| {
    RadioGroup::new(&mut sel)
        .options(["Windowed", "Fullscreen"])
        .show(ui);
});
}

FieldSeparator

A horizontal Divider between field groups, optionally with a centered inline caption below the rule.

(v1: rule + centered caption; true inline line–text–line is a later refinement.)

API

MethodEffect
FieldSeparator::new() -> SelfPlain divider. (Default also available.)
.label(label: impl Into<String>) -> SelfAdd a centered muted caption (e.g. "OR").
.show(self, ui: &mut Ui) -> ResponseRender; returns the divider Response.

Usage

#![allow(unused)]
fn main() {
use ouroboros_ui::molecules::FieldSeparator;

FieldSeparator::new().show(ui);              // bare rule
FieldSeparator::new().label("OR").show(ui);  // rule + centered caption
}

Composition

The family composes Text, Surface (with SurfaceFill::None), and Divider only; controls and grouped content are caller closures. Nothing here paints — see the guards.

Notes

  • Field::show’s control closure must return a Response — pass through the inner atom’s .show(ui).
  • Error wins over hint: when both are set, only the error caption renders.
  • The label row is omitted (and no label→control gap added) when the label string is empty.
  • Responsive reads available_width live, so wrap it in a sized region if you need deterministic behavior.