Skip to content

Move the macOS window by its toolbar - #22

Merged
chrisuthe merged 2 commits into
masterfrom
chrisuthe/task/make-the-macos-window-movable-by-its-toolbar-row
Sep 2, 2026
Merged

Move the macOS window by its toolbar#22
chrisuthe merged 2 commits into
masterfrom
chrisuthe/task/make-the-macos-window-movable-by-its-toolbar-row

Conversation

@chrisuthe

@chrisuthe chrisuthe commented Sep 2, 2026

Copy link
Copy Markdown
Owner

On macOS the main window could not be moved at all. MainWindow extends the client area into the decorations, so there is no native title bar to grab, and nothing called BeginMoveDrag.

The toolbar is now the drag handle. It gets a transparent — so hit-testable — background while the decorations are extended, and none at all where they are native, and a bubbling PointerPressed calls BeginMoveDrag behind a pure static: extended decorations, the primary button, and a press outside the traffic-light cluster.

docs/ARCHITECTURE.md claimed the 28 px title strip the hint uncovers still moved the window. Re-running ShellSpike chrome on macOS 26.6.2 with Avalonia 12.1.1 shows it does not, so that line is corrected. The same reading is what makes the fix work — a press that gets eaten is a press that arrived — and it is why the surface has to be the whole toolbar Border rather than its inset content: a margin sits outside a control's bounds, and the strip is exactly the part the inset holds clear.

Verified by hand on the Mac: the toolbar and the strip drag, the gear still toggles Settings on a single click, a right-click starts no drag, the traffic lights and the art bleeding up behind them are untouched, and the top edge and top-right corner still resize — AppKit keeps the resize edges, so a press only ever begins a move on a pixel it has already declined. Nothing changes on Linux or Windows, where IsExtendedIntoWindowDecorations is False and the toolbar gets no drag surface at all.

Measured on the way and recorded: the shipped app reports a 32 px decoration margin where the spike measured 28, on the same OS and Avalonia build. The cause is not established, and no code carries the figure. Double-click-to-zoom was asked for alongside this and deliberately left out — Avalonia 12 cannot read the macOS AppleActionOnDoubleClick preference, so any behaviour would hardcode one of three answers; docs/NEXT_STEPS.md item 10 records the argument and the first action.

Extending the client area into the decorations leaves no native title bar to
grab, so the main window could not be moved at all on macOS. Re-running
ShellSpike chrome on macOS 26.6.2 with Avalonia 12.1.1 shows the 28 px title
strip the hint uncovers does not move the window either, which ARCHITECTURE.md
claimed it did; that line is corrected. The same reading is what makes a fix
possible: a press that gets eaten is a press that arrived, so BeginMoveDrag has
something to work with.

The drag surface is the whole toolbar Border, given a transparent background
while the decorations are extended and none at all where they are native, so
Linux and Windows keep an untouched, un-hit-testable toolbar. It has to be the
Border rather than the inset content, because a margin sits outside a control's
bounds and the strip is exactly the part the inset holds clear.

BeginsWindowDrag is a pure static over the three conditions: extended
decorations, the primary button — macOS opens the window's own menu on a
secondary press in the title area — and a press outside the traffic-light
cluster, whose rectangle is read off the toolbar inset rather than restating the
78 px. AppKit hit-tests those buttons first, so the carve-out is a guard that
does not depend on that staying true. The press is left to bubble and is not
taken handledEventsToo, so the settings ToggleButton marks its own press handled
and the gear keeps toggling instead of dragging.

Reading the strip's height instead of assuming it also earned its keep: the
shipped app reports a 32 px decoration margin where the spike measured 28, on
the same OS and Avalonia build, and no code carries the figure.
@chrisuthe
chrisuthe marked this pull request as ready for review September 2, 2026 12:09
The only conflict was a numbering collision in docs/NEXT_STEPS.md: master's
artwork-channel item and this branch's double-click-to-zoom item both landed at
slot 9. Master's keeps 9 and this branch's becomes 10, which also matches the
file's stated ordering — the follow-up here is the least blocking of the two.
@chrisuthe
chrisuthe merged commit a39db11 into master Sep 2, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant