yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
native_menu_bridge.h
Go to the documentation of this file.
1#ifndef YAZE_APP_PLATFORM_NATIVE_MENU_BRIDGE_H_
2#define YAZE_APP_PLATFORM_NATIVE_MENU_BRIDGE_H_
3
4// Platform-neutral model behind the macOS native main menu.
5//
6// The Cocoa side (app_delegate.mm) only builds NSMenu objects and forwards
7// clicks, key equivalents, and validation to this layer. Keeping the rules in
8// plain C++ lets unit tests prove the chord-ownership contract without AppKit.
9//
10// Chord ownership contract (one owner per chord, never both):
11// * kImGui: the item mirrors a ShortcutManager binding. The native menu
12// shows the chord for discoverability but must NOT perform it as
13// a key equivalent; SDL delivers the key to ImGui and
14// ExecuteShortcuts() runs the action once. A click on the item
15// runs the same ShortcutManager callback.
16// * kNative: nothing in ShortcutManager binds the chord exactly. NSMenu
17// performs it, and the SDL event filter drops the key so the
18// chord never also reaches ImGui (ShortcutManager matches
19// modifiers loosely, e.g. Ctrl+Cmd+F would otherwise fire Find).
20// * kNone: the item has no usable chord (its shortcut is unbound or its
21// key unrepresentable, or an exact ShortcutManager conflict with
22// a different action).
23
24#include <cstdint>
25#include <functional>
26#include <string>
27#include <vector>
28
29#ifndef IMGUI_DEFINE_MATH_OPERATORS
30#define IMGUI_DEFINE_MATH_OPERATORS
31#endif
32#include "imgui/imgui.h"
33
34namespace yaze {
35namespace editor {
36class ShortcutManager;
37} // namespace editor
38
39namespace platform {
40namespace native_menu {
41
42enum class MenuAction : int {
43 // App menu
44 kAbout = 0,
46 kHide,
49 kQuit,
50 // File
53 kSave,
54 kSaveAs,
56 // Edit
57 kUndo,
58 kRedo,
59 kCut,
60 kCopy,
61 kPaste,
63 // View
66 // Window
68 kZoom,
70 // Help
72 kCount,
73};
74
75// A macOS key chord. `key` is the unshifted ASCII character ('s', ',', '/'),
76// or 0 when there is no chord.
77struct KeyChord {
78 char key = 0;
79 bool command = false;
80 bool shift = false;
81 bool option = false;
82 bool control = false;
83
84 bool valid() const { return key != 0; }
85 bool operator==(const KeyChord& other) const {
86 return key == other.key && command == other.command &&
87 shift == other.shift && option == other.option &&
88 control == other.control;
89 }
90 bool operator!=(const KeyChord& other) const { return !(*this == other); }
91};
92
93enum class ChordOwner { kNone, kImGui, kNative };
94
95// How a menu action performs its work.
96enum class Dispatch {
97 kShortcut, // ShortcutManager callback named by `shortcut_name`.
98 kHost, // NativeMenuHost::RunHostAction (EditorManager call).
99 kTextOrShortcut, // Text-field chord when ImGui wants text, else shortcut.
100 kCocoa, // Standard AppKit selector (hide:, performMiniaturize:).
101};
102
105 const char* title;
106 // ShortcutManager entry that owns this action's chord and behavior. When the
107 // entry is missing (e.g. agent UI compiled out), the item falls back to
108 // `fallback`.
109 const char* shortcut_name;
113 // ImGui itself handles `default_chord` (text-field Cmd+X/C/V/A), so the
114 // chord stays with ImGui even without a ShortcutManager entry.
116};
117
118const std::vector<ActionSpec>& ActionSpecs();
119const ActionSpec* FindSpec(MenuAction action);
120
121// Convert a ShortcutManager key list to a macOS chord. ShortcutManager's
122// ImGuiMod_Ctrl means Command on macOS (ImGui swaps Cmd/Ctrl). Returns an
123// invalid chord for keys a menu key equivalent cannot express (F1, arrows).
124KeyChord ChordFromImGuiKeys(const std::vector<ImGuiKey>& keys);
125
126// Map the ASCII key of a chord to an ImGui key (for synthesizing text-field
127// chords). Returns ImGuiKey_None if unsupported.
128ImGuiKey ImGuiKeyFromChordKey(char key);
129
133 // Effective dispatch after considering whether `shortcut_name` exists.
135};
136
137// Resolve one item against the live ShortcutManager (may be null).
139 MenuAction action);
140
141// True when the NSMenu should perform `chord` as a key equivalent. Everything
142// else must fall through to SDL/ImGui.
143bool IsNativeOwnedChord(const editor::ShortcutManager* shortcuts,
144 const KeyChord& chord);
145
146// Gate for the root NSMenu's performKeyEquivalent:. False for every chord
147// ImGui owns (a ShortcutManager binding or an ImGui-owned item); true for
148// native-owned chords and for chords of items AppKit inserts itself
149// (Emoji & Symbols, Dictation), which ImGui never sees as shortcuts.
151 const KeyChord& chord);
152
153// Editor state and side effects the menu needs. Implemented over
154// EditorManager in app_delegate.mm and by a fake in unit tests.
156 public:
157 virtual ~NativeMenuHost() = default;
159 virtual bool HasLoadedRom() const = 0;
160 virtual bool HasCurrentEditor() const = 0;
161 virtual bool CanUndo() const = 0;
162 virtual bool CanRedo() const = 0;
163 virtual bool WantsTextInput() const = 0;
164 // Run `action` at the start of the next ImGui frame (EditorManager's
165 // deferred-action queue), where ShortcutManager callbacks normally run.
166 virtual void Defer(std::function<void()> action) = 0;
167 // Queue a Command+key press into ImGui's input queue (text fields).
168 virtual void InjectCommandChord(ImGuiKey key) = 0;
169 virtual void RunHostAction(MenuAction action) = 0;
170 virtual std::vector<std::string> RecentFiles() const = 0;
171 virtual void OpenRecentFile(const std::string& path) = 0;
172 virtual void ClearRecentFiles() = 0;
173};
174
175bool IsActionEnabled(NativeMenuHost& host, MenuAction action);
176
177// Perform a menu click. Returns false when the action is a Cocoa selector the
178// caller must route through the responder chain itself.
179bool PerformAction(NativeMenuHost& host, MenuAction action);
180
181// Short label for a recent-file entry (file name, falls back to full path).
182std::string RecentFileLabel(const std::string& path);
183
184} // namespace native_menu
185} // namespace platform
186} // namespace yaze
187
188#endif // YAZE_APP_PLATFORM_NATIVE_MENU_BRIDGE_H_
virtual std::vector< std::string > RecentFiles() const =0
virtual editor::ShortcutManager * shortcut_manager()=0
virtual void InjectCommandChord(ImGuiKey key)=0
virtual void RunHostAction(MenuAction action)=0
virtual void Defer(std::function< void()> action)=0
virtual void OpenRecentFile(const std::string &path)=0
const ActionSpec * FindSpec(MenuAction action)
bool IsActionEnabled(NativeMenuHost &host, MenuAction action)
bool IsNativeOwnedChord(const editor::ShortcutManager *shortcuts, const KeyChord &chord)
ResolvedItem ResolveItem(const editor::ShortcutManager *shortcuts, MenuAction action)
const std::vector< ActionSpec > & ActionSpecs()
bool ShouldMenuPerformKeyEquivalent(const editor::ShortcutManager *shortcuts, const KeyChord &chord)
bool PerformAction(NativeMenuHost &host, MenuAction action)
KeyChord ChordFromImGuiKeys(const std::vector< ImGuiKey > &keys)
std::string RecentFileLabel(const std::string &path)
ImGuiKey ImGuiKeyFromChordKey(char key)
bool operator==(const KeyChord &other) const
bool operator!=(const KeyChord &other) const