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/// use gpui_kit::component::status_bar::StatusBar;
27///
28/// let _ = StatusBar::new().left("Ln 1, Col 1").right("UTF-8");
29/// ```
30#[derive(IntoElement)]
31pub struct StatusBar {
32    style: StyleRefinement,
33    left: SmallVec<[AnyElement; 1]>,
34    right: SmallVec<[AnyElement; 1]>,
35    children: SmallVec<[AnyElement; 1]>,
36}
37
38impl StatusBar {
39    /// Create a new, empty [`StatusBar`].
40    pub fn new() -> Self {
41        Self {
42            style: StyleRefinement::default(),
43            left: SmallVec::new(),
44            right: SmallVec::new(),
45            children: SmallVec::new(),
46        }
47    }
48
49    /// Append an element to the left region. Call multiple times to add more.
50    pub fn left(mut self, child: impl IntoElement) -> Self {
51        self.left.push(child.into_any_element());
52        self
53    }
54
55    /// Append an element to the right region. Call multiple times to add more.
56    pub fn right(mut self, child: impl IntoElement) -> Self {
57        self.right.push(child.into_any_element());
58        self
59    }
60}
61
62/// `child` / `children` add to the center region, so a `StatusBar` without
63/// `left`/`right` items behaves like a plain container.
64impl ParentElement for StatusBar {
65    fn extend(&mut self, elements: impl IntoIterator<Item = AnyElement>) {
66        self.children.extend(elements);
67    }
68}
69
70impl Styled for StatusBar {
71    fn style(&mut self) -> &mut StyleRefinement {
72        &mut self.style
73    }
74}
75
76impl RenderOnce for StatusBar {
77    fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
78        // The center aligns by which ends are pinned: centered with both left
79        // and right, end-aligned with only left, otherwise start-aligned (only
80        // right, or neither) — so a bar with just `child`s reads like a container.
81        let has_left = !self.left.is_empty();
82        let has_right = !self.right.is_empty();
83        let region = || h_flex().overflow_hidden().items_center().gap_2();
84
85        h_flex()
86            .items_center()
87            .gap_2()
88            .py_1()
89            .px_2()
90            .border_t_1()
91            .border_color(cx.theme().status_bar_border)
92            .bg(cx.theme().tokens.status_bar)
93            .text_xs()
94            .text_color(cx.theme().muted_foreground)
95            .refine_style(&self.style)
96            .when(has_left, |this| this.child(region().children(self.left)))
97            .child(
98                region()
99                    .flex_1()
100                    .when(has_left && has_right, |this| this.justify_center())
101                    .when(has_left && !has_right, |this| this.justify_end())
102                    .children(self.children),
103            )
104            .when(has_right, |this| this.child(region().children(self.right)))
105    }
106}