Pregunta

en mi aplicación cacao, necesito un NSCell personalizado para un NSTableView.Este La subclase NSCell contiene un NSButtonCell personalizado para manejar un clic (y dos o tres NSTextFieldCells para contenidos textuales).Encontrarás un ejemplo simplificado de mi código a continuación.

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

El problema con el que estoy atrapado es: ¿Cuál es la mejor/correcta forma de obtener ese botón (más precisamente:NSButtonCell) dentro de este NSCell para funcionar correctamente? "trabajo" significa:activa el mensaje de acción asignado y muestra la imagen alternativa cuando se hace clic.Fuera de la caja, el botón no hace nada cuando se hace clic.

Es difícil encontrar información/lecturas sobre este tema.Las únicas publicaciones que encontré en la red me indicaron cómo implementar

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

¿Es esta la forma correcta de hacerlo??? Implementar trackMouse:en mi NSCell que contiene? ¿Y luego reenviar el evento a NSButtonCell?Habría esperado que el propio NSButtonCell supiera qué hacer cuando se hace clic en él (y vi el trackMouse:métodos más en conjunto con el seguimiento real de los movimientos del mouse, no como una rueda de entrenamiento para el comportamiento de clic "estándar").Pero parece que no hace esto cuando se incluye en una celda...Parece que todavía no he comprendido el panorama general de las celdas personalizadas ;-)

Me alegraría que alguien pudiera responder esto (o indicarme algún tutorial o algo similar) a partir de su propia experiencia y decirme si estoy en el camino correcto.

Gracias de antemano, Tobi

¿Fue útil?

Solución

Los requisitos mínimos son:

  • Después izquierdo del ratón sobre el botón, debe aparecer presionado cada vez que el ratón está sobre ella.
  • Si el ratón a continuación, acciona sobre el botón, la célula debe enviar el mensaje de acción apropiado.

Para hacer que el botón presionado, es necesario actualizar la propiedad highlighted de la pila de botón según sea apropiado. Cambiar el estado por sí sola no va a lograr esto, pero lo que queremos es que el botón para destacar si, y sólo si, sus estados es NSOnState.

Para enviar el mensaje de acción, es necesario tener en cuenta cuando se suelta el ratón, y luego usar -[NSApplication sendAction:to:from:] para enviar el mensaje.

Con el fin de estar en condiciones de enviar estos mensajes, tendrá que enganchar en los métodos de seguimiento de eventos proporcionados por NSCell. Tenga en cuenta que todos esos métodos de seguimiento, excepto el último método, -stopTracking:..., devuelven un booleano para responder a la pregunta, "¿Quieres seguir recibiendo mensajes de seguimiento?"

El toque final es que, con el fin de ser enviados los mensajes de seguimiento en absoluto, es necesario implementar -hitTestForEvent:inRect:ofView: y devolver una máscara de bits apropiada de los valores NSCellHit.... En concreto, si el valor devuelto no tiene el valor NSCellHitTrackableArea en ella, no recibirá los mensajes de seguimiento!

Por lo tanto, a un alto nivel, su aplicación se verá algo como:

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

Otros consejos

El punto de NSCell subclases es separar la responsabilidad de representar y controlar los elementos comunes de la interfaz de usuario (los controles) de la jerarquía visual y de eventos Responsabilidades de la NSView clases.Este emparejamiento permite que cada uno proporcione mayor especialización y variabilidad sin sobrecargar al otro.Mira la gran cantidad de NSButton instancias que uno puede crear en Cocoa.Imagínate el número de NSButton ¡subclases que existirían si esta división en la funcionalidad estuviera ausente!

Uso del lenguaje de patrones de diseño para describir los roles:un NSControl actúa como una fachada, ocultando detalles de su composición a sus clientes y transmitiendo eventos y transmitiendo mensajes a sus NSCell instancia que actúa como delegado.

Porque su NSCell subclase incluye otros NSCell instancias de subclase dentro de su composición, ya no reciben directamente estos mensajes de eventos del NSControl instancia que está en la jerarquía de vistas.Por lo tanto, para que estas instancias de celda reciban mensajes de eventos de la cadena de respuesta de eventos (de la jerarquía de vistas), su instancia de celda debe transmitir esos eventos relevantes.Estás recreando la obra del NSView jerarquía.

Esto no es necesariamente algo malo.Replicando el comportamiento de NSControl (y es NSView superclase) pero en un NSCell formulario, puede filtrar los eventos transmitidos a sus subceldas por ubicación, tipo de evento u otros criterios.El inconveniente es replicar el trabajo de NSView/NSControl en la construcción del mecanismo de filtrado y gestión.

Entonces, al diseñar su interfaz, debe considerar si el NSButtonCell (y NSTextFieldCells) están mejor en NSControls en la jerarquía de vista normal, o como subceldas en su NSCell subclase.Es mejor aprovechar la funcionalidad que ya existe en una base de código que reinventarla (y continuar manteniéndola más adelante) innecesariamente.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top