Invoke NSTextField's action to be send when clicked ouside - objective-c

I have a NSTextField that have been edited. If I press enter then action is being called as expected.
I would like to have the same effect when I click anywhere outside of NSTextField. Resigning First Responder could be the key, but that happens only when some elements are clicked/selected/focused (e.g. another NSTextField or NSTableView) and doesn't happen with others (e.g. NSButton or NSSlider).
NSTextField focus can be unset using [[self window] makeFirstResponder:nil] but that would be too ugly to call this line in every object's action.
Is there a better solution?

You will want to delve into the Cocoa Event Handling Guide.
Take a good look at NSResponder.
Many objects inherit from NSResponder, including NSWindow and NSApplication.
Taking a guess at what you are trying to accomplish, committing an edit when the view is not in focus, you may want to also include observing notifications when the app will terminate, go into the background, etc...

Related

NSTableView early detection of edit session

I've a NSTableView (view-based) whose delegate (my windowcontroller, in this case) needs to be notified as early as possibile of Text editing session starts.
I've tried with the Text Delegate method
- (BOOL)control:(NSControl *)control textShouldBeginEditing:(NSText *)fieldEditor;
but this not working in my case.
The event gets triggered (and the delegate invoked) if and only if i hit some key in the keyboard.
If, by any chance, i click outside the control without having typed anything, the edit session silently stops without notifying anybody.
Any help is appreciated.
#theAmateurProgrammer
Thank you for your suggestion. Well, what you suggest would do only in case of mouse events. Instead, i need to detect the start edit session event always, also in case of, for example, programmatically triggered editing.
However i've found the solution. I subclass the table view and then i override the validateProposedFirstResponder method.
The responder, actually, is the TextField which is about to start editing.

How to force an NSWindow to be always active/focused?

I have a transparent NSWindow that follows the user's screen everywhere he goes (the NSWindowstays in front of every app, no matter what, even fullscreen apps).
In that NSWindow i have a mouseDown event that shows a popup. Let's say i'm on safari in fullscreen mode and i have my Window in front of it, i click on safari and i click again on my Window: nothing happens, the mouseDown doesn't occur. I have to click again so the mouseDown event is triggered.
How can i force my NSWindow to be always active so i don't have to click it 2x to trigger the mouseDown when i click on a background app and click in my window again?
Thank you!
I'm not sure if this is exactly what you want (it's not quite a window wide setting), but, from the documentation:
By default, a mouse-down event in a window that isn’t the key window
simply brings the window forward and makes it key; the event isn’t
sent to the NSView object over which the mouse click occurs. The
NSView can claim an initial mouse-down event, however, by overriding
acceptsFirstMouse: to return YES.
The argument of this method is the
mouse-down event that occurred in the non-key window, which the view
object can examine to determine whether it wants to receive the mouse
event and potentially become first responder. You want the default
behavior of this method in, for example, a control that affects the
selected object in a window.
However, in certain cases it’s
appropriate to override this behavior, such as for controls that
should receive mouseDown: messages even when the window is inactive.
Examples of controls that support this click-through behavior are the
title-bar buttons of a window.
Or you could try fiddling with
- (void)sendEvent:(NSEvent *)theEvent
and see if you can handle events in a custom way.
If you add a borderless NSButton instance to your window's view and set your image as the button's image (and as its alternate image, to make it more beautiful), it will work out of the box: Just connect the button's action method to your app delegate (or the object where you want to process the click action). A click on the image (i.e. the button) will then trigger the button's action method, no matter which window is active.
This worked for me, hope that will be helpful, This will keep your window always on Top of all applications
[self.window makeKeyAndOrderFront:nil];
[self.window setLevel:NSStatusWindowLevel];
I think what you really should do is use an NSPanel (a floating palette -- a special kind of NSWindow) that will do exactly what you want in a way that's consistent with the OS rather than trying to fight intended behavior.
Here's the NSPanel documentation:
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/nspanel_Class/Reference/Reference.html
And here's some helpful and pithy information:
http://cocoadev.com/wiki/NSPanel
By default, an NSPanel will disappear when the application is inactive, but you can turn this off.
I apologize for not laying it out more fully ... pressed for time.
Edit:
Note that you can probably get your window to behave as desired simply:
"The NSView can claim an initial mouse-down event, however, by overriding acceptsFirstMouse: to return YES."
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/EventOverview/HandlingMouseEvents/HandlingMouseEvents.html
You'll need to do this with any NSView subclass to skip the "activation click".

