Skip to main content

gpui_component/
status_bar.rs

1use gpui::{
2    AnyElement, App, IntoElement, ParentElement, RenderOnce, StyleRefinement, Styled, Window,
3    prelude::FluentBuilder as _,
4};
5use smallvec::SmallVec;
6
7use crate::{ActiveTheme, StyledExt, h_flex};
8
9/// A horizontal status bar, usually placed at the bottom of a window or pane.
10///
11/// It is split into three regions — `left`, `center`, and `right`. This mirrors
12/// the status bars found in native UI frameworks (Windows `StatusStrip`, WPF
13/// `StatusBar`, macOS `NSStatusBar`): a container that holds a row of items
14/// aligned to either end.
15///
16/// Each region accepts any [`IntoElement`], so a string, an [`Icon`](crate::Icon),
17/// a ghost `Button`, a vertical `Separator`, a custom layout, etc. can be passed
18/// directly. Use a plain string for a non-interactive label.
19///
20/// `left` and `right` pin items to each end. `child`/`children` add to the
21/// center region, whose alignment follows the pinned ends: centered with both
22/// `left` and `right`, end-aligned with only `left`, and start-aligned
23/// otherwise (only `right`, or neither — like a plain container).
24///
25/// ```
26/// # mod gpui_kit { pub extern crate gpui_component as component; }
27/// use gpui_kit::component::status_bar::StatusBar;
28///
29/// let _ = StatusBar::new().left("Ln 1, Col 1").right("UTF-8");
30/// ```
31#[derive(IntoElement)]
32pub struct StatusBar {
33    style: StyleRefinement,
34    left: SmallVec<[AnyElement; 1]>,
35    right: SmallVec<[AnyElement; 1]>,
36    children: SmallVec<[AnyElement; 1]>,
37}
38
39impl StatusBar {
40    /// Create a new, empty [`StatusBar`].
41    pub fn new() -> Self {
42        Self {
43            style: StyleRefinement::default(),
44            left: SmallVec::new(),
45            right: SmallVec::new(),
46            children: SmallVec::new(),
47        }
48    }
49
50    /// Append an element to the left region. Call multiple times to add more.
51    pub fn left(mut self, child: impl IntoElement) -> Self {
52        self.left.push(child.into_any_element());
53        self
54    }
55
56    /// Append an element to the right region. Call multiple times to add more.
57    pub fn right(mut self, child: impl IntoElement) -> Self {
58        self.right.push(child.into_any_element());
59        self
60    }
61}
62
63/// `child` / `children` add to the center region, so a `StatusBar` without
64/// `left`/`right` items behaves like a plain container.
65impl ParentElement for StatusBar {
66    fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
67        self.children.extend(elements);
68    }
69}
70
71impl Styled for StatusBar {
72    fn style(&mut self) -> &mut StyleRefinement {
73        &mut self.style
74    }
75}
76
77impl RenderOnce for StatusBar {
78    fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
79        // The center aligns by which ends are pinned: centered with both left
80        // and right, end-aligned with only left, otherwise start-aligned (only
81        // right, or neither) — so a bar with just `child`s reads like a container.
82        let has_left = !self.left.is_empty();
83        let has_right = !self.right.is_empty();
84        let region = || h_flex().overflow_hidden().items_center().gap_2();
85
86        h_flex()
87            .items_center()
88            .gap_2()
89            .py_1()
90            .px_2()
91            .border_t_1()
92            .border_color(cx.theme().status_bar_border)
93            .bg(cx.theme().tokens.status_bar)
94            .text_xs()
95            .text_color(cx.theme().muted_foreground)
96            .refine_style(&self.style)
97            .when(has_left, |this| this.child(region().children(self.left)))
98            .child(
99                region()
100                    .flex_1()
101                    .when(has_left && has_right, |this| this.justify_center())
102                    .when(has_left && !has_right, |this| this.justify_end())
103                    .children(self.children),
104            )
105            .when(has_right, |this| this.child(region().children(self.right)))
106    }
107}