Respond to mouse events in text field in view-based table view
cocoa, mousedown, nstableview, nstextfield
Solution
I'll try to return the favor... Subclass `NSOutlineView` and override `-mouseDown:` like so:
- (void)mouseDown:(NSEvent *)theEvent {
[super mouseDown:theEvent];
// Only take effect for double clicks; remove to allow for single clicks
if (theEvent.clickCount < 2) {
return;
}
// Get the row on which the user clicked
NSPoint localPoint = [self convertPoint:theEvent.locationInWindow
fromView:nil];
NSInteger row = [self rowAtPoint:localPoint];
// If the user didn't click on a row, we're done
if (row < 0) {
return;
}
// Get the view clicked on
NSTableCellView *view = [self viewAtColumn:0 row:row makeIfNecessary:NO];
// If the field can be edited, pop the editor into edit mode
if (view.textField.isEditable) {
[[view window] makeFirstResponder:view.textField];
}
}
Problem
I have text fields inside a custom view inside an `NSOutlineView`. Editing one of these cells requires a single click, a pause, and another single click. The first single click selects the table view row, and the second single click draws the cursor in the field. Double-clicking the cell, which lets you edit in a cell-based table view, only selects the row. The behavior I want: one click to change the selection and edit. What do I need to override to obtain this behavior? I've read some other posts: - The `NSTextField` flyweight pattern wouldn't seem to apply to view-based table views, where the cell views are all instantiated from nibs. - I tried subclassing `NSTextField` like this solution describes, but my overridden `mouseDown` method is not called. Overridden `awakeFromNib` and `viewWillDraw` (mentioned in this post) are called. Of course `mouseDown` is called if I put the text field somewhere outside a table view. By comparison, a `NSSegmentedControl` in my cell view changes its value without first selecting the row. Here's the working solution adapted from the accepted response: In outline view subclass: ``` -(void)mouseDown:(NSEvent *)theEvent { [super mouseDown:theEvent]; // Forward the click to the row's cell view NSPoint selfPoint = [self convertPoint:theEvent.locationInWindow fromView:nil]; NSInteger row = [self rowAtPoint:selfPoint]; if (row>=0) [(CellViewSubclass *)[self viewAtColumn:0 row:row makeIfNecessary:NO] mouseDownForTextFields:theEvent]; } ``` In table cell view subclass: ``` // Respond to clicks within text fields only, because other clicks will be duplicates of events passed to mouseDown - (void)mouseDownForTextFields:(NSEvent *)theEvent { // If shift or command are being held, we're selecting rows, so ignore if ((NSCommandKeyMask | NSShiftKeyMask) & [theEvent modifierFlags]) return; NSPoint selfPoint = [self convertPoint:theEvent.locationInWindow fromView:nil]; for (NSView *subview in [self subviews]) if ([subview isKindOfClass:[NSTextField class]]) if (NSPointInRect(selfPoint, [subview frame])) [[self window] makeFirstResponder:subview]; } ```