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?