platform_linux.rs

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