NSTextField in status bar doesn't want to receive focus

For some reson sometimes a NSTextField I'm using in Status Bar menu doesn't always allow me to input text. I click it and nothing happens as if it was disabled. Upon restarting program it works again. I don't do anything with it, it's just created in the interface builder.
That's because no NSWindow contains the NSTextField. The NSWindow sets the first responder when the window gets the main window. The NSStatusBar is global. It's never focused so your textfield only will be focused in the very beginning.
I'm not sure if there's a way to solve this problem in a nice way. You might try to set the first responder manually. You could also add a global event monitor
Example:
[NSEvent addGlobalMonitorForEventsMatchingMask:NSKeyDownMask handler:^(NSEvent* incoming) {
[textfield setStringValue:[incoming characters]];
}];
Note: This is a very bad way to fix this problem. I'd first try to set the NSTextField manually as a first responder if this is possible.

How can I get the value of an NSSlider continuously?

It seems like NSSlider in Cocoa does not provide a delegate to receive an event like Value Changed for a UISlider.
How can I get the value of an NSSlider continuously and display it in an NSTextField, for example?
You need to research Cocoa's Target/Action mechanism. This is a basic Cocoa concept you'll need to understand. The slider (and any other control) can be given a target (some controller object) and an action (the method to call against that controller object).
The action is fired when the user stops dragging by default. Check the slider's Continuous property in Interface Builder to cause it to trigger the action as you're sliding it.
One advantage of using the timer approach is that it works for the case of using the keyboard rather than the mouse to adjust the slider. If the user has "Full Keyboard Access" turned on in System Preferences, they can use the Tab key to give the slider focus. They can then hold down an arrow key so that autorepeat kicks in, whereupon you have a similar situation to dragging with the mouse: the target/action is firing repeatedly, and you want to wait for a moment of calm before saving to the database.
You do need to be careful not to delete your NSTimer prematurely. For example, if the user quits the app during those couple of seconds you probably want to "flush" the slider value to the database before terminating the process.
Programmatical solution based on the answer of Joshua Nozzi:
Swift
slider.isContinuous = true
Objective-C
slider.continuous = YES;

Class Design for delegate, outlets, and mouse events

