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}