Skip to content

Commit b0fb9e0

Browse files
committed
Pin utility windows to the main emulator window
setLevel:NSFloatingWindowLevel kept utility windows above the main emulator window, but it also kept them above every other app's normal windows — Cmd-Tab away to a browser or editor and the inspector still hovered on top. NSFloatingWindowLevel is global; macOS does not expose a "per-app floating" level. Replace it with addChildWindow:ordered:NSWindowAbove against the main emulator window in eight controllers (Debugger, Memory Browser, POKE Finder, POKEs/Cheats, Preferences, Save Binary, Load Binary, Rollback). Inside FuseX a child window is guaranteed to stay above its parent; across apps both sink together when FuseX is no longer frontmost and rise together when it returns. The Joystick configuration sub-dialog already added itself as a child of Preferences and now becomes a grandchild of the main window — NSWindow supports nested relationships. The Debugger pops up unprompted on a breakpoint, possibly while the user has Cmd-Tabbed away or minimized the main window. Preserve that requirement in debugger_activate: by activating FuseX and deminiaturizing the main window before adding the child relationship — both calls are no-ops when FuseX is already frontmost and visible. Once the panel is up the user is in control: switching away sinks it with the rest of FuseX, returning rises it above main again.
1 parent 301b092 commit b0fb9e0

9 files changed

Lines changed: 121 additions & 23 deletions

fusepb/controllers/DebuggerController.m

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,10 @@ - (void)handleWillClose:(NSNotification *)note
153153
[memoryMapContents release];
154154
memoryMapContents = nil;
155155

156+
if( [[self window] parentWindow] ) {
157+
[[[self window] parentWindow] removeChildWindow:[self window]];
158+
}
159+
156160
/* Only resume execution when the user dismissed the window directly.
157161
If we arrived via deactivate_debugger(), debugger_active is already
158162
0 and the caller has set debugger_mode; calling debugger_run() now
@@ -207,11 +211,29 @@ - (void)debugger_activate:(id)sender
207211
{
208212
[[DisplayOpenGLView instance] pause];
209213

214+
/* Breakpoints fire unprompted. Bring FuseX (and the main window) back
215+
into view if the user has Cmd-Tabbed away, hidden the app, or
216+
minimized the main window — otherwise the Debugger window, as a
217+
child of main, would not be visible. After this point the panel
218+
behaves like any other child window: it sinks with FuseX when the
219+
user switches to another app, and rises with FuseX on return. */
220+
[NSApp activateIgnoringOtherApps:YES];
221+
NSWindow *mainWindow = [[DisplayOpenGLView instance] window];
222+
if( [mainWindow isMiniaturized] ) {
223+
[mainWindow deminiaturize:nil];
224+
}
225+
210226
[(NSPanel *)[singleton window] setBecomesKeyOnlyIfNeeded:NO];
211227
[singleton showWindow:nil];
212228
[[singleton window] makeFirstResponder:entry];
213229

214-
[[singleton window] setLevel:NSFloatingWindowLevel];
230+
if( mainWindow && [singleton window] != mainWindow &&
231+
[[singleton window] parentWindow] != mainWindow ) {
232+
if( [[singleton window] parentWindow] ) {
233+
[[[singleton window] parentWindow] removeChildWindow:[singleton window]];
234+
}
235+
[mainWindow addChildWindow:[singleton window] ordered:NSWindowAbove];
236+
}
215237

