yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
app_delegate.mm
Go to the documentation of this file.
1// AppDelegate.mm
2
3// Must define before any ImGui includes (needed by imgui_test_engine via editor_manager.h)
4#ifndef IMGUI_DEFINE_MATH_OPERATORS
5#define IMGUI_DEFINE_MATH_OPERATORS
6#endif
7
9#import "app/controller.h"
10#import "app/application.h"
12#import "util/file_util.h"
13#import "app/editor/editor.h"
14#import "rom/rom.h"
15#include <vector>
16
17#if defined(__APPLE__) && defined(__MACH__)
18/* Apple OSX and iOS (Darwin). */
19#include <TargetConditionals.h>
20
21#import <CoreText/CoreText.h>
22
23#if TARGET_IPHONE_SIMULATOR == 1 || TARGET_OS_IPHONE == 1
24/* iOS in Xcode simulator */
25
26#elif TARGET_OS_MAC == 1
27/* macOS */
28#import <Cocoa/Cocoa.h>
29
30@interface AppDelegate : NSObject <NSApplicationDelegate>
31- (void)setupMenus;
32@end
33
34@implementation AppDelegate
35
36- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
37 [self setupMenus];
38
39 // Disable automatic UI state persistence to prevent crashes
40 // macOS NSPersistentUIManager can crash if state gets corrupted
41 [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"NSQuitAlwaysKeepsWindows"];
42}
43
44- (NSApplicationTerminateReply)applicationShouldTerminate:
45 (NSApplication *)sender {
46 (void)sender;
47 auto& app = yaze::Application::Instance();
48 if (!app.IsReady() || app.GetController() == nullptr ||
49 app.GetController()->editor_manager() == nullptr) {
50 return NSTerminateNow;
51 }
52
53 // Cocoa's default terminate path calls exit() immediately. Because yaze
54 // drives its own SDL/ImGui loop instead of NSApplication::run, that bypasses
55 // Application::Shutdown and leaves the ImGui test engine bound to a live
56 // ImGui context. Route native Quit through EditorManager so unsaved-work
57 // guarding runs and the main loop can unwind resources in ownership order.
58 app.GetController()->editor_manager()->Quit();
59 return NSTerminateCancel;
60}
61
62- (void)setupMenus {
63 NSMenu *mainMenu = [NSApp mainMenu];
64
65 NSMenuItem *fileMenuItem = [mainMenu itemWithTitle:@"File"];
66 if (!fileMenuItem) {
67 NSMenu *fileMenu = [[NSMenu alloc] initWithTitle:@"File"];
68 fileMenuItem = [[NSMenuItem alloc] initWithTitle:@"File" action:nil keyEquivalent:@""];
69 [fileMenuItem setSubmenu:fileMenu];
70
71 NSMenuItem *openItem = [[NSMenuItem alloc] initWithTitle:@"Open"
72 action:@selector(openFileAction:)
73 keyEquivalent:@"o"];
74 [fileMenu addItem:openItem];
75
76 // Open Recent (System handled usually, but we can add our own if needed)
77 NSMenu *openRecentMenu = [[NSMenu alloc] initWithTitle:@"Open Recent"];
78 NSMenuItem *openRecentMenuItem = [[NSMenuItem alloc] initWithTitle:@"Open Recent"
79 action:nil
80 keyEquivalent:@""];
81 [openRecentMenuItem setSubmenu:openRecentMenu];
82 [fileMenu addItem:openRecentMenuItem];
83
84 [fileMenu addItem:[NSMenuItem separatorItem]];
85
86 // Save
87 NSMenuItem *saveItem = [[NSMenuItem alloc] initWithTitle:@"Save"
88 action:@selector(saveAction:)
89 keyEquivalent:@"s"];
90 [fileMenu addItem:saveItem];
91
92 // Save As
93 NSMenuItem *saveAsItem = [[NSMenuItem alloc] initWithTitle:@"Save As..."
94 action:@selector(saveAsAction:)
95 keyEquivalent:@"S"];
96 [fileMenu addItem:saveAsItem];
97
98 [fileMenu addItem:[NSMenuItem separatorItem]];
99
100 [mainMenu insertItem:fileMenuItem atIndex:1];
101 }
102
103 // Edit Menu
104 NSMenuItem *editMenuItem = [mainMenu itemWithTitle:@"Edit"];
105 if (!editMenuItem) {
106 NSMenu *editMenu = [[NSMenu alloc] initWithTitle:@"Edit"];
107 editMenuItem = [[NSMenuItem alloc] initWithTitle:@"Edit" action:nil keyEquivalent:@""];
108 [editMenuItem setSubmenu:editMenu];
109
110 NSMenuItem *undoItem = [[NSMenuItem alloc] initWithTitle:@"Undo"
111 action:@selector(undoAction:)
112 keyEquivalent:@"z"];
113 [editMenu addItem:undoItem];
114
115 NSMenuItem *redoItem = [[NSMenuItem alloc] initWithTitle:@"Redo"
116 action:@selector(redoAction:)
117 keyEquivalent:@"Z"];
118 [editMenu addItem:redoItem];
119
120 [editMenu addItem:[NSMenuItem separatorItem]];
121
122 // System-handled copy/paste usually works if we don't override,
123 // but we might want to wire them to our internal clipboard if needed.
124 // For now, let SDL handle keyboard events for copy/paste in ImGui.
125
126 [mainMenu insertItem:editMenuItem atIndex:2];
127 }
128
129 // View Menu
130 NSMenuItem *viewMenuItem = [mainMenu itemWithTitle:@"View"];
131 if (!viewMenuItem) {
132 NSMenu *viewMenu = [[NSMenu alloc] initWithTitle:@"View"];
133 viewMenuItem = [[NSMenuItem alloc] initWithTitle:@"View" action:nil keyEquivalent:@""];
134 [viewMenuItem setSubmenu:viewMenu];
135
136 NSMenuItem *toggleFullscreenItem = [[NSMenuItem alloc] initWithTitle:@"Toggle Fullscreen"
137 action:@selector(toggleFullscreenAction:)
138 keyEquivalent:@"f"];
139 [viewMenu addItem:toggleFullscreenItem];
140
141 [mainMenu insertItem:viewMenuItem atIndex:3];
142 }
143}
144
145// ============================================================================
146// Menu Actions
147// ============================================================================
148
149- (void)openFileAction:(id)sender {
150 // Use our internal file dialog via Application -> Controller -> EditorManager
151 // Or trigger the native dialog here and pass the path back.
152 // Since we have ImGui dialogs, we might prefer those, but native is nice on macOS.
153 // For now, let's just trigger the LoadRom logic which opens the dialog.
154 auto& app = yaze::Application::Instance();
155 if (app.IsReady() && app.GetController()) {
156 if (auto* manager = app.GetController()->editor_manager()) {
157 (void)manager->LoadRom();
158 }
159 }
160}
161
162- (void)saveAction:(id)sender {
163 auto& app = yaze::Application::Instance();
164 if (app.IsReady() && app.GetController()) {
165 if (auto* manager = app.GetController()->editor_manager()) {
166 (void)manager->SaveRom();
167 }
168 }
169}
170
171- (void)saveAsAction:(id)sender {
172 auto& app = yaze::Application::Instance();
173 if (app.IsReady() && app.GetController()) {
174 if (auto* manager = app.GetController()->editor_manager()) {
175 if (auto* popup_manager = manager->popup_manager()) {
176 popup_manager->Show(yaze::editor::PopupID::kSaveAs);
177 }
178 }
179 }
180}
181
182- (void)undoAction:(id)sender {
183 // Route to active editor
184 auto& app = yaze::Application::Instance();
185 if (app.IsReady() && app.GetController()) {
186 if (auto* manager = app.GetController()->editor_manager()) {
187 // manager->card_registry().TriggerUndo(); // If we exposed TriggerUndo
188 // Or directly:
189 if (auto* current = manager->GetCurrentEditor()) {
190 (void)current->Undo();
191 }
192 }
193 }
194}
195
196- (void)redoAction:(id)sender {
197 auto& app = yaze::Application::Instance();
198 if (app.IsReady() && app.GetController()) {
199 if (auto* manager = app.GetController()->editor_manager()) {
200 if (auto* current = manager->GetCurrentEditor()) {
201 (void)current->Redo();
202 }
203 }
204 }
205}
206
207- (void)toggleFullscreenAction:(id)sender {
208 // Toggle fullscreen on the window
209 // SDL usually handles this, but we can trigger it via SDL_SetWindowFullscreen
210 // Accessing window via Application -> Controller -> Window
211 // Use SDL backend logic
212 // For now, rely on the View menu item shortcut that ImGui might catch,
213 // or implement proper toggling in Controller.
214}
215
216@end
217
218extern "C" void yaze_initialize_cocoa() {
219 @autoreleasepool {
220 AppDelegate *delegate = [[AppDelegate alloc] init];
221 [NSApplication sharedApplication];
222 [NSApp setDelegate:delegate];
223 [NSApp finishLaunching];
224 }
225}
226
227extern "C" int yaze_run_cocoa_app_delegate(const yaze::AppConfig& config) {
228 yaze_initialize_cocoa();
229
230 // Initialize the Application singleton with the provided config
231 // This will create the Controller and the SDL Window
233
234 // Main loop
235 // We continue to run our own loop rather than [NSApp run]
236 // because we're driving SDL/ImGui manually.
237 // SDL's event polling works fine with Cocoa in this setup.
238
239 auto& app = yaze::Application::Instance();
240
241 while (app.IsReady() && app.GetController()->IsActive()) {
242 @autoreleasepool {
243 app.Tick();
244 }
245 }
246
247 app.Shutdown();
248 return EXIT_SUCCESS;
249}
250
251#endif
252
253#endif
static Application & Instance()
void Initialize(const AppConfig &config)
constexpr const char * kSaveAs
Configuration options for the application startup.
Definition application.h:26