Round window corners on the sidebar side on Linux (#61229)

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:
<img width="366" height="239" alt="image"
src="https://github.com/user-attachments/assets/e0ee8d28-6143-42ea-bfd2-10d069df5492"
/>

After:
<img width="426" height="219" alt="image"
src="https://github.com/user-attachments/assets/51d2979d-0103-4742-90bd-a97f5837d99b"
/>

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>
This commit is contained in:
Eric HolkandDanilo Leal authored and GitHub committed 2026-07-17 23:58:06 +00:00
1 parent 2e405c7c7a
commit ce3a79d607
5 files changed
+54 -37

No files matched your search

@@ -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(),
)
}
}
+2 -4
View File
@@ -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(),
)
}
}
+46 -6
View File
@@ -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())
+2 -7
View File
@@ -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()
},
)
}
}
+2 -17
View File
@@ -11103,19 +11103,10 @@ fn parse_pixel_size_env_var(value: &str) -> Option<Size<Pixels>> {
/// 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<Div> {
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))