216238
[continueButton setEnabled:YES];
217239
[breakButton setEnabled:NO];
@@ -770,7 +792,9 @@ - (BOOL)tableView:(NSTableView *)aTableView shouldSelectRow:(int)rowIndex
770792
leave the window in place — closing and reopening it on every step
771793
produces a visible flash and risks losing window ordering. */
772794
if( interruptable ) {
773-
[[singleton window] setLevel:NSNormalWindowLevel];
795+
if( [[singleton window] parentWindow] ) {
796+
[[[singleton window] parentWindow] removeChildWindow:[singleton window]];
797+
}
774798
if( !debugger_closing && [[singleton window] isVisible] ) {
775799
[[singleton window] close];
776800
}

fusepb/controllers/JoystickConfigurationController.m

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,9 @@ - (void)showWindow:(id)sender
174174
[super showWindow:sender];
175175

176176
/* Pin to the parent (Preferences) so this sub-dialog cannot be hidden
177-
behind it when the parent is at NSFloatingWindowLevel. */
177+
behind it. Preferences is itself a child of the main emulator window,
178+
making this a grandchild — addChildWindow: is the only mechanism that
179+
enforces z-order for two windows at the same level. */
178180
NSWindow *currentParent = [[self window] parentWindow];
179181
if( parent != [self window] && currentParent != parent ) {
180182
if( currentParent ) {

fusepb/controllers/LoadBinaryController.m

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,11 @@ - (IBAction)apply:(id)sender
8989

9090
- (IBAction)cancel:(id)sender
9191
{
92-
[[self window] setLevel:NSNormalWindowLevel];
92+
if( [[self window] parentWindow] ) {
93+
[[[self window] parentWindow] removeChildWindow:[self window]];
94+
}
9395
[[self window] close];
94-
96+
9597
[[DisplayOpenGLView instance] unpause];
9698
}
9799

@@ -106,12 +108,20 @@ - (void)showWindow:(id)sender
106108

107109
[super showWindow:sender];
108110

109-
[[self window] setLevel:NSFloatingWindowLevel];
111+
/* Pin to the main emulator window so this dialog stays above it within
112+
FuseX, and follows it (z-order, minimize, hide) across other apps. */
113+
NSWindow *parent = [[DisplayOpenGLView instance] window];
114+
if( parent && [self window] != parent && [[self window] parentWindow] != parent ) {
115+
if( [[self window] parentWindow] ) {
116+
[[[self window] parentWindow] removeChildWindow:[self window]];
117+
}
118+
[parent addChildWindow:[self window] ordered:NSWindowAbove];
119+
}
110120

111121
[file setStringValue:@""];
112122
[start setStringValue:@""];
113123
[length setStringValue:@""];
114-
124+
115125
[apply setEnabled:NO];
116126
}
117127

fusepb/controllers/MemoryBrowserController.m

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,10 @@ - (void)showWindow:(id)sender
7878
tableContents = nil;
7979

8080
[memoryBrowser reloadData];
81-
82-
[[self window] setLevel:NSNormalWindowLevel];
81+
82+
if( [[self window] parentWindow] ) {
83+
[[[self window] parentWindow] removeChildWindow:[self window]];
84+
}
8385

8486
[[DisplayOpenGLView instance] unpause];
8587
}];
@@ -110,7 +112,15 @@ - (void)showWindow:(id)sender
110112

111113
[super showWindow:sender];
112114

113-
[[self window] setLevel:NSFloatingWindowLevel];
115+
/* Pin to the main emulator window so the browser stays above it within
116+
FuseX, and follows it (z-order, minimize, hide) across other apps. */
117+
NSWindow *parent = [[DisplayOpenGLView instance] window];
118+
if( parent && [self window] != parent && [[self window] parentWindow] != parent ) {
119+
if( [[self window] parentWindow] ) {
120+
[[[self window] parentWindow] removeChildWindow:[self window]];
121+
}
122+
[parent addChildWindow:[self window] ordered:NSWindowAbove];
123+
}
114124

115125
[memoryBrowser reloadData];
116126
}

fusepb/controllers/PokeFinderController.m

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,9 @@ - (void)handleWillClose:(NSNotification *)note
101101

102102
[matchList reloadData];
103103

104-
[[self window] setLevel:NSNormalWindowLevel];
104+
if( [[self window] parentWindow] ) {
105+
[[[self window] parentWindow] removeChildWindow:[self window]];
106+
}
105107

106108
[[DisplayOpenGLView instance] unpause];
107109
}
@@ -125,7 +127,15 @@ - (void)showWindow:(id)sender
125127

126128
[super showWindow:sender];
127129

128-
[[self window] setLevel:NSFloatingWindowLevel];
130+
/* Pin to the main emulator window so this dialog stays above it within
131+
FuseX, and follows it (z-order, minimize, hide) across other apps. */
132+
NSWindow *parent = [[DisplayOpenGLView instance] window];
133+
if( parent && [self window] != parent && [[self window] parentWindow] != parent ) {
134+
if( [[self window] parentWindow] ) {
135+
[[[self window] parentWindow] removeChildWindow:[self window]];
136+
}
137+
[parent addChildWindow:[self window] ordered:NSWindowAbove];
138+
}
129139

130140
[self update_pokefinder];
131141
}

fusepb/controllers/PokeMemoryController.m

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,10 @@ - (id)init
6464
[trainersController setContent:nil];
6565

6666
[trainers reloadData];
67-
68-
[[self window] setLevel:NSNormalWindowLevel];
67+
68+
if( [[self window] parentWindow] ) {
69+
[[[self window] parentWindow] removeChildWindow:[self window]];
70+
}
6971

7072
[[DisplayOpenGLView instance] unpause];
7173
}];
@@ -101,7 +103,15 @@ - (void)showWindow:(id)sender
101103

102104
[super showWindow:sender];
103105

