NSStatusWindowLevel disabled when presenting window sheet - macos

I'm using an NSWindow and set it's level to NSStatusWindowLevel :
[aWindow setLevel:NSStatusWindowLevel];
Now, when I present a sheet (self is my sheet window)
modalAnswer = 0;
[NSApp beginSheet:self modalForWindow:aWindow modalDelegate:self didEndSelector:NULL contextInfo:nil];
[self makeKeyAndOrderFront:nil];
then the window doesn't show up above the dock, it hides underneath (meaning that NSStatusWindowLevel doesn't work anymore).
I've tried changing the level of the sheet window, but it won't work.
What's causing this issue?

I asked for technical support and they found it was a silly mistake : sheets normally are modal, but I was making it modal myself too.
The combination of beginModalSessionForWindow and runModalSession is the issue here.
It appears to be servicing the default run loop which is causing the menu bar and Dock to appear over your window.
I'm a bit confused as to why you are calling beginSheet:modalForWindow, and then using NSApp's beginModalSessionForWindow. Why do you manually add a modal session? Sheets are automatically modal to whatever window it is attached to. I am used to seeing the use of "beginModalSessionForWindow" without sheets on top of them.

Related

How can I display a modal sheet in NSWindow fullscreen mode without visually shifting the host window?

Objective-C desktop Cocoa ap. The application is a highly specific edge case (a standalone kiosk with a one-button interface). I mention this first in hopes of avoiding the HIG lecture. ;)
I am using a modal sheet view to display a PDF document over top of my usual content. The code is very simple:
[docViewerHostWindow beginSheet:docViewer completionHandler:nil];
DocViewerHostWindow is an NSWindow accessed as the parent of the NSView that calls the sheet. If it matters, "docViewer" is a subclass of NSWindow containing a PDFView.
When the application is running in a window, everything functions as it should. When the application is running fullscreen things work as advertised but there is a visual glitch: the entire content behind the model shifts to the right until the sheet is dismissed. It appears this is deliberate (and animated) but I can't figure out where it is coming from or how to disable it.
I enter fullscreen this way:
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:NO],NSFullScreenModeAllScreens,nil];
[self enterFullScreenMode:[NSScreen mainScreen] withOptions:options];
This basic NSLog illustrates the problem:
NSLog(#"Before: %f,%f",docViewerHostWindow.frame.origin.x,docViewerHostWindow.frame.origin.y);
[docViewerHostWindow beginSheet:docViewer completionHandler:nil];
NSLog(#"After: %f,%f",docViewerHostWindow.frame.origin.x,docViewerHostWindow.frame.origin.y);
**Log when running windowed (same origin before and after) **
2015-08-09 22:52:46.641 Before: 311.000000,491.000000
2015-08-09 22:52:47.050 After: 311.000000,491.000000
**Log when running fullscreen (origin shifts in fullscreen) **
2015-08-09 22:52:46.641 Before: 0.000000,0.000000
2015-08-09 22:52:47.050 After: 80.000000,-23.000000
I have tried to force the origin of the window both before and after opening the sheet, but this didn't help. I'm not even sure where to look - any clues on what is causing this and why?
Update: To be clear I am NOT talking about the default animation on the sheet itself (as described in this question) rather this is something that happens to the host window, but only in fullscreen.
The effect seen is the application moving out of the way of the (invisible) dock and the menubar (thank you #KenThomases whose comment helped figure that out). In addition to the accepted answer above, I found another solution:
Apple provides a number of kiosk mode tricks which are helpful here, in particular note their example regarding hiding the menubar and dock by adding code to awakeFromNib.
However, this is less than ideal in a non-kiosk ap, what we'd like is to have the kiosk options only in full screen mode. Fortunately this is possible by passing the options to enterFullScreenMode:withOptions:. The regular presentation mode options are restored when we exitFullScreenMode:withOptions
The documentation is a bit thin, but this did the trick:
NSApplicationPresentationOptions kioskOptions = NSApplicationPresentationHideDock + NSApplicationPresentationHideMenuBar;
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:NO],NSFullScreenModeAllScreens,
[NSNumber numberWithInteger:kioskOptions],NSFullScreenModeApplicationPresentationOptions, nil];
[self enterFullScreenMode:[NSScreen mainscreen] withOptions:options];
The issue is apparently related to the window avoiding the Dock.
I think it's also related to the fact that you're putting a view, not a window, in full-screen mode. When you do that, the view is sort of moved out of its normal window. But then, when you begin a sheet on the view's window, you're bringing that back into the picture, so to speak. (You might check if the view's window property is actually the same window it was before it entered full-screen mode. If it's not, you really have no business attaching a sheet to it.)
I would recommend that you use a borderless window, positioned to cover the screen, at a window level of NSStatusWindowLevel + 1. That's the technique recommended in Apple's OpenGL Programming Guide for Mac: Drawing to the Full Screen (which is, admittedly, a somewhat different context that you're working in). So, you would not use -[NSView enterFullScreenMode:withOptions:]. If you want to set the application's presentationOptions for kiosk-mode-type behavior, you can do that, too.

NSWindowController displays nib before windowDidLoad finishes

When I try launching my app through Spotlight without building it through XCode, the NSWindowController displays the unmodified nib (with the standard gray background color, etc) for a split second before windowDidLoad finishes. This looks awful, since views are in the wrong places and aren't colored correctly.
I tried removing everything from windowDidLoad to see if something in there was slowing it down, but that didn't improve things. I also tried moving the setting of the background color to initWithWindowNibName, but that didn't help either.
Is there a way to delay showing the window while it finishes loading?
Here's the code I'm using to initialize the NSWindowController:
self.windowController = [[WindowController alloc] initWithWindowNibName:#"WindowController"];
[self.windowController showWindow:self];
[[self.windowController window] makeKeyAndOrderFront:nil];
Disable NSWindowController's "visible at launch" property in Interface Builder.
(Of course, I post the question then immediately figure out the answer.)

Child Windows Not Being Added Properly

I'm trying to add a secondary window to bottom of my main window in an OS X application, with the hopes of eventually making it so it animates out from underneath the window when a toggle button is pressed. As far as I can tell, the best way to do this with the SDK is to add a window as a child window using - [NSWindow addChildWindow:ordered:]. However, when I call that function, although the secondary window is displayed, it isn't added as a child window. Here's the function called when I press the button:
- (IBAction)childToggleButtonPressed:(id)sender {
[self.window addChildWindow:_secondaryWindowController.window ordered:NSWindowBelow];
NSLog(#"Child Windows: %#", [[self.window childWindows] count]);
}
(I haven't added the code to dismiss it yet because I'm making sure it shows up in the first place first.)
And here's the output to the console:
2011-08-31 12:37:25.312 Playground[1712:707] Child Windows: (null)
Does anyone know why the window isn't being added as a child and what I can do to fix this?
Some additional context that might help is that I'm drawing a custom window using an image as the background for both the window itself and the title bar. The code I'm modifying can be found at http://cocoawithlove.com/2008/12/drawing-custom-window-on-mac-os-x.html.
Thank you!
EDIT: I tried overriding the - [NSWindow addChildWindow:ordered:] function and logging any information I could find, and it turns out the window itself isn't passed to the function. Hopefully this will help someone find out the problem!
I ended up solving this problem by making it so that the child windows did not have NSWindowControllers. Apparently you can't assign the window of an NSWindowController to be a child window. As soon as I turned them into NSWindow subclasses, I could add them as child windows using the code I showed above (replacing _secondaryWindowController.window with the window, of course).
In short, don't use an NSWindowController's window as a child window, use just an NSWindow.

Cocoa/Objective-C - Child window with text input without main window becoming inactive

I have a need to spawn a window that will hover just above my main window in a cocoa application. I want this main window to allow the user to enter some text in an input box. All is well until the text input box actually gains focus. The main window becomes "deactivated." This window is borderless and is a slightly custom shape -- its more like a hover card than anything else, I suppose.
Basically, I'd like this thing to work almost exactly like Spotlight (Apple + Space) -- you can enter text, but this is such an an ancillary operation that in the context of the greater UX, you don't want the jarring effect of the main window graying out (becoming inactive). You'll notice when you have some application open and in-focus, spotlight will not cause the window of that application to become inactive.
This problem arises because text input seems to REQUIRE that the child window become the key window (it will not let you place the cursor in the text input field). When it becomes key, the main window becomes inactive.
So far I've tried:
Subclassing NSWindow for my main application and overriding isKeyWindow such that it only loses key when the application is no longer the users focus (as opposed to the window). This had the unintended effect of colliding with key status of the child window and having very strange effects on the keyboard input (some keys are not captured, like delete)
Creating a view instead of a window. Doesn't work because of this problem -- you cannot draw over a Webkit WebView these days.
Anybody Cocoa/OSX wizards have any ideas? I've become a little obsessed with this one. An itch I can't scratch.
Edit: have also tried overriding the following on the child window. When clicked, the window makes the main application window become inactive.
- (BOOL)canBecomeKeyWindow {
return YES;
}
- (BOOL)canBecomeMainWindow {
return NO;
}
Edit 2:
After screwing about with NSMenu for a while, I scrapped that approach. It seems I found something, however. In NSPanel there is a window style mask called:
NSNonactivatingPanelMask
The panel can receive keyboard input without activating the owning application.
Valid only for an instance of NSPanel or its subclasses; not valid for a window.
Trying this out now...
Edit 3: NSNonactivatingPanelMask did not do the trick. No ideas.
What you want is a window that can become the key window but which cannot become the main window. You could implement such a class yourself, but this is basically what NSPanel is for, so you might try that first.
I think this can help you:
[self.childWindow makeKeyAndOrderFront:self];

Programmatically closing an NSWindow when it loses focus

I am making an image picker that will display an n by n grid of selectable button when the picker is popped up. This grid of buttons will be contained within an NSWindow but I would like for the window to be close automatically if the user clicks off the screen. Is there a flag that can be set so that when the window looses focus it will be closed automatically?
There are two notifications that you may be interested in: NSWindowDidResignKeyNotification and NSWindowDidResignMainNotification. You can simply register for the one you're interested in in awakeFromNib (or windowDidLoad if you have a custom controller) and then close or hide the window as appropriate when you receive the notifications.
I won't delve too much into whether or not this is a good idea from UI standpoint. But, it might be a better idea to have either an overlay view or a panel for the functionality you describe.
You might check out NSPanel. It's an NSWindow subclass that will hide itself when the app is in the background, and that behavior sounds very similar to what you are looking for.

Resources