NSMenuView: opt-in Tognazzini "aim triangle" submenu tracking - #451
NSMenuView: opt-in Tognazzini "aim triangle" submenu tracking#451mrtonik wants to merge 2 commits into
Conversation
Replace the ad-hoc time/velocity heuristic that decides when a just-opened submenu stops being protected with a directional wedge, enabled by the GSMenuSubmenuAimTracking user default. While the pointer keeps aiming into the open submenu it stays open at any speed; leaving the wedge (a deliberate vertical move to a sibling) switches at once, and a short park timeout still switches if the pointer stops over another item. The previous behaviour is kept byte-for-byte when the default is unset, and aim tracking is confined to vertical menus so horizontal bars keep their instant switch. Adds a geometry regression test and documents the new default. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Thank you very much @mrtonik, this will greatly improve how submenus work. When I try to build this, I get: Do you have an idea what might be causing this? |
That build error isn't from this PR — it's in Fix on your side: gmake clean
gmake # rebuild against local Headers/
sudo -E gmake install # refresh the installed headersIf it persists, confirm the header actually got updated: grep _linkNumber /System/Library/Headers/AppKit/NSDataLink.hCI here builds clean on all three configs, so the aim-triangle change itself is unaffected. |
|
Must be something with my local system then. Could it theoretically be that it picks up on already-installed library rather than the one just built but not yet installed? I cannot install this because I cannot build it, and the already-installed version doesn't have |
NSMenuView: opt-in Tognazzini "aim triangle" submenu tracking
Summary
What: This PR adds the "aim triangle" (wedge) algorithm feature from the
classic Macintosh hierarchical menus, as specified by Bruce Tognazzini to the
submenu-tracking logic in NSMenuView.m.
Why: The current approach protects a newly opened submenu from closing using
a short fixed delay combined with a horizontal-movement check. This works
reasonably well, but since human arm movement pivots at the elbow, users
naturally drift vertically while sliding toward a submenu, and a timer-based
approach can't fully account for that.
The wedge algorithm instead tracks direction rather than time: it forms a
triangle from the point where the pointer entered the parent item to the near
edge of the open submenu, and keeps the submenu open as long as the pointer
stays within that triangle, however slowly the user moves. A clear vertical move
outside the triangle switches items immediately. This mirrors how the original
Mac implementation handled the problem and removes the need for a fixed timeout,
while ideally making submenu navigation a bit more forgiving for natural mouse
movement.
Behavior
existing behavior.
GSMenuSubmenuAimTracking = YESenables the wedge.Scope / limitations
Aim tracking applies to vertical menus only. It is deliberately disabled for
horizontal menu views (
-[NSMenuView isHorizontal]), so switching betweentop-level items on a horizontal menu bar stays instant, as it should. A
practical consequence worth calling out: for a horizontal / global menu bar,
the first-level submenu opens below the bar and is therefore not wedge-tracked
— only the deeper, vertically-stacked submenus benefit from the aim triangle.
This matches the intent (the diagonal-traversal problem the wedge solves only
exists for side-opening vertical submenus) and keeps menu-bar navigation
feeling instant.
Implementation notes
-[NSMenuView _trackWithEvent:startingMenuView:];the legacy heuristic is preserved unchanged in the
elsebranch.(
+_mouseAt:aimsAtSubmenuFrame:fromApex:slack:) using three edgecross-products. The all-same-sign check is orientation independent, so it is
immune to GNUstep's flipped screen coordinates and to submenus nudged leftward
by
shiftOnScreen.compares window-base points (translation-invariant distance).
Compatibility
Opt-in via a user default; no API or behavior change unless the default is set.
Works across interface styles (NeXTstep / Win95 / Macintosh) since the wedge only
gates the just-attached-submenu reset and falls back cleanly when the submenu
window is not available.
Documentation
Documentation/GuiUser/DefaultsSummary.gsdocdocuments the new default.NSMenuView.hheader comment now describes the opt-in mode (replacing the"rather adhoc" note).
Testing
Tests/gui/NSMenuView/aim_triangle.m— geometry regression test covering thediagonal-inside, vertical-outside, slack-boundary, and left-opening (right
edge) cases.
gnustep-guibuild is warning-free.Let me know your thoughts, if I missed something, or if I should change anything.