Licence CC BY-NC-SA

Intéragir avec l'utilisateur

Ce contenu est obsolète. Il peut contenir des informations intéressantes mais soyez prudent avec celles-ci.

Maintenant que toutes les bases sur la programmation événementielle sont posées, nous allons pouvoir mettre tout ceci en pratique au cours de ce chapitre. Au programme, nous apprendrons comment utiliser et manipuler des données saisies par l'utilisateur. Cela pourra être des données issues de la souris ou du clavier.

La souris

L'objet MouseEvent

Introduction

Ici nous allons apprendre à gérer et manipuler des évènements provenant de la souris. Lorsque qu'un tel évènement se produit, un objet de la classe MouseEvent est généré. Comme nous l'avons vu, tous les objets évènements héritent de la classe Event et de ses propriétés (voir figure suivante).

La classe MouseEvent

Dans le chapitre précédent, j'avais mentionné le fait que les objets renfermaient des informations liés à l'évènement. Ainsi la classe MouseEvent dispose principalement de propriétés contenant des informations en lien avec la souris, notamment la position du curseur au moment de la génération de l'évènement. Étant donné que les interactions avec la souris sont étroitement liés à l'interface graphique, cette classe sera évidemment distribuée dans le flux d'évènements.

Nous allons maintenant faire un tour des principales constantes et propriétés de cette classe. Néanmoins, nous ne les verrons pas toutes, et je vous conseille donc de vous rendre sur la page de la classe MouseEvent du guide de référence.

Les différents types d'évènements

Les types d'évènements liés à la souris sont définis sous forme de constantes statiques de la classe MouseEvent. On les utilisent ainsi :

1
2
3
4
MouseEvent.NOM_DE_LA_CONSTANTE

// Par exemple :
MouseEvent.CLICK

Voici ces constantes :

  • CLICK : cette constante définit un clic avec le bouton gauche de la souris (il faut que le bouton soit successivement enfoncé puis relâché tout en étant au-dessus de la cible à chaque fois).
  • DOUBLE_CLICK : celle-ci définit un double-clic de la souris. Toutefois la gestion de ce type d'évènements nécessite d'affecter la valeur true à la propriété doubleClickEnabled de l'objet en question.
  • MOUSE_DOWN : cette constante décrit l'enfoncement du bouton gauche de la souris.
  • MOUSE_UP : à l'opposé de MOUSE_DOWN, MOUSE_UP est produit par le relâchement du bouton gauche de la souris.
  • MIDDLE_CLICK : cette fois-ci, il s'agit d'un clic à l'aide du bouton du milieu de la souris (qui est en général la molette de la souris).
  • MIDDLE_MOUSE_DOWN : cette constante décrit l'enfoncement du bouton du milieu de la souris.
  • MIDDLE_MOUSE_UP : comme pour MOUSE_UP, MIDDLE_MOUSE_UP est produit par le relâchement du bouton du milieu de la souris, à l'opposé de MIDDLE_MOUSE_DOWN.
  • RIGHT_CLICK : pour ce type d'événement, il s'agit d'un clic à l'aide du bouton droit de la souris.
  • RIGHT_MOUSE_DOWN : cette constante décrit l'enfoncement du bouton droit de la souris.
  • RIGHT_MOUSE_UP : RIGHT_MOUSE_UP est produit par le relâchement du bouton droit de la souris, à l'opposé de RIGHT_MOUSE_DOWN.
  • MOUSE_MOVE : un tel évènement se produit à chaque déplacement de la souris, même d'un seul et unique pixel !
  • MOUSE_WHEEL : cette constante définit une action de défilement à l'aide de la molette de la souris. La direction de la molette est définie dans l'attribut delta de l'objet de l'événement, qui est alors un entier positif ou négatif.
  • ROLL_OVER : cette constante définit le survol de l'objet courant, ou de l'un de ses enfants, par la souris.
  • ROLL_OUT : à l'inverse de MOUSE_OVER, cet évènement est généré lorsque la souris quitte la « zone de tracé » d'un objet d'affichage, et de l'ensemble de ses enfants.

Grâce à toutes ces constantes que vous pouvez combiner, il vous est maintenant possible de prendre en compte les commande de l'utilisateur avec la souris.

Il existe également les constantes MOUSE_OVER et MOUSE_OUT, très semblables au couple ROLL_OVER / ROLL_OUT. La différence se situe au niveau des objets d'affichage enfants de la cible de l'événement : dans le cas de MOUSE_OVER et MOUSE_OUT, l'événement sera déclenché à chaque fois que la souris quitte la surface d'un enfant pour aller sur un autre, même s'il n'y a pas d'espace vide entre eux. A l'inverse, pour ROLL_OVER et ROLL_OUT, la cible est ses enfants sont considérés comme étant une unique surface : l'événement n'est déclenché qu'une seule fois. Il est donc fortement conseillé de toujours utiliser ROLL_OVER et ROLL_OUT.

Imaginons que vous avez créé un bouton constitué d'une image de fond assez grande et d'un champ de texte au milieu, et que vous voulez l'animez au passage de la souris. En utilisant MOUSE_OVER et MOUSE_OUT, un événement de type MOUSE_OVER se déclenchera d'abord lorsque la souris arrivera sur l'image de fond. Mais si la souris passe ensuite sur le champ de texte, un événement de type MOUSE_OUT apparaîtra (la souris a quitté la surface d'un enfant du bouton) puis immédiatement après un autre événement de type MOUSE_OVER sera déclenché (la souris arrive au-dessus du champ de texte), et vice-versa. Cela n'est pas vraiment optimal, et en plus cela peut provoquer des bugs d'animation pas très agréables sur le bouton lorsque la souris passe dessus.

Si vous utilisez ROLL_OVER et ROLL_OUT, un événement de type ROLL_OVER se déclenchera une seule fois lorsque la souris entrera sur la surface de l'image de fond, même si la souris passe de l'image au champ de texte. Enfin, un unique événement de type ROLL_OUT sera déclenché lorsque la souris quittera l'image de fond. En effet, les surfaces de chaque enfant ne sont pas considérées comme indépendantes : elles forment un tout (la surface totale du bouton).

Quelques propriétés utiles

La classe MouseEvent possède un certain nombre d'attributs, ou de propriétés. En voici quelques unes qui pourront vous êtes très utiles :

  • stageX : contient la position horizontale globale où s'est produit l'évènement.
  • stageY : contient la position verticale globale où s'est produit l'évènement.
  • localX : contient la position horizontale locale par rapport au conteneur parent où s'est produit l'évènement.
  • localY : contient la position verticale locale par rapport au conteneur parent où s'est produit l'évènement.
  • buttonDown : indique si le bouton est enfoncé, principalement utile pour les évènements de type MOUSE_MOVE.
  • altKey : indique si la touche Alt est enfoncée en parallèle de l'événement de la souris.
  • ctrlKey : indique si la touche Ctrl (ou Cmd pour les utilisateurs de Mac) est enfoncée en parallèle de l'événement de la souris.
  • shiftKey : indique si la touche Shift est enfoncée en parallèle de l'événement de la souris.
  • delta (uniquement pour les événements de type MOUSE_WHEEL) : contient un entier positif si la molette est défilée vers le haut, négatif dans le cas contraire.

