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