Skip to content

fix(library): anchor the native context menu popup at the pointer position - #5182

Merged
chrox merged 2 commits into
mainfrom
fix/library-context-menu-wayland
Jul 18, 2026
Merged

fix(library): anchor the native context menu popup at the pointer position#5182
chrox merged 2 commits into
mainfrom
fix/library-context-menu-wayland

Conversation

@chrox

@chrox chrox commented Jul 18, 2026

Copy link
Copy Markdown
Member

What

Two commits:

  1. Anchor the native library context menu at an explicit position instead of calling menu.popup() with no arguments, so the popup gets a parent surface on Wayland.
  2. Map the click to window-logical coordinates via its fraction of the CSS viewport, and cache/prewarm the built native menu per item.

Why

Fixes #5181, fixes #5041 — on Linux/Wayland the library context menu flashed and disappeared immediately when opened with a touchpad two-finger tap.

The reporter's terminal capture showed the root cause:

Gdk-WARNING **: Couldn't map window 0x… as popup because it doesn't have a parent

In muda's GTK backend, a positionless popup is anchored to the screen's root window. On X11 the root window is real, so this works; on Wayland there is no root window — the popup gets no parent and the compositor refuses to map it. With an explicit position, muda anchors the popup to the app window's own GdkWindow, which always maps.

Coordinate mapping. CSS pixels are not window-logical pixels when the webview carries a page zoom: WebKitGTK folds the desktop text-scaling factor into one without reflecting it in devicePixelRatio (measured on X11 with GDK scale 2 and a 0.8 text scale: CSS renders at 1.6× physical while dPR reports 2). Both LogicalPosition(clientX) and PhysicalPosition(clientX × dPR) therefore land the menu offset from the click by a factor that grows with distance (right-click book 3, menu on book 4). The zoom-proof conversion maps the click's fraction of the CSS viewport onto the window's logical size:

logicalX = clientX / window.innerWidth × (innerSize.width / scaleFactor)

Any uniform zoom cancels out of the ratio, and on macOS/Windows (where CSS px = logical px) the formula degenerates to the plain client coordinates.

Latency. Building a dozen-item native menu over the IPC boundary on every right-click made the popup visibly lag. The menu is now built once per item, cached, invalidated when item state baked into it changes, and prewarmed on pointer enter so the first right-click pops instantly.

Verification

  • Verified on X11 GNOME with 1.6× effective scaling by synthesizing right-clicks (XTEST) against the built release binary and measuring the popup window position: the menu now opens exactly at the click point; before the fix it landed at 1.25× the click's distance from the window origin.
  • book-context-menu-popup-position.test.tsx pins the viewport-fraction conversion (no-zoom and 0.8-zoom cases); book-context-menu-cache.test.tsx pins the cache/prewarm/invalidation behavior.
  • pnpm test (7606 passed) and pnpm lint clean.

🤖 Generated with Claude Code

chrox and others added 2 commits July 18, 2026 23:06
…ition

A positionless menu.popup() makes muda anchor the GTK popup to the X11
root window, which doesn't exist on Wayland — the popup gets no parent,
the compositor refuses to map it (Gdk-WARNING: Couldn't map window as
popup because it doesn't have a parent), and the menu disappears
immediately. Pass the contextmenu pointer position (or the item's center
for keyboard invocation) so muda anchors the popup to the app window's
own GdkWindow instead.

Fixes #5181
Fixes #5041

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
CSS pixels are not window-logical pixels when the webview carries a page
zoom: WebKitGTK folds the desktop text-scaling factor into one without
reflecting it in devicePixelRatio (e.g. GDK scale 2 with 0.8 text scale
renders CSS at 1.6x physical while dPR reports 2), so popping the menu at
the raw client coordinates lands it offset from the click by a factor
that grows with distance. Map the click's fraction of the CSS viewport
onto the window's logical size instead — any uniform zoom cancels out of
the ratio, and on macOS/Windows the formula degenerates to the plain
client coordinates.

Also cache the built native menu per item and prewarm it on pointer
enter, so popup() no longer waits for a dozen Menu items to cross the
IPC boundary on every right-click.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@chrox
chrox merged commit 5f01052 into main Jul 18, 2026
12 checks passed
@chrox
chrox deleted the fix/library-context-menu-wayland branch July 18, 2026 16:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant