2010-02-17 3 views
9

Dans mon application de cacao, j'ai besoin d'un NSCell personnalisé pour un NSTableView. Cette sous-classe NSCell contient un NSButtonCell personnalisé pour gérer un clic (et deux ou trois NSTextFieldCells pour le contenu textuel). Vous trouverez un exemple simplifié de mon code ci-dessous.NSButtonCell à l'intérieur personnalisé NSCell

@implementation TheCustomCell 

- (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView { 
    // various NSTextFieldCells 
    NSTextFieldCell *titleCell = [[NSTextFieldCell alloc] init]; 
    .... 
    // my custom NSButtonCell 
    MyButtonCell *warningCell = [[MyButtonCell alloc] init]; 
    [warningCell setTarget:self]; 
    [warningCell setAction:@selector(testButton:)]; 
    [warningCell drawWithFrame:buttonRect inView:controlView]; 
} 

Le problème que je suis coincé avec est: quelle est la meilleure/bonne façon d'obtenir ce bouton (plus précisément: la NSButtonCell) à l'intérieur de cette NSCell fonctionne correctement? "travail" signifie: déclencher le message d'action attribué et afficher l'autre image lorsque l'utilisateur clique dessus. Hors de la boîte, le bouton ne fait rien quand on clique dessus.

Informations/lectures sur ce sujet est difficile à trouver. Les seuls messages que j'ai trouvés sur le net m'ont orienté vers la mise en œuvre

- (BOOL)trackMouse:(NSEvent *)theEvent inRect:(NSRect)cellFrame ofView:(NSView *)controlView untilMouseUp:(BOOL)untilMouseUp; 

Est-ce la bonne façon de le faire? Implémenter trackMouse: dans mon NSCell contenant? Et puis transférez l'événement à NSButtonCell? Je m'attendais à ce que NSButtonCell lui-même sache quoi faire quand on clique dessus (et j'ai vu le trackMouse: les méthodes sont plus en cunjunction avec les mouvements de souris - pas comme une roue d'entraînement pour un comportement de clic standard). Mais il semble que ce n'est pas le cas lorsqu'il est inclus dans une cellule elle-même ... Il semble que je n'ai pas saisi la grande image sur les cellules personnalisées, pourtant ;-)

Je serais heureux si quelqu'un pouvait répondez à cette question (ou indiquez-moi un tutoriel ou autre) de sa propre expérience - et dites-moi si je suis sur la bonne voie.

Merci à l'avance, Tobi

Répondre

8

Les exigences minimales sont les suivantes:

  • Après gauche de la souris sur le bouton, il doit apparaître chaque fois que la souris enfoncé est dessus.
  • Si la souris relâche ensuite le bouton, votre cellule doit envoyer le message d'action approprié.

Pour que le bouton semble enfoncé, vous devez mettre à jour la propriété highlighted de la cellule bouton, selon le cas. Changer l'état seul ne le fera pas, mais ce que vous voulez, c'est que le bouton soit surligné si, et seulement si, ses états sont NSOnState.

Pour envoyer le message d'action, vous devez savoir quand la souris est relâchée, puis utiliser -[NSApplication sendAction:to:from:] pour envoyer le message. Afin d'être en mesure d'envoyer ces messages, vous devrez vous connecter aux méthodes de suivi d'événement fournies par NSCell

Notez que toutes ces méthodes de suivi, à l'exception de la méthode finale -stopTracking:..., renvoient une valeur booléenne pour répondre à la question "Voulez-vous continuer à recevoir des messages de suivi?" La dernière inflexion est que, pour recevoir des messages de suivi, vous devez implémenter -hitTestForEvent:inRect:ofView: et renvoyer un masque de bit approprié de NSCellHit.... Plus précisément, si la valeur retournée n'a pas la valeur NSCellHitTrackableArea, vous n'aurez aucun message de suivi!

Ainsi, à un niveau élevé, votre implémentation ressemblera à quelque chose comme:

- (NSUInteger)hitTestForEvent:(NSEvent *)event 
         inRect:(NSRect)cellFrame 
         ofView:(NSView *)controlView { 
    NSUInteger hitType = [super hitTestForEvent:event inRect:cellFrame ofView:controlView]; 

    NSPoint location = [event locationInWindow]; 
    location = [controlView convertPointFromBase:location]; 
    // get the button cell's |buttonRect|, then 
    if (NSMouseInRect(location, buttonRect, [controlView isFlipped])) { 
     // We are only sent tracking messages for trackable areas. 
     hitType |= NSCellHitTrackableArea; 
    } 
    return hitType; 
} 

+ (BOOL)prefersTrackingUntilMouseUp { 
    // you want a single, long tracking "session" from mouse down till up 
    return YES; 
} 

- (BOOL)startTrackingAt:(NSPoint)startPoint inView:(NSView *)controlView { 
    // use NSMouseInRect and [controlView isFlipped] to test whether |startPoint| is on the button 
    // if so, highlight the button 
    return YES; // keep tracking 
} 

- (BOOL)continueTracking:(NSPoint)lastPoint at:(NSPoint)currentPoint inView:(NSView *)controlView { 
    // if |currentPoint| is in the button, highlight it 
    // otherwise, unhighlight it 
    return YES; // keep on tracking 
} 

- (void)stopTracking:(NSPoint)lastPoint at:(NSPoint)stopPoint inView:(NSView *)controlView mouseIsUp:(BOOL)flag { 
    // if |flag| and mouse in button's rect, then 
    [[NSApplication sharedApplication] sendAction:self.action to:self.target from:controlView]; 
    // and, finally, 
    [buttonCell setHighlighted:NO]; 
} 
+0

Où est-ce que vous dites à la table de dire à sa source de données que le bouton a été coché? – Richard

+0

@Jeremy W. Sherman: C'est probablement une question stupide, mais comment obtenez-vous "bouton de la cellule bouton |"? J'ai essayé différentes choses comme [frame frame], mais ça ne semble pas fonctionner ... – houbysoft

5

Le point de NSCell sous-classes est de séparer la responsabilité pour le rendu et la manipulation des éléments de l'interface utilisateur commune (les contrôles) de la visuo et la hiérarchie des événements responsabilités des NSView classes. Cet appariement permet à chacun d'offrir une plus grande spécialisation et variabilité sans encombrer l'autre. Regardez le grand nombre de NSButton instances que l'on peut créer dans Cocoa. Imaginez le nombre de sous-classes NSButton qui existeraient si cette fonctionnalité était absente! Utilisation du langage de modèle de conception pour décrire les rôles: un NSControl agit comme une façade, cachant les détails de sa composition de ses clients et passant des événements et rendant des messages à son instance NSCell qui agit en tant que délégué.

Parce que votre NSCell sous-classe comprend d'autres NSCell instances de sous-classe dans sa composition, ils ne reçoivent plus directement ces messages d'événement de l'instance NSControl qui est dans la hiérarchie de la vue. Ainsi, pour que ces instances de cellule reçoivent des messages d'événement de la chaîne de répondeurs d'événements (de la hiérarchie de vue), votre instance de cellule doit transmettre ces événements pertinents. Vous recréer le travail de la hiérarchie NSView.

Ce n'est pas nécessairement une mauvaise chose. En répliquant le comportement NSControl (et sa superclasse NSView) mais dans un formulaire NSCell, vous pouvez filtrer les événements transmis à vos sous-cellules par emplacement, type d'événement ou autres critères. L'inconvénient est la réplication du travail de NSView/NSControl dans la construction du mécanisme de gestion de filtrage &.

Ainsi, dans la conception de votre interface, vous devez considérer si le NSButtonCell (et NSTextFieldCell s) sont mieux lotis dans NSControl s dans la hiérarchie de la vue normale, ou en tant que sous-cellules dans votre sous-classe NSCell. Il est préférable de tirer parti de la fonctionnalité qui existe déjà pour vous dans une base de code plutôt que de la réinventer (et de la maintenir plus tard) inutilement.

+1

Merci, Huperniketes - une bonne prise sur le sujet! – Tobidobi

+0

@Huperniketes donc si j'allais implémenter une cellule personnalisée pour une table (c'est-à-dire pré Lion avec la nouvelle capacité de cellule NSView de table), je suis obligé d'imiter fondamentalement le comportement de chaque contrôle que j'intègre et j'utilise simplement la cellule pour la partie dessin vraiment. Est-ce exact? – David

+1

@David, je ne suis pas clair sur votre question. Si vous demandez à propos de l'incorporation d'une sous-classe NSView dans une cellule personnalisée dans une vue de table, votre cellule doit se conformer au protocole NSControl, soit en gérant les messages de la sous-classe view elle-même, soit en les laissant écrasé. Vous ne devriez pas avoir besoin d'incorporer une sous-classe NSControl dans une cellule personnalisée car vous pouvez généralement utiliser la cellule du contrôle elle-même. En ce qui concerne le comportement de la cellule, vous pouvez utiliser la cellule pour dessiner des parties, trouver des parties cliquées, etc. – Huperniketes

Questions connexes