MenuBar Internals
BeepMenuBar internal architecture -- 13 partial files, SubmenuTriangleTracker drift heuristic, popup management, keyboard navigation, theme/font/icon helpers, and high contrast support
Table of Contents
Overview
Namespace: TheTechIdea.Beep.Winform.Controls.Menus
BeepMenuBar is a full-featured menu bar control decomposed across 13 partial files. It handles menu bar layout, popup submenus with drift-corrected hover persistence (via the SubmenuTriangleTracker), theming, accessibility, keyboard navigation with mnemonics, and high-contrast rendering. The SubmenuTriangleTracker is a classic Amazon-style drift heuristic based on Bret Victor's "Pull-Down Menus, Faster" (2005) research.
Partial File Layout (13 Files)
| File | Responsibility |
|---|---|
| .cs | Main control definition: constructor, internal fields (items list, popup tracking, timer state) |
| .Theming.cs | Theme token application, style resolution, color mapping from IBeepTheme to menu elements |
| .Properties.cs | Items collection, Orientation (Horizontal/Vertical), Style, ShowIcons, background appearance |
| .Layout.cs | Menu item position calculation, bar sizing, overflow handling, item width measurement |
| .Popup.cs | Submenu popup lifecycle: create, open with delay, close with delay, position anchoring, z-order |
| .Keyboard.cs | Arrow key navigation (up/down/left/right), Enter key activation, Escape dismiss, mnemonics (Alt+letter) |
| .Input.cs | Mouse hover/click handling, hit testing for item detection, hover-open timer management |
| .Drawing.cs | Paint overrides: background, item text/icon/shortcut rendering, selection highlight, separator lines |
| .Activation.cs | Menu activation/deactivation, focus management, dismissal on outside click |
| .HighContrast.cs | High contrast mode detection and palette override, system contrast colors for borders and focus |
| .Lifecycle.cs | Control lifecycle: initialization, Dispose, resource cleanup, event unsubscription |
| .Utility.cs | Shared utilities: color blending, rectangle calculations, text measurement helpers |
| .Accessibility.cs | UIAutomation provider pattern, screen reader support, role/name/value properties per item |
SubmenuTriangleTracker
Visibility: internal static in Menus/Helpers/SubmenuTriangleTracker.cs. A stateless geometry helper that solves the classic "submenu flicker" problem. When the cursor moves diagonally from a parent menu item toward an open submenu, this tracker determines whether the cursor is "heading toward" the submenu.
internal static class SubmenuTriangleTracker
{
public static bool IsHeadingTowardSubmenu(
Point lastPos, // previous cursor position (screen coords)
Point currentPos, // current cursor position (screen coords)
Point submenuTopLeft, // submenu window top-left corner
Point submenuBottomLeft // submenu window bottom-left corner
);
// Returns true when currentPos is inside the triangle formed by
// lastPos, submenuTopLeft, and submenuBottomLeft
// -> cursor is plausibly moving toward the submenu
// -> should NOT dismiss the submenu
}
This is based on the same research used in WPF's MenuItem.IsSubmenuOpen heuristic and Amazon's retail mega-menu analysis. The tracker allows users to move the cursor diagonally to a submenu without the timer closing it prematurely.
Menu Item Submenu
[File]----------+
[Edit] +--+ Open Recent
[View] | +--------------+
+---->| LastFile1.cs |
cursor moves | LastFile2.cs |
diagonally -> +--------------+
Tracker keeps
submenu openPopup Management
BeepMenuBar.Popup.cs handles the complete submenu popup lifecycle:
- Hover to open -- After a configurable hover delay, the submenu form is created, positioned anchored to the parent item, and shown
- Leave to close -- On mouse-leave, a brief timeout starts before closing. The SubmenuTriangleTracker extends this timeout when the cursor plausibly heads toward the submenu
- Z-order -- Submenus are created as owned Forms with
ShowWithoutActivationto avoid stealing focus - Position anchoring -- Submenu position is calculated relative to the parent item's rectangle, adjusting to stay within screen bounds
Keyboard Navigation
BeepMenuBar.Keyboard.cs routes all keyboard input:
- Arrow keys -- Left/Right navigate between top-level items. Up/Down navigate within submenu items. Right on a parent item opens the submenu. Left on a submenu item closes it and returns to parent.
- Enter -- Invokes the active menu item command or opens its submenu
- Escape -- Closes the current submenu level, or dismisses the entire menu bar
- Mnemonics -- Alt+letter activates the menu item with matching mnemonic character
- Tab -- Cycles focus away from the menu bar
Helper Classes
| Class | Namespace | Role |
|---|---|---|
| SubmenuTriangleTracker | Helpers | Drift heuristic for submenu persistence during diagonal cursor transit |
| MenuThemeHelpers | Helpers | Theme token resolution for menu items: resolves active/inactive/hover/disabled colors, font, icon colors from IBeepTheme |
| MenuIconHelpers | Helpers | Icon loading with caching, sizing (16, 20, 24px), SVG rendering via StyledImagePainter, fallback placeholder generation |
| MenuFontHelpers | Helpers | Font resolution from theme, measurement for layout (text width, height), mnemonic underline positioning |
High Contrast
BeepMenuBar.HighContrast.cs detects system high contrast mode via SystemInformation.HighContrast. When active:
- All theme colors are overridden with system-defined contrast colors
- Borders use
SystemColors.ControlDark(2px thickness) - Focus rectangles use
SystemColors.Highlight - Selection uses
SystemColors.Highlight/SystemColors.HighlightText - Icons are rendered in high-contrast-compatible mode (outline style)
Code Examples
var menu = new BeepMenuBar();
menu.Items.Add(new BeepMenuItem("File", null,
new List<BeepMenuItem>
{
new BeepMenuItem("New", Icons.NewFile, () => NewFile()),
new BeepMenuItem("Open", Icons.OpenFolder, () => OpenFile()),
new BeepMenuSeparator(),
new BeepMenuItem("Save", Icons.Save, () => SaveFile())
{ Shortcut = "Ctrl+S" },
new BeepMenuItem("Exit", null, () => Application.Exit())
}));
menu.Items.Add(new BeepMenuItem("Help", null,
new List<BeepMenuItem>
{
new BeepMenuItem("About", Icons.Info, () => ShowAbout())
}));