This is a very simple issue that I know I've encountered before, but for the life of me, I can neither remember the solution nor find a page describing it:
I have an NSTableView bound to a content array. When I click on rows with the cursor, the table sends an action that I can handle. When I navigate within the table with the up/down arrow keys, it doesn't send the action.
I've verified that "Column Select" is disabled (this is the equivalent of the old "Full Row Select" option, right?) and have tried setting the NSTableView to Continuous... no luck.
Help pls? Thanks...
I can't find documentation that explains exactly when an NSTableView calls the action that belongs to its target, but from what you say it seems to be specifically linked to a mouse-down event in the table. By thinking of it as a 'selected-row' action you may be generalizing more than is justified. Instead, perhaps you should think of it as a 'mouse-down-in-table' action in which case it's behaving exactly as it should - the action fires only when you mouse down. Such an event will often result in a selection change, but not always - and as you're finding out the selection can change independently of mouse-down events.
Instead of using the table's action to catch selection changes, it's much more common to use the various NSTableView delegate methods. Is there a reason you aren't using these?
These provide more detailed information about how the selection is changing, and they'll catch changes triggered by any kind of event (mouse, keyboard, or programmatic)
Related
I'm experience some event handling issues when attempting to use arrow keys without modifiers as key equivalents for menu items in the main menu bar. The problem I'm experiencing is that the main menu bar is handling the key down event as a key equivalent event before a tableView is able to. When the tableView is the first responder, the up/down arrow keys do not change the tableView's selection but rather trigger the key equivalent in the main menu bar.
The reason for this is that the incoming keyDown event for an arrow key is first passed to performKeyEquivalent on the target window, which in turns passes that event down the chain. NSTableView does not respond to this so the event bubbles back up to the application where it next dispatches it to the main menu, via performKeyEquivalent, and thus the event is consumed.
If the main menu does not have a key equivalent, then the event goes back to the window and down the chain via keyDown, which the tableView does respond to and correctly handles.
This is documented by Apple (more or less) in their Event Handling Guide.
Is there a proper way to handle key equivalents like arrow keys without modifiers such that they both appear in the menu item when it's being displayed, but are also properly consumed by any subviews that might handle them?
I've tried various tricks, but each one has numerous pros-and-cons:
NSMenu delegate
One can implement menuHasKeyEquivalent but it appears that you have to implement that for the entire main menu. While you could easily filter out the arrow keys, you also have to validate every other key equivalent, which isn't very practical.
Subclass NSApplication
You can override sendEvent: in NSApplication but the logic for keeping track of where you are in the event handling chain gets a bit hairy.
NSEvent tap
Similar to subclassing NSApplication. Things are a bit cleaner here because I can cheat and have the event tap a bit closer to the tableView, but you're still left with a lot of logic to determine when the tap should consume the event and "force-feed" it to the tableView versus when you should let the event be handled normally.
I'm curious if anyone has any suggestions on how best to implement an arrow key as a key equivalent when no modifiers are present and a tableView might be present.
(macOS 10.11+)
OK, here's the situation. I have:
A Webview
Lots of NSTextFields
Other unrelated controls
Normally, the Edit > Undo menu item links to First Responder's undo: action. And everything works fine + you can even "undo" while typing in an NSTextField.
Now, what if I want to handle this "undo" action, in a different way, only for my WebView.
I've been thinking of two approaches:
Link the "Undo" item to a custom action and check who is the First Responder. If it's the Webview, then do what needs to be done. Else, "pass" the event to the control. (However, when attempting a [FIRST_RESPONDER performSelector:#selector(undo:)], first it doesn't seem to recognize the selector and last but not least nothing happens.)
Link the "Undo" to the first responder's undo:(as usual), subclass the Webview and add a custom - (void)undo:(id)sender action. In that case though, when the webview is active, the "Undo" item is grayed-out, so I can't do anything whatsoever, not even check whether the custom method would be called.
Suggestions? How would you go about that?
What am I missing?
I think this is the answer: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/WebKit/Protocols/WebEditingDelegate_Protocol/
Also reference this: Removing undo actions for a WebView's NSUndoManager
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
I have a program with a NSOutlineView (that supports single selection only) from which I'd like to be able to drag elements. These elements should either be received as text or files: for instance, dropping the item on a TextEdit window should put text, but dropping the item on the Finder should create a file. I don't want anything to be dropped over my outline view, even it it comes from itself. This seems easy enough, but for some reason, I can't get it to work.
I checked the NSOutlineView drag and drop example from Apple, and I came to implement the following methods (plus a few definitely unrelated ones):
-(BOOL)outlineView:shouldSelectItem: // I don't expect to drag unselectable items
-(NSArray*)outlineView:namesOfPromisedFilesDroppedAtDestination:forDraggedItems:
-(BOOL)outlineView:writeItems:toPasteboard:
However, when I try to drag an item from my outline view, nothing happens. Instead, it just changes the selection following the cursor.
I've put breakpoints in the two last methods, and they never get called, so their implementation is not the immediate issue.
I must be missing something really obvious here.
Also, this is not (yet) a problem, but how am I supposed to provide contents to my promised files?
I was being stupid and I implemented the methods in the delegate instead of the data source (the two are distinct in my app). Problem solved!
Are you using a custom table view cell? The result of NSCell's hitTestForEvent:inRect:ofView: determines whether a dragging operation can be initiated. It also determines whether your outlineView:writeItems:toPasteboard: should be called.
This method should return NSCellHitContentArea to initiate a drag, or NSCellHitTrackableArea to extend or change the selection.
A standard text cell returns NSCellHitContentArea when you click on the actual text of the cell, and NSCellHitTrackableArea when you click outside of the text. This produces the drag behavior you see in Finder's table view.
You can override this method and always return NSCellHitContentArea if you want all areas of the cell to initiate a drag operation.
See Hit Testing for more information.
I'm having trouble creating a mouseover function with an NSTableView. The idea is that (if the feature is selected in prefs) placing the mouse pointer above a particular row in an NSTabelView will display a small popup window with additional information regarding the entry in that particular row. The effect should not be immediate (as e.g. highliting a button when rolling over it), but come with a delay of a few seconds.
So far, I have implemented this functionality using the tooltip delegate method, but this does not allow for customization of the window and does not work well (layout wise) if more than 1-3 rows of info need to be displayed.
In Hillegass' book it is suggested that one uses the mouseEntered/mouseExited methods for rollovers, but as far as I can tell this works with pre-defined areas of a window and not rows in a table view.
Does anyone have any suggestions?
Gregor Tomasevic,
Sweden
You're on the right track with -mouseEntered: and -mouseExited:.
Look into NSView's -addTrackingRect:owner:userData:assumeInside: and -removeTrackingRect: methods. You can either set up your tableView to create trackingRects for every row that's in there whenever the contents of the tableView change, or alternatively, set up/update one tracking area on the entire tableView whenever -tile or another layout related method is called.