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.

Install

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. Chain label, 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

GPUINo GPUI version of this example yet.
SlintNo Slint version of this example yet.
// 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 both left and right, end-aligned with only left, and start-aligned otherwise (only right, 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 Button only for clickable items.
  • Colors come from the status_bar (background) and status_bar_border theme tokens, which fall back to background / border.

#API Reference

#StatusBar

MethodDescription
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

NameKindTypeDefault
kindfieldStatusItemKind
iconfieldstring
labelfieldstring

#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.

NameKindTypeDefaultDescription
leftin[StatusItem]
rightin[StatusItem]
backgroundincolorTheme.sidebarOverridable, the way `Styled` lets a GPUI `StatusBar` restyle itself.
border-colorincolorTheme.border
left-clickedcallback(index: int)
right-clickedcallback(index: int)