Exit fullscreen breaks in Mountain Lion after changing window's delegate - objective-c

I created my project from the Xcode template for non-document based Cocoa Application.
I have a custom NSWindowController that, after being instantiated on startup, takes possession of the default window (defined in MainMenu.xib).
If I leave the app delegate's window as it is, I can toggle fullscreen mode with command-F (set to -toggleFullscreen: in a menu item), or I can exit from fullscreen by pressing ESC.
Once I set my window controller as the window's delegate (I need this to do some OpenGL adjustments on enter/exit fullscreen, etc.), I can still enter fullscreen by pressing command+F, but I can no longer exit fullscreen (save for command+tab to another app, or command+Q).
Also, the Apple docs mention setting the menu action to -toggleFullscreen: and the target to nil. How is this last part done in Interface Builder? (I connected the action to First Responder's -toggleFullscreen:)
What should I do?

So, I found the problem (posting the question in SO seems to be a condition to finding the solution, always...)
The offending line was not setting the delegate, but what I was doing to the window after entering fullscreen mode. In particular, as soon as I commented out the following line
[window setStyleMask:NSBorderlessWindowMask]; in the code below:
- (void) windowDidEnterFullScreen:(NSNotification*) notification
{
NSWindow* window = [self window];
NSRect mainDisplayRect = [[NSScreen mainScreen] frame];
[window setStyleMask:NSBorderlessWindowMask];
[window setContentSize:mainDisplayRect.size];
[window setLevel:NSMainMenuWindowLevel + 1];
[window makeKeyAndOrderFront:self];
NSRect windowFrame = [window frame];
windowFrame.origin.x = 0;
windowFrame.origin.y = 0;
[window setFrame:windowFrame display:YES];
}
...the expected enter/exit fullscreen mode behaviour was fixed.

Related

Cocoa: NSUserNotification appears behind NSPopover

I have no idea where to even start trying to fix this but maybe someone can point me in the right direction. I have a status item application that shows a popover when clicked, some user interactions produce an NSUserNotification to warn/inform about changes or errors. But when the notification appears it is shown behind the popover, so if they overlay the user can't read what it says.
Is there a way to force the notification to appear in front of the popover? Like order the notification to the front.
Edit: here's a picture showing part of the notification hidden behind the popover after a user gave bad input. I don't really want to hide the popover to show the error notification.
I've tried stuff lake makeKeyAndOrderFront, activateIgnoringOtherApps, the type of stuff you'd use on a window, but none of it applies to an NSUserNotification so it doesn't work.
Edit 2: I should note that to get the popover to appear in front of everything when called I use activateIgnoringOtherApps and becomeFirstResponder on the popover, but even getting rid of that still shows the notification behind the popover.
Edit 3: The popover is shown relative to a status item in the status bar using
[[self popover] showRelativeToRect:statusItem.view.bounds ofView:statusItem.view preferredEdge:NSMaxYEdge];
When clicked, the status item calls a method that does this:
- (void)openPopover:(id)sender {
if (![_popover isShown] | (_popoverIsClosing)) {
[_preferencesWindow close];
[NSApp activateIgnoringOtherApps:YES];
[_popover becomeFirstResponder];
[statusItemView setHighlighted:YES];
[[self popover] showRelativeToRect:statusItem.view.bounds ofView:statusItem.view preferredEdge:NSMaxYEdge];
[self performSelector:#selector(defaultTextField)];
NSLog(#"popover opened");
} else {
[_preferencesWindow close];
[NSApp activateIgnoringOtherApps:NO];
NSLog(#"popover closed");
[_popover close];
[_popover resignFirstResponder];
[[NSApplication sharedApplication] hide: self];
[statusItemView setHighlighted:NO];
}
}
Thanks for the help.
You want to adjust the window.level.
A notification is using level 18, so as long as your level is lower, you will not have this issue.
You can use these default NSWindow.Level
.normal = 0
.floating = 3
.submenu = 3
.tornOffMenu = 3
Or a custom level NSWindow.Level(rawValue: 17).
An important thing to note is you should set the windows level during or after the popover did show. Otherwise your setting will be reset.
You can do this from the popover.delegate using the function,
func popoverDidShow(_ notification: Notification)
Or listen to the notification directly with NSPopoverDidShowNotification.
This has been tested on macOS 10.15

Show Window without activating (keep application below it active)

I need to show a window (without title bar) above third party applications without my window taking focus.
I have tried using an NSPanel and setting enabling non-activating, but that didn't help.
I tried orderFront:self, but that didn't help either.
I always needed to add [NSApp activateIgnoringOtherApps:YES]; because the window wouldn't show otherwise.
I have here a sample project for just this functionality:
http://users.telenet.be/prullen/TopW2.zip
UIElement is set to true in the application's plist file, so there is no dock. You can activate the window by pressing ALT + SPACE at the same time. You will see that the app below it looses focus. Any thoughts on how to fix this? I've seen other apps do it so I know it's possible.
Edit: here's the code so far. Remember the window is a non-activating NSPanel.
I still need that last NSApp activateIgnoringOtherApps line or otherwise it doesn't display. But of course that makes the window the active one.
_windowController = [[MyWindowController alloc] initWithWindowNibName:#"MyWindowController"];
[[_windowController window] setLevel:NSNormalWindowLevel+1];
[[_windowController window] orderFrontRegardless];
[_windowController showWindow:self];
[NSApp activateIgnoringOtherApps:YES];
I've also subclassed NSPanel and added two methods:
- (BOOL)canBecomeKeyWindow
{
return YES;
}
- (BOOL)canBecomeMainWindow
{
return YES;
}
Edit: OK, unchecking setHidesOnDeactivate fixes this, but now the window will never hide. I need it to hide when the user presses the app below it or switches to another app.
Edit 2: OK, this seems to fix the above issue:
- (void)awakeFromNib
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(hideWindow) name:NSWindowDidResignKeyNotification object:nil];
}
- (void)hideWindow {
[self setHidesOnDeactivate:YES];
}
Not sure if there's a better way.
And for those that want to know how to display the window:
[[_windowController window] setLevel:NSPopUpMenuWindowLevel];
[[_windowController window] orderFrontRegardless];
[[_windowController window] makeKeyWindow];
[_windowController showWindow:self];
Either one of these should do the trick:
Use -[NSWindow orderFrontRegardless] to get a normal level window to the front without activating the corresponding app, or
Use -[NSWindow setLevel:] to increase the window level to something higher than NSNormalWindowLevel
Not to take away from #puzzle's useful answer, but it sounds like your problem has something to do with using an NSPanel instead of an NSWindow.
The "How Panels Work" docs say:
Onscreen panels, except for alert dialogs, are removed from the screen when the application isn’t active and are restored when the application again becomes active. This reduces screen clutter.
Specifically, the NSWindow implementation of the hidesOnDeactivate method returns NO, but the NSPanel implementation of the same method returns YES.
So perhaps you could override hidesOnDeactivate to return NO, or change to NSWindow

FullScreen Loading View didn't work

I have been working on a fullscreen loading view for my apps, but it can't block any user interaction. The way I implement it is like this:
Create a singleton object - LoadingView
Call [LoadingView show] - add the loading view to [[[UIApplication sharedApplication] delegate] window]
So I want to ask :
Is my concept wrong?
Are there any method calls that can disable all user interaction to my apps?
Is there another better way to do this? (I prefer knowing about the principle inside than just using others' libraries)
Thanks
I use such way to block user interaction:
create UIView with window frame
set view's user interaction enabled to true
(optional) add UIActivityIndicator to view
add view to window subviews
when I need to show it I set hidden to NO and use bringSubviewToFront function to show loading view
when I don't need it I set hidden to YES and use sendSubviewToBack
id application = [[UIApplication sharedApplication] delegate];
UIView *loadingView = [[UIView alloc] initWithFrame:[[application window] frame]];
loadingView.userInteractionEnabled = YES;
loadingView.backgroundColor = [UIColor colorWithWhite:0 alpha:0.5];
[[application window] addSubview:loadingView];
Are you wanting to create a splash screen when the application starts? If so, all you need to do is add an image file called Default.png and/or Default#2x.png (for retina displays) to your project. This image will work as a splash screen for when the app launches.
You can also control the splash screen info.plist file.
Read more about the iOS launch screen here.

OSX Lion: different views in fullscreen and in windowed mode

I'm trying to make it so that a certain view contained in a window becomes the main content view when fullscreen mode is toggled and goes back to taking just a portion of the window when the user leaves fullscreen mode.
I've come up with the following:
- (void)windowWillEnterFullScreen:(NSNotification *)notification
{
NSLog(#"entering fullscreen");
oldView = [[[NSApplication sharedApplication] mainWindow] contentView];
[oldView retain];
[[[NSApplication sharedApplication] mainWindow] setContentView:myViewOfInterest];
}
-(void)windowWillExitFullScreen:(NSNotification *)notification
{
[[[NSApplication sharedApplication] mainWindow] setContentView:oldView];
}
However this only works for the first bit: the window maximises and the view of interest becomes the only one, but when fullscreen mode is left, the view that was the only one visible in fullscreen mode is no longer in the window.
I'm very new to Objective-C and Cocoa, so could anyone tell me what am I doing wrong?
Thanks in advance!
A view can only be a sub-view to one other view at a time. Your myViewOfInterest is removed as a sub-view (of the view hierarchy) of oldView when you make it the contentView of the window. When you later restore oldView you need to add myViewOfInterest back where it was (and what size it was etc.).

Loading NIB using [NSBundle loadNibNamed:owner:] but the window does not appear in the foreground

I wrote a menu application that has no persistent window or standard menu. When another application has focus and I use the menulet to trigger a window to be opened, it appears behind the foreground application (but above anything else that is present on the screen).
Basically...
-(IBAction)aboutWindow:(id)sender {
[NSBundle loadNibNamed:#"About" owner:self];
}
Can anyone point me in the right direction so I can get this window to appear above all other applications when it is initially spawned?
[Edit]
I have tried using a custom NSWindowController with the window linked up, and awakeFromNib calling a makekeyandorderfront method, but that wasn't doing anything.
I now have instead of the NSBundle call:
NSWindowController* awc = [[NSWindowController alloc] initWithWindowNibName:#"About"];
[[awc window] makeKeyAndOrderFront:nil];
And that spawns the window, but still does not make it in the foreground
Figured it out. Nothing was wrong with the Window, it was the Application. It was not in the foreground because of its nature as a menulet with no windows before this one is spawned. Final code:
-(IBAction)aboutWindow:(id)sender {
NSWindowController* awc = [[NSWindowController alloc] initWithWindowNibName:#"About"];
[[awc window] makeKeyAndOrderFront:nil];
[[NSApplication sharedApplication] arrangeInFront:nil];
}
You could try makeKeyAndOrderFront:
For example, in the About window's controller - assuming the controller had a reference to the window as myWindow:
- (void)awakeFromNib
{
[myWindow makeKeyAndOrderFront:nil];
}