NSSlider le sous-classement: Besoin d'une solution de contournement pour manque souris vers le haut des événements (Cocoa OSX)

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

  •  29-09-2019
  •  | 
  •  

Question

Je suis en train de sous-classe NSSlider pour créer un contrôle appelé molette de défilement. Fondamentalement, ce que j'ai besoin est un curseur qui commence toujours au milieu et quand il est déplacé vers la gauche ou la droite, il envoie des notifications tous si souvent (déterminée par un un attribut peut définir) informer son récipient de sa valeur actuelle, lorsque vous vous lâchez le bouton, il retournera au milieu. J'espérais mettre en œuvre la fonctionnalité pour ramener le curseur aux notifications moyen et l'envoi d'arrêt en cas mouseUp du curseur, mais il semble que, pour une pomme raison désactive l'événement MouseUp après un événement mouseDown sur le curseur et les poignées de toutes les fonctionnalités de curseur à un niveau inférieur. Y at-il de toute façon je peux obtenir le retour de l'événement mouseUp? Sinon quelqu'un peut-il suggérer une solution raisonnable?

Était-ce utile?

La solution

Chaque fois que vous remarquez que n'est pas appelé la mise en œuvre de mouseDragged: ou mouseUp: d'une superclasse, il est plus probable que la mise en œuvre de la classe de mouseDown: entre dans une boucle de suivi. Cela est certainement vrai de nombreuses sous-classes de NSControl y compris NSSlider.

Une meilleure façon de détecter une souris vers le haut est la sous-classe de la cellule et remplacer la méthode de suivi approprié. Dans ce cas, vous voulez probablement - (void)stopTracking:(NSPoint)lastPoint at:(NSPoint)stopPoint inView:(NSView *)controlView mouseIsUp:(BOOL)flag, mais les variantes de startTracking: et continueTracking: pourrait se révéler utile aussi bien pour ce que vous essayez de faire.

Autres conseils

Il y a un truc que j'utilise (mais n'a pas inventé) pour de telles situations. Tout d'abord, en IB, désigner le curseur comme « continu », de sorte que vous obtenez des messages d'action que le curseur est déplacé. Ensuite, dans la méthode d'action, faites ceci:

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

Après la souris est relâchée, la méthode finishTrack sera appelée.

Cela fonctionne pour moi (et est plus facile que le sous-classement 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
    }
}

Il semble que l'idée de Kperryua fournirait la solution la plus propre, donc je marquerai que la réponse acceptée, mais je me suis retrouvé avec un peu d'un hack qui a fonctionné pour ma situation spécifique, donc je pensais que je pourrais partager aussi.

L'application que je fais a besoin d'être multi-plateforme, donc je suis en utilisant Cocotron (un projet open source qui implémente une grande partie de l'API Cocoa sur Windows) pour atteindre cet objectif et cocotron ne supporte pas le stopTracking: ... méthode mentionné ci-dessus.

Par chance, tout en jouant avec un petit programme de test que nous avons fait, il a été découvert que si vous remplacez le la méthode mouseDown du NSSlider et appelez super de cette méthode, il ne retourne pas jusqu'à ce que le bouton de la souris est relâché, de sorte que vous peut simplement mettre ce code doit être à l'intérieur de la méthode mouseUp mouseDown après l'appel à super. Ceci est un peu un hack sale, mais il semble être la seule solution qui fonctionnera dans notre cas donc nous allons devoir aller avec elle.

Juste au cas où quelqu'un se demande comment y parvenir à Swift 3 avec l'état d'un NSSlider est réglé en continu:

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

...

Cela peut être fait que par des méthodes telles que (sous-classement de @mprudhom ne fonctionnera pas à 100% pour la libération de savoir)

Pour le début de la traînée, vous devez becomeFirstResponder catch (pour cela, vous devez également fournir needsPanelToBecomeKey)

pour la fin de glisser, vous devez sous-classe mouseDown: (elle est appelée mouseDown, mais il est appelé lorsque le bouton relâché)

Remarque: cette approche fera de votre curseur le premier répondeur, annulant tout autre premier courant répondeur

Note: approche de 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

J'ai eu un besoin similaire, mais pour un curseur sur un NSTouchBar. J'ai utilisé une approche similaire « rapide et sale », comme Marc Prud'hommeaux et Martin Majewski, à des événements légèrement différentes pour inspecter. Voici le code Swift qui vous permet de suivre à la fois la valeur continue et la valeur finale du curseur sur une barre tactile.

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)")
            }
        }
    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top