tags:

views:

556

answers:

2

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.

+1  A: 

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.

Dirk Stoop
A: 

What I ended up doing was abandoning NSAlert and instead I load an alertish NSWindow from a NIB.

Here is the code that displays the window:

- (void)showAlert {
    NSWindow *w = [self window];
    [w makeFirstResponder:nil];
    [w setLevel:NSFloatingWindowLevel];
    [w center];
    [w makeKeyAndOrderFront:self];
}

This is intended to make it act like an alert, except that it also floats, and it is not modal, so menu items can be selected while it is up.

Is there anything else I should have done?

Kristopher Johnson