objective-ccocoamouseeventnscellnscontrol

What method should I call on my NSCell


I am writing a custom NSControl with custom NSCells. It is a control, so it has to respond to the mouse. I created an NSTrackingArea over my control, implemented -mouseEntered:, -mouseExited: and -mouseMoved:. (And I will have to implement -mouseUp/Down:, but I have no idea what to do in there, so for now I haven't overridden those methods yet.) In these methods I successfully determine on which cell the mouse currently is. Now I have two questions:

So, basically: When should I call what method on my NSCell to let it respond to mouse events?

EDIT:
Rereading the docs, I think I should call NSCell's -trackMouse:inRect:ofView:untilMouseUp: and override -startTrackingAt:inView:, -continueTracking:at:inView: and -stopTracking:at:inView:mouseIsUp:. Again two questions: 1) the docs give the impression these are only called when the mouse is down. Is that correct? Then what should I do instead? 2) Where/when should I call NSCell's -trackMouse:inRect:ofView:untilMouseUp:?


Solution

  • I ended up implementing my own mouse tracking mechanism:

    // MyControl.m:
    
    - (void)mouseDown:(NSEvent *)theEvent {
        int currentCellIndex = [self indexOfCellAtPoint:[self convertPoint:[theEvent locationInWindow] fromView:nil]];
        if (currentCellIndex < [cells count]) {
            MKListCell *cell = [cells objectAtIndex:currentCellIndex];
            currentCell = cell;
            [currentCell mouseDown:theEvent];
        }
    }
    
    - (void)mouseUp:(NSEvent *)theEvent {
        int currentCellIndex = [self indexOfCellAtPoint:[self convertPoint:[theEvent locationInWindow] fromView:nil]];
        if (currentCellIndex < [cells count]) {
            MKListCell *cell = [cells objectAtIndex:currentCellIndex];
            [cell mouseUp:theEvent];
        }
    }
    
    - (void)mouseEntered:(NSEvent *)theEvent {
        int currentCellIndex = [self indexOfCellAtPoint:[self convertPoint:[theEvent locationInWindow] fromView:nil]];
        if (currentCellIndex < [cells count]) {
            MKListCell *cell = [cells objectAtIndex:currentCellIndex];
            currentCell = cell;
            [currentCell mouseEntered:theEvent];
        }
    }
    
    - (void)mouseExited:(NSEvent *)theEvent {
        int currentCellIndex = [self indexOfCellAtPoint:[self convertPoint:[theEvent locationInWindow] fromView:nil]];
        if (currentCellIndex < [cells count]) {
            MKListCell *cell = [cells objectAtIndex:currentCellIndex];
            [cell mouseExited:theEvent];
            currentCell = nil;
        }
    }
    
    - (void)mouseMoved:(NSEvent *)theEvent {
        int currentCellIndex = [self indexOfCellAtPoint:[self convertPoint:[theEvent locationInWindow] fromView:nil]];
        MKListCell *cell;
        if (currentCellIndex < [cells count]) {
            cell = [cells objectAtIndex:currentCellIndex];
        }
        if (currentCell != cell) {
            [currentCell mouseExited:theEvent];
            [cell mouseEntered:theEvent];
            currentCell = cell;
        }
        else {
            [currentCell mouseMoved:theEvent];
        }
    }
    
    - (void)mouseDragged:(NSEvent *)theEvent {
        int currentCellIndex = [self indexOfCellAtPoint:[self convertPoint:[theEvent locationInWindow] fromView:nil]];
        MKListCell *cell = nil;
        if (currentCellIndex < [cells count]) {
            cell = [cells objectAtIndex:currentCellIndex];
        }
        if (currentCell != cell) {
            [currentCell mouseExited:theEvent];
            [cell mouseEntered:theEvent];
            currentCell = cell;
        }
        else {
            [currentCell mouseMoved:theEvent];
        }   
    }
    
    - (int)indexOfCellAtPoint:(NSPoint)p {
        int cellIndex = (self.bounds.size.height - p.y) / cellHeight;
        return cellIndex;
    }
    

    And of course, in MyCell.h:

    - (void)mouseDown:(NSEvent *)event;
    - (void)mouseUp:(NSEvent *)event;
    - (void)mouseMoved:(NSEvent *)event;
    - (void)mouseEntered:(NSEvent *)event;
    - (void)mouseExited:(NSEvent *)event;
    

    With an empty implementation for those methods (so the compiler doesn't complain and I can leave the implementation of the mouse handling methods to subclasses).