Question

dans ma demande de cacao, je besoin d'un NSCell personnalisé pour un NSTableView. Cette sous-classe NSCell contient une coutume NSButtonCell pour la manipulation d'un clic (et deux ou trois NSTextFieldCells pour le contenu textuel). Vous trouverez un exemple simplifié de mon code ci-dessous.

@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: le NSButtonCell)? Intérieur de ce NSCell fonctionne correctement « travail » signifie: déclenchement le message d'action assigné et lui montrer l'autre image lorsque vous cliquez 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 postes que j'ai trouvé sur le net m'a fait de mettre en œuvre

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

Est-ce la bonne façon de le faire ??? Mettre en œuvre trackMouse: dans mon contenant NSCell Et puis transmettre l'événement au NSButtonCell? Je me serais attendu à la NSButtonCell lui-même pour savoir quoi faire quand il clique dessus (et je vis les trackMouse: méthodes plus cunjunction avec le suivi des mouvements de la souris vraiment - non pas comme une roue de formation pour les « standard » cliquez sur le comportement). Mais il semble que cela ne fait pas lorsqu'il est inclus dans une cellule elle-même ... Il semble que je ne l'ai pas saisi la grande image sur les cellules personnalisées, encore; -)

Je serais heureux si quelqu'un pourrait répondre à cette (ou me pointer vers un certain tutoriel ou similaires) de sa propre expérience - et me dire si je suis sur la bonne voie <. / p>

Merci d'avance, Tobi

Était-ce utile?

La solution

Les exigences minimales sont les suivantes:

  • Après gauche de la souris sur le bouton, il doit apparaître pressé chaque fois que la souris est dessus.
  • Si la souris libère alors sur le bouton, votre cellule doit envoyer le message d'action approprié.

Pour le look bouton pressé, vous devez mettre à jour la propriété highlighted de pile bouton selon le cas. Modification de l'état seul ne sera pas accomplir cela, mais ce que vous voulez pour le bouton pour être mis en évidence si, et seulement si, ses états est NSOnState.

Pour envoyer le message d'action, vous devez être au courant lorsque la souris est relâché, et ensuite utiliser -[NSApplication sendAction:to:from:] pour envoyer le message.

Pour être en mesure d'envoyer ces messages, vous devrez connecter dans les méthodes de suivi des événements fournis par NSCell. Notez que toutes ces méthodes de suivi, à l'exception de la dernière méthode, -stopTracking:..., renvoient un booléen pour répondre à la question: « Voulez-vous continuer à recevoir des messages de suivi? »

La torsion finale est que, pour être envoyé des messages de suivi du tout, vous devez implémenter -hitTestForEvent:inRect:ofView: et retourner un bitmask approprié des valeurs de NSCellHit.... Plus précisément, si la valeur retournée n'a pas la valeur de NSCellHitTrackableArea en elle, vous ne recevez des messages de suivi!

Alors, à 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];
}

Autres conseils

Le point de sous-classes NSCell 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 classes NSView. Cette association permet à chacun de fournir une plus grande spécialisation et à la variabilité sans surcharger l'autre. Regardez le grand nombre d'instances de NSButton on peut créer à Cocoa. Imaginez le nombre de sous-classes de NSButton qui existerait si cette scission fonctionnalité étaient absents!

En utilisant un langage de modèle de conception pour décrire les rôles:. Un agit NSControl comme une façade, cachant les détails de sa composition de ses clients et des événements de passage et le rendu des messages à son instance de NSCell qui agit en tant que délégué

Parce que votre sous-classe de NSCell comprend d'autres instances de sous-classe de NSCell 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 cellules pour recevoir des messages d'événement de la chaîne de répondeur d'événements (de la hiérarchie de la vue), votre instance de cellule doit transmettre ces événements pertinents. Vous recréez le travail de la hiérarchie NSView.

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

Ainsi, dans la conception de votre interface, vous devez examiner si le NSButtonCell (et NSTextFieldCells) sont mieux en NSControls 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 que de réinventer (et continuer à maintenir ultérieurement) inutilement.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top