Class PopupMenu

java.lang.Object
limn.components.PopupMenu

public final class PopupMenu extends Object
A context menu / dropdown that presents a 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 windowed scene; headless scenes have no display to place it on.

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. Two platforms have no choice and always draw in scene: macOS exclusive fullscreen, where taking focus minimizes the fullscreen owner, and any window reporting no NativeWindow.supportsAbsolutePositioning() (Wayland), where a menu window would open somewhere other than the anchor.


 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
    Constructor
    Description
    A popup presenting menu; show it with one of the show* methods.
  • Method Summary

    Modifier and Type
    Method
    Description
    void
    Closes the menu (idempotent).
     
     
    boolean
    Whether the cascade is currently showing.
    onClose(Runnable listener)
    Runs listener when the menu closes (dismissed or an item chosen).
    Hook for the arrow that walks toward the leading side at the root column (MenuBar: go to the previous menu).
    Hook for the arrow that walks toward the trailing side on a root item without a submenu (MenuBar: the next menu).
     
    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 void
    The presentation every PopupMenu built 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.
    Asks for a presentation; see DisplayMode.
    setModal(boolean value)
    Makes the native popup block the owner window (window-modal), enforced by the backend exactly like a Dialog: a press on the parent is ignored and beeps, and the menu closes only on a choice or Escape.
    void
    showAnchored(Scene ownerScene, float ax, float ay, float aw, float ah)
    Opens the menu attached to an anchor rectangle in scene coordinates.
    void
    showAnchored(Widget anchor, float ax, float ay, float aw, float ah)
    Opens the menu attached to an anchor rectangle in anchor's scene coordinates, sized at the step resolved on anchor: it drops below the rect (a menu-bar dropdown) and flips above when there is no room.
    void
    showAt(Scene ownerScene, float x, float y)
    Opens the menu with its corner at the scene point (x, y), at the scene's step.
    void
    showAt(Widget anchor, float x, float y)
    Opens the menu with its corner at the scene point (x, y) (a context menu at the pointer), sized at the step resolved on anchor.

    Methods inherited from class java.lang.Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
  • Constructor Details

    • PopupMenu

      public PopupMenu(Menu menu)
      A popup presenting menu; show it with one of the show* methods.
  • Method Details

    • onClose

      public PopupMenu onClose(Runnable listener)
      Runs listener when the menu closes (dismissed or an item chosen).
    • onRootLeading

      public PopupMenu onRootLeading(Runnable listener)
      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.
    • onRootTrailing

      public PopupMenu onRootTrailing(Runnable listener)
      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; onRootLeading states the direction rule both share.
    • setModal

      public PopupMenu setModal(boolean value)
      Makes the native popup block the owner window (window-modal), enforced by the backend exactly like a Dialog: a press on the parent is ignored and beeps, and the menu closes only on a choice or Escape. Default false (a press outside dismisses). Purely native: no in-scene overlay.
    • setControlSize

      public PopupMenu setControlSize(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". 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 (see tokens). null restores inheritance. UI thread only.
    • isOpen

      public boolean isOpen()
      Whether the cascade is currently showing.
    • popupWindow

      public NativeWindow popupWindow()
      Returns:
      the popup's native window while open (screenshots/tests), else null.
    • showAt

      public void showAt(Widget anchor, float x, float y)
      Opens the menu with its corner at the scene point (x, y) (a context menu at the pointer), sized at the step resolved on anchor. 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 the Scene overload cannot express.

      The coordinates are the anchor's scene coordinates (what MouseEvent.x() reports), not anchor-local ones.

    • showAt

      public void showAt(Scene ownerScene, float x, float y)
      Opens the menu with its corner at the scene point (x, y), at the scene's step.
    • showAnchored

      public void showAnchored(Widget anchor, float ax, float ay, float aw, float ah)
      Opens the menu attached to an anchor rectangle in anchor's scene coordinates, sized at the step resolved on anchor: 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. UI thread only.
    • showAnchored

      public void showAnchored(Scene ownerScene, float ax, float ay, float aw, float ah)
      Opens the menu attached to an anchor rectangle in scene coordinates. A no-op on a headless scene (no display to place the window on). 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.

    • close

      public void close()
      Closes the menu (idempotent). UI thread only.
    • setDisplayMode

      public PopupMenu setDisplayMode(DisplayMode mode)
      Asks for a presentation; see DisplayMode. Default DisplayMode.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_WINDOW still draws in scene on Wayland and in macOS exclusive fullscreen, because on those a menu window does not land where the anchor is. IN_SCENE is always honoured; it needs nothing from the platform. Read displayMode() for what happened.

      Takes effect on the next open; a menu already on screen is not re-presented under it.

    • setDefaultDisplayMode

      public static void setDefaultDisplayMode(DisplayMode mode)
      The presentation every PopupMenu built 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) and Fonts. Menus already open are unaffected, and setDisplayMode(limn.components.DisplayMode) still overrides it for one menu. UI thread.

    • defaultDisplayMode

      public static DisplayMode defaultDisplayMode()
      Returns:
      the presentation new menus start from.
    • displayMode

      public DisplayMode displayMode()
      Returns:
      how the menu is presented: while it is open, what it actually chose, including a platform forcing IN_SCENE over a request for a window. While it is closed, what setDisplayMode(limn.components.DisplayMode) last asked for, since nothing has been decided yet.