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