Here's a simplification:
I have an application with several buttons. If it is the first time the application is launching, I want to do some special things. In my AppController class, which is a delegate of NSApp, I use the delegate method -applicationDidFinishLaunching: to perform the test. After I've detected that it is the first time, I first want to access some IBOutlets. Then, I'd like to be able to get mouse events for each button, so that I can do other things.
I can't figure out want to do with the classes. I'd like to make a new class (FirstLaunch) for the first launch, but I'm not sure what to call from AppDelegate. Also, to get mouse events, shouldn't I be a sublass of the buttons, and considering that I have multiple buttons, I'm confused. I could probably tackle these issues one-by-one, but taken all together, they're confusing me.
Broken down, I need to access & manipulate IBOutlets I have set in IB, determine when buttons are clicked (and which button was clicked). I'd like to be able to do this from another class so as to not clutter up the AppDelegate.
Thanks for the help!
To be more clear, what I'm actually trying to do is to use Matt Gemmel's MAAttachedWindow to put up a help bubble by a button. When the button is clicked clicked, the bubble disappears and another one is put somewhere else. The bubbles will be attached to controls in the main window.
I'm guessing you want to show some additional user interface on the first launch? If it's a separate window, I'd advise creating a subclass of NSWindowController. Add a new NIB file for the first-run user interface to your project and change the class of the File's Owner object to FirstLaunch. Control-drag a wire from the File's Owner delegate onto the window to connect it with the window outlet.
You create IBOutlets by adding an instance variable to the class. If your app will only run on Leopard or higher, it's better to declare your outlets like this:
#interface FirstLaunch : NSWindowController {
NSTextField *myTextField;
}
#property (nonatomic, retain) IBOutlet NSTextField *myTextField;
#end
In Interface Builder, you'll control-drag a wire from the File's Owner onto the control to associate it with that outlet. Make sure that you release your reference to each IBOutlet in your class's dealloc method (if you're not using garbage collection) or else your app will leak memory.
Buttons send action messages when they're clicked, so you'll need to provide an action method for the button to call. You do that by declaring a method with a signature like this:
- (IBAction)myButtonClicked:(id)sender;
In Interface Builder, you'll control-drag a wire from the button onto your window controller and choose the myButtonClicked: method.
To make all this work, you'll need to create an instance of the window controller and tell it to load the NIB file at runtime. So, in your AppDelegate class, when you've determined that this is the first launch, you'll do this:
FirstLaunch *firstLaunchController = [[FirstLaunch alloc] initWithWindowNibName:#"nameOfNibFile"];
[firstLaunchController show:self];
You'll probably want to keep the reference to the window controller in an instance variable instead of a local variable like I've done here. And, depending on your application, it may make more sense to show this as a sheet. But once you've made it this far, you'll be able to figure out how to do that on your own.
Then, I'd like to be able to get mouse events for each button, so that I can do other things.
Don't worry about the mouse. There may not even be a mouse (think of the ever-popular tablet-Mac rumor).
I'd like to make a new class (FirstLaunch) for the first launch, but I'm not sure what to call from AppDelegate.
You make your own methods here. You'll probably make it a singleton*; then, you'll implement a method named something like runFirstLaunchPanel:, which will be an action method (more on those in a moment):
- (IBAction) runFirstLaunchPanel:(id)sender;
Instantiate the object in the nib, then, from your app delegate, call the action method with nil as the sender.
The reason to put the object in your nib and make the method an action method is that this makes it easy to hook up a menu item to it, so that the user can re-run the first-launch panel at a later time. (For example, if it's a Starting Points window, you might connect the New menu item to this action instead of the default one.)
*Yes, I've seen the articles about singletons, and I agree with them. In a case like this, it's OK.
Also, to get mouse events,
This is the wrong way of thinking about it. What you need to do is set your button up to send a message to your controller to make the controller (probably AppDelegate) do something. The message you want the button to send is an action message.
Implement an action method in the object that owns the nib containing the window with the buttons. Declare this method in the class's header, then connect the button to it in IB by right-clicking on your controller and dragging from the correct action method's circle to the button.
This is called the target-action paradigm, and it insulates controller responsibilities (doing things) from the views that ordered them. Because each action method does only one thing, you can have a button, a menu item, and even another controller (your app delegate, above) send the same action message, and the receiving controller won't have to care which control is sending the action, because it already knows what it has to do.
shouldn't I be a sublass of the buttons,
No. You very rarely create subclasses of anything other than NSObject (or, for model objects in Core Data, NSManagedObject) in Cocoa.
Note that I said “rarely”, not “never”. You will have to make the occasional subclass, especially if you want to create custom or customized views and cells (and, maybe, customized windows). However, subclassing is not necessary in Cocoa to the degree that (I hear) it is in some other frameworks on other platforms.
and considering that I have multiple buttons, I'm confused.
The target-action paradigm means you don't have to create one button subclass per button. One controller class implements all the actions, and the stock buttons, because you've hooked them up in IB, simply tell the controller “do this”.
Broken down, I need to access & manipulate IBOutlets I have set in IB,
Probably not. At least, not as much as you think you do.
determine when buttons are clicked (and which button was clicked).
Nope. The buttons will worry about being clicked; you just worry about setting them up to send, and then responding to, their action messages.