Présentation de l'interface MultiTouch (MT) - fil
←
→
Transcription du contenu de la page
Si votre navigateur ne rend pas la page correctement, lisez s'il vous plaît le contenu de la page ci-dessous
Présentation de l’interface MultiTouch (MT) PJE Multi-Touch Fabrice Aubert fabrice.aubert@univ-lille.fr 2019-2020 F. Aubert PJE MultiTouch/ Présentation de l’interface MT 2019-2020 1 / 32
Outils de développement utilisés I Développement en Java I Utilisation de Swing pour l’interface I Utilisation d’un client TUIO pour les couches basses (connexion UDP, etc) I Utilisation de Java2D pour la visualisation F. Aubert PJE MultiTouch/ Présentation de l’interface MT 2019-2020 2 / 32
Objectifs du toolkit MultiTouch I Structure logicielle calquée sur les librairies d’interfaces usuelles (Component, Container, Widget, Programmation événementielle). I Analyse du geste d’interaction (translate/rotate/scale, one dollar, axe principaux). I Illustration de ces concepts avec un visualiseur d’images. F. Aubert PJE MultiTouch/ Présentation de l’interface MT 2019-2020 3 / 32
Hello World I Cours JAVA (POO : LS4, COO : LS5) : http://www.fil.univ-lille1.fr/portail/ I Tutorial SWING : http://docs.oracle.com/javase/tutorial/uiswing/ import j a v a x . swing . ∗ ; import j a v a . awt . ∗ ; import j a v a . awt . event . ∗ ; public class Main { s t a t i c public void createAndShowGUI ( ) { JFrame f =new JFrame ( " H e l l o World ! ! ! " ) ; f . s e t P r e f e r r e d S i z e (new Dimension ( 5 0 0 , 5 0 0 ) ) ; f . pack ( ) ; f . s e t V i s i b l e ( true ) ; } s t a t i c public void main ( S t r i n g arg [ ] ) { ... } } F. Aubert PJE MultiTouch/ Présentation de l’interface MT 2019-2020 5 / 32
Event Dispatch Thread I La gestion des événements SWING est réalisée dans un thread appelé Event Dispatch Thread. I Différent du Initial Thread qui correspond à l’exécution du static public main() {}. I Il faut s’assurer que tout ce qui concerne la gestion de l’interface est faite dans le Event Dispatch Thread ⇒ utiliser invokeLater pour la création ! import j a v a x . swing . ∗ ; import j a v a . awt . ∗ ; import j a v a . awt . event . ∗ ; public class Main { s t a t i c public void createAndShowGUI ( ) { JFrame f =new JFrame ( " H e l l o World ! ! ! " ) ; ... f . s e t V i s i b l e ( true ) ; } s t a t i c public void main ( S t r i n g arg [ ] ) { S w i n g U t i l i t i e s . i n v o k e L a t e r (new Runnable ( ) { public void run ( ) { createAndShowGUI ( ) ; } }); } } F. Aubert PJE MultiTouch/ Présentation de l’interface MT 2019-2020 6 / 32
JFrame = Top-Level Container I Component : brique de base de l’interface (= widgets : boutons, listes déroulantes, etc). I Container : ensemble (ou groupe) de composants de l’interface graphique (pour organiser l’interface en sous ensembles ; boutons radios ; ...). I Un container est lui-même un composant ⇒ possibilité d’organiser l’interface hiérarchiquement. I La racine de la structure de l’interface (Top-Level Container) est gérée par la classe JFrame : • d’un container (container racine de tous les composants de l’interface) : contentPane. • du fenêtrage système (fermeture, iconification, look and feel). • de la possibilité d’ajouter un menu. F. Aubert PJE MultiTouch/ Présentation de l’interface MT 2019-2020 7 / 32
Ajouter des composants à un container s t a t i c public void createAndShowGUI ( ) { JFrame f =new JFrame ( " H e l l o World ! ! ! " ) ; f . s e t L a y o u t (new FlowLayout ( ) ) ; f . s e t P r e f e r r e d S i z e (new Dimension ( 5 0 0 , 5 0 0 ) ) ; J B u t t o n b u t t o n 1 =new J B u t t o n ( " Bouton 1 " ) ; b u t t o n 1 . s e t P r e f e r r e d S i z e (new Dimension ( 1 0 0 , 1 0 0 ) ) ; f . getContentPane ( ) . add ( b u t t o n 1 ) ; f . pack ( ) ; f . s e t V i s i b l e ( true ) ; } F. Aubert PJE MultiTouch/ Présentation de l’interface MT 2019-2020 8 / 32
Layout Manager I Tout container possède un « Layout Manager » : fixe la stratégie de positionnement des composants. I La taille et la position des composants sont fixés au moment du f.pack() qui respecte au mieux le layout manager. I Exemple : JFrame avec un FlowLayout, et 2 boutons dans le conteneur (remarquer les setPreferredSize) s t a t i c public void createAndShowGUI ( ) { JFrame f =new JFrame ( " H e l l o World ! ! ! " ) ; f . s e t P r e f e r r e d S i z e (new Dimension ( 5 0 0 , 5 0 0 ) ) ; f . s e t L a y o u t (new FlowLayout ( ) ) ; J B u t t o n b u t t o n 1 =new J B u t t o n ( " b u t t o n 1 " ) ; b u t t o n 1 . s e t P r e f e r r e d S i z e (new Dimension ( 1 0 0 , 3 0 ) ) ; f . getContentPane ( ) . add ( b u t t o n 1 ) ; J B u t t o n b u t t o n 2 =new J B u t t o n ( " b u t t o n 2 " ) ; b u t t o n 2 . s e t P r e f e r r e d S i z e (new Dimension ( 2 0 0 , 5 0 ) ) ; f . getContentPane ( ) . add ( b u t t o n 2 ) ; f . pack ( ) ; f . s e t V i s i b l e ( true ) ; } F. Aubert PJE MultiTouch/ Présentation de l’interface MT 2019-2020 9 / 32
Evénement I L’interface doit réagir aux événements utilisateurs (click sur un JButton par exemple). I Programmation événementielle : • Il faut indiquer à SWING quelle est la méthode à appeler dans le cas où un événement donné se produit pour un composant donné (mécanisme de callback). • A l’exécution, les événements (click, affichage d’un composant, etc) reçus par les composants sont gérés par la boucle d’événements (le Event Dispatch Thread) : pour chaque événement le callback correspondant est appelé. I Pour spécifier les callbacks : on utilise les Listener (= écouteur d’événements). ... J B u t t o n b u t t o n 1 =new J B u t t o n ( " Bouton 1 " ) ; b u t t o n 1 . s e t P r e f e r r e d S i z e (new Dimension ( 1 0 0 , 1 0 0 ) ) ; / / l e s événements du bouton1 s e r o n t gé r és par un é c o u t e u r a.addActionListener(b) : / / l ’ o b j e t un_ecouteur e s t dé f i n i dans l e t r a n s p a r e n t s u i v a n t b u t t o n 1 . a d d A c t i o n L i s t e n e r ( un_ecouteur ) ; l’objet b écoute le composant a f . getContentPane ( ) . add ( b u t t o n 1 ) ; ... F. Aubert PJE MultiTouch/ Présentation de l’interface MT 2019-2020 10 / 32
ActionListener I La classe de un_ecouteur doit implémenter l’interface ActionListener. I Méthode à implémenter : actionPerformed. import j a v a . awt . event . ∗ ; public class M y L i s t e n e r implements A c t i o n L i s t e n e r { / / c l a s s e de un_ecouteur public void a c t i o n P e r f o r m e d ( A c t i o n E v e n t arg0 ) { System . o u t . p r i n t l n ( " Bouton c l i c k " ) ; } } public class Main { s t a t i c A c t i o n L i s t e n e r un_ecouteur=new M y L i s te n e r ( ) ; s t a t i c public void createAndShowGUI ( ) { JFrame f =new JFrame ( " H e l l o World ! ! ! " ) ; ... b u t t o n 1 . a d d A c t i o n L i s t e n e r ( un_ecouteur ) ; ... } ... } F. Aubert PJE MultiTouch/ Présentation de l’interface MT 2019-2020 11 / 32
En plus concis... On peut utiliser une classe anonyme qui implémente l’interface ActionListener (évite de définir une nouvelle classe pour les écouteurs) : s t a t i c public void createAndShowGUI ( ) { JFrame f =new JFrame ( " H e l l o World ! ! ! " ) ; f . s e t L a y o u t (new FlowLayout ( ) ) ; f . s e t P r e f e r r e d S i z e (new Dimension ( 5 0 0 , 5 0 0 ) ) ; J B u t t o n b u t t o n 1 =new J B u t t o n ( " Bouton 1 " ) ; b u t t o n 1 . s e t P r e f e r r e d S i z e (new Dimension ( 1 0 0 , 1 0 0 ) ) ; b u t t o n 1 . a d d A c t i o n L i s t e n e r (new A c t i o n L i s t e n e r ( ) { public void a c t i o n P e r f o r m e d ( A c t i o n E v e n t e ) { actionButton1 (e ) ; } }); f . getContentPane ( ) . add ( b u t t o n 1 ) ; f . pack ( ) ; f . s e t V i s i b l e ( true ) ; } s t a t i c void a c t i o n B u t t o n 1 ( A c t i o n E v e n t e ) { System . o u t . p r i n t l n ( " j ’ appuie s u r J1 " ) ; } F. Aubert PJE MultiTouch/ Présentation de l’interface MT 2019-2020 12 / 32
Components/Containers I Tous les composants et conteneurs héritent de JComponent I Containers SWING utilisés pour le projet : JFrame (pour lancer l’interface de l’appli), JPanel pour faire un cadre et dessiner à l’intérieur. I Mais nous allons définir nos propres composants et containers (avec leur mécanisme de listeners) : • MTComponent : affichage = photo ; événements = message des curseurs (add, update, remove des doigts). • MTSurface : initialisera et contiendra les MTComponent (i.e. les photos). F. Aubert PJE MultiTouch/ Présentation de l’interface MT 2019-2020 13 / 32
2 Créer ses propres listeners F. Aubert PJE MultiTouch/ Présentation de l’interface MT 2019-2020 14 / 32
Basé sur le mécanisme de SWING I On veut retrouver ce style de code sur nos propres composants (exemple du bouton cliqué) : / / L i s t e n e r d ’ un c l i c k ( i . e . événement A c t i o n ) s u r un bouton SWING : J B u t t o n b=new J B u t t o n ( " h e l l o " ) ; b . a d d A c t i o n L i s t e n e r (new A c t i o n L i s t e n e r ( ) { public void a c t i o n P e r f o r m e d ( A c t i o n E v e n t e v t ) { System . o u t . p r i n t l n ( " c l i c k s u r h e l l o " ) ; } }); I Autrement dit : • Créer des objets qui diffusent des événements qui puissent être écoutés (écoutables avec un add...Listener) • Créer des écouteurs qui appellent la méthode voulue pour un événement donné (écouteurs avec des méthodes ...Performed). F. Aubert PJE MultiTouch/ Présentation de l’interface MT 2019-2020 15 / 32
Exemple I L’exemple présenté s’appuie sur un générateur de nombres aléatoires : • On suppose une boucle infinie qui génère des nombres aléatoires (à comparer à l’EDT) • Un nombre impair génère un événement (à comparer au click sur un bouton). • Cet événement est diffusé par un objet Ecoutable (le bouton). • On souhaite alors qu’un objet quelconque puisse écouter (et réagir à) cet événement. I On propose donc de : 1 Définir la classe des événements pouvant être générés (la classe ImpairEvent dans l’exemple). 2 Faire la génération d’événements (voir méthode generate dans la suite). 3 Inclure une liste des écouteurs pour la classe écoutable (classe Ecoutable). 4 Faire l’interface des écouteurs possibles (interface ImpairListener dans la suite). 5 Exploiter le mécanisme d’écouteur pour réagir aux événements (simple affichage d’un message dans l’exemple qui suit). F. Aubert PJE MultiTouch/ Présentation de l’interface MT 2019-2020 16 / 32
Exploiter le mécanisme On souhaite donc pouvoir faire le code suivant (à comparer avec l’exemple du bouton swing) : / / f a i r e un é c o u t e u r d ’ un événement I m p a i r s u r un o b j e t de c l a s s e Ecoutable : Ecoutable unEcoutable=new Ecoutable ( ) ; unEcoutable . a d d I m p a i r L i s t e n e r (new I m p a i r L i s t e n e r ( ) { public void i mp ai r Pe r fo rm e d ( I m p a i r E v e n t e v t ) { System . o u t . p r i n t l n ( " Evénement nombre i m p a i r : " + e v t . getValue ( ) ) ; } }); Pour mémoire (exemple du bouton SWING) : / / L i s t e n e r d ’ un événement A c t i o n ( i . e . un c l i c k ) s u r un bouton SWING : J B u t t o n b=new J B u t t o n ( " h e l l o " ) ; b . a d d A c t i o n L i s t e n e r (new A c t i o n L i s t e n e r ( ) { public void a c t i o n P e r f o r m e d ( A c t i o n E v e n t e v t ) { System . o u t . p r i n t l n ( " c l i c k s u r h e l l o " ) ; } }); F. Aubert PJE MultiTouch/ Présentation de l’interface MT 2019-2020 17 / 32
La classe des événements Classe des événements (elle inclut toutes les infos utiles/nécessaires à l’événement) : public class I m p a i r E v e n t extends AWTEvent { p r i v a t e double v ; public I m p a i r E v e n t ( O b j e c t source , i n t i d , double v a l u e ) { super ( source , i d ) ; v= v a l u e ; } public double getValue ( ) { return v ; } } F. Aubert PJE MultiTouch/ Présentation de l’interface MT 2019-2020 18 / 32
La génération des événements A inclure dans une boucle infinie (thread qui gère les événements : à comparer avec l’Event Dispatch Thread) : public void generate ( ) { i n t a =( i n t ) ( Math . random ( ) ∗ 1 0 0 0 . 0 ) ; i f ( a % 2==1) { I m p a i r E v e n t e v t =new I m p a i r E v e n t ( unComposant , 0 , a ) ; / / => dé t e r m i n e r l ’ o b j e t concern é par l ’ événement ; i c i on suppose qu ’ i l s ’ a g i t de unEcoutable unEcoutable . f i r e I m p a i r P e r f o r m e d ( e v t ) ; / / d i f f u s e r l ’ événement aux é c o u t e u r s } } F. Aubert PJE MultiTouch/ Présentation de l’interface MT 2019-2020 19 / 32
Interface de l’écouteur Tous les écouteurs doivent implémenter la méthode qui doit être appelée lors de l’événement : public i n t e r f a c e I m p a i r L i s t e n e r extends E v e n t L i s t e n e r { public void i m p a i r Pe r fo r me d ( I m p a i r E v e n t e v t ) ; } F. Aubert PJE MultiTouch/ Présentation de l’interface MT 2019-2020 20 / 32
Les objets écoutables public class Ecoutable extends JComponent { / / L i s t e d ’ écouteurs : private EventListenerList l i s t e n e r L i s t =null ; public Composant ( ) { l i s t e n e r L i s t =new E v e n t L i s t e n e r L i s t ( ) ; } / / a j o u t e r un é c o u t e u r public void a d d I m p a i r L i s t e n e r ( I m p a i r L i s t e n e r e ) { l i s t e n e r L i s t . add ( I m p a i r L i s t e n e r . class , e ) ; } / / informer tous les écouteurs public void f i r e I m p a i r P e r f o r m e d ( AWTEvent event ) { Object [ ] l i s t e n e r s = l i s t e n e r L i s t . g e t L i s t e n e r L i s t ( ) ; f o r ( i n t i = l i s t e n e r s . l e n g t h −1; i >=0; i −−) { i f ( ( l i s t e n e r s [ i ] == I m p a i r L i s t e n e r . class ) ) ( ( I m p a i r L i s t e n e r ) l i s t e n e r s [ i + 1 ] ) . im p a i rP e rf or m ed ( ( I m p a i r E v e n t ) event ) ; } } } F. Aubert PJE MultiTouch/ Présentation de l’interface MT 2019-2020 21 / 32
3 Introduction à JAVA 2D F. Aubert PJE MultiTouch/ Présentation de l’interface MT 2019-2020 22 / 32
paintComponent I Tutoriel : http://docs.oracle.com/javase/tutorial/2d/ I JComponent : méthode paintComponent appelée à chaque événement repaint() (provoqué par le système, ou directement par le programmeur). I Exemple : surcharge de JButton : s t a t i c public void createAndShowGUI ( ) { JFrame f =new JFrame ( " H e l l o World ! ! ! " ) ; import j a v a x . swing . ∗ ; f . s e t P r e f e r r e d S i z e (new Dimension ( 5 0 0 , 5 0 0 ) ) ; import j a v a . awt . ∗ ; import j a v a . awt . geom. ∗ ; CButton b u t t o n 1 =new CButton ( " Bouton 1 " ) ; ... public class CButton extends J B u t t o n { public CButton ( S t r i n g s ) { super ( s ) ; } public void paintComponent ( Graphics g ) { Graphics2D g2 =( Graphics2D ) g ; g2 . f i l l (new E l l i p s e 2 D . Double ( 3 0 , 3 0 , 4 0 , 4 0 ) ) ; } } F. Aubert PJE MultiTouch/ Présentation de l’interface MT 2019-2020 23 / 32
Graphics2D I paintComponent(Graphics g) : g = environnement graphique du composant I Cast en Graphics2D pour bénéficier de toutes les fonctionnalités de Java2D I Les méthodes de Graphics2D permettent de : • lire/modifier les attributs de tracé (hauteur, largeur, couleur, épaisseur de traits, transformations...) : l’ensemble des valeurs de ces attributs à un instant donné constitue l’état courant du tracé. • dessiner (rectangle, ellipse, polygones, images, textes...) dans l’état courant. Repère initial : Pour connaitre la hauteur et la lar- geur (en pixels), utiliser getWidth() et getHeight() du composant. F. Aubert PJE MultiTouch/ Présentation de l’interface MT 2019-2020 24 / 32
draw/fill I draw(Shape s) : trace une forme géométrique. I fill(Shape s) : remplit la forme géométrique. public class MyPanel extends JPanel { public class Main { public MyPanel ( ) { super ( ) ; s t a t i c public void createAndShowGUI ( ) { } JFrame f =new JFrame ( " H e l l o World ! ! ! " ) ; f . s e t L a y o u t (new FlowLayout ( ) ) ; public void paintComponent ( Graphics g ) { f . s e t P r e f e r r e d S i z e (new Dimension ( 5 0 0 , 5 0 0 ) ) ; Graphics2D g2 =( Graphics2D ) g ; MyPanel panel=new MyPanel ( ) ; g2 . s e t C o l o r (new C o l o r ( 2 0 0 , 0 , 0 ) ) ; panel . s e t P r e f e r r e d S i z e (new Dimension ( 3 0 0 , 3 0 0 ) ) ; g2 . draw (new Rectangle2D . Double ( 1 0 , 1 0 , 1 0 0 , 1 0 0 ) ) ; panel . s e t B o r d e r (new L i n e B o r d e r ( g2 . s e t C o l o r (new C o l o r ( 0 , 0 , 1 0 0 ) ) ; new C o l o r ( 0 , 2 0 0 , 0 ) , 3 ) ) ; g2 . f i l l (new Rectangle2D . Double ( 1 5 0 , 1 0 , 1 0 0 , 1 0 0 ) ) ; } f . getContentPane ( ) . add ( panel ) ; } f . pack ( ) ; f . s e t V i s i b l e ( true ) ; } s t a t i c public void main ( S t r i n g arg [ ] ) { S w i n g U t i l i t i e s . i n v o k e L a t e r (new Runnable ( ) { public void run ( ) { createAndShowGUI ( ) ; } }); } } F. Aubert PJE MultiTouch/ Présentation de l’interface MT 2019-2020 25 / 32
Remarques I pour Graphics2D • setColor(new Color(r,v,b)) : fixe la couleur courante en composantes (rouge,vert,bleu) (∈ [0, 255]). • new Rectangle2D.Double(x0,y0,width,height) : dérive de Shape I pour JPanel : • Container simple, sans particularité : très souvent utilisé pour faire un espace de dessin. • setBorder(new LineBorder(Color c, int thickness)) : permet de déssiner un bord. F. Aubert PJE MultiTouch/ Présentation de l’interface MT 2019-2020 26 / 32
Lignes Polygonales public void paintComponent ( Graphics g ) { Graphics2D g2 =( Graphics2D ) g ; GeneralPath p=new GeneralPath ( ) ; p . moveTo ( 5 0 , 2 0 0 ) ; p . lineTo (150 , 100); p . lineTo (250 , 200); / / p . c l o s e P a t h ( ) ; / / s i on s o u h a i t e f e r m e r l e path / / ( i . e . r e l i e r avec l e p o i n t i n i t i a l ) g2 . draw ( p ) ; } F. Aubert PJE MultiTouch/ Présentation de l’interface MT 2019-2020 27 / 32
Tracer une image import java . i o .∗; import j a v a x . swing . ∗ ; import j a v a x . imageio . ∗ ; import j a v a . awt . ∗ ; import j a v a . awt . image . ∗ ; import j a v a . awt . geom. ∗ ; public class MyPanel extends JPanel { BufferedImage img ; public MyPanel ( ) { super ( ) ; try { img=ImageIO . read ( new F i l e ( " data / univ − l i l l e 1 _ p e t i t e . j p g " ) ) ; } catch ( IOException e ) { e . printStackTrace ( ) ; } } public void paintComponent ( Graphics g ) { super . paintComponent ( g ) ; Graphics2D g2 =( Graphics2D ) g ; g2 . drawImage ( img , 5 0 , 1 0 0 , n u l l ) ; } } F. Aubert PJE MultiTouch/ Présentation de l’interface MT 2019-2020 28 / 32
4 Fondations de l’application MultiTouch F. Aubert PJE MultiTouch/ Présentation de l’interface MT 2019-2020 29 / 32
Vers un Widget Toolkit I MTsurface : Top-Level container des composants multi-touch. • hérite de JPanel pour être intégré à une application SWING. • possèdera un unique MTcontainer (container racine). • reçoit tous les messages de position des curseurs : c’est MTsurface qui détermine à quel composant cela s’adresse (fonctionnement simplifié par rapport à l’edt de SWING). I MTedt : reçoit les événements TUIO et les transmets à MTsurface (intermédiaire entre le serveur TUIO et MTsurface). F. Aubert PJE MultiTouch/ Présentation de l’interface MT 2019-2020 30 / 32
MTedt : lanceur et écouteur du client TUIO I On utilise un client TUIO déjà implémenté en Java (http ://www.tuio.org/ ?software). • classe TuioClient : assure la connexion UDP avec le serveur TUIO. • classe TuioListener : interface pour écouter les événements TUIO. public class MTedt implements T u i o L i s t e n e r { TuioClient c l i e n t =null ; MTSurface s u r f a c e ; public MTedt ( MTSurface s ) { i n t p o r t =3333; c l i e n t =new T u i o C l i e n t ( p o r t ) ; c l i e n t . addTuioListener ( this ) ; c l i e n t . connect ( ) ; i f ( ! c l i e n t . isConnected ( ) ) { System . e x i t ( 1 ) ; } System . o u t . p r i n t l n ( " c o n n e c t i o n " ) ; s u r f a c e =s ; } ... } F. Aubert PJE MultiTouch/ Présentation de l’interface MT 2019-2020 31 / 32
MTedt : réponses aux messages public class MTedt implements T u i o L i s t e n e r { TuioClient c l i e n t =null ; MTSurface s u r f a c e ; ... public void addTuioCursor ( TuioCursor t c u r ) { // System . o u t . p r i n t l n ( " add c u r "+ t c u r . g e t Cu r s o r ID ( ) + " ( " + t c u r . getSessionID ( ) + " ) "+ t c u r . getX ( ) + " "+ t c u r . getY ( ) ) ; s u r f a c e . addCursor ( t c u r . ge t C u r so r I D ( ) , t c u r . getX ( ) , t c u r . getY ( ) ) ; } public void updateTuioCursor ( TuioCursor t c u r ) { // System . o u t . p r i n t l n ( " s e t c u r "+ t c u r . g e t Cu r s o r ID ( ) + " ( " + t c u r . getSessionID ( ) + " ) "+ t c u r . getX ( ) + " "+ t c u r . getY ( ) + " "+ // t c u r . getMotionSpeed ( ) + " "+ t c u r . g e t M o t i o n A c c e l ( ) ) ; s u r f a c e . updateCursor ( t c u r . ge t C u r so r I D ( ) , t c u r . getX ( ) , t c u r . getY ( ) ) ; } public void removeTuioCursor ( TuioCursor t c u r ) { // System . o u t . p r i n t l n ( " d e l c u r "+ t c u r . g e t Cu r s o r ID ( ) + " ( " + t c u r . getSessionID ( ) + " ) " ) ; s u r f a c e . removeCursor ( t c u r . ge t C u r so r I D ( ) , t c u r . getX ( ) , t c u r . getY ( ) ) ; } } F. Aubert PJE MultiTouch/ Présentation de l’interface MT 2019-2020 32 / 32
Vous pouvez aussi lire