Domanda

nella mia applicazione per il cacao, ho bisogno di un NSCell personalizzato per un NSTableView.Questo La sottoclasse NSCell contiene un NSButtonCell personalizzato per gestire un clic (e due o tre NSTextFieldCells per i contenuti testuali).Di seguito troverai un esempio semplificato del mio codice.

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

Il problema con cui sono bloccato è: qual è il modo migliore/giusto per ottenere quel pulsante (più precisamente:il NSButtonCell) all'interno di questo NSCell per funzionare correttamente? "lavoro" significa:attivare il messaggio di azione assegnato e mostrare l'immagine alternativa quando si fa clic.Per impostazione predefinita, il pulsante non fa nulla quando viene cliccato.

Informazioni/letture su questo argomento sono difficili da trovare.Gli unici post che ho trovato in rete mi hanno indirizzato all'implementazione

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

È questo il modo corretto per farlo??? Implementa trackMouse:nel mio NSCell contenente? E poi inoltrare l'evento a NSButtonCell?Mi sarei aspettato che lo stesso NSButtonCell sapesse cosa fare quando viene cliccato (e ho visto il trackMouse:metodi più in combinazione con il monitoraggio reale dei movimenti del mouse - non come una ruota di allenamento per il comportamento dei clic "standard").Ma sembra che non lo faccia quando è incluso in una cella stessa...Sembra che non abbia ancora colto il quadro generale delle celle personalizzate ;-)

Sarei felice se qualcuno potesse rispondere a questa domanda (o indicarmi qualche tutorial o simili) in base alla propria esperienza - e dirmelo se sono sulla strada giusta.

Grazie in anticipo, Tobi.

È stato utile?

Soluzione

I requisiti minimi sono:

  • Dopo aver premuto il pulsante sinistro del mouse sul pulsante, questo deve apparire premuto ogni volta che il mouse si trova sopra di esso.
  • Se il mouse viene rilasciato sul pulsante, il tuo cellulare deve inviare il messaggio di azione appropriato.

Per far sì che il pulsante sembri premuto, è necessario aggiornare la cella del pulsante highlighted proprietà a seconda dei casi.Cambiare lo stato da solo non porterà a questo risultato, ma quello che vuoi è che il pulsante venga evidenziato se, e solo se, il suo stato è NSOnState.

Per inviare il messaggio di azione, è necessario essere consapevoli di quando il mouse viene rilasciato e quindi utilizzarlo -[NSApplication sendAction:to:from:] per inviare il messaggio.

Per poter inviare questi messaggi, dovrai agganciarti ai metodi di tracciamento degli eventi forniti da NSCell.Tieni presente che tutti questi metodi di tracciamento, tranne quello finale, -stopTracking:... metodo, restituisce un valore booleano per rispondere alla domanda "Vuoi continuare a ricevere i messaggi di tracciamento?"

La svolta finale è che, per poter ricevere qualsiasi messaggio di tracciamento, è necessario implementare -hitTestForEvent:inRect:ofView: e restituire una maschera di bit appropriata di NSCellHit... valori.Nello specifico, se il valore restituito non ha l'estensione NSCellHitTrackableArea valore in esso, non riceverai alcun messaggio di tracciamento!

Quindi, ad alto livello, la tua implementazione sarà simile a:

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

Altri suggerimenti

Il punto di NSCell sottoclassi è quello di separare la responsabilità per il rendering e la gestione di elementi comuni dell'interfaccia utente (i controlli) dalla visual- ed eventi-gerarchia responsabilità della NSView classi.Questo abbinamento consente a ciascuno di fornire maggiore specializzazione e variabilità senza gravare sull'altro.Guarda il gran numero di NSButton istanze che si possono creare in Cocoa.Immagina il numero di NSButton sottoclassi che esisterebbero se questa suddivisione delle funzionalità fosse assente!

Utilizzando il linguaggio del design pattern per descrivere i ruoli:UN NSControl agisce come una facciata, nascondendo i dettagli della sua composizione ai suoi clienti e trasmettendo eventi e trasmettendo messaggi ai suoi NSCell istanza che funge da delegato.

Perché il tuo NSCell la sottoclasse include altro NSCell istanze della sottoclasse all'interno della sua composizione, non ricevono più direttamente questi messaggi di evento da NSControl istanza che si trova nella gerarchia della vista.Pertanto, affinché queste istanze di cella ricevano messaggi di eventi dalla catena di risponditori di eventi (della gerarchia di visualizzazione), l'istanza di cella deve trasmettere quegli eventi rilevanti.Stai ricreando il lavoro di NSView gerarchia.

Questa non è necessariamente una cosa negativa.Replicando il comportamento di NSControl (e il suo NSView superclasse) ma in an NSCell modulo, puoi filtrare gli eventi trasmessi alle tue sottocelle per posizione, tipo di evento o altri criteri.Lo svantaggio è replicare il lavoro di NSView/NSControl nella costruzione del meccanismo di filtraggio e gestione.

Quindi, nel progettare la tua interfaccia, devi considerare se il file NSButtonCell (E NSTextFieldCells) stanno meglio dentro NSControls nella normale gerarchia di visualizzazione o come sottocelle nel tuo NSCell sottoclasse.È meglio sfruttare la funzionalità che già esiste in una codebase piuttosto che reinventarla (e continuare a mantenerla in seguito) inutilmente.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top