Implement non-macos titlebars for the new sidebar (#52794)

Mikayla Maki created

Self-Review Checklist:

- [x] I've reviewed my own diff for quality, security, and reliability
- [x] Unsafe blocks (if any) have justifying comments
- [x] The content is consistent with the [UI/UX
checklist](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable


Icons aren't showing because I'm forcing the UI to be visible on macOS,
but things look ok on the right sidebar:

<img width="389" height="955" alt="Screenshot 2026-03-30 at 7 36 06 PM"
src="https://github.com/user-attachments/assets/269fe9c9-1212-4c1e-b8d9-1694db70adf3"
/>


Release Notes:

- N/A

Change summary

Cargo.lock                                          |   1 
crates/platform_title_bar/src/platform_title_bar.rs | 140 +++++++++++---
crates/sidebar/Cargo.toml                           |   1 
crates/sidebar/src/sidebar.rs                       |  40 +++
4 files changed, 139 insertions(+), 43 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -15975,6 +15975,7 @@ dependencies = [
  "gpui",
  "language_model",
  "menu",
+ "platform_title_bar",
  "pretty_assertions",
  "project",
  "prompt_store",

crates/platform_title_bar/src/platform_title_bar.rs 🔗

@@ -1,4 +1,4 @@
-mod platforms;
+pub mod platforms;
 mod system_window_tabs;
 
 use feature_flags::{AgentV2FeatureFlag, FeatureFlagAppExt};
@@ -115,6 +115,72 @@ impl PlatformTitleBar {
     }
 }
 
+/// Renders the platform-appropriate left-side window controls (e.g. Ubuntu/GNOME close button).
+///
+/// Only relevant on Linux with client-side decorations when the window manager
+/// places controls on the left.
+pub fn render_left_window_controls(
+    button_layout: Option<WindowButtonLayout>,
+    close_action: Box<dyn Action>,
+    window: &Window,
+) -> Option<AnyElement> {
+    if PlatformStyle::platform() != PlatformStyle::Linux {
+        return None;
+    }
+    if !matches!(window.window_decorations(), Decorations::Client { .. }) {
+        return None;
+    }
+    let button_layout = button_layout?;
+    if button_layout.left[0].is_none() {
+        return None;
+    }
+    Some(
+        platform_linux::LinuxWindowControls::new(
+            "left-window-controls",
+            button_layout.left,
+            close_action,
+        )
+        .into_any_element(),
+    )
+}
+
+/// Renders the platform-appropriate right-side window controls (close, minimize, maximize).
+///
+/// Returns `None` on Mac or when the platform doesn't need custom controls
+/// (e.g. Linux with server-side decorations).
+pub fn render_right_window_controls(
+    button_layout: Option<WindowButtonLayout>,
+    close_action: Box<dyn Action>,
+    window: &Window,
+) -> Option<AnyElement> {
+    let decorations = window.window_decorations();
+    let height = platform_title_bar_height(window);
+
+    match PlatformStyle::platform() {
+        PlatformStyle::Linux => {
+            if !matches!(decorations, Decorations::Client { .. }) {
+                return None;
+            }
+            let button_layout = button_layout?;
+            if button_layout.right[0].is_none() {
+                return None;
+            }
+            Some(
+                platform_linux::LinuxWindowControls::new(
+                    "right-window-controls",
+                    button_layout.right,
+                    close_action,
+                )
+                .into_any_element(),
+            )
+        }
+        PlatformStyle::Windows => {
+            Some(platform_windows::WindowsWindowControls::new(height).into_any_element())
+        }
+        PlatformStyle::Mac => None,
+    }
+}
+
 impl Render for PlatformTitleBar {
     fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
         let supported_controls = window.window_controls();
@@ -173,20 +239,23 @@ impl Render for PlatformTitleBar {
                     })
             })
             .map(|this| {
+                let show_left_controls = !(sidebar.open && sidebar.side == SidebarSide::Left);
+
                 if window.is_fullscreen() {
                     this.pl_2()
-                } else if self.platform_style == PlatformStyle::Mac
-                    && !(sidebar.open && sidebar.side == SidebarSide::Left)
-                {
+                } else if self.platform_style == PlatformStyle::Mac && show_left_controls {
                     this.pl(px(TRAFFIC_LIGHT_PADDING))
-                } else if let Some(button_layout) =
-                    button_layout.filter(|button_layout| button_layout.left[0].is_some())
+                } else if let Some(controls) = show_left_controls
+                    .then(|| {
+                        render_left_window_controls(
+                            button_layout,
+                            close_action.as_ref().boxed_clone(),
+                            window,
+                        )
+                    })
+                    .flatten()
                 {
-                    this.child(platform_linux::LinuxWindowControls::new(
-                        "left-window-controls",
-                        button_layout.left,
-                        close_action.as_ref().boxed_clone(),
-                    ))
+                    this.child(controls)
                 } else {
                     this.pl_2()
                 }
@@ -224,33 +293,30 @@ impl Render for PlatformTitleBar {
                     .children(children),
             )
             .when(!window.is_fullscreen(), |title_bar| {
-                match self.platform_style {
-                    PlatformStyle::Mac => title_bar,
-                    PlatformStyle::Linux => {
-                        if matches!(decorations, Decorations::Client { .. }) {
-                            let mut result = title_bar;
-                            if let Some(button_layout) = button_layout
-                                .filter(|button_layout| button_layout.right[0].is_some())
-                            {
-                                result = result.child(platform_linux::LinuxWindowControls::new(
-                                    "right-window-controls",
-                                    button_layout.right,
-                                    close_action.as_ref().boxed_clone(),
-                                ));
-                            }
+                let show_right_controls = !(sidebar.open && sidebar.side == SidebarSide::Right);
 
-                            result.when(supported_controls.window_menu, |titlebar| {
-                                titlebar.on_mouse_down(MouseButton::Right, move |ev, window, _| {
-                                    window.show_window_menu(ev.position)
-                                })
-                            })
-                        } else {
-                            title_bar
-                        }
-                    }
-                    PlatformStyle::Windows => {
-                        title_bar.child(platform_windows::WindowsWindowControls::new(height))
-                    }
+                let title_bar = title_bar.children(
+                    show_right_controls
+                        .then(|| {
+                            render_right_window_controls(
+                                button_layout,
+                                close_action.as_ref().boxed_clone(),
+                                window,
+                            )
+                        })
+                        .flatten(),
+                );
+
+                if self.platform_style == PlatformStyle::Linux
+                    && matches!(decorations, Decorations::Client { .. })
+                {
+                    title_bar.when(supported_controls.window_menu, |titlebar| {
+                        titlebar.on_mouse_down(MouseButton::Right, move |ev, window, _| {
+                            window.show_window_menu(ev.position)
+                        })
+                    })
+                } else {
+                    title_bar
                 }
             });
 

crates/sidebar/Cargo.toml 🔗

@@ -30,6 +30,7 @@ fs.workspace = true
 git.workspace = true
 gpui.workspace = true
 menu.workspace = true
+platform_title_bar.workspace = true
 project.workspace = true
 recent_projects.workspace = true
 remote.workspace = true

crates/sidebar/src/sidebar.rs 🔗

@@ -39,8 +39,8 @@ use ui::{
 use util::ResultExt as _;
 use util::path_list::PathList;
 use workspace::{
-    AddFolderToProject, FocusWorkspaceSidebar, MultiWorkspace, MultiWorkspaceEvent, Open,
-    Sidebar as WorkspaceSidebar, SidebarSide, ToggleWorkspaceSidebar, Workspace, WorkspaceId,
+    AddFolderToProject, CloseWindow, FocusWorkspaceSidebar, MultiWorkspace, MultiWorkspaceEvent,
+    Open, Sidebar as WorkspaceSidebar, SidebarSide, ToggleWorkspaceSidebar, Workspace, WorkspaceId,
     sidebar_side_context_menu,
 };
 
@@ -3061,22 +3061,31 @@ impl Sidebar {
     ) -> impl IntoElement {
         let has_query = self.has_filter_query(cx);
         let sidebar_on_left = self.side(cx) == SidebarSide::Left;
-        let traffic_lights =
-            cfg!(target_os = "macos") && !window.is_fullscreen() && sidebar_on_left;
+        let sidebar_on_right = self.side(cx) == SidebarSide::Right;
+        let not_fullscreen = !window.is_fullscreen();
+        let traffic_lights = cfg!(target_os = "macos") && not_fullscreen && sidebar_on_left;
+        let left_window_controls = !cfg!(target_os = "macos") && not_fullscreen && sidebar_on_left;
+        let right_window_controls =
+            !cfg!(target_os = "macos") && not_fullscreen && sidebar_on_right;
         let header_height = platform_title_bar_height(window);
 
         h_flex()
             .h(header_height)
             .mt_px()
             .pb_px()
+            .when(left_window_controls, |this| {
+                this.children(Self::render_left_window_controls(window, cx))
+            })
             .map(|this| {
                 if traffic_lights {
                     this.pl(px(ui::utils::TRAFFIC_LIGHT_PADDING))
-                } else {
+                } else if !left_window_controls {
                     this.pl_1p5()
+                } else {
+                    this
                 }
             })
-            .pr_1p5()
+            .when(!right_window_controls, |this| this.pr_1p5())
             .gap_1()
             .when(!no_open_projects, |this| {
                 this.border_b_1()
@@ -3113,6 +3122,25 @@ impl Sidebar {
                             }),
                     )
             })
+            .when(right_window_controls, |this| {
+                this.children(Self::render_right_window_controls(window, cx))
+            })
+    }
+
+    fn render_left_window_controls(window: &Window, cx: &mut App) -> Option<AnyElement> {
+        platform_title_bar::render_left_window_controls(
+            cx.button_layout(),
+            Box::new(CloseWindow),
+            window,
+        )
+    }
+
+    fn render_right_window_controls(window: &Window, cx: &mut App) -> Option<AnyElement> {
+        platform_title_bar::render_right_window_controls(
+            cx.button_layout(),
+            Box::new(CloseWindow),
+            window,
+        )
     }
 
     fn render_sidebar_toggle_button(&self, _cx: &mut Context<Self>) -> impl IntoElement {