Editable NSTextField in NSTableView - cocoa

I have a view based NSTableView with some labels in my customized and subclassed view. One of the label should be editable, so therefore I set this NSTextField to editable.
But now I have two problems, I can't solve:
1) If I move the mouse over the editable NSTextField, the cursor don't change to the IBeamCursor (the edit cursor).
2) I need to double click at the label, to be able to edit. I want to have a single click. I found some solutions for this problem here at stackoverflow, the best one is to override the acceptsFirstResponder to return always true, but then, clicking at the NSTextField selects the whole text instead of placing the cursor at the clicked position.

Sorry... this is a duplicate. I found this:
NSTableView - select row and respond to mouse events immediately
You have to subclass NSTableView. My swift code:
class TableViewEditing: NSTableView {
...
override func validateProposedFirstResponder(responder: NSResponder, forEvent event: NSEvent?) -> Bool {
return true
}
}
EDIT:
Just one disadvantage: Sometimes entering the edit mode, it seems that the text is just shortly selected and deselected. But you can see, that this is a cocoa problem, it's the same for example in Apple reminders app.

Related

Receive trigger when active NSView focus changes

I'm surprised that I haven't been able to find an answer for this from searching. So if there is a page describing how to do this, let me know, but I've been unable to find it.
I have 3 sibling NSOutlineViews all inside an NSSplitView I have added observers for NSOutlineViewSelectionDidChange which, when triggered update an NSTextView editor. I even test for negative row values to indicate that the user has unselected the row and clear the text.
The NSOutlineViews are connected to custom datasource objects and the NSSplitView is created by my custom NSWindowController in turn created by my custom NSDocument and custom NSDocumentController (I'm not using .nibs)
However I cannot seem to receive triggers for the NSOutlineViews changing their active status, it works if the user selects a different row in a different NSOutlineView, as the selection has changed but if they click the selected row of a different view, I don't receive any event or notification that anything has changed. Visually I can see the change as the row selection highlight colour changes from coloured to grey, in the outline view that has lost focus and the selection row colour changes from grey to coloured in the newly activated view.
I've tried to catch mouseDown events, tried becoming first responder, tried observing changes in the NSSplitView I've been right through the NSObject hierarchy from NSOutlineView to NSResponder looking for the appropriate notification or method. I found deprecated documentation regarding a focus change notification. I've tried combinations of nsview, nsnotification, nsoutlineview and various actions in google but can't find the 'this is how you do it'
EDIT:
this is the code I've added to my NSOutlineView subclass (along with prototypes in the headers) to become first responder, but it is never triggered.
- (BOOL)acceptsFirstResponder { return YES; }
- (BOOL)becomeFirstResponder {
NSLog(#"becomefirstResponder %#",self);
return YES;
}

Incorrect NSCursor originating from background NSView

I wanted to change the cursor in my cocoa application. I've followed the example in this answer, with success.
I have an NSView, let's call it NSViewA, behind another NSView, NSViewB. NSViewA contains a subclassed NSButton where the cursor has been changed. NSViewA and NSViewB have the same superview. So something like this:
- NSWindow
- NSViewA
- NSButtonSubclass
- NSViewB
My problem is that when NSViewB is shown, and the cursor is ontop of NSViewB, but in the same x y coordinates of the NSButton behind NSViewB, the cursor changes to that specified in the NSButton subclass.
How do I stop this behaviour?
The reason for this layout is that I'm creating a 'lightbox' control. If you think something along the lines of the NSWindow greying out, and a centred box appearing showing an error message. That sort of thing.
I previously had a problem where you could still click buttons, etc, behind NSViewB. This was solved by suppressing mouseDown: and mouseUp:. I've tried doing something similar with other mouse-related events, such as mouseEntered: and mouseExited: with no luck.
Could you make the addition of your custom cursor rectangle contingent on the enabled status of your button? In other words, your resetCursorRects would look like this:
// MyButton.swift
override func resetCursorRects() {
if enabled {
addCursorRect(bounds, cursor: NSCursor.pointingHandCursor())
}
}
Whenever viewB is about to be shown, disable the button, and call for the rects belonging to your button to be invalidated. If you're using Swift, you could do this second bit in a property observer attached to the enabled property itself:
// MyButton.swift
override var enabled: Bool {
didSet {
window!.invalidateCursorRectsForView(self)
}
}
If you don't want your button to take on a disabled look, make the addCursorRect call contingent on some other flag.

Disabling the NSTableView row focus ring

How do you disable the focus ring around an NSTableView row when the user right-clicks on it? I can't get it to disappear. Setting focus ring of an individual NSTableViewCell in the table to None has no effect.
Subclass the table view and implement the following method:
- (void)drawContextMenuHighlightForRow:(NSInteger)row {
// do nothing
}
Note:
The blue outline is not the focus ring.
This is an undocumented private method Apple uses to draw the outline. Providing an empty implementation will prevent anything from being drawn, but I am not 100% sure that whether it can pass the review.
New:
Here is how I did it.
You can handle the menu manually. Subclass NSTableRowView or NSTableCellView, then use rightMouseDown: and mouseDown: (check for control key) and then notify your tableViewController (notification or delegate) of the click. Don't forget to pass the event as well, then you can display the menu with the event on the table view without the focus ring.
The above answer is easier, but it may not pass the review, as the author mentioned.
Plus you can show individual menu items for each row (if you have different sorts of views)
Old:
I think the focus ring is defined by NSTableRowView, not NSTableCellView, because it is responsible for the complete row. Try to change the focus ring there. You can subclass NSTableRowView and add it to the tableView via IB or NSTableViewDelegate's method:
- (NSTableRowView *)tableView:(NSTableView *)tableView rowViewForRow:(NSInteger)row
If your goal is just displaying a contextual menu, you also can try this.
Wrap the NSOutlineView within another NSView.
Override menuForEvent method on the wrapper view.
Do not set menu on outline-view.
Now the wrapper view shows the menu instead of your outline-view, so it won't show the focus ring. See How do you add context senstive menu to NSOutlineView (ie right click menu) for how to find a node at the menu event.

Which NSTableView receives NSMenuItem?

I have a custom NSViewController with two NSTableViews side-by-side, something like a split-view setup where the selection on the left tableView changes the right tableView's list. I'm not sure how to handle NSMenuItem events in this case. For e.g. if I press the Delete button, how do I distinguish between whether it's the left tableview or the right tableview that was highlighted when the Delete button was pressed? All I get is the delete: selector called with the NSMenuItem as the sender.
First, some background:
In Cocoa terminology, the "active" view or control is known as the "first responder." For instance, when you're entering text into a text field, the text field is considered the "first responder" because it's the object that's first to respond to keyboard input. An NSTableView can also receive first responder status (you can control the selected row by using the arrow keys).
You can ask the window for it's first responder like so:
// it's not necessarily a sure thing that the first responder is a TableView.
id myFirstResponder = [_parentWindow firstResponder];
if (myFirstResponder == _leftTableView) {
// left tableview is selected
} else if (myFirstResponder == _rightTableView) {
// right tableview is selected
}

Maintaining focus or first responder state of NSOutlineView

I have a subclass of an NSOutline. When it loses focus, the disclosure triangle of a selected row changes from its inverted template form (white) back to black. I don't want this to happen: I want the appearance of a selected row to stay the same, as in XCode or Mail.
I've tried intercepting firstResponder-based messages in the NSOutlineView subclass, as well in my custom rowViews, but to no avail. Any ideas?
In my subclass of NSTableRowView, doing the following works:
- (BOOL)isEmphasized {
return YES;
}

Resources