Les trois propriétés altKey, ctrlKey et shiftKey sont uniquement des indicateurs permettant de déterminer si les touches correspondantes sont enfoncées lors de la génération de l'évènement. Des actions sur ces boutons n'engendrent en aucun cas l'émission d'un objet MouseEvent.

Exemple: bouton simple

Pour illustrer ce que nous venons de voir, créons notre premier bouton ensemble ! Voici le code à écrire pour le bouton :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// Bouton
var bouton:Sprite = new Sprite();
// Autorisation du double-clic
bouton.doubleClickEnabled = true;
// Curseur de bouton (généralement en forme de main)
bouton.buttonMode = true;
bouton.useHandCursor = true;
// Les enfants ne sont pas gérés par la souris afin qu'ils n'interfèrent pas avec le curseur de la souris
// (sinon le champ de texte pourrait annuler le curseur de bouton)
bouton.mouseChildren = false;
addChild(bouton);

// Texte
var texte:TextField = new TextField();
// Pour éviter que le texte soit sélectionné
texte.selectable = false;
// Taille automatique
texte.autoSize = TextFieldAutoSize.LEFT;
// Format du texte
texte.defaultTextFormat = new TextFormat('Arial', 32, 0xffffff);
// Contenu
texte.text = "Je suis un bouton";
// Ajout du texte dans le bouton
bouton.addChild(texte);

// Fond bleu
var fond:Shape = new Shape();
// Ligne
fond.graphics.lineStyle(2, 0x9999ff, 1, true);
// Remplissage
fond.graphics.beginFill(0x0000ff);
// Dessin d'un rectangle à coins arrondis
fond.graphics.drawRoundRect(0, 0, texte.width, texte.height, 12);
// Ajout du fond bleu dans le bouton, en dessous du texte (cad, à l'index 0)
bouton.addChildAt(fond, 0);

// Alignement du bouton au centre de la scène
bouton.x = (stage.stageWidth - bouton.width) / 2;
bouton.y = (stage.stageHeight - bouton.height) / 2;

Nous créons en premier lieu un sprite qui va représenter le bouton. On active le double clic à l'aide de l'attribut doubleClickEnabled, puis on active le curseur de bouton (généralement en forme de main) en mettant les attributs buttonMode et useHandCursor à true.

Pourquoi désactiver la gestion de la souris sur les enfants du bouton ?

Si nous ne le faisons pas, le champ de texte aura le fâcheux comportement de mal faire fonctionner le curseur, qui sera remis en pointeur par défaut (au lieu du curseur de bouton en forme de main). Ainsi, par sécurité, il vaut mieux mettre l'attribut mouseChildren à false pour les enfants soient plus directement gérés par la souris. Toutefois, ils comptent quand-même dans la surface totale du bouton. ;)

Puis nous créons le champ de texte qui affichera l'étiquette du bouton, suivie d'un dessin de rectangle arrondi bleu pour embellir tout ceci.

Ensuite, écrivons les fonctions d'écouteur qui nous permettront de tester différents événements de la souris :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// Fonctions d'écouteurs
function onOver(e:MouseEvent):void
{
    trace("La souris est entrée au-dessus du bouton.");
}
function onOut(e:MouseEvent):void
{
    trace("La souris n'est plus au-dessus du bouton.");
}
function onDown(e:MouseEvent):void
{
    trace("Le bouton gauche de la souris est enfoncé au-dessus du bouton.");
}
function onUp(e:MouseEvent):void
{
    trace("Le bouton gauche de la souris est relâché au-dessus du bouton.");
}
function onClick(e:MouseEvent):void
{
    trace("Le bouton a été cliqué.");
}
function onDoubleClick(e:MouseEvent):void
{
    trace("Le bouton a été double-cliqué.");
}
function onMiddleClick(e:MouseEvent):void
{
    trace("Le bouton a été cliqué milieu.");
}
function onRightClick(e:MouseEvent):void
{
    trace("Le bouton a été cliqué droit.");
}
function onMove(e:MouseEvent):void
{
    trace("La souris s'est déplacée au-dessus du bouton.");
}

Enfin, nous écoutons les différents événements de la souris sur le bouton, qui en sera alors la cible :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// Evénements de la souris
bouton.addEventListener(MouseEvent.ROLL_OVER, onOver);
bouton.addEventListener(MouseEvent.ROLL_OUT, onOut);
bouton.addEventListener(MouseEvent.MOUSE_DOWN, onDown);
bouton.addEventListener(MouseEvent.MOUSE_UP, onUp);
bouton.addEventListener(MouseEvent.CLICK, onClick);
bouton.addEventListener(MouseEvent.DOUBLE_CLICK, onDoubleClick);
bouton.addEventListener(MouseEvent.MIDDLE_CLICK, onMiddleClick);
bouton.addEventListener(MouseEvent.RIGHT_CLICK, onRightClick);
bouton.addEventListener(MouseEvent.MOUSE_MOVE, onMove);

Nous avons à l'écran quelque chose comme la figure suivante.

Notre bouton bleu !

Si vous jouez un peu avec, vous obtiendrez des informations dans la console sur les différents événements qui se déclenchent :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
La souris est entrée au-dessus du bouton.
La souris s'est déplacée au-dessus du bouton.
La souris s'est déplacée au-dessus du bouton.
La souris s'est déplacée au-dessus du bouton.
Le bouton gauche de la souris est enfoncé au-dessus du bouton.
Le bouton gauche de la souris est relâché au-dessus du bouton.
Le bouton a été cliqué.
Le bouton a été cliqué milieu.
Le bouton a été cliqué droit.
La souris s'est déplacée au-dessus du bouton.
La souris s'est déplacée au-dessus du bouton.
La souris s'est déplacée au-dessus du bouton.
La souris n'est plus au-dessus du bouton.

La technique du « glisser-déposer »

Le glisser-déposer (ou drag and drop en anglais) est une méthode qui consiste à déplacer un élément graphique d'un endroit à un autre. Il s'agit du même principe que lorsque que vous déplacez une icône sur votre bureau. Pour cela, vous effectuez un clic que vous maintenez le temps du déplacement. Puis le déplacement s'arrête lorsque vous relâchez le bouton de la souris.

En Actionscript, ce concept est beaucoup plus poussé et puissant, puisqu'il est possible de contrôler à souhaits ce fameux « glisser-déposer ». Une manière de réaliser ceci, serait de combiner intelligemment les trois types d'évènements MOUSE_DOWN, MOUSE_MOVE et MOUVE_UP. Mais l'Actionscript facilite davantage la mise en place de cette technique. C'est pourquoi la classe Sprite dispose des deux méthodes startDrag() et stopDrag(), permettant respectivement d'activer ou de désactiver le glisser-déposer sur cet objet.

Ici, nous allons utiliser un exemple qui sera beaucoup plus parlant. Commençons par dessiner un carré violet dans notre scène :

