Class PopupMenu
Menu (with checkable items,
separators and cascading submenus) as a real native window: an
undecorated, floating, transparent OS window (like a ComboBox popup) that may
overflow the owner window exactly like a true OS menu.
The menu is positioned relative to the screen and never overflows the visible area: each column flips (opens above / to the left) and then clamps against the monitor's work area, so it stays on screen no matter where the pointer is. A single native window is sized to the open cascade's bounding box and re-fit as submenus open/close; the whole cascade is rendered and hit-tested inside it.
It grabs keyboard focus while open (arrows navigate, Enter chooses, Esc closes); a press back in the owner window, an item choice, or Esc dismisses. Requires a scene bound to a window that is still open: a scene in no window, or in one that has closed, has nowhere to draw it, and the menu opens nothing there.
Two presentations: a window of its own, or an overlay inside the owner window;
same cascade, flip, clamp and navigation either way, but an overlay is clamped to that window
rather than to the display, so a menu near the bottom edge has less room than it would
elsewhere. setDisplayMode(limn.components.DisplayMode) chooses; displayMode() answers what was actually
used, which is not always the same thing. Three cases have no choice and always draw in scene: a
fullscreen window, which at a named mode
is minimized as soon as another window takes the focus, as a menu's does at once, and which at
the mode the display is in already covers the screen, so the menu loses nothing inside it;
any window reporting no WindowPeer.supportsAbsolutePositioning()
(Wayland), where a menu window would open somewhere other than the anchor; and a window
reporting no display, as one does where the
windowing system lists no monitor, the only one asleep or a session with none, which leaves no
work area to keep a menu window inside. All three are asked when the menu opens; a menu already
open in a window of its own when the owner enters or leaves fullscreen closes instead, cascade
and all, as if dismissed, running no item: see DisplayMode.
new PopupMenu(menu).showAt(grid, mouseX, mouseY); // context menu at the pointer
The whole cascade renders at one ControlSize step, resolved once when
the menu opens: from setControlSize(limn.scene.ControlSize) if set, else through the anchor widget, else
from the owner scene. Mixing steps across columns would desynchronise the submenu
y-alignment, the column overlap and the shared border weight.
And at one LayoutDirection, resolved through the same link at the same
moment. Reading right to left the whole cascade is mirrored: the root column aligns to the
anchor's right edge, submenus open to the left, the check gutter is on the right and the
accelerator hint and the submenu chevron on the left, and the arrow keys that open and close
a submenu swap. What does not mirror is the check mark itself, which no platform mirrors,
and the accelerator strings, which name physical keys.
-
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionvoidclose()Closes the menu (idempotent).static DisplayModebooleanisOpen()Whether the cascade is currently showing.observeClose(Runnable observer) Observes this menu closing: a dismissal, an item chosen, or aclose()from code alike, because a close is a lifecycle fact and not a gesture.onRootLeading(@Nullable Runnable listener) Hook for the arrow that walks toward the leading side at the root column (MenuBar: go to the previous menu).onRootTrailing(@Nullable Runnable listener) Hook for the arrow that walks toward the trailing side on a root item without a submenu (MenuBar: the next menu).@Nullable NativeWindowThe window the open menu is drawn in, for an application that captures it or a test that drives it: the menu is a window of its own, so a capture of the owner window does not contain it.setControlSize(@Nullable ControlSize size) Pins the step of the whole cascade, overriding what the anchor or the owner scene would give it, the escape hatch for "a compact context menu over a MEDIUM surface".static voidThe presentation everyPopupMenubuilt after this call starts from, including the ones the toolkit builds where an application never sees the object:MenuBar's dropdowns,ContextMenus, and a text field's own context menu.setDisplayMode(DisplayMode mode) Asks for a presentation; seeDisplayMode.setModal(boolean value) Makes the native popup block the owner window (window-modal), enforced by the backend exactly like aDialog: a press on the parent is ignored and beeps, and the menu closes only on a choice or Escape.voidshowAnchored(Scene ownerScene, float ax, float ay, float aw, float ah) Opens the menu attached to an anchor rectangle in scene coordinates.voidshowAnchored(Widget<?> anchor, float ax, float ay, float aw, float ah) Opens the menu attached to an anchor rectangle inanchor's scene coordinates, sized at the step resolved onanchor: it drops below the rect (a menu-bar dropdown) and flips above when there is no room.voidOpens the menu with its corner at the scene point(x, y), at the scene's step.voidOpens the menu with its corner at the scene point(x, y)(a context menu at the pointer), sized at the step resolved onanchor.
-
Constructor Details
-
PopupMenu
A popup presentingmenu; show it with one of theshow*methods.
-
-
Method Details
-
observeClose
Observes this menu closing: a dismissal, an item chosen, or aclose()from code alike, because a close is a lifecycle fact and not a gesture. Any number may observe; the menu bar that built this popup holds one, and an application may hold another.- Parameters:
observer- what to run when the menu closes; never null- Returns:
- a handle that unregisters; cancelling it twice is a no-op. UI thread
-
onRootLeading
Hook for the arrow that walks toward the leading side at the root column (MenuBar: go to the previous menu). That is LEFT reading left to right and RIGHT reading right to left; the physical key is flipped once, inside this class, so a listener registered here means "the previous menu" in either direction and must not flip again.One handler at a time:
nullclears it, and setting a second while one is set throwsIllegalStateException. -
onRootTrailing
Hook for the arrow that walks toward the trailing side on a root item without a submenu (MenuBar: the next menu). The trailing arrow is the one that opens a submenu where there is one, which is why it is the one that walks on where there is not;onRootLeadingstates the direction rule both share.One handler at a time:
nullclears it, and setting a second while one is set throwsIllegalStateException. -
setModal
Makes the native popup block the owner window (window-modal), enforced by the backend exactly like aDialog: a press on the parent is ignored and beeps, and the menu closes only on a choice or Escape. Defaultfalse(a press outside dismisses). Purely native: no in-scene overlay. -
setControlSize
Pins the step of the whole cascade, overriding what the anchor or the owner scene would give it, the escape hatch for "a compact context menu over a MEDIUM surface". Applied when the menu opens; a call on an already-open menu is ignored, because the columns' geometry snapshots were built from the row captured at open (seetokens).nullrestores inheritance. UI thread only. -
isOpen
public boolean isOpen()Whether the cascade is currently showing. -
popupWindow
The window the open menu is drawn in, for an application that captures it or a test that drives it: the menu is a window of its own, so a capture of the owner window does not contain it. Supported API, not a test hook.- Returns:
- this menu's native window while open, else
null, which is also the answer while the menu is drawn in the scene
-
showAt
Opens the menu with its corner at the scene point(x, y)(a context menu at the pointer), sized at the step resolved onanchor. This is the overload apps should use:new PopupMenu(m).showAt(grid, event.x(), event.y())makes a context menu over an XSMALL data grid XSMALL, which theSceneoverload cannot express.The coordinates are the anchor's scene coordinates (what
MouseEvent.x()reports), not anchor-local ones.The menu hangs from
anchor: it closes when the anchor leaves the scene, by itself, with a container or with the pageScene.setRoot(limn.scene.Widget<?>)replaces, and when it stops being shown, so its rows never act on a widget that has gone. A row of a list, a table or a tree that a refresh keeps because its item is still there does not leave: the menu stays, acting on the item as the row was built with it. -
showAt
Opens the menu with its corner at the scene point(x, y), at the scene's step. It hangs from no widget: nothing leaving the scene closes it. -
showAnchored
Opens the menu attached to an anchor rectangle inanchor's scene coordinates, sized at the step resolved onanchor: it drops below the rect (a menu-bar dropdown) and flips above when there is no room. A no-op for a widget outside a scene. Hangs fromanchorasshowAt(Widget, float, float)does. UI thread only. -
showAnchored
Opens the menu attached to an anchor rectangle in scene coordinates. A no-op on a scene bound to no window, or to one that has closed. UI thread only.The cascade takes the step resolved on the scene's root, the documented fallback, and the difference between "a context menu over an XSMALL data grid is XSMALL" and "it is whatever the scene says". Pass the widget it belongs to (
showAnchored(Widget, float, float, float, float)) to get the former. WhenScene.setRoot(limn.scene.Widget<?>)switches the page while the menu is open, the menu stays open and is hosted by the new root, holding nothing of the page that left; its columns keep the geometry they were built with until it closes. -
close
public void close()Closes the menu (idempotent). UI thread only. -
setDisplayMode
Asks for a presentation; seeDisplayMode. DefaultDisplayMode.NATIVE_WINDOW, which is what the platform's own menus do wherever a window can be placed at an anchor.A preference, not a guarantee, and only in one direction: a menu asked for
NATIVE_WINDOWstill draws in scene on Wayland, where a menu window does not land where the anchor is, in a fullscreen window, for the reasonsDisplayModegives, and in a window that reports no display, with no work area to keep a menu window inside.IN_SCENEis always honoured; it needs nothing from the platform, a display included. ReaddisplayMode()for what happened.Takes effect on the next open; a menu already on screen is not re-presented under it.
-
setDefaultDisplayMode
The presentation everyPopupMenubuilt after this call starts from, including the ones the toolkit builds where an application never sees the object:MenuBar's dropdowns,ContextMenus, and a text field's own context menu.This exists because the choice is almost never per-menu. An application either shows its popups in windows or it does not: because everything it draws has to be inside one window for a screen share, because it is being screenshotted for its own documentation, or because it is a kiosk with no desktop behind it to float over. Setting that per call site means finding every call site, including the ones inside this toolkit.
Process-wide, like
Theme.setCurrent(limn.components.Theme)andFonts. Menus already open are unaffected, andsetDisplayMode(limn.components.DisplayMode)still overrides it for one menu. UI thread. -
defaultDisplayMode
- Returns:
- the presentation new menus start from.
-
displayMode
- Returns:
- how the menu is presented: while it is open, what it actually chose, including a
platform forcing
IN_SCENEover a request for a window. While it is closed, whatsetDisplayMode(limn.components.DisplayMode)last asked for, since nothing has been decided yet.
-