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 const 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}