API — ICoreEssentials/UI/MenuBar
The public contract of 5 header(s) under ICoreEssentials/UI/MenuBar — 3 class/struct definition(s), 65 declaration(s). Each section shows the header's banner and its public (and protected-virtual) surface exactly as the file writes it.
| Header | Defines | Declarations | Bases |
|---|---|---|---|
ICoreMenu.h | ICoreMenuItem, ICoreMenu | 51 | public ICoreWidget |
ICoreMenuBar.h | ICoreMenuBar | 14 | public ICoreWidget |
ICoreMenuMirror.h | — | 0 | — |
ICoreMenuNativeMirrorAccess.h | — | 0 | — |
ICoreMenuSystemBar.h | — | 0 | — |
ICoreMenu.h#
ICoreEssentials/UI/MenuBar/ICoreMenu.h
ICoreMenuItem#
ICoreMenu.h:116 · class · final · pImpl · 25 declaration(s)
One line of a menu.
class ICoreMenuItem final {
public:
enum class Kind { Action, Separator, Caption };
ICoreMenuItem(ICoreMenu* ownerMenu, Kind kind, ICoreString text);
~ICoreMenuItem();
ICoreMenuItem(const ICoreMenuItem&) = delete;
ICoreMenuItem& operator=(const ICoreMenuItem&) = delete;
[[nodiscard]] Kind getKind() const;
[[nodiscard]] bool isAction() const;
void setText(const ICoreString& text);
[[nodiscard]] ICoreString getText() const;
// Right column. Set for you when addItem() is given a key sequence.
void setShortcutText(const ICoreString& shortcutText);
[[nodiscard]] ICoreString getShortcutText() const;
// Own flag rather than a widget's enabled state: the row is painted and
// hit-tested by its panel, and a disabled row must still be hit-tested --
// otherwise the pointer resting on it would leave the PREVIOUS row lit.
void setItemEnabled(bool enabled);
[[nodiscard]] bool isItemEnabled() const;
void setCheckable(bool checkable);
[[nodiscard]] bool isCheckable() const;
void setChecked(bool checked);
[[nodiscard]] bool isChecked() const;
// A row that takes no height and no place under the pointer, while keeping
// its index -- which is what lets a caller walk rows without renumbering.
void setItemHidden(bool hidden);
[[nodiscard]] bool isItemHidden() const;
// Leading icon, named from the SVG registry (":/SVGs/TrashIcon.svg"). It is
// recoloured to the row's own text colour rather than kept as the file drew
// it, so it follows the label through hover, disabled and a theme switch --
// the icons in the registry are black line art, which a dark surface would
// otherwise swallow.
void setIconPath(const ICoreString& resourcePath);
[[nodiscard]] bool hasIcon() const;
[[nodiscard]] ICoreMenu* getSubMenu() const;
// The row's counterpart in the system menu bar, on the platforms that have
// one (see ICoreMenuNativeMirrorAccess.h). Null everywhere else, and null
// for a menu that is not part of the menu bar.
//
// ⚠ ZERO callers tree-wide (measured 2026-08-20 and again at the redesign;
// the only mentions are the declaration and its own definition). Narrowed
// rather than deleted, the way ICoreMenuBar::addTrailingWidget was:
// removing published API is the owner's call. Recommend deleting it -- a
// handle nobody unwraps is a seam with no consumer.
[[nodiscard]] ICoreNativeHandle nativeActionHandle() const;
// Whether the row leaves room for the leading column that holds the tick or
// the icon. The menu turns this on for every row once any one of its rows
// needs it, so the labels of a mixed menu still line up.
//
// ⚠ RECORDED ON THE ROW, DECIDED BY THE PANEL. ICoreMenuCore's
// icoreMenuReservesLeadColumn() is what actually answers it at paint time,
// over every row at once; this flag is what that function reads.
void setReservesLeadColumn(bool reserves);
[[nodiscard]] bool reservesLeadColumn() const;
// Width this row would like: label, the gap, and its right column. Measured
// here because measuring text is the one thing ICoreMenuCore cannot do --
// it takes the advance as an input and computes the rest.
[[nodiscard]] int preferredWidth() const;
private:
class Impl; // the two-line residue; state lives here
std::unique_ptr<Impl> impl;
};
ICoreMenu#
ICoreMenu.h:203 · class · final · bases public ICoreWidget · pImpl · 26 declaration(s)
The menu itself.
class ICoreMenu final : public ICoreWidget {
public:
explicit ICoreMenu(ICoreNativeWidget* parent = nullptr);
~ICoreMenu() override;
// ---------------- building
ICoreMenuItem* addItem(const ICoreString& text, std::function<void()> onActivated);
ICoreMenuItem* addItem(const ICoreString& text, const ICoreKeySequence& shortcut,
std::function<void()> onActivated);
ICoreMenuItem* addCheckableItem(const ICoreString& text, bool checked,
std::function<void(bool)> onToggled);
ICoreMenuItem* addCaption(const ICoreString& caption);
void addSeparator();
// A second key for a row that already has one (Ctrl+B beside Ctrl+D). It is
// registered but not drawn: the row keeps showing the key it was made with,
// so the shortcut column stays one line.
void addAlternateShortcut(ICoreMenuItem* item, const ICoreKeySequence& shortcut) const;
// A nested menu, owned by this one. Fill it exactly like its parent.
ICoreMenu* addSubMenu(const ICoreString& text);
// The row in the parent menu that opens this one -- for giving a submenu's
// row an icon, since addSubMenu hands back the menu rather than the row.
// Null on a menu that is not a submenu.
[[nodiscard]] ICoreMenuItem* getOwnerItem() const;
void clearItems();
// How many rows this menu has, and the row at an index -- so a caller that
// built a menu in a loop can walk it back without keeping its own vector.
[[nodiscard]] int itemCount() const;
[[nodiscard]] ICoreMenuItem* itemAt(int index) const;
// ---------------- the system menu bar
//
// On a desktop that keeps the app's menus outside its windows -- macOS, in
// the bar beside the Apple logo -- the menus still have to be declared once,
// here. Given a native menu to mirror into, every row added from now on gets
// a counterpart in it, and the two stay in step: what the row says,
// whether it is enabled and whether it is ticked are set on both, and either
// one being activated runs the same callback. The painted menu is simply
// never shown there.
//
// Set by ICoreMenuBar on the menus it owns, before anything is added to
// them. Submenus inherit a mirror of their own. A context menu has none.
//
// ⚠ Q2.3 MOVED THE ACCESSOR PAIR IMPL-SIDE (§5), and A3.3 made it toolkit-
// neutral. The mirror is the PLATFORM's own menu, not something this API
// builds, so there is no wrapper to name instead -- it crosses as an opaque
// ICoreNativeHandle, behind ICoreMenuNativeMirrorAccess.h.
// Where the real shortcuts of addItem() are installed. Application-context,
// so the keys work in every window of the app and not only the one the host
// belongs to -- an editor torn off into its own window answers them too.
// Submenus inherit it. Set this before adding items that carry a shortcut.
void setShortcutHost(ICoreNativeWidget* host);
// Asked just before an application-context shortcut runs its row; false stands
// the key down. Application context means every window hears the key, and not
// every window is somewhere the key means anything: a project browser or a
// script window is only borrowing the keyboard, and a bare Delete pressed in
// one of them must not reach the canvas sitting behind it. A menu bar that
// never sets one fires everywhere, which is what a single-window app wants.
// Submenus inherit it. Set this before adding items that carry a shortcut --
// each shortcut takes the hook that was in place when it was registered.
void setShortcutScopeHook(std::function<bool()> hook);
// Run just before the menu shows: where a menu refreshes what its rows say
// and which of them are enabled, so it never opens on stale state.
void setAboutToShowHook(std::function<void()> hook);
// Left / Right at the top level walk the menu bar. Installed by ICoreMenuBar
// on the menus it owns; a menu opened as a plain context menu has none and
// simply ignores those keys.
void setSiblingNavigationHook(std::function<void(int delta)> hook);
// ---------------- showing
void popupUnder(ICoreNativeWidget* anchor); // drop-down: under the widget that owns it
// ⚠⚠ `anchor` IS "WHICH WINDOW IS THIS OVER", AND A CONTEXT MENU CANNOT
// ANSWER IT ANY OTHER WAY (W10.18). Every context menu in this tree is
// built parentless on purpose -- so no ancestor's styling reaches it -- and
// a parentless popup has no element chain for a backend to walk up. Where a
// popup is promoted into a window's overlay rather than given a window of
// its own, the backend then guesses from a roster of live windows, and that
// guess has been measured wrong three times, differently each time. The
// last of them clamped a menu summoned at (1101, 488) to (392, 0), which is
// what *"context menus still don't pop up properly"* looks like.
//
// Pass the widget the menu was summoned from. It is NOT a parent: nothing
// is reparented and nothing acquires a second owner -- see
// `ICoreWidget::declareOverlayAnchor`, which is where it goes.
//
// ⚠ THE DEFAULT IS null AND IT IS THE OLD BEHAVIOUR EXACTLY, so a call
// site that has not been told about this is no worse off than before. It is
// a default rather than a required argument because the backends whose
// popups are real windows need nothing here and never will.
void popupAt(const ICorePoint& globalTopLeft,
ICoreNativeWidget* anchor = nullptr); // context menu: at the pointer
// Closes this menu and everything it opened. closeWholeChain() walks up to
// the root first, which is what activating an item does.
void closeChain();
void closeWholeChain();
[[nodiscard]] bool isMenuOpen() const;
// This menu closed -- for the menu bar, which un-presses its title.
ICoreSignal<> onMenuClosed;
protected:
bool keyPressed(const ICoreKeyEvent& event) override;
// Chain ROUTING rather than a plain press: it re-routes a press that landed
// on another menu in the chain, by global position. P5.8's decision was
// that this needs no bespoke hook after all — ICoreMouseEvent carries BOTH
// the widget-local `pos` and `globalPos`, which is the whole reason the
// routing was thought to need the Qt event.
bool mousePressed(const ICoreMouseEvent& event) override;
void pointerEntered() override;
void hidden() override;
// The chain's hover router. Only a ROOT menu arms this, and only while the
// chain is open — one watcher hit-testing every menu in it. Replaces an
// app-wide eventFilter; see prepareToShow()/hidden() for the arming.
void applicationPointerMoved(const ICorePoint& globalPos) override;
// The chain's DISMISSAL, armed and disarmed beside the hover router above.
//
// ⚠ THIS USED TO BE THE TOOLKIT'S JOB AND ON ONE BACKEND IT IS NOT DONE.
// A Qt::Popup grabs the mouse, so every press outside it is routed INTO the
// popup and arrives at mousePressed(). A backend whose popup is not a
// grabbing window has no such routing: on the WinUI seat a menu is an
// element promoted to the top of the window, so a press on a button, a list
// row or the page behind it simply never reached the menu and the menu
// stayed open. Asking for the outside-press watch says what was being
// relied on, on every backend, instead of leaning on one toolkit's grab.
//
// Harmless where the grab already exists: closeWholeChain() is idempotent
// -- closeChain() closes only what is still visible.
void pointerPressedOutside() override;
private:
class Impl; // the two-line residue; state lives here
std::unique_ptr<Impl> impl;
};
ICoreMenuBar.h#
ICoreEssentials/UI/MenuBar/ICoreMenuBar.h
ICoreMenuBar#
ICoreMenuBar.h:34 · class · final · bases public ICoreWidget · pImpl · 14 declaration(s)
The app's menu strip.
class ICoreMenuBar final : public ICoreWidget {
public:
explicit ICoreMenuBar(ICoreNativeWidget* parent = nullptr);
~ICoreMenuBar() override;
// Whether this desktop keeps an application's menus outside its windows.
// macOS does -- the bar at the top of the screen beside the Apple logo is
// where a Mac user looks for File and Edit, and putting a second strip
// inside the window would be the app disagreeing with the system about where
// its own menus live. Windows and Linux keep the strip in the window, which
// is where those desktops put it.
//
// Where it is true, the menus declared here are mirrored into a real
// QMenuBar (see ICoreMenu::setNativeMirror), this widget is never shown, and
// there is nothing for the editor's Show Menu Bar switch to switch.
static bool usesSystemMenuBar();
// "&File" gives a title reading "File" whose mnemonic is Alt+F. The menu is
// owned by the bar; fill it with ICoreMenu's own API.
ICoreMenu* addMenu(const ICoreString& titleWithMnemonic);
// Where the menus' action shortcuts are registered. Set this to the window
// before adding any menu, and Ctrl+S keeps working while the strip itself is
// hidden -- Qt will not fire a shortcut whose host widget is invisible, and
// saving a project is not something that should depend on a strip being on
// screen. The Alt mnemonics stay on the strip on purpose: with nothing to
// drop a menu from, they have nothing to do.
void setShortcutHost(ICoreNativeWidget* host);
// Narrows where those shortcuts are allowed to act. They are registered
// application-wide so that a window holding a torn-off editor answers them
// too, which means windows with nothing to do with editing hear them as well;
// this hook is asked first and false stands the key down. Set it beside the
// host, before adding any menu. See ICoreMenu::setShortcutScopeHook.
void setShortcutScopeHook(std::function<bool()> hook);
// Anything that should ride on the right end of the strip.
// ⚠ ZERO callers tree-wide as of P6.9 (the usage line above is the only
// mention). Narrowed rather than deleted: removing published API is the
// owner's call, the way P1.1/P1.2's deletions were. Recommend deleting it.
void addTrailingWidget(ICoreNativeWidget* widget);
void closeOpenMenu();
// For a window with menus of its own beside the application's main window
// (a tool window, the script IDE). Where the system carries the menus
// (usesSystemMenuBar()), this bar is the one at the top of the screen only
// while `window` -- any widget in that top-level window -- is the MAIN
// window, and the application's own bar is back the moment another window
// is: switching windows switches the menus, the way every Mac application
// behaves. Null unties it. Elsewhere a no-op: the strip lives inside its
// window and never needs switching.
//
// Without it, ANY second bar replaces the application's menus for good the
// moment it is built, because a desktop has one menu bar and the last bar
// made takes it.
void setSystemMenuBarWindow(ICoreNativeWidget* window);
// Show / hide the whole strip. Any open menu goes with it. Does nothing
// where the system carries the menus: that bar is not the app's to hide.
void setBarVisible(bool visible);
[[nodiscard]] bool isBarVisible() const;
// ICore signal, not a Qt one: this class is a converted ICoreWidget.
ICoreSignal<bool> onBarVisibilityChanged;
// Paints the strip's ground in place of the theme's flat menu background
// and its hairline, for a window whose chrome runs through the strip -- the
// editor, where the title bar above and the panels below read as one piece
// across it. Returning false paints the flat ground after all. The titles
// draw on top either way. Null puts the flat ground back.
void setBackgroundPainter(std::function<bool(ICorePainter&)> painter);
protected:
void paintContent(ICorePainter& painter) override;
// Watches the pointer while a menu is open, so sliding along the strip
// switches menus. The open popup holds the mouse, so the titles cannot see
// those moves themselves. Armed only while a menu is open — an
// application-wide watch left installed sees every move in the app.
void applicationPointerMoved(const ICorePoint& globalPos) override;
private:
class Impl; // the two-line residue; state lives here
std::unique_ptr<Impl> impl;
};
ICoreMenuMirror.h#
ICoreEssentials/UI/MenuBar/ICoreMenuMirror.h
IMPLEMENTATION SIDE ONLY. Include from a .cpp/.mm inside the sanctioned MenuBar zone or a backend zone -- never from a public wrapper header. Third of the MenuBar seams, beside ICoreMenuNativeMirrorAccess.h (which points an ICoreMenu at a platform menu) and ICoreMenuSystemBar.h (which makes one).
WHAT THIS SEAM IS FOR. ICoreMenu declares the application's menus once, as a painted tree, and on a desktop that carries menus outside its windows every row is ALSO given a counterpart in the platform's own menu. Keeping the two in step is the only part of ICoreMenu.cpp that is a toolkit's work; the rows, the highlight, the submenu timers, the keyboard model and every pixel of the painting are an ICoreWidget over ICorePainter and are the same source on every backend (A3.3).
Qt QMenu / QAction, with QMenu::addAction and QAction::triggered
Declares no class of its own — see the file.
ICoreMenuNativeMirrorAccess.h#
ICoreEssentials/UI/MenuBar/ICoreMenuNativeMirrorAccess.h
Declares no class of its own — see the file.
ICoreMenuSystemBar.h#
ICoreEssentials/UI/MenuBar/ICoreMenuSystemBar.h
IMPLEMENTATION SIDE ONLY. Include this from a .cpp/.mm inside the sanctioned MenuBar zone or a backend zone -- never from a public wrapper header. Peer of ICoreMenuNativeMirrorAccess.h, and it exists for the same reason: the object on the other side is the PLATFORM's own menu bar, so there is no wrapper to name and it crosses as an opaque handle.
WHAT THIS SEAM IS FOR. ICoreMenuBar is a painted strip and its body is toolkit-free -- it lays out titles, opens and walks menus and paints itself through ICorePainter, and A3.3 moved that body up to UI/MenuBar/ where every backend compiles it. Exactly THREE things in it were the toolkit's, and they are the three below: on a desktop that keeps the application's menus outside its windows, the bar has to ask the platform for a real menu bar, hang real top-level menus off it, and destroy it. Each backend answers in its own zone:
Declares no class of its own — see the file.