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