From ce3a79d607ff2123d04d4e72114cd276d8e94012 Mon Sep 17 00:00:00 2001 From: Eric Holk Date: Fri, 17 Jul 2026 16:58:06 -0700 Subject: [PATCH] Round window corners on the sidebar side on Linux (#61229) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On Linux with client-side decorations, opening the workspace sidebar squared off the window corners on the sidebar's side while the rest of the window stayed rounded: Before: image After: image Previously, `client_side_decorations` took a `border_radius_tiling` override that `MultiWorkspace` used to deliberately square the window shape on the sidebar's side, since the sidebar painted a square background that would otherwise poke out past the rounded window border. The title bar and status bar already skip rounding their corners on the sidebar side, implying the sidebar is expected to own those window corners — it just never rounded them. This PR makes the sidebar round its outer corners the same way the title bar and status bar do (including overlapping the 1px window border on untiled edges to avoid a transparent gap in the rounded corners), and removes the now-unneeded `border_radius_tiling` parameter so the window border and backdrop round purely based on actual tiling state. Only applies when the window uses client-side decorations, so macOS and Windows are unaffected. Fixes #54724 Release Notes: - Fixed square window corners on the sidebar side of client-decorated (Linux) windows when the workspace sidebar is open. --------- Co-authored-by: Danilo Leal <67129314+danilo-leal@users.noreply.github.com> --- .../src/pages/audio_test_window.rs | 5 +- crates/settings_ui/src/settings_ui.rs | 6 +-- crates/sidebar/src/sidebar.rs | 52 ++++++++++++++++--- crates/workspace/src/multi_workspace.rs | 9 +--- crates/workspace/src/workspace.rs | 19 +------ 5 files changed, 54 insertions(+), 37 deletions(-) diff --git a/crates/settings_ui/src/pages/audio_test_window.rs b/crates/settings_ui/src/pages/audio_test_window.rs index 060a1c0d35..f1441b0833 100644 --- a/crates/settings_ui/src/pages/audio_test_window.rs +++ b/crates/settings_ui/src/pages/audio_test_window.rs @@ -1,8 +1,8 @@ use audio::{AudioSettings, CHANNEL_COUNT, RodioExt, SAMPLE_RATE}; use cpal::DeviceId; use gpui::{ - App, Context, Entity, FocusHandle, Focusable, Render, Size, Tiling, Window, WindowBounds, - WindowKind, WindowOptions, prelude::*, px, + App, Context, Entity, FocusHandle, Focusable, Render, Size, Window, WindowBounds, WindowKind, + WindowOptions, prelude::*, px, }; use platform_title_bar::PlatformTitleBar; use release_channel::ReleaseChannel; @@ -244,7 +244,6 @@ impl Render for AudioTestWindow { .child(content), window, cx, - Tiling::default(), ) } } diff --git a/crates/settings_ui/src/settings_ui.rs b/crates/settings_ui/src/settings_ui.rs index f3cf844d78..93ea20626a 100644 --- a/crates/settings_ui/src/settings_ui.rs +++ b/crates/settings_ui/src/settings_ui.rs @@ -11,9 +11,8 @@ use fuzzy::StringMatchCandidate; use gpui::{ Action, App, AsyncApp, ClipboardItem, DEFAULT_ADDITIONAL_WINDOW_SIZE, Div, Entity, FocusHandle, Focusable, Global, KeyContext, ListState, ReadGlobal as _, Role, ScrollHandle, Stateful, - Subscription, Task, Tiling, TitlebarOptions, UniformListScrollHandle, WeakEntity, Window, - WindowBounds, WindowHandle, WindowOptions, actions, div, list, point, prelude::*, px, - uniform_list, + Subscription, Task, TitlebarOptions, UniformListScrollHandle, WeakEntity, Window, WindowBounds, + WindowHandle, WindowOptions, actions, div, list, point, prelude::*, px, uniform_list, }; use language::Buffer; @@ -4556,7 +4555,6 @@ impl Render for SettingsWindow { ), window, cx, - Tiling::default(), ) } } diff --git a/crates/sidebar/src/sidebar.rs b/crates/sidebar/src/sidebar.rs index deb983a3f2..b28ea7af1e 100644 --- a/crates/sidebar/src/sidebar.rs +++ b/crates/sidebar/src/sidebar.rs @@ -28,9 +28,9 @@ use feature_flags::{ AgentThreadWorktreeLabel, AgentThreadWorktreeLabelFlag, FeatureFlag, FeatureFlagAppExt as _, }; use gpui::{ - Action as _, AnyElement, App, ClickEvent, Context, DismissEvent, Entity, EntityId, FocusHandle, - Focusable, KeyContext, ListState, Modifiers, Pixels, Render, SharedString, Task, TaskExt, - WeakEntity, Window, WindowBackgroundAppearance, WindowHandle, linear_color_stop, + Action as _, AnyElement, App, ClickEvent, Context, Decorations, DismissEvent, Entity, EntityId, + FocusHandle, Focusable, KeyContext, ListState, Modifiers, Pixels, Render, SharedString, Task, + TaskExt, WeakEntity, Window, WindowBackgroundAppearance, WindowHandle, linear_color_stop, linear_gradient, list, prelude::*, px, }; use itertools::Itertools; @@ -52,7 +52,7 @@ use std::mem; use std::path::{Path, PathBuf}; use std::rc::Rc; use std::sync::Arc; -use theme::ActiveTheme; +use theme::{ActiveTheme, CLIENT_SIDE_DECORATION_ROUNDING}; use ui::{ AgentThreadStatus, CommonAnimationExt, ContextMenu, ContextMenuEntry, Divider, GradientFade, HighlightedLabel, KeyBinding, PopoverMenu, PopoverMenuHandle, ProjectEmptyState, ScrollAxes, @@ -8076,8 +8076,48 @@ impl Render for Sidebar { this.recent_projects_popover_handle.toggle(window, cx); })) .font(ui_font) - .h_full() - .w(self.width) + .map(|el| { + let on_left = self.side(cx) == SidebarSide::Left; + match window.window_decorations() { + Decorations::Server => el.h_full().w(self.width), + // With client-side decorations the sidebar owns the window + // corners on its side, so round them like the title bar and + // status bar do. The sidebar is stretched 1px outwards over + // the window border on untiled edges (with compensating + // padding) so its rounded background lines up exactly with + // the window shape, avoiding a transparent gap in the + // rounded corners. + Decorations::Client { tiling, .. } => el + .absolute() + .top(if tiling.top { px(0.) } else { px(-1.) }) + .bottom(if tiling.bottom { px(0.) } else { px(-1.) }) + .when(!tiling.top, |el| el.pt_px()) + .when(!tiling.bottom, |el| el.pb_px()) + .map(|el| { + if on_left { + el.right(px(0.)) + .left(if tiling.left { px(0.) } else { px(-1.) }) + .when(!tiling.left, |el| el.pl(px(1.))) + } else { + el.left(px(0.)) + .right(if tiling.right { px(0.) } else { px(-1.) }) + .when(!tiling.right, |el| el.pr(px(1.))) + } + }) + .when(on_left && !(tiling.top || tiling.left), |el| { + el.rounded_tl(CLIENT_SIDE_DECORATION_ROUNDING) + }) + .when(on_left && !(tiling.bottom || tiling.left), |el| { + el.rounded_bl(CLIENT_SIDE_DECORATION_ROUNDING) + }) + .when(!on_left && !(tiling.top || tiling.right), |el| { + el.rounded_tr(CLIENT_SIDE_DECORATION_ROUNDING) + }) + .when(!on_left && !(tiling.bottom || tiling.right), |el| { + el.rounded_br(CLIENT_SIDE_DECORATION_ROUNDING) + }), + } + }) .bg(bg) .when(self.side(cx) == SidebarSide::Left, |el| el.border_r_1()) .when(self.side(cx) == SidebarSide::Right, |el| el.border_l_1()) diff --git a/crates/workspace/src/multi_workspace.rs b/crates/workspace/src/multi_workspace.rs index 11461d38ee..49b19bd134 100644 --- a/crates/workspace/src/multi_workspace.rs +++ b/crates/workspace/src/multi_workspace.rs @@ -3,8 +3,8 @@ use fs::Fs; use gpui::{ AnyView, App, Context, DragMoveEvent, Entity, EntityId, EventEmitter, FocusHandle, Focusable, - ManagedView, MouseButton, Pixels, Render, Subscription, Task, TaskExt, Tiling, WeakEntity, - Window, WindowId, actions, deferred, px, + ManagedView, MouseButton, Pixels, Render, Subscription, Task, TaskExt, WeakEntity, Window, + WindowId, actions, deferred, px, }; pub use project::ProjectGroupKey; use project::{DisableAiSettings, Project}; @@ -2292,11 +2292,6 @@ impl Render for MultiWorkspace { })), window, cx, - Tiling { - left: !sidebar_on_right && multi_workspace_enabled && self.sidebar_open(), - right: sidebar_on_right && multi_workspace_enabled && self.sidebar_open(), - ..Tiling::default() - }, ) } } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index e91019f6ca..c26cde9bf8 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -11103,19 +11103,10 @@ fn parse_pixel_size_env_var(value: &str) -> Option> { /// Add client-side decorations (rounded corners, shadows, resize handling) when /// appropriate. -/// -/// The `border_radius_tiling` parameter allows overriding which corners get -/// rounded, independently of the actual window tiling state. This is used -/// specifically for the workspace switcher sidebar: when the sidebar is open, -/// we want square corners on the left (so the sidebar appears flush with the -/// window edge) but we still need the shadow padding for proper visual -/// appearance. Unlike actual window tiling, this only affects border radius - -/// not padding or shadows. pub fn client_side_decorations( element: impl IntoElement, window: &mut Window, cx: &mut App, - border_radius_tiling: Tiling, ) -> Stateful
{ const BORDER_SIZE: Pixels = px(1.0); let decorations = window.window_decorations(); @@ -11123,12 +11114,6 @@ pub fn client_side_decorations( Decorations::Server => Tiling::default(), Decorations::Client { tiling } => tiling, }; - let corner_tiling = Tiling { - top: tiling.top || border_radius_tiling.top, - bottom: tiling.bottom || border_radius_tiling.bottom, - left: tiling.left || border_radius_tiling.left, - right: tiling.right || border_radius_tiling.right, - }; match decorations { Decorations::Client { .. } => window.set_client_inset(theme::CLIENT_SIDE_DECORATION_SHADOW), @@ -11144,7 +11129,7 @@ pub fn client_side_decorations( .map(|div| match decorations { Decorations::Server => div, Decorations::Client { .. } => div - .rounded_client_corners(corner_tiling) + .rounded_client_corners(tiling) .when(!tiling.top, |div| { div.pt(theme::CLIENT_SIDE_DECORATION_SHADOW) }) @@ -11199,7 +11184,7 @@ pub fn client_side_decorations( Decorations::Server => div, Decorations::Client { .. } => div .border_color(cx.theme().colors().border) - .rounded_client_corners(corner_tiling) + .rounded_client_corners(tiling) .when(!tiling.top, |div| div.border_t(BORDER_SIZE)) .when(!tiling.bottom, |div| div.border_b(BORDER_SIZE)) .when(!tiling.left, |div| div.border_l(BORDER_SIZE))