Action sur un mouseUp d'un NSSlider
UniX
Membre
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].
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].
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
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.
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é.
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.
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).
C'est excatement ce que je ferais.
Dans la méthode action du slider :
.
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.
[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.
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.
C'est vrai que c'est étonnant de ne pas avoir un fonctionnement classique down/drag/up pour les sliders.
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.
.
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 !
.
Les deux modes sont ajoutés sinon au relachement du slider rien ne se passe.
...
- 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... ?
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).
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 ??
?? Si c'est envoyé dans l'action du Slider de manière continue, je ne vois pas pourquoi...
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...