NSControl with multiple actions - cocoa

I'm trying to create a complex custom NSControl that must be able to send more than one message.
For example, on mouse over in must send an action, and on mouse drag in must send another action.
I can't understand how to wire a target to a control and make the control sends whatever message to the target.
In my opinion i have to follow these steps:
Instantiate the NSControl i.e. myControl
set the Target action for myControl for every actions (I don't know how to do that!)
The myControl instance will send action with [NSApp sendAction: [self action] to: [self target] from: self]
Can you help me on step 2? and confirm my steps?

You need Delegation pattern. Standard Cocoa controls send at most one action, and use delegation for anything additional. IB does not support setting more than one action, so you can't solve step 2.
If delegate is an outlet, you can set it right from IB whenever the delegate is file's owner or also instantiated in this nib, like you would do that for e.g. NSWindow or NSTableView.

Related

How to implement my own selectAll: for UITextView without subclass it?

How do I implement my own selectAll: for UITextView without subclassing it? I don't want to subclass UITextView because if I do I need to change every place in my codes (.m/.xib) that need to use this subclass.
I had thought (wrongly!) that maybe I can connect UITextView action like selectAll: to my code (I do declare - (IBAction)select:(id)sender in my h file) ? I drag the action to File's Owner or to .h/.m file, nothing happens.
Now I know they are received action so I can't do that. Then how to ?
You cannot connect it because there is no such action. You are reading the Connections inspector incorrectly. The things in your screen shot are not actions. selectAll: is not an action. It is a received action. It is not emitted by the text view. It is sent to the text view, as a nil targeted action passing up the responder chain. To change or add to the text view behavior when it receives this action, you subclass UITextView. It's easy to do.

Best place to intercept Cmd-Key in NSDocument based app

What would be the most appropriate place to intercept CmdAnyKey key events in an NSDocument based application?
Intention is to switch to some component in the active window - kind of like Firefox allows you to switch the tabs - without having a matching shortcut on a menu command to perform that task.
I.e. ideally the framework should do it's normal processing including handling menu commands and just after all other responders fail to respond to that particular shortcut it should be routed to the custom method.
I've searched NSDocument, NSAppDelegate, NSWindowController but couldn't locate any appropriate mechanism to hook into in order to receive these commands on window level.
So lacking any existing customization mechanism does override keyDown: in a custom NSWindowController look like the most appropriate way to achieve the desired effect?
Yes, subclassing NSWindow is the way to do this if you need to get the keyboard event after everything up the responder chain refused to handle it.
Here's how I did it in one of my projects:
- (void)keyDown:(NSEvent*)event
{
SEL keyDownBool = #selector(keyDownBool:);
if ([[self delegate] respondsToSelector:keyDownBool]
&& [[self delegate] performSelector:keyDownBool withObject:event])
{
return;
}
[super keyDown:event];
}
My custom keyDownBool: delegate method returned YES if it handled particular key event. Otherwise this method passes key event down to super.
Now I'm using + (id)addLocalMonitorForEventsMatchingMask:(NSEventMask)mask handler:(NSEvent* (^)(NSEvent*))block instead of subclassing. The difference is that it handles (and optionally discards) events before they are dispatched.

How can I receive actions listed in here? (Cocoa)

I'm trying to connection the actions to specific object in IB tree. When I drag the action from the circle on the right side of each action name, no object accepts the line except NSScroller instance.
NSWIndow, NSResponder or any other kind of objects doesn't accept the dragged action. What's required to accept the action listed here?
These are action messages that you can send to the selected object (i.e., that it can receive and respond to). You can drag from any of those circles to any object (most probably a control, most probably a button) that has a target and an action; doing so will set the control's target property to this object and the control's action property to the action you connected it to.
I decided to use a delegate method.
- (BOOL)textView:(NSTextView *)textView doCommandBySelector:(SEL)commandSelector
Here's a usage: http://www.cocoabuilder.com/archive/cocoa/48070-adding-carriage-return-behavior-to-nstextview.html

How to define a class that sends an action like NSButton?

In Cocoa, how do you define a class that sends an action? I want to be able to connect the action to the selector of another object in IB in the style of NSButton. I would prefer not to subclass NSControl if possible.
Give it a property (informal is fine) holding an id, for the target. I'm not sure whether this should retain or not; I'd say no, since the target will normally be the controller that owns the window that (indirectly) owns the view.
Give it a property (informal in fine) holding a SEL, for the action.
Respond to mouseUp: and keyDown: (checking that the key in question is the space bar or return or enter) by sending yourself an accessibilityPerformAction: message, passing NSAccessibilityPressAction.
Respond to the accessibilityPerformAction: message by either sending your action message to your target (NSAccessibilityPressAction) or calling up to super (other), as described in the documentation for that method.
You should also implement the rest of the NSAccessibility protocol while you're at it. Test that work with a mix of the Accessibility Inspector and VoiceOver.

Why for only some actions must I call setTarget?

For most actions, I just click and drag in InterfaceBuilder to "wire up" a call from some interface object to my code. For example, if I want to know when the user single-clicks a row in a table, I drag a connection from the table's action to my controller's action.
But now let's consider the user double-clicking a row. If I want one of my actions to be called when this happens, I need to call not only -[NSTableView setDoubleAction] but also -[NSControl setTarget]. Why?
To be clear, I am not asking why Interface Builder doesn't support setDoubleAction. All tools have limitations. I am trying to gain a greater understanding about how and why setTarget doesn't seem to be necessary unless and until I want setDoubleAction to work. Another way to ask this question would be: Why don't I need to do anything in Interface Builder to set the target of the table's (single-click) action?
If you connect your table view to an action in IB, then call setDoubleAction on it, there should be no need to make an additional call to setTarget. However, if you only wish to receive the double click message, and you didn’t connect the table view to an action in IB, you will have to call setTarget.
A table view will send action and doubleAction to the same target. You can imagine NSTableView as being implemented like this:
#implementation NSTableView
- (void)theUserClickedOnMe
{
[self sendAction:[self action] to:[self target];
}
- (void)theUserDoubleClickedOnMe
{
[self sendAction:[self doubleAction] to:[self target]];
}
#end
And what you’re doing in IB is something like this:
- (void)userConnectedControl:(NSControl *)control
toAction:(SEL)action
ofObject:(id)object
{
[control setTarget:object];
[control setAction:action];
}
The real implementations are nowhere close to that, but that is effectively what’s going on.
If you set an action (or double-click action) and don't set a target (or set the target to nil), then the action message will go through the responder chain.
If you set a target in addition to an action, the action message will go only to that object.

Resources