Keeping window of another application active while still receiving mouse events from another app's window? - objective-c

Is there a way to have my app's window receive keyboard and/or mouse events (i.e. user clicking on window's buttons) while still retaining focus to another, unrelated app?
I've tried configuring my window at different levels, including [myWindow setLevel:NSPopUpMenuWindowLevel] to no avail.

You should be able to handle mouse clicks without ordering your window front by making your views (at least the ones that handle mouse clicks) respond to acceptsFirstMouse: messages by sending NSApp a preventWindowOrdering message and then returning YES.
You should not make your app handle typing without ordering itself front. The user might not realize where their typing is going if the field where it's appearing is obscured by another window.

Found it. Simple, yet elusive.
Use NSPanel and make sure panel style is Non Activating (NSNonactivatingPanelMask) or tick the same option in IB's inspector.

Related

In Objective-C/Cocoa, are there global events for when mission control is activated or spaces are changed?

I'm currently working on an OSX menubar app that uses a custom status item view and a NSPopover to display content.
I'm trying to get it to dismiss at times that would make sense like when spaces are changed, since the popover doesn't move spaces like a window does, or when mission controller is activated.
Currently, when in mission control, the NSPopover stays on top as shown in this screenshot.
Currently I'm using NSEvent addGlobalMonitorForEventsMatchingMask: with some mouse event masks and that works alright but doesn't cover all needed events.
So, is there a way to detect when major OS events happen like opening mission control, changing spaces etc?
Any help would be greatly appreciated.
You can get notified of space changes by registering for NSWorkspace's NSWorkspaceActiveSpaceDidChangeNotification. There isn't a notification as such for Mission Control, but you might investigate whether NSWorkspaceDidActivateApplicationNotification or other notifications can be used to determine what you need.
HTH

Is it possible to have focus on an NSPanel without losing focus of other background windows?

I have a custom NSPanel: http://cl.ly/K8SY
I have it set to NSPopUpMenuWindowLevel, the level at which I want it to stay as. An example is the spotlight menu, when you click on it any other focus in the windows in the background remains yet you can still type into the search field.
I open it with:
[window orderFront:nil]
but this doesn't focus on the window as well as the background.
Is it possible to achieve this? If so, how?
You need to use the -[NSWindow makeKeyAndOrderFront:] call instead.
NOTE: keyboard focus can only be directed at one view in one window. Cocoa's notion of mainWindow and keyWindow can be different windows, but its only the first responder within the keyWindow that accepts keyboard input.

Cocoa/ObjC: Place a floating window above a modal window

I have two NSWindows. Window A with level 0, and Window B with level 1. I'm using B as a floating window.
This works as expected until I put A as a modal window (i.e., send runModalForWindow:A message to NSApplication). Then B is always behind A.
Is there a way that I can have B above A, even when A is running as a modal window?
Much appreciated for the help.
To put your floating window in front, use [myfloater setLevel: NSModalPanelWindowLevel+1].
Other people are telling you that you will have trouble interacting with the floater, but I've done it and it worked for me. (My floater uses NSPanel of "Utility Panel" style rather than plain NSWindow, but I'm not sure if that's important. You may also need to say [myPanel setWorksWhenModal: YES].)
AFAIK that is not possible.
From the Apple class reference:
NSApplication runModalForWindow:
This method runs a modal event loop for the specified window
synchronously. It displays the specified window, makes it key, starts
the run loop, and processes events for that window. (You do not need
to show the window yourself.) While the application is in that loop,
it does not respond to any other events (including mouse, keyboard, or
window-close events) unless they are associated with the window. It
also does not perform any tasks (such as firing timers) that are not
associated with the modal run loop. In other words, this method
consumes only enough CPU time to process events and dispatch them to
the action methods associated with the modal window.
Given this, your Window A will become the key window and will always be shown on top of any other window of your app. I think this is how modal windows are supposed to work.
As an alternative you can consider using an NSPopover which has a clear presence to the user but does not force your application into a modal state.
Check out this page from the Apple docs on guidelines re various options available to you.
I think if you set the window level to NSScreenSaverWindowLevel, it will always be on top.
You can use orderFront:, orderFrontRegardless, or orderFront:relativeTo:, to have B moved in front of A. However not sure how many tasks can you do in B, as due to the fact that A is modal, you won't be able to interact with B.

Controlling NSSegmentedControl with the keyboard

I have a form in my Cocoa app that contains an NSSegmentedControl that I want to be controllable via the keyboard. It seems that NSSegmentedControl is very reluctant to become the first responder, however.
Setting the initial first responder of the window to the segmented control does nothing -- it will not have keyboard focus when the window is first loaded. It does receive focus if I manually set the first responder like this, however:
[segmentedControl.window makeFirstResponder: segmentedControl];
That will work fine if the only part of the form is the segmented control. If I add another field (say, an NSTextField), and I set the nextResponder of the segmented control to that field, the segmented control will never become first responder. Focus will immediately go to the text field, and pressing tab to switch back to the segmented control doesn't work.
I've tried subclassing NSSegmentedControl and overriding acceptsFirstResponder, becomeFirstResponder, etc. to no avail. The only one that makes any difference is resignFirstResponder -- if I return NO from that method then the segmented control will indeed retain focus, but obviously I don't want it to retain focus all the time.
Any ideas on how to get the control to behave like a normal responder?
It's behaving as intended. Not all controls participate in the "key view loop". Full keyboard navigation is turned on through Universal Access in System Preferences for all apps and it's not for individual apps to implement on their own.
It's best not to use a segmented control in a form intended for heavy keyboard entry. NSPopUpButton works more closely to what we all exepect in a web form so it's not as if it's necessarily the wrong choice in your app's UI.
Rather than answer exactly the question you asked (which someone else can do), I humbly suggest you choose on the side of functionality at the cost of a slightly prettier UI element since that prettier UI element wasn't intended to get along with the keyboard.

Disable application activation on window click

In my Cocoa/Objective-C application I have a utility panel floating "always on top" to be accessible even when my application is not active. I am trying to disable the "switching to my application when a user clicks on that panel".
The behaviour I would like to achieve is similar to OSX's Keyboard Viewer, (which is also a never activating panel), so that some other application remained active after clicking on my app's panel. i.e. Safari stays active when typing an address using Keyboard Viewer. Even third-party onscreen keyboards have this functionality (for example the one from CORALLO Software), which means this behavior is not reserved system-only.
I was messing around with NSApplicationActivationPolicy, but without positive results. In which direction should I go?
You should take a look at the canBecomeKeyWindow and canBecomeMainWindow methods on NSWindow. It sounds like you want your window to maintain key status while not being able to be the main window. Here are some resources to help you:
Window Programming Guide - Explains the difference between main and key windows
NSWindow class reference - Jump to the sections on canBecomeKeyWindow and canBecomeMainWindow