Sottoclasse NSSlider: Hai bisogno di una soluzione per il mouse gli eventi mancanti (Cacao OSX)

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

  •  29-09-2019
  •  | 
  •  

Domanda

Sto cercando di sottoclasse NSSlider per creare un controllo chiamato un jog dial. Fondamentalmente quello che mi serve è un cursore che inizia sempre a metà e quando viene spostato a sinistra oa destra invierà notifiche ogni tanto (determinato da un attributo si può impostare) informare il suo contenitore del suo valore corrente, poi quando si Let You Go della manopola, ritornerà al centro. Speravo di implementare la funzionalità di restituire il dispositivo di scorrimento per le notifiche di medie e interrompere l'invio nell'evento MouseUp del cursore, ma sembra che per qualche motivo mela disabilita l'evento MouseUp dopo un evento mouseDown sul cursore e gestisce tutte le funzionalità di scorrimento ad un livello inferiore. C'è qualche cosa che posso ottenere la parte posteriore evento mouseUp? Se non è in grado chiunque suggerire una soluzione ragionevole?

È stato utile?

Soluzione

Ogni volta che si nota che l'attuazione di una superclasse di mouseDragged: o mouseUp: non è sempre chiamato, è più probabile perché implementazione della classe di mouseDown: entra in un ciclo di monitoraggio. Questo è certamente vero per molti sottoclassi NSControl tra cui NSSlider.

Un modo migliore per rilevare un mouse verso l'alto è quello di creare una sottoclasse della cella e sovrascrivere il metodo di monitoraggio appropriato. In questo caso, probabilmente si desidera - (void)stopTracking:(NSPoint)lastPoint at:(NSPoint)stopPoint inView:(NSView *)controlView mouseIsUp:(BOOL)flag, ma le varianti startTracking: e continueTracking: potrebbe rivelarsi utile anche per quello che stai cercando di fare.

Altri suggerimenti

C'è un trucco che uso (ma non ho inventato) per tali situazioni. In primo luogo, in IB, designare il cursore come "continuo", in modo che si otterrà messaggi di azione, come si sposta il cursore. Poi, nel metodo di azione, fare questo:

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

Dopo che il mouse viene rilasciato, sarà chiamato il metodo finishTrack.

Questo funziona per me (ed è più facile che sottoclasse 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
    }
}

Sembra che l'idea di Kperryua fornirebbe la soluzione più pulita quindi mi segnerò che come la risposta accettata, ma ho finito con un po 'di un hack che ha lavorato per la mia situazione specifica così ho pensato che avrei potuto condividere che pure.

L'applicazione che sto facendo ha bisogno di essere cross-platform in modo da sto usando Cocotron (un progetto open source che implementa gran parte del API Cocoa su Windows) per raggiungere questo obiettivo e cocotron non supporta lo stopTracking: ... Metodo menzionato sopra.

Per fortuna, mentre giocare con un piccolo programma di test che abbiamo fatto è stato scoperto che se si esegue l'override del metodo mouseDown del NSSlider e chiamare il super di tale metodo, non tornare fino a quando il pulsante del mouse viene rilasciato, in modo da può semplicemente mettere qualsiasi codice dovrebbe essere in mouseUp all'interno del metodo mouseDown dopo la chiamata a super. Questo è un po 'di hack sporco ma sembra essere l'unica soluzione che funziona nel nostro caso quindi stiamo andando ad avere per andare con esso.

Nel caso in cui qualcuno si sta chiedendo come raggiungere questo obiettivo a Swift 3 con un NSSlider questo è stato è impostato su continua:

@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)
    }
}

...

Questo può essere fatto solo da sottoclasse (metodi come da @mprudhom non funziona al 100% per il rilascio di know)

Per l'inizio del trascinamento, è necessario becomeFirstResponder cattura (per questo è necessario fornire anche needsPanelToBecomeKey)

per la fine di trascinamento, è necessario sottoclasse mouseDown: (la sua chiamata mouseDown, ma viene chiamato quando la manopola rilasciato)

Nota: questo approccio renderà il vostro cursore del primo soccorritore, annullando ogni altro attuale first responder

Nota: l'approccio da 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

Ho avuto un simile esigenza, ma per un cursore su un NSTouchBar. Ho usato un approccio simile "quick-e sporco", come Marc Prud'hommeaux e Martin Majewski, a soli eventi leggermente diverse per ispezionare. Ecco il codice Swift che consente di monitorare sia il valore continuo e il valore finale del cursore lungo una touchbar.

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)")
            }
        }
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top