Widgets widgets/menu
Menu
A navigable menu with nested submenus to arbitrary depth.
A navigable menu with nested submenus to arbitrary depth.
Package widgets/menu. API reference: pkg.go.dev/widgets/menu.
Menu is a tree of items with a cursor path, nested submenus and a keyboard contract.
It is Focusable: keys are consumed only while focused, and a press inside the widget both selects and takes focus, so a click is a complete interaction without the application writing a click handler.
Rendered output
cmd/capture through
widgettest.Capture: colour at 3 widths
(40, 80, 120 columns), and the exact plain-text cell grid at
the widest of them.
MinSize() returns 16 × 4 cells.
That is the whole widget including its own chrome, and it is the
widget's assertion about itself — below it, Draw clips rather
than blanks, and what to do about that is the application's decision.
40 columns × 14 rows
╭ File ────────────────────────────────╮ │[1 ]│ │›Open ^O ▸│ │ Reload data ^R │ │ Toggle wrap ^W │ │ Archive (unavailable) │ │ Quit ^Q │ │ │ │ │ │ │ │ │ │ │ │ │ ╰──────────────────────────────────────╯
80 columns × 14 rows
╭ File ────────────────────────────────────────────────────────────────────────╮ │[1 ]│ │›Open ^O ▸│ │ Reload data ^R │ │ Toggle wrap ^W │ │ Archive (unavailable) │ │ Quit ^Q │ │ │ │ │ │ │ │ │ │ │ │ │ ╰──────────────────────────────────────────────────────────────────────────────╯
120 columns × 14 rows
╭ File ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │[1 ]│ │›Open ^O ▸│ │ Reload data ^R │ │ Toggle wrap ^W │ │ Archive (unavailable) │ │ Quit ^Q │ │ │ │ │ │ │ │ │ │ │ │ │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
Plain text — the exact cell grid, trimmed per line. This is the honest form: Braille and block-element glyphs can take a different advance width in a web font than a terminal gives them, which breaks the alignment they depend on.
# Menu — A navigable menu with nested submenus to arbitrary depth. # package: widgets/menu # constructor: menu.New(r buffer.Rect, items ...menu.Item) *menu.Menu # 120 columns x 14 rows, rendered through widgettest.Capture ╭ File ────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │[1 ]│ │›Open ^O ▸│ │ Reload data ^R │ │ Toggle wrap ^W │ │ Archive (unavailable) │ │ Quit ^Q │ │ │ │ │ │ │ │ │ │ │ │ │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
What you are looking at. This is the cell grid the renderer produced, in a web page. It is not a screenshot of a terminal, and it cannot show interaction, resize or animation — a capture is one settled frame after N frames of settling. Braille and Block Elements can take a different advance width in your browser's font than in a terminal, so if a gauge or a sparkline above looks wrong, the plain-text form below it is the exact output and the colour one is the decoration. The colour frames are at 40, 80, 120 columns; the plain-text frame is at 120. More on this.
About this capture. The selected item and any open branch are marked with a character and a rule, not colour alone. Depth beyond one submenu is available but not shown here.
Package context
Package menu provides Menu, a keyboard-driven menu with nested submenus to arbitrary depth.
Constructing it
menu.New(r buffer.Rect, items ...menu.Item) *menu.Menu
NewMenu takes a rect and variadic Items. An Item with a non-empty Items field is a branch and gets the submenu marker; everything else is a leaf.
Items—[]Item— the top level. Set it again at any time withSetItems.Open— Opens the menu, if it is closed. A closed menu is not drawn at all.ItemStyle / SelectedStyle / DisabledStyle / HintStyle / CheckStyle / HeaderStyle—buffer.Styleper row role. An unsetSelectedStylemeansItemStylewithAttrReverse, so a selection is legible with no configuration.Checkable— AnItem.Checkablemakes the row carry a check gutter, so a setting can be both visible and togglable in one list rather than two.Ascii—bool— use ASCII markers instead of▸and✓for terminals or fonts that misrender them.
More on accessibility
The selected row carries a > in the marker gutter, which is a character rather than colour — so it survives NO_COLOR and appears in the plain-text capture. Level headers are bracketed when active and not when inactive, so depth is legible without colour. Disabled rows have no marker at all rather than a dimmed one, because a dimmed row is easy to miss entirely. The check glyph and the submenu arrow are drawn in CheckStyle, so on a selected row they are not left in ItemStyle against a reversed background — which would be the one unreadable thing on that row. Fixed in v0.5.1.
When not to use it
Do not use it for a flat list of items. A row of tabs or a list of results is not a menu, and a menu implies commands with sub-commands. Use Tabs or List.
Do not nest it inside a Dialog for a simple yes/no. A confirm is Dialog with two actions; building it from a menu loses the modal guarantee and the cancel-by-default behaviour.
Do not use it as a command palette. A palette is fuzzy-matched text over many commands, and this is an exact list over a known tree. The keymap package gives you the rows a palette renders — DescribeGrouped returns one entry per command — but there is still no palette UI in the framework.
Instead: A flat set of modes is Tabs; a modal question is Dialog; named commands with help text rendered from one place need the keymap package, which shipped in v0.6.0 — it has no palette of its own yet.
Related
- Dialog
- KeyHint
- TermMosaic limitations that apply to every widget
- Source:
widgets/menuon GitHub