Class ContextMenus
column.add(ContextMenus.attach(fileList, () -> new Menu()
.addItem("Rename…", this::rename)
.addItem("Delete", this::delete)));
It wraps rather than sets, and that is not a style choice. A widget has no
context-menu property to assign because a Widget cannot name a Menu: the
two live on opposite sides of a dependency boundary, and the toolkit side is the one that
depends on nothing. So the menu is held here, one layer above the widget, which is the
nearest place the type exists. attach(limn.scene.Widget, java.util.function.Supplier<limn.components.Menu>) therefore returns the widget to add to the
tree, not the one passed in.
The gesture is more than the right button. A keyboard user asks for the menu with
the Menu key or Shift+F10 and never presses a mouse button at all; a hand-rolled
right-press check leaves them with no route to the same commands, which is the failure this
class exists to stop repeating. isRequest(MouseEvent) and
isRequest(KeyEvent) are the same question for code that already has its own
onMouseEvent and only wants the answer.
-
Method Summary
Modifier and TypeMethodDescriptionstatic WidgetWrapscontentso that a context request anywhere inside it opens the menusourcesupplies.static booleanWhether this event is the keyboard's request for a context menu: the dedicated Menu key that sits between the right Alt and Control on most keyboards, or Shift+F10 for the many that do not have one.static booleanisRequest(MouseEvent event) Whether this event is the pointer's request for a context menu, for a widget that has its ownonMouseEventand wants the answer rather than the wrapper.static voidOpensmenuwith its corner at a point inanchor's own coordinates (whatMouseEvent.x()reports) rather than the scene coordinates a popup is placed in.static voidshowForFocus(Widget anchor, Menu menu) Opensmenufor a request that carries no point: the keyboard route.
-
Method Details
-
attach
Wrapscontentso that a context request anywhere inside it opens the menusourcesupplies. The wrapper is invisible: it measures, lays out and paints ascontentalone would, and adds nothing to the picture.sourceis asked at the moment of the gesture, never at attach time, because the interesting menus depend on what is under the pointer or what is selected when it happens. Returningnull(or a menu with no rows) opens nothing, which is how a region says "not here" for a particular spot without the caller writing a second gesture check.A child that answers the gesture itself keeps it. Events bubble and stop at the first widget that consumes them, so a
TextFieldinside an attached region still raises its own Cut/Copy/Paste menu rather than the region's. That is the desired order and not a leak: the specific widget is the better answer.- Parameters:
content- the widget to give a menu to; it becomes the wrapper's only childsource- consulted per gesture, on the UI thread, and may answernull- Returns:
- the widget to put in the tree in
content's place
-
isRequest
Whether this event is the pointer's request for a context menu, for a widget that has its ownonMouseEventand wants the answer rather than the wrapper.True only for the press, never the release or the click: a menu that waited for the release would open under a button the user has already let go of, and every desktop raises this one on the way down.
-
isRequest
Whether this event is the keyboard's request for a context menu: the dedicated Menu key that sits between the right Alt and Control on most keyboards, or Shift+F10 for the many that do not have one.True only on the press, and false for a repeat: holding the key must not raise a stack of menus.
-
showAt
Opensmenuwith its corner at a point inanchor's own coordinates (whatMouseEvent.x()reports) rather than the scene coordinates a popup is placed in. The conversion is the whole reason this exists: it is two field reads, it is wrong in a way that only shows up on a scrolled or nested widget, and every place that hand-rolled a context menu wrote it out again.A
nullor empty menu opens nothing, so a caller that computes its rows can hand the result straight over.The point is not mirrored, and that is the whole of the direction story here. A menu raised at the pointer lands on the pointer reading either way; which corner of the column meets that point is
PopupMenu's decision and is already taken there. Reflecting the point as well would move the menu away from the spot the user aimed at. -
showForFocus
Opensmenufor a request that carries no point: the keyboard route. It drops from the lower leading corner of whatever currently holds focus — the bottom left reading left to right and the bottom right reading right to left — so the menu appears at the row or field the user was on rather than at a corner of the region containing it.Falls back to
anchor's own lower leading corner when nothing in the scene has focus, which is the only place left that is still related to the request.The direction is the focused widget's, not the region's — for the corner and for the cascade both. A right-to-left field inside a left-to-right form starts reading at its own right edge, and that is the corner the user's eye is at when the key arrives; taking the region's direction instead would drop the menu at the end of a field the user is not reading from. The cascade must agree: a menu whose corner is the field's right edge but whose column grows as the region reads opens away from the field it dropped from, so the popup is anchored on the same widget the corner came from, and its growth, its step and its corner are one answer.
-