Showing a modal NSWindow, without activating the other application windows - cocoa

I have an NSStatusItem that is properly displaying in the MenuBar. One of the items (when clicked) displays a modal NSWindow from my application, which is meant to perform a one-off task, then disappear. (Eg. the user enters a small bit of text, clicks "Save", and the modal NSWindow goes away.)
The issue occurs when the application is running in the background. The modal window properly appears above whatever application is running in the foreground, but when the user clicks the "Save" button, the rest of the application's windows also are made active. This is undesirable, as the user then has to click back to whatever app they were using. (Destroying the convenience of the NSStatusItem.) I'm displaying the modal window using:
[myWindow setFrame:finalRect display:YES animate:NO];
[myWindow setLevel:NSPopUpMenuWindowLevel];
[NSApp runModalForWindow:myWindow];
Is there any way to prevent clicks/events in my popup window from causing the rest of the application to become active? Or a way to let NSApp know that this particular panel shouldn't automatically activate the rest of the app? Thanks!

Instead of creating an NSWindow, create an NSPanel with the style NSNonactivatingPanelMask. You can then do the usual makeKeyAndOrderFront: and orderOut: to show/hide panel as needed.

NSApp's beginModalSessionForWindow, runModalSession, endModalSession are methods you need.
Have a look here for example how to use it:
Creating a fully customized NSAlert

A solution by Ken Thomases on the cocoa-dev list a couple years ago looks applicable here too:
[[NSApplication sharedApplication] hide:self];
[[NSApplication sharedApplication] performSelector:#selector(unhideWithoutActivation)
withObject:nil
afterDelay:0.05];
Which in theory tells the application to hide itself and unhide at the bottom of the window stack.
You could also intercept the mouse click event and use [NSApp preventWindowOrdering]

You can try something like:
...
if ([NSApp isHidden])
[myWindow makeKeyAndOrderFront:self];
else
[NSApp runModalForWindow:myWindow];
...
and when finish:
...
if ([NSApp isHidden])
[myWindow orderOut:self];
else
[NSApp stopModal];
...

Related

NSApplication make window front on app switch

When run my app(OSX, macOS SDK 10.15), it shows the main window, as expected. I then CMD-Tab to another app, but when I CMD-Tab back to my app, it won't show the window. The same happens if I click on it in the dock.
I've tried various suggestions, such as:
[NSApp activateIgnoringOtherApps:YES];
[window makeKeyAndOrderFront:nil];
[window orderFrontRegardless];
and
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:#selector(applicationSwitchedTo) name:NSWorkspaceDidActivateApplicationNotification object:nil];
-(void)applicationSwitchedTo
{
[window makeKeyAndOrderFront:nil];
}
I've set up all the callback methods in both the window and app delegates, but I don't get anything,except the above callback, when I switch to or from my app. I would think it would default behavior to show the apps main window when switched to it from another app. Any help is greatly appreciated!
I figured out a fix, but I'm not sure if it's the proper thing to do.
I noticed that if I called
[NSApp finishLaunching]
then the window ordering functions don't work. I don't need it's functionality, so I removed it.
Furthermore, I'm now handling the event type NSEventTypeAppKitDefined, which is called when you switch on and off the app. I then check the subtype and call the window ordering function:
case NSEventTypeAppKitDefined:
{
if([theEvent subtype] == NSEventSubtypeApplicationActivated) {
[window makeKeyAndOrderFront:nil];
}
}
Hope this helps someone down the line!

Performing an action when _statusItem is clicked, with a NSMenu and the window not in focus seems impossible

