fix(dock): resize a split without GPUI's drag machinery - #55
Merged
Conversation
A dock resize handle was wired with `on_drag` + `on_drag_move`. That machinery exists to float a preview under the cursor, and GPUI pays for it by calling `Window::refresh()` on EVERY mouse move while a drag is active — "redraw the window so that the active drag can follow the mouse cursor". `refresh()` sets `refreshing`, which makes every cached view in the window miss. So dragging one handle rebuilt, re-laid-out and repainted every panel in the dock, including the ones nowhere near it. `DockResizeDrag::render` returned `Empty`: the window was being refreshed per mouse move to move nothing. The handle now arms a gesture on mouse-down and is followed by window-level listeners installed from a `canvas` paint closure — `Window::on_mouse_event` is a paint-phase API, so registering from `render` would be the wrong phase. Window-level rather than element-scoped because the platform captures the pointer for the life of the press (`SetCapture`/`ReleaseCapture` on Windows), so a handle dragged past the window edge keeps receiving moves. The listeners exist only while a gesture is in flight. Repaints are paced to 60 Hz. The layout is not: `resize_*` still runs on every move, so the handle never trails the pointer — only the repaint is thinned. The release notifies unconditionally, settling whatever the pacer dropped. The cursor is held for the gesture through `set_window_cursor_style`, since the pointer leaves the thin handle immediately and the axis is known only where the press happened. It is a per-frame request, so it lapses when the gesture ends. `DockResizeDrag` and its `Render` impl are removed with the mechanism that needed them. Measured in a host terminal on a 120 Hz display, one splitter drag, per second: window draws 100 -> 80, `Window::draw` total 776 -> 515 ms, and the two panels whose size does NOT change went from being rebuilt 54 and 53 times a second to 4 and 1. The two beside the handle still rebuild per frame, which is the actual work of resizing them.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
A dock resize handle was wired with
on_drag+on_drag_move. That machinery exists to float a preview under the cursor, and GPUI pays for it by callingWindow::refresh()on every mouse move while a drag is active — "redraw the window so that the active drag can follow the mouse cursor" (window.rs).refresh()setsrefreshing, which makes every cached view in the window miss.So dragging one handle rebuilt, re-laid-out and repainted every panel in the dock, including the ones nowhere near it. And
DockResizeDrag::renderreturnedEmpty— the window was being refreshed per mouse move to move nothing.How it works now
canvaspaint closure:Window::on_mouse_eventis a paint-phase API, so registering fromrenderwould be the wrong phase.SetCapture/ReleaseCaptureon Windows) — a handle dragged past the window edge keeps receiving moves.Notable decisions
resize_*still runs on every move, so the handle never trails the pointer; only the repaint is thinned. The release notifies unconditionally, settling whatever the pacer dropped.set_window_cursor_style: the pointer leaves the thin handle immediately, and the axis is known only where the press happened. It is a per-frame request, so it lapses on its own when the gesture ends.DockResizeDragand itsRenderimpl are removed along with the mechanism that needed them.Known limitations
DockEvent::LayoutChangedis still emitted per changed move, now at the raw mouse-move rate. Measured at 58 µs per consumer dump in the host terminal (≈5 ms/s), so it was left alone rather than paced on a guess.Measured
Host terminal, 120 Hz display, one splitter drag, per second:
Window::drawtotalThe two panels beside the handle still rebuild per frame — that is the actual work of resizing them.
How to verify
MoonUI guardrails PASS, exit 0 — fmt, gallery check, 431 component tests, 7 gallery tests, and the component-audit / component-api / donor-mirror baselines.