Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Toggling NSWindow level for "always on top" behaviour

I have a simple single-window application with a menu item that allows users to have the NSWindow appear always on top.

My function looks like this:

@IBAction func changeAlwaysOnTop(sender: AnyObject) {
    if (alwaysOnTopMenuItem.state == NSOnState) {
        alwaysOnTopMenuItem.state = NSOffState;
        window.level = kCGNormalWindowLevelKey;
    } else {
        alwaysOnTopMenuItem.state = NSOnState;
        window.level = kCGStatusWindowLevelKey;
    }
}

Turning "Always on Top" on works well - the window floats above all other applications as it should. However, when the option is turned off, the window continues to float above all other windows, as if window.level = kCGNormalWindowLevelKey; isn't actually doing anything.

I've tried different window levels, and I've tried ordering the window out and back in again. The window continues to float above all others.

How can I set the window back to normal after setting the level to kCGNormalWindowLevelKey?

Edit: the following Objective C code works just fine:

- (IBAction)changeOnTop:(id)sender {
    if (self.onTopMenuItem.state == NSOnState) {
        self.onTopMenuItem.state = NSOffState;
        self.window.level = NSNormalWindowLevel;
    } else {
        self.onTopMenuItem.state = NSOnState;
        self.window.level = NSStatusWindowLevel;
    }
}
like image 439
colincameron Avatar asked Dec 01 '22 14:12

colincameron


2 Answers

You are using the wrong values for the level. You are using the keys by which window levels are looked up.

First, since you're using Cocoa, you should use the Cocoa constants for the window level: NSNormalWindowLevel and NSStatusWindowLevel.

If you look at the definitions of those constants, you'll find:

#define NSNormalWindowLevel kCGNormalWindowLevel
#define NSStatusWindowLevel kCGStatusWindowLevel

Notice the lack of the word "Key" on the end of those kCG... constants. If you then look up how those constants are defined, you'll find:

#define kCGNormalWindowLevel                        \
  CGWindowLevelForKey(kCGNormalWindowLevelKey)
#define kCGStatusWindowLevel                        \
  CGWindowLevelForKey(kCGStatusWindowLevelKey)

So, the values you used are keys that are passed to CGWindowLevelForKey() to get the actual level.

like image 91
Ken Thomases Avatar answered Dec 06 '22 08:12

Ken Thomases


This works for me, and looks a bit nicer! (Swift 4)

    window.level = .floatingWindowLevel
like image 33
Jamie West Avatar answered Dec 06 '22 08:12

Jamie West