1
2
3
4
var objet:Sprite = new Sprite();
addChild(objet);
objet.graphics.beginFill(0x8800FF);
objet.graphics.drawRect(0, 0, 50, 50);

En utilisant ces fameuses méthodes startDrag() et stopDrag(), il n'est maintenant plus nécessaire de se préoccuper des déplacements de la souris. Seuls le démarrage et l'arrêt du glisser-déposer doivent être contrôlés. Nous pouvons alors très facilement mettre ceci en place grâce aux deux écouteurs suivants :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
objet.addEventListener(MouseEvent.MOUSE_DOWN, glisser);
function glisser(event:MouseEvent):void 
{ 
    objet.startDrag();
}
objet.addEventListener(MouseEvent.MOUSE_UP, deposer);
function deposer(e:MouseEvent):void 
{ 
    objet.stopDrag();
}

À l'intérieur du Flash Player, il est maintenant possible de déplacer notre objet à volonté !

Exercice : Créer et animer un viseur

Dessiner le viseur

Dans cet exercice, nous allons voir comment dessiner et animer un viseur, tel que nous pourrions en trouver dans un jeu de tir. Nous commencerons donc par tracer l'élément qui vous servira de viseur.

Voici à la figure suivante le viseur que je vous propose de dessiner.

Le viseur

Créons donc un objet viseur de type Shape, et traçons les différents éléments qui compose celui-ci :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
var viseur:Shape = new Shape();
addChild(viseur);
viseur.graphics.lineStyle(2, 0x000000);
viseur.graphics.drawCircle(0, 0, 20);
viseur.graphics.lineStyle(1, 0x550000);
viseur.graphics.drawCircle(0, 0, 16);
viseur.graphics.moveTo(-25, 0);
viseur.graphics.lineTo(25, 0);
viseur.graphics.moveTo(0, -25);
viseur.graphics.lineTo(0, 25);
viseur.x = stage.stageWidth / 2;
viseur.y = stage.stageHeight / 2;

Comme vous pouvez le voir, il n'y a rien de bien compliqué dans ce code. Nous avons uniquement tracé deux cercles concentriques et deux segments perpendiculaires.

En réalité pour cet exercice, nous n'allons pas réellement remplacé le curseur. Nous allons nous contenter de « suivre » la position de la souris, et nous masquerons la souris elle-même. Cela donnera alors l'illusion que le curseur est un viseur.

Pour masquer la souris, utilisez l'instruction suivante :

1
Mouse.hide();

La fonction hide() est une méthode statique de la classe Mouse. N'oubliez donc pas d'importer cette classe dans votre projet. Pour faire réapparaître votre curseur, vous pouvez utiliser la méthode statique inverse : show().

Gérer les évènements

À présent, occupons-nous de la gestion des évènements ! Nous allons donc faire ça en deux temps. Premièrement, nous allons faire en sorte que le viseur suive le curseur en mettant à jour sa position. Ensuite, nous ajouterons un second écouteur pour simuler un tir . Bien sûr tout cela sera basique, mais l'idée sera là.

Pour mettre à jour la position du viseur, nous allons utiliser les évènements de type MOUSE_MOVE. Les propriétés stageX et stageY de la classe MouseEvent nous serviront à replacer le viseur.

Voilà donc la mise en place de notre écouteur :

1
2
3
4
5
6
stage.addEventListener(MouseEvent.MOUSE_MOVE, repositionnerViseur);
function repositionnerViseur(event:MouseEvent):void 
{ 
    viseur.x = event.stageX;
    viseur.y = event.stageY;
}

Nous allons maintenant nous concentrer sur la gestion des évènements de type « tirs ». Je vous propose pour cela, d'utiliser le clic de la souris, ou plutôt l'enfoncement du bouton de gauche. Il s'agit bien évidemment de l'évènement MOUSE_DOWN.

À l'intérieur de la fonction d'écouteur associée, nous créerons un petit rond rouge, grossièrement semblable à une tâche de sang. Puis nous la positionnerons au niveau du viseur. Rien de bien complexe ici, voici le code correspondant :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
stage.addEventListener(MouseEvent.MOUSE_DOWN, tirer);
function tirer(event:MouseEvent):void 
{ 
    var impact:Shape = new Shape();
    impact.graphics.beginFill(0xFF0000);
    impact.graphics.drawCircle(0, 0, 4);
    impact.x = event.stageX;
    impact.y = event.stageY;
    addChildAt(impact, 0);
}

Ici, nous avons utilisé un évènement de type MOUSE_DOWN, mais nous pourrions très bien pu le faire à l'aide des évènements MOUSE_UP ou CLICK. Lorsque vous programmer votre interface, c'est à vous de choisir le type d'évènements qui correspond le mieux à ce que vous souhaitez faire.

Le rendu

Pour admirer le magnifique rendu de cette application (voir figure suivante), je ne peux que vous inviter à exécuter le code, et à tirer dans tous les sens !!!

Les prémices d'un jeu de tir

Curseurs personnalisés

Il est possible de remplacer le curseur de la souris par une image ou une animation de notre choix, ce qui présente des avantages par rapport à la technique que nous venons de voir pour le viseur : le curseur sera un curseur natif, c'est-à-dire qu'il sera directement géré par le système d'exploitation de l'utilisateur. Cela implique de meilleures performances : moins de ressources seront consommé pour afficher le curseur et même si votre application est victime de ralentissement, votre curseur personnalisé n'en sera pas affecté, ce qui améliore grandement l'expérience utilisateur. De plus, le curseur ne sera pas coupé aux bords de la zone d'affichage de votre application : il pourra dépasser en dehors de la fenêtre (par exemple, s'il se situe en bas de votre application, il continuera d'être affiché, même en dehors de la scène). Il est donc très fortement conseillé d'utiliser les curseurs natifs personnalisés si vous voulez remplacer le curseur de la souris : c'est la meilleure méthode.

