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]; } ```

Original source

Related problems