Handling tabs in Cocoa view containing a text field and scrolling view - objective-c

In my Cocoa app, I have a window that contains an NSTextField (as is) and an NSScrollView (sub-classed). I've got an NSViewController that manages the window's NSView containing the text field and scrolling view.
At app startup, the NSTextField has focus, and typing enters characters into that text box. When I hit the TAB key, it loses focus. But nothing else in the interface, like the NSScrollView, gains focus. I can't tell where any key down events are going.
How does one transfer focus to the NSScrollView, so that key down events can affect it (e.g., arrow keys, implicit searching, etc.)?

First, you should try hitting Tab repeatedly to see if focus ever makes it to the scroll view or comes back around to the text field.
You don't say what's in the scroll view as the document view. Rather than having the scroll view itself accept focus, it's more common that the document view or one of its descendant views accepts focus. Then, the movement keys would be delivered to that and, if nothing consumes them, they'd bubble up the responder chain to the scroll view and it would handle them automatically.
If you really want your scroll view to accept focus, you need to override the -acceptsFirstResponder method to return YES.
The behavior of Tab is governed by the window. It maintains a key view loop. It can automatically recalculate the key view loop as views are added and removed. That's probably the most reliable way. See the autorecalculatesKeyViewLoop property.
Alternatively, you can manually tell it to recalculate as you desire, by turning off autorecalculatesKeyViewLoop and calling -recalculateKeyViewLoop.
Or, you can explicitly set up the key view loop yourself by connecting each view's nextKeyView and previousKeyView properties, either in code or in Interface Builder.

Related

NSTextField Key Interface Control (KIC) behaviour

Background: I have a custom derivation of NSTextField that I am trying to use as a cell in a grid similar to a spreadsheet.
I have set the nextKeyView of my NSTextField and am confused between two states that the NSTextField can have BOTH of which involve it having the blue focus-ring drawn around it:
the cursor is blinking in it => in this case the TAB key behaves as expected and moves to the nextKeyView and it is in a "text accepting" mode
it has not quite "entered" the text field for the purposes of text editing => the TAB key does not respond with KIC behaviour and move to the nextKeyView and trying to enter any text just in fact results in the following:
-[NSEvent key]: unrecognized selector sent to instance 0x60800012bf40
From background reading and a bit of debugging it appears that the first state corresponds to control/focus being handed off to the window's shared text editor and the custom NSTextField actually loses firstResponder status in this process.
What's interesting is how this differs from the behaviour of two standard NSTextField controls dragged onto an NSView in Interface Builder. Running such a simple app only the first state above is present at all as you tab from one NSTextField to the other! I am glad to have the intermediate second state in my app (because I need it) but I don't understand why it is happening...
Summary: two related questions
How do I programmatically control the NSTextField entering these two states of effectively accepting text and not while it has the focus [I do not know how to achieve this at all so this is my main problem...]
How can I get the KIC behaviour in the situation where the NSTextField has the focus but is not in "text editing" mode since this is the behaviour I am trying to get. [My current conclusion is that KIC does not work in this situation and that standard practise would be to resort to the regular overriding of keyDown for the set of keys that would normally be associated with KIC.]

How can I make command-A select all the NSTextView text in rows in an NSTableView?

