Iam working on an application that tries to extend some Spotify functionality (not another client). I would like to show Spotify's main window when my icon is clicked on the dock - even if the main window in Spotify is closed.
This is my code now:
- (void) applicationDidBecomeActive:(NSNotification *)notification {
// Causes Spotify to hit the front when selecting it!
[[[NSRunningApplication
runningApplicationsWithBundleIdentifier:#"com.spotify.client"] lastObject]
activateWithOptions:NSApplicationActivateAllWindows];
}
It works when the window is open but not in focus (background), but not when I close the Spotify main window (which I people tend to do). Is there any way to re-open this window if it's closed from another application?
The Spotify icon can do this (obviously). Try to hit the close button (the red x) and press the icon (it will reappear). Is that possible from another app?
The Dock sends the reopen Apple Event to the application you clicked on, which then executes code as it sees fit. To simulate a Dock click, you need to send the reopen event to the application manually.
The Apple Documentation on the matter states that the reopen event has the ID of rapp code (kAEReopenApplication) and is part of the kCoreEventClass class.
From there, it's relatively simple to construct the Apple Event in code and send it off to the application. Note that you really should do some error checking from the AESendMessage call, as the event will likely fail if the application isn't launched, etc — I haven't really tested those cases.
Here is my solution. Note that you'll need to keep your original code to bring the application to the front — the Apple Event doesn't change application order.
NSAppleEventDescriptor *target = [[NSAppleEventDescriptor alloc]
initWithDescriptorType:typeApplicationBundleID
data:[#"com.spotify.client" dataUsingEncoding:NSUTF8StringEncoding]];
NSAppleEventDescriptor *event = [[NSAppleEventDescriptor alloc]
initWithEventClass:kCoreEventClass
eventID:kAEReopenApplication
targetDescriptor:target
returnID:kAutoGenerateReturnID
transactionID:kAnyTransactionID];
AESendMessage(event.aeDesc, NULL, kAEWaitReply | kAENeverInteract, kAEDefaultTimeout);
Related
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.
I'm writing an application with a main window that's displayed when the app starts. When the window is closed, I'd like the app to remain running (with a menu-bar menu), and if the user clicks on the dock icon again, I'd like the main window to be presented again.
I'm about 90% of the way there: my app properly keeps running after the main window is closed with Cmd-W, and since "Release When Closed" is unchecked, the window could be [makeKeyAndOrderFront:]-ed to show it again when the dock icon is clicked.
The only missing piece of this puzzle is intercepting the actual dock-icon click.
The other threads about this topic recommend implementing either applicationShouldHandleReopen:hasVisibleWindows: or applicationShouldOpenUntitledFile: in the window controller. I've done both, and neither one ever gets called.
Any other ideas?
The other threads about this topic recommend implementing either applicationShouldHandleReopen:hasVisibleWindows: or applicationShouldOpenUntitledFile: in the window controller.
That's only true if the window controller is the application's delegate. That is the object to which the application sends those messages.
I would not make a window controller the application's delegate, though. I typically make them two separate objects. Make one object specifically to be the application's delegate, and when that object receives the relevant delegate messages, send a message to your window controller telling it to do whatever it needs to do.
Actually, what I usually do in single-window apps is make the application's delegate create and own the window controller. You can respond to window closure by throwing away the WC, and respond to reopen by checking whether you have a WC and creating one (and thereby reopening the window) if you don't.
Use [NSApp setDelegate:self]; in awakeFromNib.
I have an LSUIElement application that needs to keep it's status as an LSUIElement (it cannot have a dock icon) this application is launched and handled by a plugin.
When I change the app so it is a "Regular" app (without LSUIElement), [NSApp activateIgnoringOtherApps:YES] works perfectly. However when I make it an LSUIElement it shows the window, but the window is stuck behind safari (where the plugin is running) but on top of everything else. I am calling -[NSWindow makeKeyAndOrderFront:self]; but that doesn't make any difference.
To be clear the "plugin" side is irrelevant as the plugin and application are two separate processes. The plugin sends an event (over a mach port) to the application which calls some code to open the window and bring it to focus. The problem is the code to bring it to focus only works when it's not an LSUIElement application.
I've exhaustively searched for an existing call to make this work, I'm open to suggestions on how to fake a mouse click, or even run some applescript to make this work, maybe there's an undocumented method of doing this.
The Apple docs for LSUIElement state "The application does not appear in the Dock and does not have a menu bar, but it may be activated programmatically" but I cannot find the magic that enables this.
You need to activate your app first:
[NSApp activateIgnoringOtherApps:YES];
and then make your window become key window:
[NSWindow makeKeyAndOrderFront:nil];
This works in my project.
The following approach works for me in an app with "LSUIElement" set to "1", even if the entire application is using Cocoa:
ProcessSerialNumber psn;
if (noErr == GetCurrentProcess(&psn))
{
(OSStatus)SetFrontProcess(&psn);
}
(This is part of the Carbon API.)
In one of my first Cocoa applications I have just a single window with 2 NSTextField instances - for user text input and of output of text processing.
If my user clicks on the red x on the top left, the window closes but the application is still running and icon stays in the dock - the normal Mac behavior.
When the user double-clicks on the icon in dock or on the desktop, this does not reopen the window - apparently also normal Mac behavior, but confusing to the user.
To get the app back into a running state, the user has to force Quit from the main menu or the context menu, and restart the app by clicking on one of the icons.
I searched Apple doc and forums, and it seemed that the following should prevent the closing of the window (my first preference : hide the widow so it can be reopened later) :
add a delegate to NSApp
delegate implements -applicationShouldHandleReopen which calls [mainWindow makeKeyAndOrderFront:self]; and returns TRUE
delegate implements -windowShouldClose which returns FALSE
However, although -windowShouldClose is called, the window closes.
What am I missing here?
As an alternative (my second preference), I added to the delegate
-applicationShouldTerminateAfterLastWindowClosed which returns YES
This works, i.e. the application closes when the used clicks on the red x,
and the user can restart the app later without further ado.
Clarifications and pointers to specific doc and working code examples would be appreciated.
Rudi
"When the user double-clicks on the icon in dock or on the desktop, this does not reopen the window - apparently also normal Mac behavior, but confusing to the user."
If you want the window to re-open in that case, implement applicationShouldHandleReopen:hasVisibleWindows:. There's nothing un-Mac-like about opening a window when the user clicks the dock icon after closing all the windows; lots of apps do it and the delegate exists specifically to support that behavior.
First of all, your "alternative" behavior of terminating the app on window close is probably the preferred approach for your situation. Users may be confused when they can't close the window.
If you really want to prevent the window from being closed, why not just disable the close control on the window in IB?
I have a small OSX Cocoa app that just bring up an IKPicutreTaker and saves the picture to a file if one is set. I use applicationShouldTerminateAfterLastWindowClosed: to close the application when the pictureTaker is closed. This all works fine when I either set(this is done when you have picked the picture you want) or when you hit cancel, but when I click on the red arrow in the top left of the windows, the application does not quit when the window is closed this way. Is this intended functionality or am I doing something wrong (not setting some flag?). Also, is there some way to disable this button?
Clicking the red button does not close an application, partly because that verb does not go with that noun. The red button closes the window it's on; it does not quit your application. (An application can and will often have multiple windows on Mac OS X.)
That's where applicationShouldTerminateAfterLastWindowClosed: comes in. Whenever the user closes the last window on the screen (whether by clicking the red button, by choosing Close from the File menu, or by some other means), the NSApplication object will send an applicationShouldTerminateAfterLastWindowClosed: message to its delegate, to ask it whether the application should terminate. If the delegate responds to the message by returning YES, then the application will terminate itself.
You don't say exactly how you're “using” applicationShouldTerminateAfterLastWindowClosed:. If you're just sending it to some object, that's not going to work, because you're asking a question (“should the application terminate after the last window is closed?”) and then ignoring the answer. If you implement the method in an object, but that object isn't the application's delegate, that won't work either—the application object only sends that message to its delegate.
You need to implement the method in your delegate, and return YES when it is appropriate for the application to terminate when the user closes its last window.