Skip to main content

ui/components/
app_shell.rs

1use gpui::AnyElement;
2
3use crate::prelude::*;
4
5/// Composes an already-styled [`Navbar`] (top) and [`Sidebar`] (left) around a
6/// main content slot. Pure composition — no new styling logic; `Navbar` and
7/// `Sidebar` own all of their own visuals (colors/borders/width).
8#[derive(IntoElement, RegisterComponent)]
9pub struct AppShell {
10    navbar: Option<AnyElement>,
11    sidebar: Option<AnyElement>,
12    content: Option<AnyElement>,
13}
14
15impl AppShell {
16    pub fn new() -> Self {
17        Self {
18            navbar: None,
19            sidebar: None,
20            content: None,
21        }
22    }
23
24    pub fn navbar(mut self, navbar: impl IntoElement) -> Self {
25        self.navbar = Some(navbar.into_any_element());
26        self
27    }
28
29    pub fn sidebar(mut self, sidebar: impl IntoElement) -> Self {
30        self.sidebar = Some(sidebar.into_any_element());
31        self
32    }
33
34    pub fn content(mut self, content: impl IntoElement) -> Self {
35        self.content = Some(content.into_any_element());
36        self
37    }
38}
39
40impl Default for AppShell {
41    fn default() -> Self {
42        Self::new()
43    }
44}
45
46impl RenderOnce for AppShell {
47    fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
48        v_flex().w_full().h_full().children(self.navbar).child(
49            h_flex()
50                .flex_1()
51                .items_stretch()
52                .children(self.sidebar)
53                .child(div().flex_1().h_full().children(self.content)),
54        )
55    }
56}
57
58impl Component for AppShell {
59    fn scope() -> ComponentScope {
60        ComponentScope::Layout
61    }
62
63    fn description() -> Option<&'static str> {
64        Some("Composes a Navbar (top) and Sidebar (left) around a main content slot.")
65    }
66
67    fn preview(_window: &mut Window, _cx: &mut App) -> Option<AnyElement> {
68        Some(
69            div()
70                .h(px(320.))
71                .child(
72                    AppShell::new()
73                        .navbar(Navbar::new().child(Label::new("Application")))
74                        .sidebar(
75                            Sidebar::new()
76                                .child(SidebarItem::new("shell-home", "Home").active(true))
77                                .child(SidebarItem::new("shell-settings", "Settings")),
78                        )
79                        .content(div().p_6().child(Label::new("Main content area"))),
80                )
81                .into_any_element(),
82        )
83    }
84}