StatusBar
StatusBar is a horizontal bar split into three regions — left, center, and right. It is usually placed at the bottom of a window or pane to show contextual information and quick actions.
The design mirrors the status bars found in native UI frameworks: Windows StatusStrip, WPF StatusBar, and macOS NSStatusBar.
#Import
use gpui_kit::component::status_bar::StatusBar;// Installed in ui/components. That directory is on the Slint include path, so import by file name:
import { StatusItemKind, StatusItem, StatusBar } from "status-bar.slint";use iced::widget::{Space, column, container, mouse_area, scrollable, svg, text};
use iced::{Alignment, Element, Length, Task, Theme};#Regions
Pass any impl IntoElement — a string, an Icon, a Button, a custom layout, etc. — to a region. left and right pin items to each end; child / children add to the center, whose alignment follows the pinned ends — centered with both left and right, end-aligned with only left, start-aligned otherwise (only right, or neither, like a plain container). Call a method multiple times to add more.
- For a non-interactive label, pass a plain string — it inherits the bar’s text style and has no hover.
- For a clickable button, pass a ghost, xsmall
Button—Button::new(id).ghost().xsmall()— so buttons stay a consistent size. Chainlabel,icon,tooltip,on_click, etc. - For a separator, pass
Separator::vertical(). - For anything else, pass the element directly.
#Usage
#Labels
StatusBar::new()
.left("Ready")
.child("README.md")
.right("UTF-8")Rectangle {
width: 100%;
height: 60px;
border-width: 1px;
border-color: Theme.border;
border-radius: Theme.radius-md;
clip: true;
VerticalLayout {
Rectangle {
vertical-stretch: 1;
}
StatusBar {
left: [
{ kind: StatusItemKind.label, icon: "", label: "Ready" }
];
right: [
{ kind: StatusItemKind.label, icon: "", label: "UTF-8" }
];
Text {
text: "README.md";
color: Theme.muted-foreground;
font-size: Theme.font-size-xs;
vertical-alignment: center;
}
}
}
}// A label segment is read-only: muted ink, no hover and no surface. The
// centre's alignment follows the two ends, so with both present it is
// centered between them.
let labels = pane(
60.0,
StatusBar::new()
.left(StatusItem::label("Ready"))
.child(StatusItem::label("README.md"))
.right(StatusItem::label("UTF-8")),
);#Buttons
StatusBar::new()
.left(
Button::new("branch").ghost().xsmall()
.icon(IconName::Github)
.label("main")
.on_click(|_, window, cx| { /* ... */ }),
)
.right(
Button::new("go-to-line").ghost().xsmall()
.label("Ln 1, Col 1")
.tooltip("Go to Line/Column")
.on_click(cx.listener(|this, _, window, cx| { /* ... */ })),
)VerticalLayout {
spacing: 10px;
alignment: start;
Rectangle {
width: 100%;
height: 48px;
border-width: 1px;
border-color: Theme.border;
border-radius: Theme.radius-md;
clip: true;
VerticalLayout {
Rectangle {
vertical-stretch: 1;
}
StatusBar {
left: [
{ kind: StatusItemKind.button, icon: "github", label: "main" }
];
right: [
{ kind: StatusItemKind.button, icon: "", label: "Ln 1, Col 1" }
];
left-clicked(index) => {
root.pressed = "the branch";
}
right-clicked(index) => {
root.pressed = "the cursor position";
}
}
}
}
Text {
text: "Last click: " + root.pressed;
color: Theme.muted-foreground;
font-size: Theme.font-size-xs;
}
}// A button segment is the shared ghost button at the extra-small size, so it
// takes the hover accent and reports the message its caller gave it.
let buttons = column![
pane(
48.0,
StatusBar::new()
.left(
StatusItem::button("main")
.icon(github())
.on_press(Message::Pressed("the branch")),
)
.right(
StatusItem::button("Ln 1, Col 1")
.on_press(Message::Pressed("the cursor position")),
),
),
muted(format!("Last click: {}", app.pressed)),
]
.spacing(10.0);#Separators and custom elements
StatusBar::new()
.left(Button::new("branch").ghost().xsmall().icon(IconName::Github).label("main"))
.left(Separator::vertical())
.left(
// Any custom element works.
h_flex()
.items_center()
.gap_1()
.child(Icon::new(IconName::CircleCheck).xsmall())
.child("0 problems"),
)
.child(Progress::new("indexing").value(60.).w_24())Rectangle {
width: 100%;
height: 60px;
border-width: 1px;
border-color: Theme.border;
border-radius: Theme.radius-md;
clip: true;
VerticalLayout {
Rectangle {
vertical-stretch: 1;
}
StatusBar {
left: [
{ kind: StatusItemKind.button, icon: "github", label: "main" },
{ kind: StatusItemKind.separator, icon: "", label: "" },
{ kind: StatusItemKind.label, icon: "circle-check", label: "0 problems" },
];
right: [
{ kind: StatusItemKind.label, icon: "", label: "Ln 12, Col 4" }
];
Rectangle {
width: 96px;
Progress {
value: 60;
size: ControlSize.xsmall;
}
}
}
}
}// A separator divides runs of items, and the centre holds any element — here
// the same progress bar the Slint example puts there.
let mixed = pane(
60.0,
StatusBar::new()
.left(StatusItem::button("main").icon(github()))
.left(StatusItem::separator())
.left(StatusItem::label("0 problems").icon(circle_check()))
.right(StatusItem::label("Ln 12, Col 4"))
.child(
container(Progress::new(0.6).size(ControlSize::XSmall))
.width(Length::Fixed(PROGRESS_WIDTH)),
),
);#Custom styling
// The fill and the edge are the bar's own properties; both resolve the theme
// in force when the bar is drawn, not when it is built.
let custom = pane(
60.0,
StatusBar::new()
.background(|theme| Tokens::of(theme).secondary)
.border_color(|theme| Tokens::of(theme).border)
.left(StatusItem::label("Ready"))
.right(StatusItem::label("Rust")),
);StatusBar implements Styled, so any style method overrides the defaults.
StatusBar::new()
.bg(cx.theme().secondary)
.border_color(cx.theme().border)
.left("Ready")Rectangle {
width: 100%;
height: 60px;
border-width: 1px;
border-color: Theme.border;
border-radius: Theme.radius-md;
clip: true;
VerticalLayout {
Rectangle {
vertical-stretch: 1;
}
StatusBar {
background: Theme.secondary;
border-color: Theme.border;
left: [
{ kind: StatusItemKind.label, icon: "", label: "Ready" }
];
right: [
{ kind: StatusItemKind.label, icon: "", label: "Rust" }
];
}
}
}#Notes
- The center (via
child/children) is centered with bothleftandright, end-aligned with onlyleft, and start-aligned otherwise (onlyright, or neither — like a plain container). - Use a plain string (or any non-interactive element) for read-only items to avoid the button hover effect; use a ghost xsmall
Buttononly for clickable items. - Colors come from the
status_bar(background) andstatus_bar_bordertheme tokens, which fall back tobackground/border.
#API Reference
#StatusBar
| Method | Description |
|---|---|
new() | Create a new, empty status bar |
left(child) | Append an element to the left region (call to add more) |
right(child) | Append an element to the right region |
child(c) / children(cs) | Add element(s) to the center region |
Each region method takes impl IntoElement. StatusBar also implements Styled, so style methods (bg, border_color, py, etc.) can override the defaults.
#StatusItemKind
What a segment is. A `button` takes the hover affordance and reports clicks; a `label` is read-only; a `separator` is the vertical rule between groups.
label, button, separator
#StatusItem
| Name | Kind | Type | Default |
|---|---|---|---|
kind | field | StatusItemKind | |
icon | field | string | |
label | field | string |
#StatusBar
A window footer. Mirrors GPUI Component's `StatusBar`: a left region, a right region, and a centre between them. GPUI appends to the left and right with repeated `left()` / `right()` calls; here each region is a list. The centre is `@children`, so it holds anything a progress bar or a custom readout needs. The centre is centred when both regions have something in them, end-aligned when only the left does, and start-aligned otherwise -- which is what `space-between` does with the groups that are present.
| Name | Kind | Type | Default | Description |
|---|---|---|---|---|
left | in | [StatusItem] | ||
right | in | [StatusItem] | ||
background | in | color | Theme.sidebar | Overridable, the way `Styled` lets a GPUI `StatusBar` restyle itself. |
border-color | in | color | Theme.border | |
left-clicked | callback | (index: int) | ||
right-clicked | callback | (index: int) |