1use gpui::{Action, Hsla, MouseButton, prelude::*, svg};
2use ui::prelude::*;
3
4#[derive(IntoElement)]
5pub struct LinuxWindowControls {
6 close_window_action: Box<dyn Action>,
7}
8
9impl LinuxWindowControls {
10 pub fn new(close_window_action: Box<dyn Action>) -> Self {
11 Self {
12 close_window_action,
13 }
14 }
15}
16
17impl RenderOnce for LinuxWindowControls {
18 fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
19 h_flex()
20 .id("generic-window-controls")
21 .px_3()
22 .gap_3()
23 .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation())
24 .child(WindowControl::new(
25 "minimize",
26 WindowControlType::Minimize,
27 cx,
28 ))
29 .child(WindowControl::new(
30 "maximize-or-restore",
31 if window.is_maximized() {
32 WindowControlType::Restore
33 } else {
34 WindowControlType::Maximize
35 },
36 cx,
37 ))
38 .child(WindowControl::new_close(
39 "close",
40 WindowControlType::Close,
41 self.close_window_action,
42 cx,
43 ))
44 }
45}
46
47#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
48pub enum WindowControlType {
49 Minimize,
50 Restore,
51 Maximize,
52 Close,
53}
54
55impl WindowControlType {
56 /// Returns the icon name for the window control type.
57 ///
58 /// Will take a [PlatformStyle] in the future to return a different
59 /// icon name based on the platform.
60 pub fn icon(&self) -> IconName {
61 match self {
62 WindowControlType::Minimize => IconName::GenericMinimize,
63 WindowControlType::Restore => IconName::GenericRestore,
64 WindowControlType::Maximize => IconName::GenericMaximize,
65 WindowControlType::Close => IconName::GenericClose,
66 }
67 }
68}
69
70#[allow(unused)]
71pub struct WindowControlStyle {
72 background: Hsla,
73 background_hover: Hsla,
74 icon: Hsla,
75 icon_hover: Hsla,
76}
77
78impl WindowControlStyle {
79 pub fn default(cx: &mut App) -> Self {
80 let colors = cx.theme().colors();
81
82 Self {
83 background: colors.ghost_element_background,
84 background_hover: colors.ghost_element_hover,
85 icon: colors.icon,
86 icon_hover: colors.icon_muted,
87 }
88 }
89
90 #[allow(unused)]
91 /// Sets the background color of the control.
92 pub fn background(mut self, color: impl Into<Hsla>) -> Self {
93 self.background = color.into();
94 self
95 }
96
97 #[allow(unused)]
98 /// Sets the background color of the control when hovered.
99 pub fn background_hover(mut self, color: impl Into<Hsla>) -> Self {
100 self.background_hover = color.into();
101 self
102 }
103
104 #[allow(unused)]
105 /// Sets the color of the icon.
106 pub fn icon(mut self, color: impl Into<Hsla>) -> Self {
107 self.icon = color.into();
108 self
109 }
110
111 #[allow(unused)]
112 /// Sets the color of the icon when hovered.
113 pub fn icon_hover(mut self, color: impl Into<Hsla>) -> Self {
114 self.icon_hover = color.into();
115 self
116 }
117}
118
119#[derive(IntoElement)]
120pub struct WindowControl {
121 id: ElementId,
122 icon: WindowControlType,
123 style: WindowControlStyle,
124 close_action: Option<Box<dyn Action>>,
125}
126
127impl WindowControl {
128 pub fn new(id: impl Into<ElementId>, icon: WindowControlType, cx: &mut App) -> Self {
129 let style = WindowControlStyle::default(cx);
130
131 Self {
132 id: id.into(),
133 icon,
134 style,
135 close_action: None,
136 }
137 }
138
139 pub fn new_close(
140 id: impl Into<ElementId>,
141 icon: WindowControlType,
142 close_action: Box<dyn Action>,
143 cx: &mut App,
144 ) -> Self {
145 let style = WindowControlStyle::default(cx);
146
147 Self {
148 id: id.into(),
149 icon,
150 style,
151 close_action: Some(close_action.boxed_clone()),
152 }
153 }
154
155 #[allow(unused)]
156 pub fn custom_style(
157 id: impl Into<ElementId>,
158 icon: WindowControlType,
159 style: WindowControlStyle,
160 ) -> Self {
161 Self {
162 id: id.into(),
163 icon,
164 style,
165 close_action: None,
166 }
167 }
168}
169
170impl RenderOnce for WindowControl {
171 fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement {
172 let icon = svg()
173 .size_4()
174 .flex_none()
175 .path(self.icon.icon().path())
176 .text_color(self.style.icon)
177 .group_hover("", |this| this.text_color(self.style.icon_hover));
178
179 h_flex()
180 .id(self.id)
181 .group("")
182 .cursor_pointer()
183 .justify_center()
184 .content_center()
185 .rounded_2xl()
186 .w_5()
187 .h_5()
188 .hover(|this| this.bg(self.style.background_hover))
189 .active(|this| this.bg(self.style.background_hover))
190 .child(icon)
191 .on_mouse_move(|_, _, cx| cx.stop_propagation())
192 .on_click(move |_, window, cx| {
193 cx.stop_propagation();
194 match self.icon {
195 WindowControlType::Minimize => window.minimize_window(),
196 WindowControlType::Restore => window.zoom_window(),
197 WindowControlType::Maximize => window.zoom_window(),
198 WindowControlType::Close => window.dispatch_action(
199 self.close_action
200 .as_ref()
201 .expect("Use WindowControl::new_close() for close control.")
202 .boxed_clone(),
203 cx,
204 ),
205 }
206 })
207 }
208}