mirror of
https://github.com/zed-industries/zed.git
synced 2026-05-24 13:39:08 +00:00
Implement non-macos titlebars for the new sidebar (#52794)
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
This commit is contained in:
parent
fb87786375
commit
dfafd62afc
4 changed files with 139 additions and 43 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue