MenuOverlayContent class Core
A self-drawn dropdown/context menu — the first IOverlayContent. Host-neutral: it draws via IRenderSurface and handles pointer + keyboard, so the operator menu, Σ menu and context menu render identically on every host. Data/actions come from the caller (e.g. IGridViewOps).
Public as pane SPI (level 3): panes outside this assembly (the pivot field list) open their submenus as THIS menu — a second menu drawing next to it would acquire exactly the differences nobody wants. Contract: entries + callbacks in, drawing and keyboard built in.
public sealed class MenuOverlayContent : IOverlayContent
- Namespace
- ArionUI.Presentation.Overlay
- Package
- ArionUI.Presentation ·
dotnet add package ArionUI.Presentation --prerelease - Implements
- IOverlayContent
Constructors
MenuOverlayContent
public MenuOverlayContent(IReadOnlyList<MenuItem> items, Func<string, TextStyle, double> measure);
Properties
AccessibleDescription
public string? AccessibleDescription { get; }What a screen reader says when this pane OPENS ("Menü, 5 Einträge"). Null = the generic pane announcement. The self-drawn panes are invisible to assistive technology as pixels — this sentence is their whole existence there.
AccessibleParts
public IReadOnlyList<AccessiblePart>? AccessibleParts { get; }The entries as structure for assistive technology: a reader can walk the menu and pick an entry, not only hear the highlight move. Separators carry no action, so they are no elements. Boxes are grid-absolute, like every pane's parts; an empty list before the first layout, never null — the menu IS structure, it just does not know where it sits yet.
MinWidth
public double MinWidth { get; init; }The narrowest the menu gets; a dropdown list passes its trigger's width, so the list lines up under the field it opened from.
WantsTextInput
public bool WantsTextInput { get; }A menu takes typing: letters jump to the matching entry, the way every native menu and combo does. This is also the DEFAULT dropdown style, so without it the most common picker in the grid ignored the keyboard entirely.
WantsTick
public bool WantsTick { get; }Frames while a type-ahead prefix is still collecting — that is what lets its pause expire without an ambient clock.
Methods
AccessibleToggleStateOf
public bool? AccessibleToggleStateOf(string id);The check mark of an entry; null for an entry that does not check.
AttachAnnouncer
public void AttachAnnouncer(Action<string> announce);The host hands the pane a voice: everything passed to announce is spoken through the grid's screen-reader channels (highlight moves, state changes). Default: mute, for panes that say nothing.
CursorAt
public GridCursor CursorAt(LogPoint local);A menu is nothing but actions — the whole surface is click-to-act.
IndexOfLabel
public int IndexOfLabel(string needle);Index of the first item whose label CONTAINS needle, or −1. By label rather than by position on purpose: the entries of a header menu depend on the column's state, so a positional target would silently start aiming at a different command the moment one entry appears or drops out.
InvokeAccessiblePart
public bool InvokeAccessiblePart(string id);Pick an entry the way a click does — the same Activate the pointer and Enter reach, so a veto or a close rule can never apply to only one of them.
Measure
public LogSize Measure(double maxWidth, double maxHeight);Desired size given the space available (for placement/clamping).
OnKey
public bool OnKey(ArionKey key, ArionModifiers mods);Key press. Esc/Enter/arrows etc. Return true if handled.
OnPointerMoved
public void OnPointerMoved(LogPoint local, bool buttonDown);
OnPointerPressed
public bool OnPointerPressed(LogPoint local, ArionModifiers mods, int clickCount = 1);Pointer press in LOCAL coords. clickCount is 2 for a double-click and 3 for a triple-click, so a text field in a pane selects a word or everything exactly like the grid's own fields do (the grid gets it from InteractionController.OnMultiClick). Return true if handled.
OnPointerReleased
public void OnPointerReleased(LogPoint local);
OnTextInput
public void OnTextInput(string text);
Placed
public void Placed(LogRect bounds);Where the overlay manager placed this pane, in grid-logical coordinates — called on the UI thread whenever the layout runs, and before the pane is announced as opened. A pane that reports parts needs it: its boxes are grid-absolute, and taking them from the last DRAW would both come too late for the first query and read a field the render thread writes.
Render
public void Render(IRenderSurface s, LogRect b, OverlayFrameState state);Paint into bounds (already placed + clamped). Panes without text input ignore state.
RowCenterY
public double RowCenterY(int index);Vertical CENTRE of an item, in the menu's own local coordinates — the point a scripted scenario aims at to activate it. Same walk as MenuOverlayContent.ItemAt (separators are shorter), so a target and the press that lands on it cannot drift apart. NaN when the index names nothing clickable.
Tick
public void Tick(double dtMs);Advance animations by dtMs milliseconds (called once per frame by the hosting surface while IOverlayContent.WantsTick).
Events
CloseRequested
public event Action? CloseRequested;Raised when the pane wants to close itself (Enter/apply/pick).
Invalidated
public event Action? Invalidated;Raised when the pane needs a repaint (hover, caret, content change).
