1use gpui::{Rgba, WindowAppearance, prelude::*};
2
3use ui::prelude::*;
4
5#[derive(IntoElement)]
6pub struct WindowsWindowControls {
7 button_height: Pixels,
8}
9
10impl WindowsWindowControls {
11 pub fn new(button_height: Pixels) -> Self {
12 Self { button_height }
13 }
14
15 #[cfg(not(target_os = "windows"))]
16 fn get_font() -> &'static str {
17 "Segoe Fluent Icons"
18 }
19
20 #[cfg(target_os = "windows")]
21 fn get_font() -> &'static str {
22 use windows::Wdk::System::SystemServices::RtlGetVersion;
23
24 let mut version = unsafe { std::mem::zeroed() };
25 let status = unsafe { RtlGetVersion(&mut version) };
26
27 if status.is_ok() && version.dwBuildNumber >= 22000 {
28 "Segoe Fluent Icons"
29 } else {
30 "Segoe MDL2 Assets"
31 }
32 }
33}
34
35impl RenderOnce for WindowsWindowControls {
36 fn render(self, window: &mut Window, _: &mut App) -> impl IntoElement {
37 let close_button_hover_color = Rgba {
38 r: 232.0 / 255.0,
39 g: 17.0 / 255.0,
40 b: 32.0 / 255.0,
41 a: 1.0,
42 };
43
44 let button_hover_color = match window.appearance() {
45 WindowAppearance::Light | WindowAppearance::VibrantLight => Rgba {
46 r: 0.1,
47 g: 0.1,
48 b: 0.1,
49 a: 0.2,
50 },
51 WindowAppearance::Dark | WindowAppearance::VibrantDark => Rgba {
52 r: 0.9,
53 g: 0.9,
54 b: 0.9,
55 a: 0.1,
56 },
57 };
58
59 div()
60 .id("windows-window-controls")
61 .font_family(Self::get_font())
62 .flex()
63 .flex_row()
64 .justify_center()
65 .content_stretch()
66 .max_h(self.button_height)
67 .min_h(self.button_height)
68 .child(WindowsCaptionButton::new(
69 "minimize",
70 WindowsCaptionButtonIcon::Minimize,
71 button_hover_color,
72 ))
73 .child(WindowsCaptionButton::new(
74 "maximize-or-restore",
75 if window.is_maximized() {
76 WindowsCaptionButtonIcon::Restore
77 } else {
78 WindowsCaptionButtonIcon::Maximize
79 },
80 button_hover_color,
81 ))
82 .child(WindowsCaptionButton::new(
83 "close",
84 WindowsCaptionButtonIcon::Close,
85 close_button_hover_color,
86 ))
87 }
88}
89
90#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
91enum WindowsCaptionButtonIcon {
92 Minimize,
93 Restore,
94 Maximize,
95 Close,
96}
97
98#[derive(IntoElement)]
99struct WindowsCaptionButton {
100 id: ElementId,
101 icon: WindowsCaptionButtonIcon,
102 hover_background_color: Rgba,
103}
104
105impl WindowsCaptionButton {
106 pub fn new(
107 id: impl Into<ElementId>,
108 icon: WindowsCaptionButtonIcon,
109 hover_background_color: Rgba,
110 ) -> Self {
111 Self {
112 id: id.into(),
113 icon,
114 hover_background_color,
115 }
116 }
117}
118
119impl RenderOnce for WindowsCaptionButton {
120 fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
121 // todo(windows) report this width to the Windows platform API
122 // NOTE: this is intentionally hard coded. An option to use the 'native' size
123 // could be added when the width is reported to the Windows platform API
124 // as this could change between future Windows versions.
125 let width = px(36.);
126
127 h_flex()
128 .id(self.id)
129 .justify_center()
130 .content_center()
131 .w(width)
132 .h_full()
133 .text_size(px(10.0))
134 .hover(|style| style.bg(self.hover_background_color))
135 .active(|style| {
136 let mut active_color = self.hover_background_color;
137 active_color.a *= 0.2;
138
139 style.bg(active_color)
140 })
141 .child(match self.icon {
142 WindowsCaptionButtonIcon::Minimize => "\u{e921}",
143 WindowsCaptionButtonIcon::Restore => "\u{e923}",
144 WindowsCaptionButtonIcon::Maximize => "\u{e922}",
145 WindowsCaptionButtonIcon::Close => "\u{e8bb}",
146 })
147 }
148}