104-
[[self window] setLevel:NSFloatingWindowLevel];
106+
/* Pin to the main emulator window so this dialog stays above it within
107+
FuseX, and follows it (z-order, minimize, hide) across other apps. */
108+
NSWindow *parent = [[DisplayOpenGLView instance] window];
109+
if( parent && [self window] != parent && [[self window] parentWindow] != parent ) {
110+
if( [[self window] parentWindow] ) {
111+
[[[self window] parentWindow] removeChildWindow:[self window]];
112+
}
113+
[parent addChildWindow:[self window] ordered:NSWindowAbove];
114+
}
105115

106116
[trainers reloadData];
107117
}

fusepb/controllers/PreferencesController.m

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,17 @@ - (void)showWindow:(id)sender
181181

182182
[super showWindow:sender];
183183

184-
[[self window] setLevel:NSFloatingWindowLevel];
184+
/* Pin to the main emulator window so Preferences stays above it within
185+
FuseX, and follows it (z-order, minimize, hide) across other apps.
186+
The Joystick sub-dialog adds itself as a child of Preferences when
187+
opened, producing a main → Preferences → Joystick chain. */
188+
NSWindow *parent = [[DisplayOpenGLView instance] window];
189+
if( parent && [self window] != parent && [[self window] parentWindow] != parent ) {
190+
if( [[self window] parentWindow] ) {
191+
[[[self window] parentWindow] removeChildWindow:[self window]];
192+
}
193+
[parent addChildWindow:[self window] ordered:NSWindowAbove];
194+
}
185195

186196
[self setMassStorageType];
187197
[self setExternalSoundType];
@@ -280,7 +290,9 @@ - (void)handleWillClose:(NSNotification *)note
280290
[machineRoms release];
281291
machineRoms = nil;
282292

283-
[[self window] setLevel:NSNormalWindowLevel];
293+
if( [[self window] parentWindow] ) {
294+
[[[self window] parentWindow] removeChildWindow:[self window]];
295+
}
284296

285297
[[DisplayOpenGLView instance] unpause];
286298
}

fusepb/controllers/RollbackController.m

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,9 @@ - (IBAction)apply:(id)sender
8888

8989
- (IBAction)cancel:(id)sender
9090
{
91-
[[self window] setLevel:NSNormalWindowLevel];
91+
if( [[self window] parentWindow] ) {
92+
[[[self window] parentWindow] removeChildWindow:[self window]];
93+
}
9294
[[self window] close];
9395

9496
[[DisplayOpenGLView instance] unpause];
@@ -105,7 +107,15 @@ - (void)showWindow:(id)sender
105107

106108
[super showWindow:sender];
107109

108-
[[self window] setLevel:NSFloatingWindowLevel];
110+
/* Pin to the main emulator window so this dialog stays above it within
111+
FuseX, and follows it (z-order, minimize, hide) across other apps. */
112+
NSWindow *parent = [[DisplayOpenGLView instance] window];
113+
if( parent && [self window] != parent && [[self window] parentWindow] != parent ) {
114+
if( [[self window] parentWindow] ) {
115+
[[[self window] parentWindow] removeChildWindow:[self window]];
116+
}
117+
[parent addChildWindow:[self window] ordered:NSWindowAbove];
118+
}
109119

110120
[self update];
111121

fusepb/controllers/SaveBinaryController.m

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,11 @@ - (IBAction)apply:(id)sender
8080

8181
- (IBAction)cancel:(id)sender
8282
{
83-
[[self window] setLevel:NSNormalWindowLevel];
83+
if( [[self window] parentWindow] ) {
84+
[[[self window] parentWindow] removeChildWindow:[self window]];
85+
}
8486
[[self window] close];
85-
87+
8688
[[DisplayOpenGLView instance] unpause];
8789
}
8890

@@ -97,12 +99,20 @@ - (void)showWindow:(id)sender
9799

98100
[super showWindow:sender];
99101

100-
[[self window] setLevel:NSFloatingWindowLevel];
102+
/* Pin to the main emulator window so this dialog stays above it within
103+
FuseX, and follows it (z-order, minimize, hide) across other apps. */
104+
NSWindow *parent = [[DisplayOpenGLView instance] window];
105+
if( parent && [self window] != parent && [[self window] parentWindow] != parent ) {
106+
if( [[self window] parentWindow] ) {
107+
[[[self window] parentWindow] removeChildWindow:[self window]];
108+
}
109+
[parent addChildWindow:[self window] ordered:NSWindowAbove];
110+
}
101111

102112
[file setStringValue:@""];
103113
[start setStringValue:@""];
104114
[length setStringValue:@""];
105-
115+
106116
[apply setEnabled:NO];
107117
}
108118

0 commit comments

Comments
 (0)