Il est possible que le système sur lequel votre application est lancée ne supporte pas les curseurs natifs (c'est par exemple le cas pour les smartphones) : vous pouvez le savoir avec l'attribut statique supportsNativeCursor de la classe Mouse.

Ainsi, nous n'utiliserons de curseur natif que si cette fonctionnalité est supportée :

1
2
3
4
if(Mouse.supportsNativeCursor)
{
    // Création du curseur natif personnalisé ici
}

Un curseur natif ne peut pas dépasser une taille de 32 pixels sur 32 pixels.

Préparer son curseur

Il faut savoir qu'un curseur personnalisé ne peut être qu'une image bitmap (ou une série d'images dans le cas des curseurs animés). Ce qui signifie qu'il nous est impossible d'utiliser directement un objet d'affichage comme le dessin (de la classe Shape) que nous avons créé pour afficher notre viseur. Toutefois, il existe un moyen de prendre une capture de n'importe quel objet d'affichage pour obtenir une image bitmap ! :magicien:

Il est bien entendu également possible d'utiliser directement des images que vous aurez incorporées au préalable. ;)

Reprenons le code que nous avons utilisé pour créer notre viseur sans l'ajouter à la scène :

1
2
3
4
5
6
7
8
9
var viseur:Shape = new Shape();
viseur.graphics.lineStyle(2, 0x000000);
viseur.graphics.drawCircle(0, 0, 20);
viseur.graphics.lineStyle(1, 0x550000);
viseur.graphics.drawCircle(0, 0, 16);
viseur.graphics.moveTo(-25, 0);
viseur.graphics.lineTo(25, 0);
viseur.graphics.moveTo(0, -25);
viseur.graphics.lineTo(0, 25);

Nous ne pouvons pas utiliser ce dessin pour en faire un curseur natif, mais nous pouvons en prendre une capture (comme vous prendriez une capture d'écran). Pour cela, nous allons d'abord créer une image vide avec la classe BitmapData et utiliser sa méthode draw() (qui signifie 'dessiner'). Cette méthode prend en paramètre n'importe quel objet qui implémente l'interface IBitmapDrawable. Comme nous pouvons le voir dans la documentation, il s'agit de tous les objets ayant pour classe (ou super-classe) BitmapData ou DisplayObject.

Pour créer l'image bitmap, il nous faut les bornes d'affichage de notre viseur : en effet, notre dessin est centré par rapport à son origine, donc si nous en faisons une capture sans en tenir compte, il sera coupé à partir de son origine (donc nous n'obtiendrons qu'un quart du viseur). Nous calculons les bornes d'affichage de notre dessin grâce à la méthode getBounds() de la classe DisplayObject, qui prend en paramètre un objet d'affichage servant de référence. Ici, nous passons donc le viseur lui-même pour savoir de combien il dépasse par rapport à sa propre origine :

1
2
3
// Bornes d'affichage de l'objet
var bornes:Rectangle = viseur.getBounds(viseur);
trace("bornes:" + bornes);

Si vous testez votre code tout de suite, vous obtiendrez ceci dans la console :

1
bornes:(x=-25.5, y=-25.5, w=51, h=51)

Nous pouvons alors remarquer que notre dessin dépasse de 25 pixels et demi par rapport à son origine, verticalement et horizontalement. Sa taille totale est de 51 pixels sur 51 pixels.

Créons notre image vide :

1
2
// Image vide de bonne taille
var capture:BitmapData = new BitmapData(32, 32, true, 0);

Ensuite, pour prendre en compte le fait que notre dessin dépasse de son origine, nous allons "déplacer" la capture pour compenser, à l'aide d'une matrice de transformation :

1
2
3
// Déplacement de la capture en fonction des bornes d'affichage
var transformation:Matrix = new Matrix();
transformation.translate( -bornes.x, -bornes.y);

Comme un curseur natif ne peut dépasser une taille de 32 pixels de côté, nous allons réduire la taille du viseur :

1
2
// La taille maximale d'un curseur natif est de 32 pixels
transformation.scale(32 / bornes.width, 32 / bornes.height);

Enfin, il ne nous reste plus qu'à prendre la capture de notre viseur :

1
2
// Capture
capture.draw(viseur, transformation);

Désormais, notre image contient une capture bitmap de notre viseur, utilisable pour créer un curseur personnalisé ! :)

Pour vérifier que nous ne nous sommes pas trompés, affichons la capture avec un objet de la classe Bitmap :

1
2
3
// Affichage de la capture pour vérification
var apercu:Bitmap = new Bitmap(capture);
addChild(apercu);

Nous obtenons alors la figure suivante.

L'aperçu de la capture

Pour bien comprendre qu'il s'agit d'une capture bitmap, agrandissons l'aperçu quatre fois :

1
2
apercu.scaleX = 4;
apercu.scaleY = apercu.scaleX;

Ce qui nous donne la figure suivante.

Il s'agit bien d'une image bitmap

Les gros pixels nous confirment que nous avons là une image bitmap et non plus un dessin vectoriel.

Créer le curseur natif

Chaque curseur natif est créé à l'aide d'un objet de la classe MouseCursorData contenant les images et autres informations à propos du curseur. Commençons donc par en créer une instance :

1
2
// Données du curseur natif personnalisé
var curseur:MouseCursorData = new MouseCursorData();

Ensuite, il nous faut un vecteur contenant une ou plusieurs images (ici nous lui insérons la capture du viseur) :

1
2
3
4
// Images du curseur (s'il y en a plusieurs, le curseur sera animé)
var images:Vector.<BitmapData> = new Vector.<BitmapData>();
images.push(capture);
curseur.data = images;

Puis, nous renseignons le point actif du curseur, c'est-à-dire la position qui représente le point où l'utilisateur va cliquer. Dans notre cas, nous voulons que le point actif soit le centre du viseur :

1
2
// Point actif du curseur -> centre de notre viseur
curseur.hotSpot = new Point(capture.width / 2, capture.height / 2);

Enfin, il faut enregistrer notre curseur natif en lui donnant un nom, grâce à la méthode statique registerCursor de la classe Mouse :

1
2
// Enregistrement du cuseur natif personnalisé
Mouse.registerCursor("viseur", curseur);

Notre curseur est fin prêt à être utilisé !

Activer un curseur natif

Pour activer notre nouveau curseur natif, il faut affecter à l'attribut statique cursor de la classe Mouse le nom de notre curseur :

1
2
// Activons notre cuseur
Mouse.cursor = "viseur";

Et voilà que notre curseur de souris s'est transformé en viseur, comme le montre la figure suivante.

Notre curseur natif personnalisé

Comme vous pouvez le remarquer, le curseur n'est plus coupé par les bords de notre application, et il est très fluide quelque soit la fréquence de rafraîchissement de l'application. :)

Voici le code complet permettant de créer ce curseur natif :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
if (Mouse.supportsNativeCursor)
{
    // Dessin
    var viseur:Shape = new Shape();
    viseur.graphics.lineStyle(2, 0x000000);
    viseur.graphics.drawCircle(0, 0, 20);
    viseur.graphics.lineStyle(1, 0x550000);
    viseur.graphics.drawCircle(0, 0, 16);
    viseur.graphics.moveTo(-25, 0);
    viseur.graphics.lineTo(25, 0);
    viseur.graphics.moveTo(0, -25);
    viseur.graphics.lineTo(0, 25);

    // Bornes d'affichage de l'objet
    var bornes:Rectangle = viseur.getBounds(viseur);
    trace("bornes:" + bornes);

    // Image vide de bonne taille
    var capture:BitmapData = new BitmapData(32, 32, true, 0);
    // Déplacement de la capture en fonction des bornes d'affichage
    var transformation:Matrix = new Matrix();
    transformation.translate( -bornes.x, -bornes.y);
    // La taille maximale d'un curseur natif est de 32 pixels
    transformation.scale(32 / bornes.width, 32 / bornes.height);
    // Capture
    capture.draw(viseur, transformation);

    // Affichage de la capture pour vérification
    /*var apercu:Bitmap = new Bitmap(capture);
    addChild(apercu);
    apercu.scaleX = 4;
    apercu.scaleY = apercu.scaleX;*/


    // Données du curseur natif personnalisé
    var curseur:MouseCursorData = new MouseCursorData();
    // Images du curseur (s'il y en a plusieurs, le curseur sera animé)
    var images:Vector.<BitmapData> = new Vector.<BitmapData>();
    images.push(capture);
    curseur.data = images;
    // Point actif du curseur -> centre de notre viseur
    curseur.hotSpot = new Point(capture.width / 2, capture.height / 2);
    // Enregistrement du cuseur natif personnalisé
    Mouse.registerCursor("viseur", curseur);

    // Activons notre cuseur
    Mouse.cursor = "viseur";

}

Désactiver le curseur natif personnalisé

Si vous n'avez plus besoin de votre curseur natif en forme de viseur, vous pouvez très simplement revenir au curseur par défaut du système d'exploitation de l'utilisateur à l'aide de l'instruction suivante :

1
Mouse.cursor = MouseCursor.AUTO;

Le clavier

L'objet KeyboardEvent

Tout comme ceux qui proviennent de la souris, les évènements issus du clavier génèrent leur propre classe, à savoir KeyboardEvent. Celle-ci hérite évidemment de la classe Event, en y ajoutant ses propres propriétés.

Voici à la figure suivante un schéma UML qui permet de résumer un peu toutes les propriétés ou méthodes fournies par ces deux classes.

La classe KeyboardEvent

Contrairement à la souris, les types d'évènements produits par le clavier sont beaucoup moins nombreux, puisqu'ils ne sont que deux ! Voici donc les constantes de la classe KeyboardEvent qui les définissent :

  • KEY_DOWN : cette constante décrit l'enfoncement de n'importe quelle touche du clavier.
  • KEY_UP : celle-ci représente le relâchement de n'importe quelle touche du clavier.

Même s'il est possible de trouver un grand nombre d'applications nécessitant la combinaison de ces deux types d'évènements, vous n'en utiliserez qu'un la majorité du temps. Et comme cela semble souvent plus naturel, il s'agira de l'évènement KEY_DOWN.

Les attributs, ou propriétés, associées à la classe KeyboardEvent ne sont également pas très nombreuses, et les voici :

  • charCode : cet attribut représente la lettre, ou plutôt le caractère, de la touche concernée par l'évènement. Ce caractère est définit par un code numérique.
  • keyCode : cette propriété contient le code de la touche enfoncée ou relâchée.
  • keyLocation : indique l'emplacement de la touche sur le clavier.
  • altKey : indique si la touche Alt est enfoncée en parallèle de l'événement de la souris.
  • ctrlKey : indique si la touche Ctrl (ou Cmd pour les utilisateurs de Mac) est enfoncée en parallèle de l'événement de la souris.
  • shiftKey : indique si la touche Shift est enfoncée en parallèle de l'événement de la souris.

Pour savoir sur quelle touche l'utilisateur a appuyé, nous pouvons utiliser la classe Keyboard qui contient le code de chaque touche du clavier. Par exemple, pour effectuer une action sur l'utilisateur appuie sur la touche A, nous procéderons ainsi :

1
2
3
4
if(event.keyCode == Keyboard.A)
{
    trace("Appui sur la touche [A]");
}

Il est important de bien distinguer les deux propriétés charCode et keyCode. La première désigne le code de la touche dans le jeu de caractères actuel. Chaque caractère est associé à une valeur numérique : on parle alors de codage de caractères ou encore encodage. Par défaut, il s'agit de l'UTF-8, qui prend en charge le célèbre code ASCII. En revanche, keyCode fait référence à la touche en elle-même indépendamment du codage utilisé. Ainsi les lettres « a » et « A » sont associées à deux codes différent en UTF-8, mais la touche reste la même, et donc keyCode aussi. Inversement la touche 1 du pavé numérique est différente du 1 du clavier central, pourtant le caractère est strictement le même.

Enfin avant de basculer sur des exemples, nous allons revenir sur la propriété keyLocation. Si vous regardez votre clavier de plus prêt, vous verrez alors que celui-ci est composé de deux touches Ctrl distincts, ou encore de deux touches Shift différentes. Et aussi surprenant que cela puisse paraître, chaque couple possèdent exactement les mêmes attributs charCode et keyCode.

Mais comment les différencier alors ?

C'est là qu'entre en scène cette fameuse propriété keyLocation. Sous forme de nombre, cette propriété permet de définir l'emplacement de la touche sur le clavier. Pour cela, la classe KeyLocation propose un jeu de constantes relativement explicite. Jugez plutôt : STANDARD, LEFT, RIGHT et NUM_PAD.

Exercice : gérer l'affichage de l'animation

C'est l'heure de passer un peu à la pratique à travers un petit exemple, simple certes, mais très intéressant. L'objectif est donc de basculer l'animation en mode plein écran avec la combinaison Shift + Entrée (et uniquement à l'aide la touche Shift de gauche). Puis pour revenir à l'affichage normal, un simple appui sur la touche Échap devrait suffire.

Pour réaliser le basculement d'un mode d'affichage à un autre, nous aurons besoin d'utiliser la propriété displayState définit par la classe Stage. Cette propriété peut alors prendre les valeurs des constantes NORMAL, FULL_SCREEN et FULL_SCREEN_INTERACTIVE de la classe StageDisplayState.

Le mode FULL_SCREEN_INTERACTIVE permet le support complet du clavier contrairement au mode FULL_SCREEN qui se limite à quelques touches utiles (comme les touches fléchées). Seulement, le support complet du clavier peut introduire des failles de sécurités (un site web imitant votre bureau en plein écran pour vous demander et voler vos identifiants par exemple), c'est pourquoi un message informatif et une demande de confirmation sont affichés à l'utilisateur si votre application est sur une page web dans un navigateur. Ce n'est pas le cas pour le mode FULL_SCREEN qui restreint l'utilisation du clavier.

Avant de nous lancer tête baissé, réfléchissons un peu et analysons ce que nous voulons faire. Grâce aux propriétés keyCode et shiftKey, il est possible de détecter la combinaison des touches Shift et Entrée. En revanche dans ce cas là, la gestion de l'évènement qui engendrera le basculement en plein écran sera faite lors de l'enfoncement de la touche Entrée. C'est pourquoi à ce moment, nous n'aurons pas accès à la propriété keyLocation généré l'enfoncement de la touche Shift. Ainsi pour pouvoir gérer correctement ce cas de figure, nous serons dans l'obligation de déclarer une variable chargée de conserver la valeur de la propriété keyLocation de l'évènement précédent.

Pour commencer, nous pouvons donc mettre en place l'écouteur suivant :

1
2
3
4
5
6
7
var lastKeyLocation:uint = 0;
stage.addEventListener(KeyboardEvent.KEY_DOWN, changerDAffichage);
function changerDAffichage(event:KeyboardEvent):void 
{ 
    // Gestion du changement du mode d'affichage
    lastKeyLocation = event.keyLocation;
}

Ensuite, nous devons définir la condition de mise en plein écran. Pour cela, nous allons donc détecter l'enfoncement de la touche Entrée par sa propriété keyCode qui vaudra Keyboard.ENTER (équivalent à 13), en présence de la touche Shift, et utiliser également notre variable lastKeyLocation, comme ceci :

1
2
3
4
if (event.keyCode == Keyboard.ENTER && event.shiftKey == true && lastKeyLocation == KeyLocation.LEFT)
{
    // Mise en plein écran
}

La remise en mode d'affichage normal sera beaucoup plus simple, puisqu'il suffit de détecter une valeur Keyboard.ESCAPE (équivalent à 27) de la propriété keyCode, qui correspond à la touche Echap.

Au final, cela nous donne le code suivant :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
var lastKeyLocation:uint = 0;
stage.addEventListener(KeyboardEvent.KEY_DOWN, changerDAffichage);
function changerDAffichage(event:KeyboardEvent):void 
{ 
    if (event.keyCode == Keyboard.ENTER && event.shiftKey == true && lastKeyLocation == KeyLocation.LEFT)
    {
        if (stage.allowsFullScreen)
        {
            // Plein écran supporté
            if (stage.allowsFullScreenInteractive)
            {
                // Plein écran avec support complet du clavier
                stage.displayState = StageDisplayState.FULL_SCREEN_INTERACTIVE;
            }
            else
            {
                // Plein écran restreint
                stage.displayState = StageDisplayState.FULL_SCREEN;
            }
        }
    }
    if (event.keyCode == Keyboard.ESCAPE)
    {
        stage.displayState = StageDisplayState.NORMAL;
    }
    trace("Etat de l'écran : " + stage.displayState);

    lastKeyLocation = event.keyLocation;
}

Ne pas oublier de vérifier que les différents modes d'affichage sont disponibles à l'aide des attributs allowsFullScreen et allowsFullScreenInteractive de la classe Stage.

Si tout se passe bien, nous obtenons ceci dans la console en basculant entre l'affichage normal et le plein écran :

1
2
3
Etat de l'écran : normal
Etat de l'écran : fullScreenInteractive
Etat de l'écran : normal

Champs de saisie

Retour sur l'objet TextField

Introduction

Un champ de saisie est particulier : on y peut entrer du texte, le modifier ou l'effacer. Par exemple, vous pouvez demander le nom de l'utilisateur grâce à un champ de saisie. Il est possible de rendre un champ de texte éditable, pour le transformer en champ de saisie. Pour cela, rien de plus simple ! Il suffit de mettre l'attribut type d'un objet de la classe TextField à TextFieldType.INPUT comme ceci :

1
monSuperTexte.type = TextFieldType.INPUT;

Comme d'habitude, ne pas oublier d'importer chaque classe que vous utilisez, ici TextFieldType.

Reprenons un code à base de TextField puis faisons en sorte de créer un champ de saisie :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// Création de l'objet TextField
var monSuperTexte:TextField = new TextField();

// Formatage du texte
var format:TextFormat = new TextFormat("Arial", 14, 0x000000);
monSuperTexte.defaultTextFormat = format;

// Ajout d'une bordure
monSuperTexte.border = true;

// Taille
monSuperTexte.width = 200;
monSuperTexte.height = 20;

// Centrage
monSuperTexte.x = (stage.stageWidth - monSuperTexte.width) / 2;
monSuperTexte.y = (stage.stageHeight - monSuperTexte.height) / 2;

// Ajout à l'affichage
addChild(monSuperTexte);

// Il faut pouvoir sélectionner le texte (ligne falcutative)
monSuperTexte.selectable = true;

// Type du champ de texte : saisie
monSuperTexte.type = TextFieldType.INPUT;

Je peux maintenant écrire ce que je veux dans le champ de texte éditable, comme le montre la figure suivante.

Un champ de saisie !

Bien entendu, il est tout à fait possible de rendre un champ de saisie multi-lignes. Cela fonctionne exactement de la même façon que pour un champ de texte basique :

1
2
3
// Basculement en mode multi-lignes
monSuperTexte.multiline = true;
monSuperTexte.autoSize = TextFieldAutoSize.LEFT;

Ce qui nous donne la figure suivante.

Un champ de saisie multi-lignes

Restriction de la saisie

Lorsque nous définissons un objet TextField en tant que champ de saisie de texte, l'utilisateur peut écrire ce qu'il souhaite à l'intérieur. C'est bien, mais cela peut quand même être inapproprié dans certains cas. Imaginons que nous voulions créer un formulaire, où l'utilisateur peut renseigner des informations personnelles, telles que son nom, son prénom, sa date de naissance, etc. Il peut alors devenir gênant de se retrouver avec un nom de famille comportant des chiffres, ou encore avec une date de naissance contenant des caractères tels que « # », « @ » ou « & ».

En Actionscript, il est possible de restreindre la saisie du texte, c'est-à-dire n'autoriser la saisie que certains caractères. Pour faire ça, nous devons nous servir de la propriété restrict de la classe TextField. Il suffit alors de renseigner la liste des caractères autorisés, comme ceci :

1
2
// Restriction de la saisie du texte
monSuperTexte.restrict = "ABC";

Une fois le programme lancé, essayez de saisir divers caractères. Vous verrez alors qu'il est possible de saisir uniquement les trois caractères que nous avons préciser au-dessus, comme le montre la figure suivante.

Restriction de la saisie aux trois caractères « A », « B » ou « C »

Je pense que certains s'imaginent déjà, qu'autoriser la saisie d'un grand nombre de caractères va être une galère monstre ! Heureusement, il existe des raccourcis pour faciliter la restriction à une plage de caractères. Par exemple, voici comment autoriser seulement la saisie de chiffres :

1
monSuperTexte.restrict = "0-9";

Ce qui nous donne la figure suivante.

Restriction de la saisie aux caractères numériques

Pour les lettres, cela fonctionne exactement de la même manière. Voici comment faire, avec en prime l'ajout de l'espace (drôlement pratique pour séparer les mots) :

1
monSuperTexte.restrict = "A-Z ";

Si vous entrez des caractères alphabétiques, ceci sont alors automatiquement saisie en majuscule comme vous voir sur la figure suivante.

Restriction de la saisie aux majuscules

Pour restreindre la saisie à l'ensemble des caractères alpha-numériques, vous devrez alors renseigner la chaîne de caractères suivante : "A-Za-z0-9 ".

Quelques autres propriétés utiles

Il arrive parfois que nous ayons besoin de masquer le contenu de la saisie d'un texte, à l'instar des mots de passe. Comme d'habitude, l'Actionscript a tout prévu : un attribut de la classe TextField, nommé displayAsPassword, permet de faire ceci.

Cette propriété est un booléen qu'il suffit donc de mettre à la valeur true :

1
2
// Définition de la saisie en mode « mot de passe »
monSuperTexte.displayAsPassword = true;

À présent, si vous saisissez du texte, le contenu du champ de texte sera remplacé par une série de « * », comme sur la figure suivante.

La saisie de mot de passe

Contrairement à un champ de texte, disons traditionnel, seul l'affichage est différent. Vous pouvez ainsi utiliser la propriété text comme à son habitude. Cette dernière contient toujours le contenu de la saisie et non la série de « * ».

Enfin, vous pouvez également noter la présence de l'attribut maxChars permet de définir le nombre de caractères maximal à saisir. Une fois cette limite atteinte, plus aucun caractère ne peut être entré.

Pour l'utiliser, préciser simplement le nombre de caractères autorisé pour la saisie :

1
monSuperTexte.maxChars = 10;

Évènements et TextField

Détecter une modification

Il est possible de détecter la moindre modification du texte contenu dans le champ de saisie, car ce dernier déclenche alors un événement de type Event.CHANGE. Cela se produit lorsque :

  • un caractère est entré,
  • un caractère est supprimé,
  • du texte est collé dans le champ de saisie,
  • une partie du texte est supprimée,
  • une partie du texte est coupée.

De manière générale, le type d'événement Event.CHANGE est utilisé sur les objets qui disposent d'une valeur importante susceptible d'être modifiée : champs de saisie, barre de défilement, compteur… Vous pouvez vous-même déclencher des événements de ce type dans vos classes si besoin.

Ajoutons donc un écouteur d'événement à notre champ de saisie :

1
2
3
4
5
monSuperTexte.addEventListener(Event.CHANGE, detecterModification);
function detecterModification(event:Event):void
{
    trace(monSuperTexte.text);
}

Si vous entrez du texte dans notre champ de saisie, vous obtiendrez une sortie similaire dans la console :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
C
Ce
Cec
Ceci
Ceci
Ceci e
Ceci es
Ceci est
Ceci est
Ceci est m
Ceci est ma
Ceci est ma
Ceci est ma s
Ceci est ma sa
Ceci est ma sai
Ceci est ma sais
Ceci est ma saisi
Ceci est ma saisie
Ceci est ma saisie
Ceci est ma saisie !

L'objet TextEvent

En Actionscript, il existe un objet évènement particulièrement bien adapté aux champs de texte : il s'agit de la classe TextEvent ! Cet évènement sera donc étroitement lié à ce qui ce passe au niveau du clavier, mais pas uniquement. Toutefois, lors de la saisie d'un texte dans un champ à l'aide du clavier, cet évènement sera distribué, disons, en « doublon » de l'objet KeyboardEvent. Vous aurez alors la possibilité d'utiliser l'un ou l'autre de ces évènements, mais aussi pourquoi pas les deux !

La figure suivante reprend l'ensemble des propriétés de cette classe et de sa super-classe Event.

La classe TextEvent

Cette classe d'évènements ajoute donc un unique attribut nommé text par rapport à sa super-classe. Il vous sera donc sûrement utile !

Pour comprendre l'utilité de cet évènement, je vous propose maintenant de découvrir les deux manières dont peut être généré celui-ci. Les constantes qui servent à définir le type de ce dernier sont les suivantes :

  • LINK : est distribué lorsque que vous cliqué sur un lien présent dans un objet TextField.
  • TEXT_INPUT : est généré avant chaque modification d'un champ de saisie, donc à chaque enfoncement d'une touche, mais également lors de la répétition d'un caractère si celle-ci reste enfoncée.

L'évènement LINK est relativement simple à mettre en place. Comme nous l'avons dit, ce type d'évènements est généré lorsque vous cliqué sur un lien à l'intérieur d'un champ de texte. Souvenez-vous, pour créer un lien dans un champ de texte, nous utilisons du texte sous forme HTML. Un lien s'écrit alors de la façon suivante :

1
<a href="adresse">lien</a>

Pour utiliser l'évènement FOCUS, nous devons néanmoins spécifier dans le code HTML que nous souhaitons faire appel à cet évènement. Pour cela, il est nécessaire de rajouter la chaîne de caractères « event: » en entête du lien, comme cela :

1
monSuperTexte.htmlText = "<a href='event:adresse'>lien</a>";

Ensuite, nous pouvons définir notre fonction d'écouteur, comme vous savez maintenant le faire :

1
2
3
4
5
monSuperTexte.addEventListener(TextEvent.LINK, capterLien);
function capterLien(event:TextEvent):void
{
    trace(event.text);
}

En cliquant sur le lien dans Flash Player, vous pourrez alors voir apparaître ceci dans la console de sortie :

1
adresse

Le second évènement proposé par la classe TextEvent est donc TEXT_INPUT ! Pour commencer, je vous invite à tester l'écouteur suivant, afin de bien voir comment Flash Player réagit à cet évènement :

1
2
3
4
5
monSuperTexte.addEventListener(TextEvent.TEXT_INPUT, detecterSaisie);
function detecterSaisie(event:TextEvent):void
{
    trace(event.text);
}

Essayez alors de saisir quelques caractères au clavier :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
C
e
c
i
 
e
s
t
 
m
a
 
s
a
i
s
i
e
 
!

Quel est l'intérêt de cet évènement ? Ne pouvons-nous pas faire la même chose avec KeyboardEvent.Key_DOWN ?

Il est vrai que vu d'ici, ce type d'évènements n'apporte rien de plus par rapport à un évènement issus de la classe KeyboardEvent. Néanmoins pour juger de son utilité, il est nécessaire de bien comprendre la différence de génération de ces deux évènements. Le premier est généré dès lors qu'une touche est enfoncée, quelle qu'elle soit. En revanche, le second est distribué uniquement dans le cas d'une entrée de texte. Dans ce dernier cas, nous pouvons donc exclure l'appui sur les touches suivantes : Alt, Ctrl, Shift, Tab, ou encore Entrée. Par ailleurs, la classe TextEvent facilite énormément la gestion du texte. Par exemple, ici, pas question d'encodage ! La propriété text contient directement le caractère et non son code numérique.

Enfin, une dernière petite subtilité rend l'utilisation de cet évènement très pratique. Il faut savoir que ce n'est qu'une fois que l'évènement TextEvent.TEXT_INPUT a fini son aller-retour dans le flux d'évènements, que le champ de saisie est mis à jour. Ceci va donc nous permettre de revenir sur des propriétés de la super-classe Event. Rappelez-vous, cette classe disposait d'un attribut nommé cancelable. Et bien, écoutez bien ! Cet attribut indique si le comportement associé à l’événement peut être évité. Dans notre cas, il s'agit de la mise à jour du contenu du champ de texte actif. Si ce comportement par défaut peut être évité, la méthode preventDefault() permet quant à elle de l'annuler.

Essayer donc le code suivant :

1
2
3
4
5
6
7
8
monSuperTexte.addEventListener(TextEvent.TEXT_INPUT, detecterSaisie);
function detecterSaisie(event:TextEvent):void
{
    if (event.cancelable == true) {
        trace(event.text);
        event.preventDefault();
    }
}

Voyez plutôt le résultat, plus aucun caractère n'est affiché à l'intérieur du champ de saisie de texte.

La gestion du focus

Dans une interface graphique complexe, composée par exemple de plusieurs champs de saisie, un seul objet peut être actif à la fois. J'entends par là qu'un seul champ de texte doit être rempli lorsque vous tapez au clavier. Cela introduit donc la notion de focus, qui désigne alors l'objet cible actif. Pour repérer cet objet d'affichage au sein de l'ensemble de la liste d'affichage, une référence à ce dernier est stocké dans l'attribut focus de stage. Nous avons accès à cet attribut par un getter et un setter. Il est donc tout à fait possible de définir nous-mêmes l'objet qui a le focus.

Lorsque le focus est modifié, un évènement de type FocusEvent est alors distribué. Pour un objet donné, deux types d'évènements sont distincts :

  • FOCUS_IN : généré lorsque que l'objet interactif prend le focus.
  • FOCUS_OUT : généré lorsque que l'objet interactif perd le focus.

Sachant que l'exercice qui suit utilise ces types d'évènements, nous nous passerons d'exemples ici.

Exercice : un mini formulaire

Pour conclure ce chapitre, je vais vous présenter un petit exercice consistant à réaliser un formulaire. Ce sera l'occasion de revenir sur certains points important, tout en utilisant les évènements fournis par Flash Player.

Comme je l'ai dit, l'objectif est de réaliser un mini-formulaire. Nous utiliserons quatre champs de saisie de texte afin de pouvoir renseigner les informations suivantes, pour un quelconque traitement :

  • nom,
  • prénom,
  • date de naissance,
  • lieu de naissance.

Une particularité de notre formulaire sera de présenter les intitulés, ou étiquettes, à l'intérieur des champ de saisie eux-mêmes. Pour être plus précis, le concept est de pré-remplir les champs de texte par leur étiquette. Puis nous utiliserons les évènements FOCUS_IN et FOCUS_OUT pour effacer ou remettre en place ces intitulés.

Pour mieux visualiser la chose, voici à la figure suivante un aperçu de ce que nous obtiendrons à la fin de l'exercice.

Un formulaire avec libellé intégré

Étant donné que nous serons amenés à utiliser plusieurs champs de saisie similaires, il peut être intéressant de définir une nouvelle classe. Je suggère donc de créer un objet d'affichage personnalisé à partir d'un TextField.

Dans un premier temps, je vous propose de découvrir cette classe que j'ai nommée EditableTextField, puis nous l'analyserons ensuite :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package
{
    import flash.text.TextField;
    import flash.text.TextFormat;
    import flash.text.TextFieldType;
    import flash.events.FocusEvent;

    public class EditableTextField extends TextField
    {
        // Attributs
        private var _etiquette:String;
        private var _formatEtiquette:TextFormat;
        private var _formatEdition:TextFormat;

        public function EditableTextField(etiquette:String)
        {
            _etiquette = etiquette;
            _formatEtiquette = new TextFormat("Arial", 14, 0x999999);
            _formatEdition = new TextFormat("Arial", 14, 0x000000);
            defaultTextFormat = _formatEtiquette;
            type = TextFieldType.INPUT;
            selectable = true;
            border = true;
            text = _etiquette;
            addEventListener(FocusEvent.FOCUS_IN, effacerEtiquette);
            addEventListener(FocusEvent.FOCUS_OUT, remettreEtiquette);
        }

        // Méthodes d'écouteurs
        private function effacerEtiquette(event:FocusEvent):void
        {
            if (text == _etiquette)
            {
                defaultTextFormat = _formatEdition;
                text = "";
            }
        }

        private function remettreEtiquette(event:FocusEvent):void
        {
            if (text == "")
            {
                defaultTextFormat = _formatEtiquette;
                text = _etiquette;
            }

        }

    }

}

EditableTextField

Pour analyser cette classe, prenons les choses telles qu'elle viennent. En premier, j'ai déclaré les trois attributs suivants :

  • _etiquette : contient l'intitulé ou l'étiquette du champ de texte,
  • _formatEtiquette : définit le formatage à utiliser lors de l'affichage de l'intitulé,
  • _formatEdition : définit le formatage à adopter lors de l'édition du champ de texte.

À l'intérieur du constructeur de la classe, il n'y a rien de bien compliqué. Nous initialisons l'ensemble des attributs, et en profitons pour regrouper les différentes instructions communes, touchant aux propriétés de la super-classe TextField. Pour clôturer ce constructeur, deux écouteurs sont mis en place afin de gérer la transition entre l'affichage de l'étiquette et son effacement avant l'édition du champ de saisie. Je ne vais entrer plus dans les détails, car je juge que vous êtes à présent suffisamment doués pour comprendre le fonctionnement de cette classe tous seuls.

En définissant les écouteurs de cette manière, à l'intérieur d'une classe, vous pouvez facilement simplifier et alléger le code. Effectivement, si vous aviez dû le faire depuis l'extérieur de la classe, vous auriez très certainement défini deux fonctions d'écouteurs pour chacun des champs de saisie de l'interface graphique. N'hésitez pas à utiliser cette technique pour simplifier et organiser au mieux votre code.

Maintenant, il ne reste plus qu'à disposer les champs de texte à l'écran, et à ajuster les derniers détails :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// Champ de saisie du nom de famille
var nom:EditableTextField = new EditableTextField("Nom");
nom.restrict = "A-Za-z\\- ";
nom.width = 185;
nom.height = 20;
nom.x = 10;
nom.y = 10;
addChild(nom);

// Champ de saisie du prénom
var prenom:EditableTextField = new EditableTextField("Prénom");
prenom.restrict = "A-Za-z\\- ";
prenom.width = 185;
prenom.height = 20;
prenom.x = 205;
prenom.y = 10;
addChild(prenom);

// Champ de saisie de la date de naissance
var dateDeNaissance:EditableTextField = new EditableTextField("JJ/MM/AAAA");
dateDeNaissance.restrict = "0-9/";
dateDeNaissance.maxChars = 10;
dateDeNaissance.width = 100;
dateDeNaissance.height = 20;
dateDeNaissance.x = 10;
dateDeNaissance.y = 40;
addChild(dateDeNaissance);

// Champ de saisie du lieu de naissance
var lieuDeNaissance:EditableTextField = new EditableTextField("Lieu de naissance");
lieuDeNaissance.restrict = "A-Za-z0-9\\- ";
lieuDeNaissance.width = 270;
lieuDeNaissance.height = 20;
lieuDeNaissance.x = 120;
lieuDeNaissance.y = 40;
addChild(lieuDeNaissance);

Vous remarquerez à travers cet exemple, que la définition de la propriété restrict n'affecte que la saisie de texte depuis l'extérieur du programme. Cela explique pourquoi l'affichage de l'intitulé de l'objet dateDeNaissance ne pose aucun problème.


En résumé

  • Les actions produites par l'utilisateur sur la souris génèrent des objets de la classe MouseEvent.
  • L'affichage de la souris peut être géré via les méthodes de la classe Mouse.
  • Le glisser-déposer est une technique qui peut être contrôlé par les méthodes startDrag() et stopdrag().
  • Un objet KeyboardEvent est généré après toute action produite sur le clavier.
  • Un objet TextField est transformable en champ de saisie, en redéfinissant la valeur de la propriété type à l'aide les constantes de la classe TextFieldType.
  • Différentes propriétés de la classe TextField servent à paramétrer la saisie d'un texte, telles que restrict, displayAsPassword ou maxChars.
  • Les évènements issus des classes TextEvent et FocusEvent augmentent les possibilités de la gestion d'évènements provenant de la souris ou du clavier.