Action sur un mouseUp d'un NSSlider

UniXUniX Membre
01:52 modifié dans API AppKit #1
Salut.

Je bloque sur un pb tout bête .... :(
Je souhaiterais exécuter une action lorsque je relache le clic d'un NSSlider.

J'ai donc sous-classé NSSlider, afin de placer le code à  exécuter dans la méthode mouseUp.
Dans la sous-classe, j'ai défini acceptsFirstResponder: qui retourne YES, et mouseUp: qui contient le code.

Tel quel, mouseUp n'est jamais appelé. Si je définis également mouseDown et mouseDragged, seul mouseDown est appelé. Dans mouseDown et mouseUp, je n'ai mis que [super mouseDown:event].

Réponses

  • 01:52 modifié #2
    Pourquoi ne pas désactiver le "continuously send action" (setContinuous:) et exécuter le code lorsque l'action est effectuée?

    Sinon ce que tu décris n'est pas un bug, il est possible de "capturer" certains événements et de les retirer de l'event queue, dans le cas présent il s'agit des drag et mouse up.
  • UniXUniX Membre
    mars 2007 modifié #3
    En fait, c'est pour le slider de zoom de carte de Discoway.

    Ca fait suite au tuyau de Bru concernant l'affichage lissé d'une image. Cette manip est gourmande en ressources.
    Donc, lors d'un zoom (déplacement du slider, et donc je ne peux pas désactiver l'envoi continu), je voudrais pouvoir afficher la carte non lissée (peu gourmand en ressources), et lors du relachement du slider, lancer l'affichage lissé une seule fois. J'ai donc une action différente lorsque le slider glisse, ou lorsque il est relaché.

  • 01:52 modifié #4
    Je ne doute pas que si tu poses cette question, c'est pour une manipulation gourmande, c'est pour ça que je t'ai proposé cette solution à  base de "setContinuous:". Si ton slider est "continuous" (ou la case "Continuously send action" est cochée dans IB), l'action correspondant à  changement de valeur dans le slider se fait à  chaque déplacement de la souris. Dans le cas contraire, l'action n'est envoyée que lorsque la souris est relachée, ce qui répond donc directement à  ton besoin.
  • UniXUniX Membre
    01:52 modifié #5
    Et non .....
    Moi en fait, je voudrais combiner les 2 !

    A savoir, lorsque le slider glisse (il est continuous), j'affiche avec une méthode peu gourmande qui fait un affichage moyen, et lorsque je relache le slider, j'affiche une dernière fois avec une méthode gourmande qui fait un affichage nickel. Je n'utilise la méthode goumande qu'une seule fois.

    Le pb, pour y revenir, c'est simplement de sous-classer NSSlider, et de rajouter à  cette sous-classe une action à  faire lorsque le slider est relaché. C'est là  que je coince .... J'ai jamais trop sous-classer de NSControl, et à  priori, ça réagit pas comme je le pensais.
  • mars 2007 modifié #6
    Ah oui, fallait le dire tout de suite.

    Le problème est que tout le "déplacement" est géré dans le mouseDown, donc sous-classer n'aidera pas. Essaie peut-être en examinant le type du currentEvent ([NSApp currentEvent]) dans la méthode d'action du slider. C'est un peu bricolage, mais j'ai pas de meilleure idée (pour le moment).
  • BruBru Membre
    mars 2007 modifié #7
    dans 1175271457:

    Essaie peut-être en examinant le type du currentEvent ([NSApp currentEvent]) dans la méthode d'action du slider. C'est un peu bricolage, mais j'ai pas de meilleure idée (pour le moment).


    C'est excatement ce que je ferais.
    Dans la méthode action du slider :
    <br />if ([[[monSlider window] currentEvent] type]==NSLeftMouseUp)<br />{<br />&nbsp; &nbsp; // je réactive l&#39;anti-aliasing etc...<br />}<br />else<br />{<br />&nbsp; &nbsp;// je désactive l&#39;anti-aliasing etc...<br />}<br />
    


    .
  • mars 2007 modifié #8
    Etonnant que le mouseUp ne soit pas actif...

    Sinon, tu peux envoyer l'affichage aliasé après un certain temps lorsque l'utilisateur ne bouge plus le slider ou quand la souris est relachée.

    Un peu de lecture : http://www.mactech.com/articles/mactech/Vol.14/14.12/DelayedMessaging/

    Edit :
    Le principe est qu'après un certains temps (genre 500ms) tu demandes l'affichage aliasé. Si l'utilisateur bouge la souris avant les 500ms la demande d'affichage haute qualité est annulée.

    J'ai un faible pour ce principe car il permet d'avoir des images de bonne qualité sans relacher la souris et tout le code devrait tenir dans l'action du slider.
  • mars 2007 modifié #9
    Je crois avoir trouvé mieux, enfin, je pense:

    [tt]-(void)mouseDown:(NSEvent*)event {
        // désactivation de l'antialiasing
        [super mouseDown:event];
        // réactivation de l'antialiasing
        [self sendAction:[self action] to:[self target]]; // pour forcer la réactualisation des données
    }[/tt]

    Enfin, pour respecter la logique d'encapsulation, au lieu de désactivation de l'antialiasing, tu rajoutes une variable d'instance pour indiquer si le slider est en cours de déplacement ou pas, tu vérifies l'état de cette variable pour savoir le type de qualité à  choisir. Enfin, c'est peut-être un peu moins efficace (dans le sens où un rescaling est fait en plus, mais le premier étant de basse qualité, donc rapide, le ralentissement ne devrait pas être trop sensible), mais l'information "utile" a le mérite d'être centralisée et donc le code sera plus facile à  maintenir.

    EDIT: pas si bonne idée en fait, si NSSlider change son implémentation de mouseDown pour un classique mouseDown/mouseDrag/mouseUp, ça foirera.

    dans 1175276642:
    Etonnant que le mouseUp ne soit pas actif...

    Pas tellement: dans ce genre de cas, il est bien plus simple de "capturer" événements mouseDrag et mouseUp dans le -mouseDown:, plutôt que de rester avec le schéma habituel -mouseDown:, -mouseDrag:, -mouseUp:.
  • 01:52 modifié #10
    dans 1175278310:
     
    dans 1175276642:
    Etonnant que le mouseUp ne soit pas actif...

    Pas tellement: dans ce genre de cas, il est bien plus simple de "capturer" événements mouseDrag et mouseUp dans le -mouseDown:, plutôt que de rester avec le schéma habituel -mouseDown:, -mouseDrag:, -mouseUp:.


    Plus simple d'accord mais combien de temps as tu passé à  chercher une conception propre et logique plutôt que "simple". Il me semble qu'à  notre "niveau" (Cocoa/Objective-C c'est plus une programmation logique/bien structurée que rapide) le mouseUp devrait être actif... Ceci dit je n'ai pas dit que j'aurais fait mieux.
  • UniXUniX Membre
    01:52 modifié #11
    C'est un peu tiré par les cheveux ta proposition Renaud .... J'ai testé ce qu'a proposé Bru, et ça fonctionne bien. Je pense que je vais rester là -dessus.

    C'est vrai que c'est étonnant de ne pas avoir un fonctionnement classique down/drag/up pour les sliders.
  • 01:52 modifié #12
    Surtout que comme d'hab je reviens sur ce que je j'ai dit puisque ce que je pensais faire ne fonctionne pas (les messages arrivant après car bloqués par le slider...).
  • UniXUniX Membre
    01:52 modifié #13
    Ouais, c'est ce que j'ai constaté aussi en essayant d'utiliser mouseUp dans une sous-classe de NSSlider.
  • BruBru Membre
    01:52 modifié #14
    dans 1175276642:

    Etonnant que le mouseUp ne soit pas actif...


    dans 1175280024:

    Pas tellement: dans ce genre de cas, il est bien plus simple de "capturer" événements mouseDrag et mouseUp dans le -mouseDown:, plutôt que de rester avec le schéma habituel -mouseDown:, -mouseDrag:, -mouseUp:.


    dans 1175281807:

    C'est vrai que c'est étonnant de ne pas avoir un fonctionnement classique down/drag/up pour les sliders.


    dans 1175288060:

    (les messages arrivant après car bloqués par le slider...).


    dans 1175289692:

    Ouais, c'est ce que j'ai constaté aussi en essayant d'utiliser mouseUp dans une sous-classe de NSSlider.


    Hum, des cours de rattrapage de doc Apple s'imposent...

    N'oubliez pas qu'un contrôle, c'est 2 choses distinctes : le NSControl et le NSCell.
    Le premier détecte le mouseDown qu'il transmet à  son cell.
    Ensuite, le cell va "observer" les autres événements (surtout mouseMoved et mouseUp) afin de faire son tracking. Donc, après le mouseDown, le NSControl ne fait plus rien, donc inutile de surcharger son mouseUp.

    Techniquement, le NSCell effetue son tracking via un bête nextEventMatchingMask:NSEventTrackingRunLoopMode ou équivalent (de NSWindow). Cette méthode rentre en attente infinie jusqu'à  ce qu'un événement concernant le tracking arrive.

    Donc, ce serait plutôt le mouseUp de NSWindow du contrôle qu'il faudrait surcharger afin de respecter le schème mouseUp/mouseMoved/mouseUp.

    .
  • BruBru Membre
    01:52 modifié #15
    dans 1175276642:

    Sinon, tu peux envoyer l'affichage aliasé après un certain temps lorsque l'utilisateur ne bouge plus le slider ou quand la souris est relachée.
    [...]
    Le principe est qu'après un certains temps (genre 500ms) tu demandes l'affichage aliasé. Si l'utilisateur bouge la souris avant les 500ms la demande d'affichage haute qualité est annulée.
    J'ai un faible pour ce principe car il permet d'avoir des images de bonne qualité sans relacher la souris et tout le code devrait tenir dans l'action du slider.


    dans 1175288060:

    Surtout que comme d'hab je reviens sur ce que je j'ai dit puisque ce que je pensais faire ne fonctionne pas (les messages arrivant après car bloqués par le slider...).


    Si si, ça peut marcher !
    Si tu penses à  un NSTimer pour ce délai, faut effectuer quelques incantations magiques avant fin qu'il fonctionne aussi pendant le tracking du slider.
    Le détail de la formule magique est dans ce post.

    Et, c'est vrai que cette solution est plus élégante que la première, car cela permet à  l'utilisateur de stopper temporairement son réglage de zoom, afin d'attendre l'affichage nickel de la carte ; et de reprendre le réglage si cela ne lui convient pas !

    .
  • mars 2007 modifié #16
    Non non, je pense toujours au performSelector:withObject:afterDelay: comme indiqué dans mon premier message... mais là  aussi on aurait du lire la doc pour trouver la même méthode mais avec l'indication des "modes" :

    <br />[NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(toto:) object:sender];<br /><br />NSArray *modes=[NSArray arrayWithObjects:NSDefaultRunLoopMode,NSEventTrackingRunLoopMode,nil];<br />[self performSelector:@selector(toto:) withObject:sender afterDelay:0.5 inModes:modes];<br />
    


    Les deux modes sont ajoutés sinon au relachement du slider rien ne se passe.
    ... <3
  • schlumschlum Membre
    mars 2007 modifié #17
    Ce que j'aurais fait :
    - Le laisser en "continuously send action"
    - Dans cette action :
    * Afficher en "basse résolution"
    * Si le timer (voir étape suivante) n'est pas "nil", l'invalider et le mettre à  nil
    * Initialiser un timer (variable de la classe) pour lancer l'affichage en "haute résolution" au bout d'une seconde

    Le Timer lui affiche en "haute résolution" puis s'invalide et se remet à  nil.

    Pourquoi se prendre la tête avec les événements, les sous-classes etc... ?
  • mars 2007 modifié #18
    Et pourquoi se prendre la tête avec un timer ainsi qu'une variable gloable de plus ?
  • schlumschlum Membre
    01:52 modifié #19
    dans 1175357020:

    Et pourquoi se prendre la tête avec un timer ainsi qu'une variable gloable de plus ?


    Parce que ça prend 3 lignes... Et que ça globalise tous les avantages que vous vouliez (quand on ne bouge pas pendant x temps, ça met à  jour en haute résolution même sans relâcher la souris).
  • mars 2007 modifié #20
    Il faudra que tu me dises en quoi mon dernier message ne propose pas ces avantages... en tu l'as dit 3 lignes ?
  • schlumschlum Membre
    mars 2007 modifié #21
    dans 1175358840:

    Il faudra que tu me dises en quoi mon dernier message ne propose pas ces avantages... en tu l'as dit 3 lignes ?


    OK, début de page ratée, je l'avais pas vu...
    Ca revient exactement au même ; je suppose qu'en interne c'est un timer aussi.
    Pourquoi passer par un mode avec des événements ??

    Les deux modes sont ajoutés sinon au relachement du slider rien ne se passe.


    ?? Si c'est envoyé dans l'action du Slider de manière continue, je ne vois pas pourquoi...
  • schlumschlum Membre
    01:52 modifié #22
    En fait, c'est cette histoire de runLoop du tracking qui me chiffonne... Si le timer est géré au niveau d'une classe, on peut le contrôler entièrement sans toucher aux runloops et aux événements non ?  :crackboom:-

    Enfin je trouve ça plus "intuitif" quoi... Ca ne rentre pas dans des concepts complexes.
  • mars 2007 modifié #23
    dans 1175361913:

    En fait, c'est cette histoire de runLoop du tracking qui me chiffonne... Si le timer est géré au niveau d'une classe, on peut le contrôler entièrement sans toucher aux runloops et aux événements non ?  :crackboom:-

    Enfin je trouve ça plus "intuitif" quoi... Ca ne rentre pas dans des concepts complexes.


    Personnellement le préfère l'idée de pouvoir envoyer un message après un temps x sans me soucier du reste plutôt que de mettre en place mon propre timer. Mais c'est vrai que dans un projet plus conplexe on pourrait se poser la question...

    > Si c'est envoyé dans l'action du Slider de manière continue, je ne vois pas pourquoi...

    Parce qu'en de tracking la Runloop repasse en mode par défault et ne prend plus en compte les messages provenant de NSEventTrackingRunLoopMode... Un peu comme un timer normal qui ne tient pas compte lorsqu'on presse un bouton, etc...

    L'idéal serait donc d'indiquer le mode normal en utilisant la méthode de bru/renaud mais on se mort un peu la queue à  ce moment...

Connectez-vous ou Inscrivez-vous pour répondre.