La subclasificación NSSlider: ¿Necesita una solución para ratón hacia arriba faltante eventos (Cacao OSX)

StackOverflow https://stackoverflow.com/questions/3919905

  •  29-09-2019
  •  | 
  •  

Pregunta

Estoy tratando de subclase NSSlider para crear un control de llamada de un mando de desplazamiento. Básicamente lo que necesito es un cursor que siempre comienza en el centro y cuando se mueve hacia la izquierda o hacia la derecha va a enviar notificaciones cada cierto tiempo (determinado por un un atributo puede establecer) informar a su recipiente de su valor actual, a continuación, cuando se le permiten ir de la perilla, se volverá a la mitad. Tenía la esperanza de implementar la funcionalidad para devolver el control deslizante para las notificaciones medias y el envío de parada en caso mouseUp de la corredera, pero parece que por alguna razón manzana desactiva el evento MouseUp después de un evento mouseDown en el control deslizante y maneja todas las funcionalidades deslizador a un nivel inferior. ¿Hay alguna forma de conseguir la parte posterior evento mouseUp? Si no puede alguien sugerir una solución razonable?

¿Fue útil?

Solución

Siempre que detecte que la aplicación de una superclase de mouseDragged: o mouseUp: no está recibiendo llamadas, es más probable debido a la aplicación de mouseDown: de la clase entra en un bucle de seguimiento. Este es ciertamente el caso de muchas subclases NSControl incluyendo NSSlider.

Una mejor manera de detectar un ratón hacia arriba es a la subclase de la célula y anular el método de seguimiento apropiado. En este caso, es probable que desee - (void)stopTracking:(NSPoint)lastPoint at:(NSPoint)stopPoint inView:(NSView *)controlView mouseIsUp:(BOOL)flag, pero las variantes startTracking: y continueTracking: puede resultar útil también para lo que estamos tratando de hacer.

Otros consejos

Hay un truco que utilizo (pero no inventé) para este tipo de situaciones. En primer lugar, en el IB, designar el control deslizante como "continua", por lo que obtendrá mensajes de acción cuando el cursor se mueve. Luego, en el método de acción, hacer esto:

[NSObject cancelPreviousPerformRequestsWithTarget: self
  selector: @selector(finishTrack) object: nil ];
[self performSelector: @selector(finishTrack) withObject: nil
  afterDelay: 0.0];

Después se suelta el ratón, se llamará el método finishTrack.

Esto funciona para mí (y es más fácil que la subclasificación NSSlider):

- (IBAction)sizeSliderValueChanged:(id)sender {
    NSEvent *event = [[NSApplication sharedApplication] currentEvent];
    BOOL startingDrag = event.type == NSLeftMouseDown;
    BOOL endingDrag = event.type == NSLeftMouseUp;
    BOOL dragging = event.type == NSLeftMouseDragged;

    NSAssert(startingDrag || endingDrag || dragging, @"unexpected event type caused slider change: %@", event);

    if (startingDrag) {
        NSLog(@"slider value started changing");
        // do whatever needs to be done when the slider starts changing
    }

    // do whatever needs to be done for "uncommitted" changes
    NSLog(@"slider value: %f", [sender doubleValue]);

    if (endingDrag) {
        NSLog(@"slider value stopped changing");
        // do whatever needs to be done when the slider stops changing
    }
}

Parece que la idea de Kperryua proporcionaría la solución más limpia así que voy a marcar que a medida que la respuesta aceptada, pero terminó con un poco de un truco que funcionó para mi situación específica por lo que pensé que podría compartir eso también.

La aplicación que estoy haciendo necesidades de ser multiplataforma, así que estoy usando Cocotron (un proyecto de código abierto que implementa gran parte de la API de cacao en Windows) para lograr este objetivo y cocotron no soporta el StopTracking: ... método mencionado anteriormente.

Por suerte, mientras que jugando con un programa de prueba poco lo que hizo que se descubrió que si se reemplaza el método del mouseDown del NSSlider y llama a la súper de ese método, no se restablece hasta que se suelta el botón del ratón, por lo que Sólo se puede poner cualquier código que debe estar en mouseUp dentro del método mouseDown después de la llamada a super. Esto es un poco de un truco sucio, pero que parece ser la única solución que funcione en nuestro caso lo que vamos a tener que ir con ella.

Sólo en caso de que alguien se está preguntando cómo lograr esto en Swift 3 con un estado NSSlider eso se establece en continuo:

@IBOutlet weak var mySlider: NSSlider!

...

@IBAction func handlingNSSliderChanges(_ sender: Any) {

    // Perform updates on certain events
    if sender is NSSlider{
        let event = NSApplication.shared().currentEvent

        if event?.type == NSEventType.leftMouseUp {
            print("LeftMouseUp event inside continuous state NSSlider")
        }
    }

    // Do continuous updates
    if let value = mySlider?.integerValue{
        demoLabel.stringValue = String(value)
    }
}

...

Esto se puede hacer solamente por la subclasificación (como métodos de @mprudhom no va a funcionar al 100% para la liberación know)

Para el inicio de la resistencia al avance, es necesario becomeFirstResponder captura (para ello es necesario para proporcionar también needsPanelToBecomeKey)

para el fin de arrastrar, es necesario mouseDown: subclase (la llamada mouseDown, pero se llama cuando la perilla liberado)

Nota: este enfoque hará que su deslizador del primer nivel de respuesta, la cancelación de cualquier otra corriente de primer nivel de respuesta

Nota: aproximación desde mprudhom

@implementation CustomSlider

- (void)mouseDown:(NSEvent *)theEvent {
    [super mouseDown:theEvent];
    NSLog(@"OK");
}

- (BOOL)needsPanelToBecomeKey {
    [super needsPanelToBecomeKey];
    return YES;
}

- (BOOL)becomeFirstResponder {
    [super becomeFirstResponder];
    NSLog(@"Became first responder.");
    return YES;
}

@end

Yo tenía una necesidad similar, pero para un control deslizante en una NSTouchBar. He utilizado un enfoque similar "rápida y sucia", como Marc Prud'hommeaux y Martin Majewski, sólo ligeramente diferentes eventos para inspeccionar. Aquí está el código Swift que permite hacer un seguimiento continuo tanto el valor y el valor final del control deslizante en una barra táctil.

func sliderValueChanged(_ sender: NSSlider) {
    let doubleValue = sender.doubleValue

    Swift.print("Continuous value: \(doubleValue)")

    // find out if touch event has ended
    let event = NSApplication.shared().currentEvent
    if event?.type == NSEventType.directTouch {
        if let endedTouches = event?.touches(matching: .ended, in: nil) {
            if (endedTouches.count > 0) {
                Swift.print("The final value was: \(doubleValue)")
            }
        }
    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top