title_bar.rs

 1use gpui::{AnyElement, Interactivity, Stateful};
 2use smallvec::SmallVec;
 3
 4use crate::components::title_bar::windows_window_controls::WindowsWindowControls;
 5use crate::prelude::*;
 6
 7#[derive(IntoElement)]
 8pub struct TitleBar {
 9    platform_style: PlatformStyle,
10    content: Stateful<Div>,
11    children: SmallVec<[AnyElement; 2]>,
12}
13
14impl TitleBar {
15    pub fn height(cx: &mut WindowContext) -> Pixels {
16        (1.75 * cx.rem_size()).max(px(32.))
17    }
18
19    pub fn new(id: impl Into<ElementId>) -> Self {
20        Self {
21            platform_style: PlatformStyle::platform(),
22            content: div().id(id.into()),
23            children: SmallVec::new(),
24        }
25    }
26
27    /// Sets the platform style.
28    pub fn platform_style(mut self, style: PlatformStyle) -> Self {
29        self.platform_style = style;
30        self
31    }
32
33    fn top_padding(&self, cx: &WindowContext) -> Pixels {
34        if self.platform_style == PlatformStyle::Windows && cx.is_maximized() {
35            // todo(windows): get padding from win32 api, need HWND from window context somehow
36            // should be GetSystemMetricsForDpi(SM_CXPADDEDBORDER, dpi) * 2
37            px(8.)
38        } else {
39            px(0.)
40        }
41    }
42}
43
44impl InteractiveElement for TitleBar {
45    fn interactivity(&mut self) -> &mut Interactivity {
46        self.content.interactivity()
47    }
48}
49
50impl StatefulInteractiveElement for TitleBar {}
51
52impl ParentElement for TitleBar {
53    fn extend(&mut self, elements: impl Iterator<Item = AnyElement>) {
54        self.children.extend(elements)
55    }
56}
57
58impl RenderOnce for TitleBar {
59    fn render(self, cx: &mut WindowContext) -> impl IntoElement {
60        let height = Self::height(cx);
61        let top_padding = self.top_padding(cx);
62
63        h_flex()
64            .id("titlebar")
65            .w_full()
66            .pt(top_padding)
67            .h(height)
68            .map(|this| {
69                if cx.is_fullscreen() {
70                    this.pl_2()
71                } else if self.platform_style == PlatformStyle::Mac {
72                    // Use pixels here instead of a rem-based size because the macOS traffic
73                    // lights are a static size, and don't scale with the rest of the UI.
74                    this.pl(px(80.))
75                } else {
76                    this.pl_2()
77                }
78            })
79            .bg(cx.theme().colors().title_bar_background)
80            .content_stretch()
81            .child(
82                self.content
83                    .id("titlebar-content")
84                    .flex()
85                    .flex_row()
86                    .justify_between()
87                    .w_full()
88                    .children(self.children),
89            )
90            .when(self.platform_style == PlatformStyle::Windows, |title_bar| {
91                let button_height = Self::height(cx) - top_padding;
92
93                title_bar.child(WindowsWindowControls::new(button_height))
94            })
95    }
96}