Class MenuTreeBuilder
SJMenuBar (peer = Vaadin MenuBar) and
SJPopupMenu (peer = Vaadin ContextMenu)
translate the emulator-pushed MenuNode tree into a Vaadin
MenuItem tree the same way: MenuBar, ContextMenu,
and SubMenu all implement HasMenuItems
(addItem(String|Component, listener) -> MenuItem, items carry
getSubMenu() -> SubMenu), so the walk is byte-identical above the
peer type.
The one asymmetry is the top-level separator: addSeparator()
lives on ContextMenuBase / SubMenuBase, not on
HasMenuItems. MenuBar has no top-level addSeparator
(SD_sjmenubar drops + WARNs); ContextMenu does (SD_sjpopupmenu renders it). So
rebuild(HasMenuItems, List, Consumer) takes a per-caller topSeparator callback — the
caller supplies "WARN + skip" or "ctx.addSeparator()". Sub-menu
separators are uniform (SubMenu.addSeparator() is always present)
and handled inline.
A push that changes only checked states, click handlers or accelerators
is applied to the existing items by patchInPlace(List, List, List) rather than
rebuilt. A checkable item's click pushes a new tree in the same round-trip,
and a rebuild would detach the item Vaadin is toggling: Vaadin's queued
checked-state update for it then throws in the browser
(vaadin/flow-components#10227; SD_menu_patch_in_place).
Stateless static utility — all per-component state (accelerator
registrations, current tree, re-entrancy guard) lives in the caller's
MenuBarStateStore.
-
Method Summary
Modifier and TypeMethodDescriptionstatic com.vaadin.flow.component.ComponentbuildIconAndText(com.vaadin.flow.component.Component icon, String text) Build aSpanwith the icon first and the text second so Vaadin'saddItem(Component, ...)renders both.static voidinstallAccelerators(com.vaadin.flow.component.UI ui, com.vaadin.flow.component.Component owner, Map<MenuNode, com.vaadin.flow.component.contextmenu.MenuItem> leafToPeer, Map<KeyStroke, com.vaadin.flow.shared.Registration> registrations) Install a UI-scoped Vaadin shortcut for every leaf node carrying an accelerator.static booleanpatchInPlace(List<MenuNode> current, List<MenuNode> next, List<com.vaadin.flow.component.contextmenu.MenuItem> peers) Appliesnextto the itemscurrentbuilt, without rebuilding, when the two trees differ only in checked states, click handlers or accelerators.static voidrebuild(com.vaadin.flow.component.contextmenu.HasMenuItems root, List<MenuNode> tree, Consumer<MenuNode> topSeparator) Walktreeintoroot.static voidrecoverLeafMap(List<MenuNode> nodes, List<com.vaadin.flow.component.contextmenu.MenuItem> peers, Map<MenuNode, com.vaadin.flow.component.contextmenu.MenuItem> map) Walk parallel JDKMenuNodeand VaadinMenuItemtrees to recover the leaf-to-peer map after attach (the original rebuild'sMenuItemreferences aren't addressable later).
-
Method Details
-
patchInPlace
public static boolean patchInPlace(List<MenuNode> current, List<MenuNode> next, List<com.vaadin.flow.component.contextmenu.MenuItem> peers) Appliesnextto the itemscurrentbuilt, without rebuilding, when the two trees differ only in checked states, click handlers or accelerators. Checkable items getsetChecked; every item is rebound to its new handler. The caller reinstalls accelerators.Text and enabled stay on the rebuild path: a
MenuBarre-renders its root buttons for theme, class and tooltip changes only (MenuBarRootItem), so an in-place write there would leave the button stale. Icons compare by identity, which holds only because the emulator reuses its converted icon while the SwingIconis unchanged.- Parameters:
peers- the root's items, as itsgetItems()returns them- Returns:
false, with nothing touched, when anything else differs — text, icon, enabled, checkable, or the shape — and the caller must rebuild
-
buildIconAndText
public static com.vaadin.flow.component.Component buildIconAndText(com.vaadin.flow.component.Component icon, String text) Build aSpanwith the icon first and the text second so Vaadin'saddItem(Component, ...)renders both. Mirrors SJLabel / SJButton's icon+text composition per SD_sjlabel. -
installAccelerators
public static void installAccelerators(com.vaadin.flow.component.UI ui, com.vaadin.flow.component.Component owner, Map<MenuNode, com.vaadin.flow.component.contextmenu.MenuItem> leafToPeer, Map<KeyStroke, com.vaadin.flow.shared.Registration> registrations) Install a UI-scoped Vaadin shortcut for every leaf node carrying an accelerator. Each shortcut fires the leaf'sonClickdirectly (same path as a click on the item). ResultingRegistrations are stored inregistrationskeyed by the JDKKeyStrokeso the next rebuild can tear them down. Unmappable keystrokes log viaSHelper.onUnimplemented(Object, String, Object...)and skip browser install. -
recoverLeafMap
public static void recoverLeafMap(List<MenuNode> nodes, List<com.vaadin.flow.component.contextmenu.MenuItem> peers, Map<MenuNode, com.vaadin.flow.component.contextmenu.MenuItem> map) Walk parallel JDKMenuNodeand VaadinMenuItemtrees to recover the leaf-to-peer map after attach (the original rebuild'sMenuItemreferences aren't addressable later). Both trees are isomorphic since the same rebuild built them. Separator nodes never produce aMenuItem(top-levelMenuBardrops them;ContextMenu/SubMenuinsert an<hr>that doesn't appear ingetItems()), so skip them without stepping the peer index in both cases.