zed/crates/gpui/examples/window_movable.rs
Remco Smits 23bb2fc135
macos: Fix window move controls are disabled (#60620)
Closes https://github.com/zed-industries/zed/issues/60595

cc Anthony-Eid since you did an initial pass on this.

# Objective

Currenly on macOs there is no way right now after the following
regression to use window management short keys to move them around. This
worked before, but was regressed initially in
https://github.com/zed-industries/zed/pull/59836 that tried fixing a
macos 27 clicking delay issue on the titlebar. That accidentally set the
`native_window.setMovable_(is_movable as BOOL)` to false. Because of
this mocos detects this and disables the window managements actions
because it thinks the window can/should not be able to move.

## Solution

The macOS 27 click delay actually comes from AppKit's native titlebar
dragging, not from `NSWindow.isMovable`. The problem is that
`is_movable` is also what enables the Window menu's tiling options, and
#59836 used it to turn off dragging, which disabled the tiling menu as a
side effect.

To fix this, I restored `is_movable` to its real meaning so the tiling
menu works again, and added a separate
`WindowOptions::app_owns_titlebar_drag` flag for windows that draw their
own titlebar and handle dragging via `Window::start_window_move`. On
macOS this tells AppKit to stop owning titlebar drags (removing the
click delay) without affecting `is_movable`. Zed's main window now sets
both `is_movable: true` and `app_owns_titlebar_drag: true`, so the
tiling menu stays enabled, the click delay is gone. I also added a
`window_movable` gpui example to make this behavior easy to test
manually.

## Testing

I added a gpui test example that helps testing this bug. You can run the
newly added gpui example with 4 different window configurations that
should confirm that this change is the right one for all of the window
types that we have.

## 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 adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [x] Tests cover the new/changed behavior
- [x] Performance impact has been considered and is acceptable

## Showcase

**After** (note this now shows the window managements entries as they
were greyed out.)


https://github.com/user-attachments/assets/192cc0e1-15d8-486f-b788-a92bb51fb7d1

**GPUI** (test examples with different window configurations.)


https://github.com/user-attachments/assets/c9e0f4d3-606b-4cd7-b4d8-a5bb16508164

---

Release Notes:

- Fix macos window management controlls were grayed out/not working
2026-07-12 22:46:47 +00:00

125 lines
3.5 KiB
Rust

#![cfg_attr(target_family = "wasm", no_main)]
use gpui::{
App, Bounds, Context, FocusHandle, Window, WindowBounds, WindowOptions, div, prelude::*, px,
rgb, size,
};
use gpui::{SharedString, TitlebarOptions};
use gpui_platform::application;
struct ExampleWindow {
label: SharedString,
focus_handle: FocusHandle,
}
impl Render for ExampleWindow {
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
div()
.track_focus(&self.focus_handle)
.flex()
.flex_col()
.gap_3()
.bg(rgb(0x2e2e2e))
.size_full()
.justify_center()
.items_center()
.p_8()
.text_lg()
.text_color(rgb(0xffffff))
.child(self.label.clone())
.child(
div()
.text_sm()
.text_color(rgb(0xb0b0b0))
.child("Try to drag the titlebar, and check the Window menu."),
)
}
}
fn open_test_window(
cx: &mut App,
bounds: Bounds<gpui::Pixels>,
label: &str,
is_movable: bool,
appears_transparent: bool,
app_owns_titlebar_drag: bool,
) {
let label = SharedString::from(format!(
"{label}\nis_movable: {is_movable}\n\
appears_transparent: {appears_transparent}\n\
app_owns_titlebar_drag: {app_owns_titlebar_drag}"
));
cx.open_window(
WindowOptions {
window_bounds: Some(WindowBounds::Windowed(bounds)),
is_movable,
app_owns_titlebar_drag,
titlebar: Some(TitlebarOptions {
title: Some(label.clone()),
appears_transparent,
..Default::default()
}),
..Default::default()
},
|window, cx| {
cx.new(|cx| {
let focus_handle = cx.focus_handle();
focus_handle.focus(window, cx);
ExampleWindow {
label,
focus_handle,
}
})
},
)
.unwrap();
}
fn run_example() {
application().run(|cx: &mut App| {
let window_size = size(px(420.), px(280.0));
let base = Bounds::centered(None, window_size, cx);
// (label, is_movable, appears_transparent, app_owns_titlebar_drag, col, row)
let windows = [
("Native titlebar, movable", true, false, false, 0.0, 0.0),
(
"Native titlebar, NOT movable",
false,
false,
false,
1.0,
0.0,
),
("Custom titlebar, movable", true, true, false, 0.0, 1.0),
("Custom titlebar, NOT movable", false, true, false, 1.0, 1.0),
];
for (label, is_movable, appears_transparent, app_owns_titlebar_drag, col, row) in windows {
let mut bounds = base;
bounds.origin.x += window_size.width * col;
bounds.origin.y += window_size.height * row;
open_test_window(
cx,
bounds,
label,
is_movable,
appears_transparent,
app_owns_titlebar_drag,
);
}
});
}
#[cfg(not(target_family = "wasm"))]
fn main() {
run_example();
}
#[cfg(target_family = "wasm")]
#[wasm_bindgen::prelude::wasm_bindgen(start)]
pub fn start() {
gpui_platform::web_init();
run_example();
}