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 #[cfg(not(target_os = "windows"))]
16 pub fn height(cx: &mut WindowContext) -> Pixels {
17 (1.75 * cx.rem_size()).max(px(34.))
18 }
19
20 #[cfg(target_os = "windows")]
21 pub fn height(_cx: &mut WindowContext) -> Pixels {
22 // todo(windows) instead of hard coded size report the actual size to the Windows platform API
23 px(32.)
24 }
25
26 #[cfg(not(target_os = "windows"))]
27 fn top_padding(_cx: &WindowContext) -> Pixels {
28 px(0.)
29 }
30
31 #[cfg(target_os = "windows")]
32 fn top_padding(cx: &WindowContext) -> Pixels {
33 use windows::Win32::UI::{
34 HiDpi::GetSystemMetricsForDpi,
35 WindowsAndMessaging::{SM_CXPADDEDBORDER, USER_DEFAULT_SCREEN_DPI},
36 };
37
38 // This top padding is not dependent on the title bar style and is instead a quirk of maximized windows on Windows:
39 // https://devblogs.microsoft.com/oldnewthing/20150304-00/?p=44543
40 let padding = unsafe { GetSystemMetricsForDpi(SM_CXPADDEDBORDER, USER_DEFAULT_SCREEN_DPI) };
41 if cx.is_maximized() {
42 px((padding * 2) as f32)
43 } else {
44 px(0.)
45 }
46 }
47
48 pub fn new(id: impl Into<ElementId>) -> Self {
49 Self {
50 platform_style: PlatformStyle::platform(),
51 content: div().id(id.into()),
52 children: SmallVec::new(),
53 }
54 }
55
56 /// Sets the platform style.
57 pub fn platform_style(mut self, style: PlatformStyle) -> Self {
58 self.platform_style = style;
59 self
60 }
61}
62
63impl InteractiveElement for TitleBar {
64 fn interactivity(&mut self) -> &mut Interactivity {
65 self.content.interactivity()
66 }
67}
68
69impl StatefulInteractiveElement for TitleBar {}
70
71impl ParentElement for TitleBar {
72 fn extend(&mut self, elements: impl Iterator<Item = AnyElement>) {
73 self.children.extend(elements)
74 }
75}
76
77impl RenderOnce for TitleBar {
78 fn render(self, cx: &mut WindowContext) -> impl IntoElement {
79 let height = Self::height(cx);
80 h_flex()
81 .id("titlebar")
82 .w_full()
83 .pt(Self::top_padding(cx))
84 .h(height + Self::top_padding(cx))
85 .map(|this| {
86 if cx.is_fullscreen() {
87 this.pl_2()
88 } else if self.platform_style == PlatformStyle::Mac {
89 // Use pixels here instead of a rem-based size because the macOS traffic
90 // lights are a static size, and don't scale with the rest of the UI.
91 this.pl(px(80.))
92 } else {
93 this.pl_2()
94 }
95 })
96 .bg(cx.theme().colors().title_bar_background)
97 .content_stretch()
98 .child(
99 self.content
100 .id("titlebar-content")
101 .flex()
102 .flex_row()
103 .justify_between()
104 .w_full()
105 .children(self.children),
106 )
107 .when(
108 self.platform_style == PlatformStyle::Windows && !cx.is_fullscreen(),
109 |title_bar| title_bar.child(WindowsWindowControls::new(height)),
110 )
111 }
112}