linux/x11: Fix gap when tiling windows side by side (#13859)
By leveraging the `_GTK_EDGE_CONSTRAINTS` atom we can get all four booleans for the `Tiling` struct and figure out which side is free when the window is tiled to half of the screen. For the logic behind the `_GTK_EDGE_CONSTRAINTS` see: -8e9d13aa3b/src/x11/window-x11.c (L65-L75)
-8e9d13aa3b/src/x11/window-x11.c (L1205-L1231)
(I used Claude 3.5 Sonnet with our code and these pieces from `mutter` to generate the Rust code, that was pretty sweet) This fixes the gap in the middle when a GPUI window is tiled to the left and another window to the right. It's not _perfect_ but it looks a lot better. Here's a diff that makes it look better: ```diff diff --git a/crates/gpui/examples/window_shadow.rs b/crates/gpui/examples/window_shadow.rs index 122231f6b..7fa29dadc 100644 --- a/crates/gpui/examples/window_shadow.rs +++ b/crates/gpui/examples/window_shadow.rs @@ -72,8 +72,8 @@ impl Render for WindowShadow { .when(!(tiling.top || tiling.left), |div| div.rounded_tl(rounding)) .when(!tiling.top, |div| div.pt(shadow_size)) .when(!tiling.bottom, |div| div.pb(shadow_size)) - .when(!tiling.left, |div| div.pl(shadow_size)) - .when(!tiling.right, |div| div.pr(shadow_size)) + .when(!tiling.left, |div| div.pl(shadow_size - border_size)) + .when(!tiling.right, |div| div.pr(shadow_size - border_size)) .on_mouse_move(|_e, cx| cx.refresh()) .on_mouse_down(MouseButton::Left, move |e, cx| { let size = cx.window_bounds().get_bounds().size; ``` But that makes it look weird on Wayland, so I didn't do it. I think it's fine for now. Chromium looks bad and has a gap, so we're already better. ## Before   ## After   Release Notes: - N/A
This commit is contained in:
parent
c4dbe32f20
commit
e69f9d6cf9
2 changed files with 99 additions and 17 deletions
|
@ -86,6 +86,49 @@ impl ResizeEdge {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct EdgeConstraints {
|
||||||
|
top_tiled: bool,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
top_resizable: bool,
|
||||||
|
|
||||||
|
right_tiled: bool,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
right_resizable: bool,
|
||||||
|
|
||||||
|
bottom_tiled: bool,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
bottom_resizable: bool,
|
||||||
|
|
||||||
|
left_tiled: bool,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
left_resizable: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EdgeConstraints {
|
||||||
|
fn from_atom(atom: u32) -> Self {
|
||||||
|
EdgeConstraints {
|
||||||
|
top_tiled: (atom & (1 << 0)) != 0,
|
||||||
|
top_resizable: (atom & (1 << 1)) != 0,
|
||||||
|
right_tiled: (atom & (1 << 2)) != 0,
|
||||||
|
right_resizable: (atom & (1 << 3)) != 0,
|
||||||
|
bottom_tiled: (atom & (1 << 4)) != 0,
|
||||||
|
bottom_resizable: (atom & (1 << 5)) != 0,
|
||||||
|
left_tiled: (atom & (1 << 6)) != 0,
|
||||||
|
left_resizable: (atom & (1 << 7)) != 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_tiling(&self) -> Tiling {
|
||||||
|
Tiling {
|
||||||
|
top: self.top_tiled,
|
||||||
|
right: self.right_tiled,
|
||||||
|
bottom: self.bottom_tiled,
|
||||||
|
left: self.left_tiled,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Visual {
|
struct Visual {
|
||||||
id: xproto::Visualid,
|
id: xproto::Visualid,
|
||||||
|
@ -198,6 +241,7 @@ pub struct X11WindowState {
|
||||||
active: bool,
|
active: bool,
|
||||||
fullscreen: bool,
|
fullscreen: bool,
|
||||||
decorations: WindowDecorations,
|
decorations: WindowDecorations,
|
||||||
|
edge_constraints: Option<EdgeConstraints>,
|
||||||
pub handle: AnyWindowHandle,
|
pub handle: AnyWindowHandle,
|
||||||
last_insets: [u32; 4],
|
last_insets: [u32; 4],
|
||||||
}
|
}
|
||||||
|
@ -497,6 +541,7 @@ impl X11WindowState {
|
||||||
destroyed: false,
|
destroyed: false,
|
||||||
decorations: WindowDecorations::Server,
|
decorations: WindowDecorations::Server,
|
||||||
last_insets: [0, 0, 0, 0],
|
last_insets: [0, 0, 0, 0],
|
||||||
|
edge_constraints: None,
|
||||||
counter_id: sync_request_counter,
|
counter_id: sync_request_counter,
|
||||||
last_sync_counter: None,
|
last_sync_counter: None,
|
||||||
refresh_rate,
|
refresh_rate,
|
||||||
|
@ -686,10 +731,32 @@ impl X11WindowStatePtr {
|
||||||
|
|
||||||
pub fn property_notify(&self, event: xproto::PropertyNotifyEvent) {
|
pub fn property_notify(&self, event: xproto::PropertyNotifyEvent) {
|
||||||
let mut state = self.state.borrow_mut();
|
let mut state = self.state.borrow_mut();
|
||||||
if event.atom == state.atoms._NET_WM_STATE
|
if event.atom == state.atoms._NET_WM_STATE {
|
||||||
|| event.atom == state.atoms._GTK_EDGE_CONSTRAINTS
|
|
||||||
{
|
|
||||||
self.set_wm_properties(state);
|
self.set_wm_properties(state);
|
||||||
|
} else if event.atom == state.atoms._GTK_EDGE_CONSTRAINTS {
|
||||||
|
self.set_edge_constraints(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_edge_constraints(&self, mut state: std::cell::RefMut<X11WindowState>) {
|
||||||
|
let reply = self
|
||||||
|
.xcb_connection
|
||||||
|
.get_property(
|
||||||
|
false,
|
||||||
|
self.x_window,
|
||||||
|
state.atoms._GTK_EDGE_CONSTRAINTS,
|
||||||
|
xproto::AtomEnum::CARDINAL,
|
||||||
|
0,
|
||||||
|
4,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
.reply()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if reply.value_len != 0 {
|
||||||
|
let atom = u32::from_ne_bytes(reply.value[0..4].try_into().unwrap());
|
||||||
|
let edge_constraints = EdgeConstraints::from_atom(atom);
|
||||||
|
state.edge_constraints.replace(edge_constraints);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1194,15 +1261,19 @@ impl PlatformWindow for X11Window {
|
||||||
match state.decorations {
|
match state.decorations {
|
||||||
WindowDecorations::Server => Decorations::Server,
|
WindowDecorations::Server => Decorations::Server,
|
||||||
WindowDecorations::Client => {
|
WindowDecorations::Client => {
|
||||||
// https://source.chromium.org/chromium/chromium/src/+/main:ui/ozone/platform/x11/x11_window.cc;l=2519;drc=1f14cc876cc5bf899d13284a12c451498219bb2d
|
let tiling = if let Some(edge_constraints) = &state.edge_constraints {
|
||||||
Decorations::Client {
|
edge_constraints.to_tiling()
|
||||||
tiling: Tiling {
|
} else {
|
||||||
|
// https://source.chromium.org/chromium/chromium/src/+/main:ui/ozone/platform/x11/x11_window.cc;l=2519;drc=1f14cc876cc5bf899d13284a12c451498219bb2d
|
||||||
|
Tiling {
|
||||||
top: state.maximized_vertical,
|
top: state.maximized_vertical,
|
||||||
bottom: state.maximized_vertical,
|
bottom: state.maximized_vertical,
|
||||||
left: state.maximized_horizontal,
|
left: state.maximized_horizontal,
|
||||||
right: state.maximized_horizontal,
|
right: state.maximized_horizontal,
|
||||||
},
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
|
Decorations::Client { tiling }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1212,17 +1283,26 @@ impl PlatformWindow for X11Window {
|
||||||
|
|
||||||
let dp = (inset.0 * state.scale_factor) as u32;
|
let dp = (inset.0 * state.scale_factor) as u32;
|
||||||
|
|
||||||
let (left, right) = if state.maximized_horizontal {
|
let insets = if let Some(edge_constraints) = &state.edge_constraints {
|
||||||
(0, 0)
|
let left = if edge_constraints.left_tiled { 0 } else { dp };
|
||||||
|
let top = if edge_constraints.top_tiled { 0 } else { dp };
|
||||||
|
let right = if edge_constraints.right_tiled { 0 } else { dp };
|
||||||
|
let bottom = if edge_constraints.bottom_tiled { 0 } else { dp };
|
||||||
|
|
||||||
|
[left, right, top, bottom]
|
||||||
} else {
|
} else {
|
||||||
(dp, dp)
|
let (left, right) = if state.maximized_horizontal {
|
||||||
|
(0, 0)
|
||||||
|
} else {
|
||||||
|
(dp, dp)
|
||||||
|
};
|
||||||
|
let (top, bottom) = if state.maximized_vertical {
|
||||||
|
(0, 0)
|
||||||
|
} else {
|
||||||
|
(dp, dp)
|
||||||
|
};
|
||||||
|
[left, right, top, bottom]
|
||||||
};
|
};
|
||||||
let (top, bottom) = if state.maximized_vertical {
|
|
||||||
(0, 0)
|
|
||||||
} else {
|
|
||||||
(dp, dp)
|
|
||||||
};
|
|
||||||
let insets = [left, right, top, bottom];
|
|
||||||
|
|
||||||
if state.last_insets != insets {
|
if state.last_insets != insets {
|
||||||
state.last_insets = insets;
|
state.last_insets = insets;
|
||||||
|
|
|
@ -6491,6 +6491,8 @@ pub fn client_side_decorations(element: impl IntoElement, cx: &mut WindowContext
|
||||||
cx.set_client_inset(theme::CLIENT_SIDE_DECORATION_SHADOW);
|
cx.set_client_inset(theme::CLIENT_SIDE_DECORATION_SHADOW);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println!("decorations: {:?}", decorations);
|
||||||
|
|
||||||
struct GlobalResizeEdge(ResizeEdge);
|
struct GlobalResizeEdge(ResizeEdge);
|
||||||
impl Global for GlobalResizeEdge {}
|
impl Global for GlobalResizeEdge {}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue