diff --git a/crates/settings/src/settings_content.rs b/crates/settings/src/settings_content.rs index 94bde9e4e403a090a32e145e532114e7a3b65681..9eec9ac3d56b2ac2fa7d435d658de3f1c2123a1a 100644 --- a/crates/settings/src/settings_content.rs +++ b/crates/settings/src/settings_content.rs @@ -260,6 +260,32 @@ impl strum::VariantNames for BaseKeymapContent { ]; } +/// Position of window control buttons on Linux. +/// +/// Valid values: "left" (macOS style) or "right" (Windows/Linux style) +#[derive( + Copy, + Clone, + Debug, + Serialize, + Deserialize, + JsonSchema, + MergeFrom, + PartialEq, + Eq, + Default, + strum::VariantArray, + strum::VariantNames, +)] +#[serde(rename_all = "snake_case")] +pub enum WindowControlsPosition { + /// Window controls on the left side (macOS style) + Left, + /// Window controls on the right side (Windows style) + #[default] + Right, +} + #[skip_serializing_none] #[derive(Clone, PartialEq, Default, Serialize, Deserialize, JsonSchema, MergeFrom, Debug)] pub struct TitleBarSettingsContent { @@ -291,6 +317,10 @@ pub struct TitleBarSettingsContent { /// /// Default: false pub show_menus: Option, + /// Position of window control buttons (minimize, maximize, close) on Linux. + /// + /// Default: right + pub window_controls_position: Option, } /// Configuration of audio in Zed. diff --git a/crates/title_bar/src/platform_title_bar.rs b/crates/title_bar/src/platform_title_bar.rs index fd03e764629454411c9726ef7dcf055d54582d7e..f078777c2c05bb10afbcd400736c6a428473eeed 100644 --- a/crates/title_bar/src/platform_title_bar.rs +++ b/crates/title_bar/src/platform_title_bar.rs @@ -2,6 +2,7 @@ use gpui::{ AnyElement, Context, Decorations, Entity, Hsla, InteractiveElement, IntoElement, MouseButton, ParentElement, Pixels, StatefulInteractiveElement, Styled, Window, WindowControlArea, div, px, }; +use settings::{Settings, WindowControlsPosition}; use smallvec::SmallVec; use std::mem; use ui::prelude::*; @@ -9,6 +10,7 @@ use ui::prelude::*; use crate::{ platforms::{platform_linux, platform_mac, platform_windows}, system_window_tabs::SystemWindowTabs, + title_bar_settings::TitleBarSettings, }; pub struct PlatformTitleBar { @@ -134,35 +136,78 @@ impl Render for PlatformTitleBar { PlatformStyle::Mac => title_bar, PlatformStyle::Linux => { if matches!(decorations, Decorations::Client { .. }) { - title_bar - .child(platform_linux::LinuxWindowControls::new(close_action)) - .when(supported_controls.window_menu, |titlebar| { - titlebar - .on_mouse_down(MouseButton::Right, move |ev, window, _| { - window.show_window_menu(ev.position) - }) - }) - .on_mouse_move(cx.listener(move |this, _ev, window, _| { - if this.should_move { - this.should_move = false; - window.start_window_move(); - } - })) - .on_mouse_down_out(cx.listener(move |this, _ev, _window, _cx| { - this.should_move = false; - })) - .on_mouse_up( - MouseButton::Left, - cx.listener(move |this, _ev, _window, _cx| { - this.should_move = false; - }), - ) - .on_mouse_down( - MouseButton::Left, - cx.listener(move |this, _ev, _window, _cx| { - this.should_move = true; - }), - ) + let title_bar_settings = TitleBarSettings::get(None, cx); + match title_bar_settings.window_controls_position { + WindowControlsPosition::Left => h_flex() + .w_full() + .bg(titlebar_color) + .child(platform_linux::LinuxWindowControls::new(close_action)) + .child(title_bar) + .when(supported_controls.window_menu, |titlebar| { + titlebar.on_mouse_down( + MouseButton::Right, + move |ev, window, _| { + window.show_window_menu(ev.position) + }, + ) + }) + .on_mouse_move(cx.listener(move |this, _ev, window, _| { + if this.should_move { + this.should_move = false; + window.start_window_move(); + } + })) + .on_mouse_down_out(cx.listener( + move |this, _ev, _window, _cx| { + this.should_move = false; + }, + )) + .on_mouse_up( + MouseButton::Left, + cx.listener(move |this, _ev, _window, _cx| { + this.should_move = false; + }), + ) + .on_mouse_down( + MouseButton::Left, + cx.listener(move |this, _ev, _window, _cx| { + this.should_move = true; + }), + ), + WindowControlsPosition::Right => title_bar + .child(platform_linux::LinuxWindowControls::new(close_action)) + .when(supported_controls.window_menu, |titlebar| { + titlebar.on_mouse_down( + MouseButton::Right, + move |ev, window, _| { + window.show_window_menu(ev.position) + }, + ) + }) + .on_mouse_move(cx.listener(move |this, _ev, window, _| { + if this.should_move { + this.should_move = false; + window.start_window_move(); + } + })) + .on_mouse_down_out(cx.listener( + move |this, _ev, _window, _cx| { + this.should_move = false; + }, + )) + .on_mouse_up( + MouseButton::Left, + cx.listener(move |this, _ev, _window, _cx| { + this.should_move = false; + }), + ) + .on_mouse_down( + MouseButton::Left, + cx.listener(move |this, _ev, _window, _cx| { + this.should_move = true; + }), + ), + } } else { title_bar } diff --git a/crates/title_bar/src/platforms/platform_linux.rs b/crates/title_bar/src/platforms/platform_linux.rs index 0e7af80f80e8dcbea03a3b3375f1e4dfd7ca2f37..306d689a7c57f618cdc318c73d4f6bc962dc5a0f 100644 --- a/crates/title_bar/src/platforms/platform_linux.rs +++ b/crates/title_bar/src/platforms/platform_linux.rs @@ -1,4 +1,6 @@ +use crate::title_bar_settings::TitleBarSettings; use gpui::{Action, Hsla, MouseButton, prelude::*, svg}; +use settings::{Settings, WindowControlsPosition}; use ui::prelude::*; #[derive(IntoElement)] @@ -14,33 +16,62 @@ impl LinuxWindowControls { } } +impl LinuxWindowControls { + /// Builds the window controls based on the position setting. + fn build_controls( + position: WindowControlsPosition, + window: &Window, + close_action: Box, + cx: &mut App, + ) -> Vec { + let maximize_type = if window.is_maximized() { + WindowControlType::Restore + } else { + WindowControlType::Maximize + }; + + match position { + WindowControlsPosition::Left => { + // Left side: Close, Minimize, Maximize (left to right) + vec![ + WindowControl::new_close("close", WindowControlType::Close, close_action, cx), + WindowControl::new("minimize", WindowControlType::Minimize, cx), + WindowControl::new("maximize-or-restore", maximize_type, cx), + ] + } + WindowControlsPosition::Right => { + // Right side: Minimize, Maximize, Close (left to right) + vec![ + WindowControl::new("minimize", WindowControlType::Minimize, cx), + WindowControl::new("maximize-or-restore", maximize_type, cx), + WindowControl::new_close("close", WindowControlType::Close, close_action, cx), + ] + } + } + } +} + impl RenderOnce for LinuxWindowControls { fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement { - h_flex() + let title_bar_settings = TitleBarSettings::get(None, cx); + let controls = Self::build_controls( + title_bar_settings.window_controls_position, + window, + self.close_window_action, + cx, + ); + + let mut element = h_flex() .id("generic-window-controls") .px_3() .gap_3() - .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation()) - .child(WindowControl::new( - "minimize", - WindowControlType::Minimize, - cx, - )) - .child(WindowControl::new( - "maximize-or-restore", - if window.is_maximized() { - WindowControlType::Restore - } else { - WindowControlType::Maximize - }, - cx, - )) - .child(WindowControl::new_close( - "close", - WindowControlType::Close, - self.close_window_action, - cx, - )) + .on_mouse_down(MouseButton::Left, |_, _, cx| cx.stop_propagation()); + + for control in controls { + element = element.child(control); + } + + element } } @@ -80,7 +111,7 @@ impl WindowControlStyle { let colors = cx.theme().colors(); Self { - background: colors.ghost_element_background, + background: colors.title_bar_background, background_hover: colors.ghost_element_hover, icon: colors.icon, icon_hover: colors.icon_muted, @@ -185,6 +216,7 @@ impl RenderOnce for WindowControl { .rounded_2xl() .w_5() .h_5() + .bg(self.style.background) .hover(|this| this.bg(self.style.background_hover)) .active(|this| this.bg(self.style.background_hover)) .child(icon) diff --git a/crates/title_bar/src/title_bar_settings.rs b/crates/title_bar/src/title_bar_settings.rs index bc9b1acbaa06cf60396e61ff68470c8a544e3f5d..a02f80fbd6e5a6f8a54d0599ccb8e04369a6b76f 100644 --- a/crates/title_bar/src/title_bar_settings.rs +++ b/crates/title_bar/src/title_bar_settings.rs @@ -1,4 +1,4 @@ -use settings::{Settings, SettingsContent}; +use settings::{Settings, SettingsContent, WindowControlsPosition}; #[derive(Copy, Clone, Debug)] pub struct TitleBarSettings { @@ -9,6 +9,7 @@ pub struct TitleBarSettings { pub show_project_items: bool, pub show_sign_in: bool, pub show_menus: bool, + pub window_controls_position: WindowControlsPosition, } impl Settings for TitleBarSettings { @@ -22,6 +23,7 @@ impl Settings for TitleBarSettings { show_project_items: content.show_project_items.unwrap(), show_sign_in: content.show_sign_in.unwrap(), show_menus: content.show_menus.unwrap(), + window_controls_position: content.window_controls_position.unwrap_or_default(), } } }