Cargo.lock 🔗
@@ -15975,6 +15975,7 @@ dependencies = [
"gpui",
"language_model",
"menu",
+ "platform_title_bar",
"pretty_assertions",
"project",
"prompt_store",
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
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(-)
@@ -15975,6 +15975,7 @@ dependencies = [
"gpui",
"language_model",
"menu",
+ "platform_title_bar",
"pretty_assertions",
"project",
"prompt_store",
@@ -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
}
});
@@ -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
@@ -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 {