So if I have an NSView based tableview and inside the views are NSTextViews which are non-editable but selectable...
how can I get that nice functionality of command-A selects all the text? I don't mean row selection. I have row selection disabled for the tableview. I mean highlighting the text in blue so you can copy it to your clipboard. But not just 1 NSTextView's text from one row, all of them from all the rows.
And in addition to command-A click and drag should do this too. But out of the box it seems I can only select one row's text. Here is video showing problem:
https://dl.dropboxusercontent.com/u/2510380/table.mov
(i keep clicking and dragging but can't highlight text on the next row)
here are two mac apps (skype and gabble) that do this:
https://dl.dropboxusercontent.com/u/2510380/skype.mov
and
https://dl.dropboxusercontent.com/u/2510380/gabble.mov
Assuming they are NOT using WebViews with just HTML inside, how do you get this control over the clipboard? i.e. in Skype you select the text and only the conversation is highlighted, not the timestamp of each message. Also the text copied to the clipboard is formatted very nicely. Can you point me in the right direction to reverse engineer skype?
Unfortunately there's no way to do this easily. This is because only ONE control can be the first responder at a time. This means that, though you can have selection in multiple text views, there are several problems:
Only one text view's text will actually be highlighted with the "live" highlight color; the others will have the gray highlight of non-focused controls.
Copy commands will only apply to the first responder text view.
Drag session starts will be initiated from the control the mouse was actually pointing at (irrespective of first responder) and will only drag that control's text.
In a view-based table view, the controls may not even "exist" for a row not currently displayed, so it'll never get the message unless you forcibly create each row, which could be costly for a large table.
Knowing all this, you might be able to "fake it" by having your controller be complicit in a text view and table view subclass's special handling of a select-all message when it's first responder. On receiving this message, the text view subclass can call super then notify the controller (to get its default behavior AND to let you know it happened), at which point the controller can turn around and send the command to all (existing) text views. Highlighting can be spoofed by overriding the text view's drawing and a drag initiation could defer to a delegate (the controller), which would handle writing ALL the strings from your model to the pasteboard (not even touching the text views in possibly-nonexistent row views). The table view subclass would simply pass the same select-all message to the controller without calling super (and even forcibly making sure nothing is selected before returning for good measure).
I hope this helps. If I've forgotten any of your requirements, let me know.
Try like this:-
First create button programatically then write this code after you create button and also write this code in your load method or awakefromnib method.
NSButton *Buttn=// alloc initwithframe;
[Buttn setKeyEquivalentModifierMask:
NSCommandKeyMask];
[Buttn setKeyEquivalent:#"A"];
[Buttn
setAction:#selector(yourmeth:)];
[Buttn setTarget:self];
// now when you press cmd a write
below code in action method
- (void)selectRowIndexes:(NSIndexSet
*)indexes byExtendingSelection:
(BOOL)extend

NSOutlineView select with the first click

I have an NSPanel with a NSOutlineView to display my data. I want to select a row with a first click inside the NSOutlineView even if the panel is not in focus (i.e. not the key window) I have a delegate that allows selection and I have overridden the NSOutlineView class to override the acceptsFirstMouse: method, but I could not get the first click selection.
I checked the mouseDown event and it fired without any problems (in my class that overrides) when the panel was and wasn't the key window. But when the panel is not key, NSOutlineViewDelegate method shouldSelectItem: is not called.
What am I missing/doing wrong?
You just need to subclass your NSTableOutlineView and override:
- (BOOL)needsPanelToBecomeKey
to return NO.
NSView class reference states:
Overridden by subclasses to determine if the receiver requires its
panel, which might otherwise avoid becoming key, to become the key
window so that it can handle keyboard input and navigation.
Discussion
Such a subclass should also override acceptsFirstResponder
to return YES.
This method is also used in keyboard navigation. It determines if a
mouse click should give focus to a view (make it first responder).
Some views will want to get keyboard focus when you click in them, for
example text fields. Other views should only get focus if you tab to
them, for example, buttons. You wouldn't want focus to shift from a
textfield that has editing in progress simply because you clicked on a
check box.
Source: Apple documentation

Setting tab order for NSTextFields of an NSView

I am trying to set the tab order for NSTextFields on a given NSView. As far as I understand it I have to connect the NSTextFields with InterfaceBuilder by setting the nextKeyView outlet to the next NSTextField. I did this - and there is no effect. When I press tab, after being in one of the NSTextFields it just loses focus and that is it. No matter how often I press tab none of the other NSTextFields regain focus.
I also read that I need to set the initialFirstResponder of the window. I don't think I can do this in this particular case, because my AppDelegate calls up a NSView which in turn acts as a Menu-Area with many different buttons, which each call up different NSViews that are loaded as subviews of a different view. Therefore the AppDelegate has no way of knowing which NSView is currently loaded.
Is there a way to set the tab-order of NSTextFields on an NSView-basis?
Thanks
You have to disable auto-recalculation of the key loop in the window. See -[NSWindow setAutorecalculatesKeyViewLoop:].
Edited to add: this can be set in Interface Builder, too.
This may or may not help for your situation, but the following worked for me when dealing with an NSView within another NSView. I had two vertical stacks of NSTextFields. Rather than traversing horizontally, I needed to tab down one stack and then the next... So I embedded each of the two vertical stacks in their own box (In IB, first select your items, Editor> Embed In> box). Works perfect.
If you don't want the look of a box, you can clear its title and set its border type to none: invisible box.

Cursor in NSTextView

I have a scroll view, and a number of custom views (same class) inside it. Each custom view contains an NSTextView. I want the cursor to stay as arrow all the time, because the text views are not editable. However, setting the cursor using attributed string only controls the areas where there are characters. So I tried to override the mouseMoved method of the scroll view. This does the trick but when I scroll the view by triggering the scrollWheel event, the cursor changed back to IBeam again.
Any idea how to solve this problem?
Thanks
Have you tried just calling
[textView setSelectable:NO];
to force the arrow cursor to be used (assuming the textView is also not editable via -setEditable:)?
Otherwise, you might look into the following: Managing Cursor-Update Events.