fix(macos): clamp context-menu popup so it fits on-screen#352
Conversation
`popUpMenuPositioningItem:atLocation:inView:` does not auto-flip the menu when it would overflow the screen, so right-clicking near the bottom edge produces a menu with scroll arrows. Switch to `popUpContextMenu:withEvent:forView:`, which AppKit clamps and auto-flips against the screen's visible frame for free. The popup location is computed in the view's window's coordinate space and attached to a synthesized right-mouse-down `NSEvent` that drives the call, matching how AppKit's own contextual-menu path works.
fb97acd to
bd556b9
Compare
|
Hi, @amrbashir! Any chance I could get a review on this one? Sorry for not pinging you earlier, I somehow just assumed someone would review the PR. |
|
Could you provide a few screenshots, aka before and after? Also do you know if Windows and Linux handle the clamp automatically or do we need to also implement it there as well |
`popUpContextMenu:withEvent:forView:` automatically appends contextual-menu plug-in items (AutoFill, Services, third-party plug-ins) to the menu. The legacy positioning call never did that, so opt out via `setAllowsContextMenuPlugIns(false)` to keep the menu's contents exactly what the consumer built.
|
Sure! Setup was: window at the bottom of my screen, 12 extra items added so the menu is taller than the gap below the click point. Then I always right-clicked roughly where you see the top-left of the context menu in the first screenshot below. Before (
|
|
@amrbashir, sorry, forgot to mention you, not sure how your notifications are set up, so just wanted to send a polite ping. |



Problem
popUpMenuPositioningItem:atLocation:inView:does not auto-flip or clamp the menu when it would overflow the screen, so right-clicking near the bottom edge of a window produces a menu with scroll arrows instead of one that's fully visible. It looks bad.Easy repro: open any consumer of
ContextMenu::show_context_menu_for_nsview, right-click within ~one menu-height of the bottom of the screen → the menu opens with a downward scroll arrow rather than flipping upward like a standard AppKit contextual menu. Not nice.Fix
Switch
show_context_menuto+[NSMenu popUpContextMenu:withEvent:forView:], which is AppKit's idiomatic contextual-menu entry point and handles auto-flip, screen clamp, menu bar / Dock margins, and multi-monitor positioning natively. Driving it requires anNSEvent, so we synthesize a right-mouse-down event with+[NSEvent mouseEventWithType:location:modifierFlags:timestamp:windowNumber:context:eventNumber:clickCount:pressure:]whoselocationInWindowis the popup point in the target view's window's coordinate space.popUpContextMenu:withEvent:forView:requires a non-nilNSView. The current API already takes a*const c_voidview that the caller guarantees is valid, so we pass it through directly. If+mouseEventWithType:ever fails to synthesize an event, the code falls back topopUpMenuPositioningItem:atLocation:inView:so the menu still appears.Edge cases handled
[window windowNumber]), which is whatpopUpContextMenu:withEvent:forView:expects.Behavior change risks
User-visible end result is the same: menus that previously fit on-screen still fit, menus that previously overflowed now flip up / shift inward instead of showing scroll arrows. Internally the implementation now matches Apple's idiom, so future macOS tweaks to contextual-menu positioning come for free.
Cargo.toml
Adds
"NSGraphicsContext"toobjc2-app-kitfeatures (required by+mouseEventWithType:..., even though we passnilfor the context argument) and"NSDate"toobjc2-foundationfeatures (for the timestamp). NoNSScreenfeature needed — AppKit handles screen geometry internally.Test plan
I did this:
cargo build,cargo test,cargo clippy --all-targets -- -D warnings,cargo fmt --check, they all pass.Changelog
Covector entry included as
.changes/macos-popup-clamp.md(patchbump).