Search code examples
cocoansalert

Can NSAlert Be Used to Create a Floating Window?


I have a Cocoa application that displays an application-modal alert using the NSAlert class. I'd like the alert window to float above all other applications' windows. Can this be done with NSAlert, or do I need to implement my own window?

I don't know if any of this matters, but the application is an agent application (LSUIElement is true) implemented as an NSStatusItem. (For more info about the app, including source code, look <here>.)

Here is the code that displays the alert:

- (void)showTimerExpiredAlert {
    [NSApp activateIgnoringOtherApps:YES];

    NSAlert *alert = [[NSAlert alloc] init];
    [alert setAlertStyle:NSInformationalAlertStyle];
    [alert setMessageText:NSLocalizedString(@"Menubar Countdown Complete", @"Expiration message")];
    [alert setInformativeText:NSLocalizedString(@"The countdown timer has reached 00:00:00.",
                                                @"Expiration information")];
    [alert addButtonWithTitle:NSLocalizedString(@"OK", @"OK button title")];
    [alert addButtonWithTitle:NSLocalizedString(@"Restart Countdown...", @"Restart button title")];

    NSInteger clickedButton = [alert runModal];
    [alert release];

    if (clickedButton == NSAlertSecondButtonReturn) {
        // ...
    }
}

I've tried putting this before the runModal call:

[[alert window] setFloatingPanel:YES];

I've also tried this:

[[alert window] setLevel:NSFloatingWindowLevel];

But neither of those makes the window stay above others if I click another application's window. I suspect runModal just doesn't honor either of those settings.


Solution

  • I wrecked my brain about this exact thing a while ago.

    The only way that I could get this to work (sort of), was to subclass NSApplication, and override -sendEvent. In -sendEvent, you'd first call super's implementation, then do something like this:

    id *modalWindow = [self modalWindow];
    if (modalWindow && [modalWindow level] != MY_DESIRED_MODAL_WINDOW_LEVEL)
        [modalWindow setLevel: MY_DESIRED_MODAL_WINDOW_LEVEL];
    

    Apart from that even this didn't work quite spotlessly – when switching apps – you'd never want to do this anyway because it's a blatant, crude hack.

    So yes, sadly you're better off writing your own version of NSAlert.  If you really care about this possibility, I'd file a bug on it.  It is pretty weird that [[alert window] setLevel: someLevel] isn't honored by NSApplication and it's a waste to have to re-build NSAlert with all its neat little auto-layout features just to be able to do this.