All components live in nirvana_protocol::scene and come in with
use nirvana_protocol::prelude::*. Each has a constructor and chainable
setters, and becomes a Node with .into() (containers take
impl Into<Node>, so inside a .child(…) you rarely write it).
The shape of a window
Three shells, for three kinds of applet:
Page
A centred column of content with a large title. For an applet that is one screen of settings or one list.
Page::new()
.title(tr("Sound"))
.subtitle(tr("Output, input and what plays where."))
.child(group)
.wide() gives the column more room. .key(…) names the page, so that
replacing it with another plays the entrance of a new page.
SplitView
A glass sidebar of destinations on the left, one page on the right. For an applet with several screens (Settings).
SplitView::new()
.sidebar_item(SidebarItem::new("nav.wifi", tr("Wi-Fi")).icon("wifi").selected(true).value(tr("Home")))
.sidebar_item(SidebarItem::new("nav.sound", tr("Sound")).icon("speaker"))
.child(self.page())
SidebarItem also takes .detail(…) (a second line), .busy(true),
.action(icon, label) (a small button at the edge, which sends click to
<id>.action), .context() (right click), .reorder(group) and
.drop_target().
Pane
A working surface instead of a column of settings: a toolbar, a scrolling
body, an optional bar of actions and a status bar. For tools (Calculator,
Files, the Terminal). Used alone or as the content of a SplitView.
Pane::new()
.tool(Text::new(tr("Calculator")).style(TextStyle::Headline))
.tool(Button::icon_only("history", "clock", tr("History")).plain())
.child(body)
.status(Text::new(tr("12 items")).style(TextStyle::Footnote))
Grouping and layout
Group and Row
The workhorse. A Group is an inset rounded list; a Row is one line of it:
an icon, a title and subtitle on the left; a value, controls and an accessory
on the right.
Group::new()
.header(tr("Network"))
.footer(tr("Known networks are joined automatically."))
.child(
Row::new(tr("Wi-Fi"))
.id("open.wifi") // the whole row is clickable
.icon("wifi")
.value(tr("Home"))
.accessory(Accessory::Chevron),
)
.child(Row::new(tr("Airplane Mode")).trailing(Switch::new("airplane", self.airplane).label(tr("Airplane Mode"))))
Row setter | What it does |
|---|---|
.id(…) | Makes the whole row clickable (click). |
.key(…) | Its identity in a changing list. |
.subtitle(…), .value(…) | The quiet line under the title; the text on the right. |
.icon(…), .image(Some(name_or_path)) | A glyph; or an application’s own icon. |
.trailing(node) | A control on the right. Can be called several times. |
.accessory(…) | Chevron, Check, Spinner. |
.tone(…) | Tone::Destructive for a row that deletes, Tone::Accent for one that opens something. |
.context() | Also send context on a right click. |
.disabled(true) | Shown, not pressable. |
Group has .loading(true) (a spinner by the header) and .key(…).
Stack
Free layout where rows do not fit: children in a line, with a gap from the tokens.
Stack::horizontal().gap(Gap::Sm).wrap().child(a).child(b)
Stack::vertical().gap(Gap::Lg).align(Align::Center)
Gap: Xs, Sm, Md, Lg, Xl. Align: Start, Center, End,
Stretch. Justify: Start, Center, End, Between.
Text and marks
| Component | Use |
|---|---|
Text::new(…) | Text. .style(TextStyle::…): LargeTitle, Title, Headline, Body, Callout, Footnote. .tone(Tone::…): Primary, Secondary, Tertiary, Destructive, Success. .centered(). |
IconView::new(name) | A glyph from the icon set. .size(Size::…), .tile(tint) for a tile. |
Badge::new(…) | A short label. .tone(BadgeTone::…). Not for passing states: those go in a subtitle. |
Spinner::new(), Spinner::small() | Work under way. |
Banner::new(BannerTone::…, title) | A message about the page: Info, Success, Warning, Error. .body(…), .action(button). |
EmptyState::new(title) | Nothing to show, and why. .icon(…), .body(…), .action(button). |
Progress::new() | A bar. .level(0.0..=1.0), .label(…). |
Icons are named (wifi, trash, search, folder, settings, …): about
150 glyphs on a 24 px grid.
A tint passed for an icon tile is accepted and ignored: tiles are quiet by
design.
Controls
Button
Button::new("join", tr("Join")).primary()
Button::new("forget", tr("Forget This Network")).destructive()
Button::new("more", tr("More")).plain().size(Size::Small)
Button::icon_only("refresh", "refresh", tr("Refresh")) // the label is for assistive technology
Variants: .primary() (one per view), secondary (the default), .plain(),
.destructive(). Also .icon(name), .size(Size::Small | Regular | Large),
.fill() (as wide as its place), .disabled(true), and .opens_menu(): the
click then carries the point to open a Menu at.
The rest
| Component | Constructor | Notes |
|---|---|---|
Switch | Switch::new(id, checked) | .label(…) is required when it stands without a row title. |
Segmented | Segmented::new(id, &["Day", "Week"], active) | One of 2–5 views or modes. |
Slider | Slider::new(id, value, min, max) | .step(…), .label(…). Acts as it moves. |
TextField | TextField::new(id, value) | .placeholder(…), .label(…), .icon(…), .secure(), .multiline(), .autofocus(), .revision(n), .disabled(true). |
Shortcut | Shortcut::new(id, "Super+Space") | Records a key combination, shown as keycaps. |
Keypad, Key | Keypad::new(key, columns).key(Key::new(id, "7")) | A grid of keys; KeyRole: Digit, Operator, Action, Primary. |
PictureChoice | PictureChoice::new(id, active) | A choice drawn as pictures instead of words. |
QuickGrid | QuickGrid::new().action(id, icon, label, on, detail) | Tiles of quick actions. |
Over the window
Sheet
A modal question or a detail view. Add it as a child of the page while it is open.
Sheet::new(tr("Forget “Home”?"))
.id("forget") // Escape and a click outside send `dismiss` here
.message(tr("This computer will no longer join it automatically."))
.action(Button::new("forget.cancel", tr("Cancel")))
.action(Button::new("forget.confirm", tr("Forget")).destructive())
A sheet can hold children (.child(…)): a field for a password, a few rows.
The actions share the width, the primary one on the right.
Menu
A popup at a point: the answer to a right click, or to a button with
.opens_menu().
// in event: "row" if event.is("context") => self.menu_at = event.point(),
// in render:
if let Some(point) = self.menu_at {
pane = pane.child(
Menu::new("menu", point)
.item(MenuItem::new("menu.open", tr("Open")))
.item(MenuItem::new("menu.rename", tr("Rename…")).shortcut("F2"))
.separator()
.item(MenuItem::new("menu.delete", tr("Move to Trash")).destructive()),
);
}
A click elsewhere, Escape, or the window losing focus sends dismiss to the
menu’s id: set menu_at = None then, and after any item is chosen.
MenuItem also has .icon(…), .checked(true), .disabled(true).
Data
| Component | For |
|---|---|
DataTable | Records with sortable columns and one selected row. TableColumn::new(id, label).numeric(), TableRow::new(id, cells). |
Chart, ChartSeries | A history over time. A second series is a dashed line, never another color. |
StatGrid, Stat | Tiles of numbers. |
BarGrid | Many small levels side by side (per-core load). |
Meter | One labelled level. |
TabBar, Tab | Tabs in a toolbar, each with a close button. |
Gallery, GalleryItem | Pictures to choose one of. |
CalendarGrid, CalendarDay, TaskList, Task | A month, and things to do. |
Files
| Component | For |
|---|---|
FileView | Files as icons or as a table: selection, drag and drop, inline rename. FileLayout: Grid, List, Desktop. The applet owns the selection; the view reports what was done. |
PathBar | Breadcrumbs that become a text field when clicked. |
Preview | A look at a file without opening it. |
Live components
These are created once and kept across renders; their content does not come from the scene.
| Component | What lives in it | Needs |
|---|---|---|
Terminal | A terminal screen (xterm.js). Output goes in with Handle::stream; typing comes back as data. | |
MediaView | A picture, a video or a sound file, with zoom or playback. | "media": true in the registry |
Editor | A plain-text editor. | |
RegionSelect | A rectangle dragged out over a picture (screenshots of an area). | |
VideoEditor | Montage’s workspace: library, preview, tracks. | "media": true |
Browser | Netbird’s window: pages are <webview> elements kept by the runtime. | "webview": true |
Guided steps
For something done in a few steps, one question at a time: Wizard (a rail
of steps on glass, a page, back and next), ChoiceCard (a large choice that
says what choosing it means), DiskMap (a disk drawn to scale),
ProgressRing (long work, its parts ticked off), Persona (a person as they
type themselves in), BrandMark (the system’s mark). The installer and the
welcome are made of these.
The shell’s own
Dock, DockItem, Start, AppGrid, AppTile, AppRow, Hud, Toast,
Desktop, LockScreen, InfoCard, LayoutSwitch, WeatherView: the
pieces the dock, the launcher, notifications, the desktop and the lock screen
are built from. An ordinary applet has no use for them, but they are
components like the rest, and the shell applets are worth reading for how
they are used.
Tokens instead of numbers
| Type | Values |
|---|---|
Size | Small, Regular, Large |
Gap | Xs, Sm, Md, Lg, Xl |
Tone | Primary, Secondary, Tertiary, Accent, Destructive, Success (Accent renders as primary: there is no accent hue) |
TextStyle | LargeTitle, Title, Headline, Body, Callout, Footnote |
Tint | Blue, Green, Orange, Red, Purple, Indigo, Teal, Pink, Yellow, Gray, Accent |