Répondre aux événements de la souris dans le champ Texte en vue-vue de table basée sur la vue

j'ai des champs de texte dans une vue personnalisée dans un NSOutlineView . Éditer une de ces cellules nécessite un clic simple, une pause, et un autre clic simple. Le premier simple clic sélectionne la ligne de vue de la table, et le second simple clic dessine le curseur dans le champ. Double-cliquer la cellule, qui vous permet d'éditer dans une vue de table basée sur la cellule, ne sélectionne que la ligne.

le comportement que je veux: un clic pour modifier la sélection et éditer.

De quoi ai-je besoin pour obtenir ce comportement?

j'ai lu d'autres Billets:

  • Le NSTextField poids mouche motif ne semble pas s'appliquer selon la vue de la table de vues, où la cellule points de vue sont tous instanciés à partir de plumes.
  • j'ai essayé de sous-classer NSTextField comme cette solution décrit , mais ma méthode dépassée mouseDown n'est pas appelé. Remplacé awakeFromNib et viewWillDraw (mentionné dans ce post ) sont appelé. Bien sûr mouseDown est appelé si je mets le champ de texte quelque part en dehors d'une vue de table.

par comparaison, un NSSegmentedControl dans ma vue de cellule change sa valeur sans d'abord sélectionner la ligne.


Voici la solution de travail adapté de la réponse acceptée:

en vue de grandes lignes sous-classe:

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

dans le tableau vue de cellules sous-classe:

// 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];
}
26
demandé sur Community 2011-08-18 04:56:45

5 réponses

je vais essayer de rendre la faveur ... Sous-classe NSOutlineView et remplacer -mouseDown: comme suit:

- (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];
    }
}
14
répondu Dov 2017-05-23 12:34:30

Avait le même problème. Après beaucoup de lutte, il a fonctionné comme par magie lorsque j'ai sélectionné None contre la valeur par défaut Regular (l'autre option est Source List ) pour l'option Highlight de la vue de table en IB!

une autre option est la solution à https://stackoverflow.com/a/13579469/804616 , qui semble plus spécifique mais un peu hacky par rapport à cela.

14
répondu trss 2017-08-08 12:56:01

vous voulez vraiment outrepasser validateProposedFirstResponder et permettre à un premier intervenant particulier d'être fait (ou non) en fonction de votre logique. L'implémentation dans NSTableView est (en quelque sorte) comme ceci (je le ré-écris pour être un pseudo code):

- (BOOL)validateProposedFirstResponder:(NSResponder *)responder forEvent:(NSEvent *)event {

// We want to not do anything for the following conditions:
// 1. We aren't view based (sometimes people have subviews in tables when they aren't view based)
// 2. The responder to valididate is ourselves (we send this up the chain, in case we are in another tableview)
// 3. We don't have a selection highlight style; in that case, we just let things go through, since the user can't appear to select anything anyways.
if (!isViewBased || responder == self || [self selectionHighlightStyle] == NSTableViewSelectionHighlightStyleNone) {
    return [super validateProposedFirstResponder:responder forEvent:event];
}

if (![responder isKindOfClass:[NSControl class]]) {
    // Let any non-control become first responder whenever it wants
    result = YES;
    // Exclude NSTableCellView. 
    if ([responder isKindOfClass:[NSTableCellView class]]) {
        result = NO;
    }

} else if ([responder isKindOfClass:[NSButton class]]) {
    // Let all buttons go through; this would be caught later on in our hit testing, but we also do it here to make it cleaner and easier to read what we want. We want buttons to track at anytime without any restrictions. They are always valid to become the first responder. Text editing isn't.
    result = YES;
} else if (event == nil) {
    // If we don't have any event, then we will consider it valid only if it is already the first responder
    NSResponder *currentResponder = self.window.firstResponder;
    if (currentResponder != nil && [currentResponder isKindOfClass:[NSView class]] && [(NSView *)currentResponder isDescendantOf:(NSView *)responder]) {
        result = YES;
    }
} else {
    if ([event type] == NSEventTypeLeftMouseDown || [event type] == NSEventTypeRightMouseDown) {
        // If it was a double click, and we have a double action, then send that to the table
        if ([self doubleAction] != NULL && [event clickCount] > 1) {
           [cancel the first responder delay];
        }
   ...
          The code here checks to see if the text field 
        cell had text hit. If it did, it attempts to edit it on a delay. 
        Editing is simply making that NSTextField the first responder.
        ...

     }
4
répondu corbin dunn 2017-05-08 16:18:34

j'ai écrit ce qui suit pour supporter le cas pour quand vous avez un NSTableViewCell plus complexe avec plusieurs champs de texte ou où le champ de texte n'occupe pas toute la cellule. Il y a un truc ici pour retourner les valeurs y parce que quand vous passez entre la NSOutlineView ou NSTableView et C'est NSTableCellViews le système de coordonnées est retourné.

- (void)mouseDown:(NSEvent *)theEvent
{
    [super mouseDown: theEvent];

    NSPoint thePoint = [self.window.contentView convertPoint: theEvent.locationInWindow
                                                      toView: self];
    NSInteger row = [self rowAtPoint: thePoint];
    if (row != -1) {
        NSView *view = [self viewAtColumn: 0
                                      row: row
                          makeIfNecessary: NO];

        thePoint = [view convertPoint: thePoint
                             fromView: self];
        if ([view isFlipped] != [self isFlipped])
            thePoint.y = RectGetHeight(view.bounds) - thePoint.y;

        view = [view hitTest: thePoint];
        if ([view isKindOfClass: [NSTextField class]]) {
            NSTextField *textField = (NSTextField *)view;
            if (textField.isEnabled && textField.window.firstResponder != textField)

                dispatch_async(dispatch_get_main_queue(), ^{
                    [textField selectText: nil];
                });
        }
    }
}
3
répondu greg 2015-01-01 01:54:35

je veux juste souligner que si tout ce que vous voulez est l'édition seulement (c.-à-d. dans une table sans sélection), la suppression de -hitTest: semble être plus simple et un plus de Cacao-Comme:

- (NSView *)hitTest:(NSPoint)aPoint
{
    NSInteger column = [self columnAtPoint: aPoint];
    NSInteger row = [self rowAtPoint: aPoint];

    // Give cell view a chance to override table hit testing
    if (row != -1 && column != -1) {
        NSView *cell = [self viewAtColumn:column row:row makeIfNecessary:NO];

        // Use cell frame, since convertPoint: doesn't always seem to work.
        NSRect frame = [self frameOfCellAtColumn:column row:row];
        NSView *hit = [cell hitTest: NSMakePoint(aPoint.x + frame.origin.x, aPoint.y + frame.origin.y)];

        if (hit)
            return hit;
    }

    // Default implementation
    return [super hitTest: aPoint];
}
1
répondu Max Seelemann 2012-11-17 13:26:30