There are similar questions on SO but this have a twist.
I need to trigger an action when the user clicks on my app icon sitting on the menu bar. The action is to bring its window to the front, or in other words,
[[[NSApplication sharedApplication] keyWindow] makeKeyAndOrderFront:self];
Normally I would do this:
[_statusItem setTarget:self];
[_statusItem setAction:#selector(bringToFront:)];
but this _statusItem has a NSMenu.
If I disable the menu, bringToFront: is triggered.
So I thought, I will implement NSMenuDelegate method menuWillOpen.
- (void)menuWillOpen:(NSMenu *)menu {
[[[NSApplication sharedApplication] keyWindow] makeKeyAndOrderFront:self];
}
But there is a problem. This will work if the app window is the one selected, but suppose the app is running and I select safari. Then, my app's window is not in focus anymore, is behind 2000 Safari windows. Now I click on my app's icon on the menu bar and menuWillOpen will not be triggered.
If I want to bring the window to focus by clicking on the app's icon on the menu bar, having to bring the window to focus to make it work does not make sense.
NSApplication notifications are posted in response to your app gaining/resigning active status. Handle these notifications and enable/disable your status item's menu so that your action gets invoked when your app is in the background.

opening and closing a window programmatically in cocoa

I have two nibs in my project. I have been digging around the interwebs to figure out how to make a button close or hide the nib that the button is on and open another one. The other nib would have a button that does the same thing but in vice versa.
I don't have the faintest idea where to start with this. IF someone could just make a sample class and methods for this i might have something to work with.
Remove window from the screen
[window close];
Hide the window (doesn't release it)
[window orderOut:self];
Move the window to the front of its level in the screen list (= show window)
[window orderFront:self];
Check out
NSWindow Class Reference
Window Programming Guide

Hide Dock icon without closing window

I am creating an app in which I want to give the user the ability to show or hide the dock icon at run time. I have a preferences window with a checkbox, setting a user default value, which fires the following code using KVO:
if (!hideDockIcon) {
TransformProcessType(&psn, kProcessTransformToForegroundApplication);
} else {
TransformProcessType(&psn, kProcessTransformToUIElementApplication);
}
This works, but when hiding, the preferences window is closed directly (which makes sense as it is now a background app). However, I noticed that MS's SkyDrive client manages to hide the icon while keeping the Preferences window open. I have not been able to find out how one would do that, anybody has an idea?
I also tried using [NSApp setActivationPolicy: NSApplicationActivationPolicyRegular] and NSApplicationActivationPolicyAccessory/NSApplicationActivationPolicyProhibited but that doesn't work for me; Accessory doesn't hide the dock icon, Prohibited closes the window as well and seems to make [NSApp activateIgnoringOtherApps:YES] being ignored.
I stumbled upon this thread where the following is suggested to prevent a window from being hidden:
[window setCanHide:NO];
This just covers hiding. If your window gets closed, you might try to use the window delegate?
There's a call that let's you prevent the window from being closed
- (BOOL)windowShouldClose:(id)sender
I solved this problem by not activating the app in the same run loop turn:
dispatch_async(dispatch_get_main_queue(), ^{
[NSApp activateIgnoringOtherApps:YES];
});
Swift:
dispatch_async(dispatch_get_main_queue()) {
NSApp.activateIgnoringOtherApps(true)
}
I'm calling dispatch_async to schedule the block for execution in one of the next run loop turns a few nanoseconds later. This gives the process the chance to finish hiding itself.

Cocoa send background window to front

At the bottom of this post I included an example project that has my code in it. I have a Cocoa Application that uses a main default window, and another window nib I created. When the program starts, I want it to load the window nib and show it in front of the default 'MainMenu' nib window. How can I do this? I've tried the following code, but the window is still displayed behind the default window:
InfoWindowController *winInfo = [InfoWindowController new];
[winInfo.window makeKeyAndOrderFront:self];
[winInfo.window setOrderedIndex:0];
[winInfo.window makeKeyAndOrderFront:self];
[winInfo showWindow:self];
This is being called from AppController, which is a class I added to the MainMenu nib. InfoWindowController subclasses NSWindowController. I have included an example project here.
So, I know i'm reviving an old thread, but I was having a similar issue. Try putting [NSApp activateIgnoringOtherApps:YES]; in there.

Resources