My assumption has always been that [NSApp runModalForWindow:]
runs a modal window in NSModalPanelRunLoopMode
.
However, while -[NSApplication _doModalLoop:peek:]
seems to use NSModalPanelRunLoopMode
when pulling out the next event to process via nextEventMatchingMask:untilDate:inMode:dequeue:
, the current runloop doesn't seem to be running in that mode, so during -[NSApplication(NSEventRouting) sendEvent:]
of the modal-specific event, NSRunLoop.currentRunLoop.currentMode
returns kCFRunLoopDefaultMode
.
From what I can tell, this means that any event processing code that e.g. uses [NSTimer addTimer:forMode:]
based on the current mode will register a timer that will not fire until the modal session ends.
Is this a bug? Or if not, is the correct way to run a modal session something like this?
[NSRunLoop.currentRunLoop performInModes:@[NSModalPanelRunLoopMode] block:^{
[NSApp runModalForWindow:window];
}];
[NSRunLoop.currentRunLoop limitDateForMode:NSModalPanelRunLoopMode];
Alternatively, if the mode of the runloop should stay the same, I've seen suggestions to run modal sessions like this:
NSModalSession session = [NSApp beginModalSessionForWindow:theWindow];
for (;;) {
if ([NSApp runModalSession:session] != NSModalResponseContinue)
break;
[NSRunLoop.currentRunLoop limitDateForMode:NSModalPanelRunLoopMode];
}
[NSApp endModalSession:session];
Which would work around the fact that the timer/callbacks were scheduled in the "wrong" mode. But running NSModalPanelRunLoopMode
during a modal session seems a bit scary. Won't that potentially break the modality?