II. La gestion des évènements▲
Pour rendre notre application dynamique, il est possible (et heureusement) d'associer des méthodes à des évènements.
II-A. Principe de communication entre les objets▲
Chaque objet interactif possède un pointeur vers l'objet auquel il doit envoyer ses messages. L'objet qui reçoit les messages connait les associations messages / méthodes, il peut donc appeler les fonctions correspondantes lorsqu'il reçoit des messages. Chaque type de message possède un identifiant ainsi qu'une correspondance avec un évènement. Donc, quand un objet reçoit un message, il en connait l'identifiant ainsi que l'évènement qui a généré le message.
La liste des associations entre messages et méthodes est définie par la macro FXDEFMAP. Mais c'est au programmeur de renseigner cette macro de la manière suivante :
FXDEFMAP(NotreWindow) NotreWindowMap[]={
//___Type du message________Identifiant________Méthode__________
FXMAPFUNC(SEL_COMMAND , NotreWindow::
ID_NEW , NotreWindow::
fct1),
FXMAPFUNC(SEL_MINIMIZE, NotreWindow::
ID_END , NotreWindow::
fct2),
FXMAPFUNC(SEL_COMMAND , NotreWindow::
ID_FOX , NotreWindow::
fct3),
FXMAPFUNC(SEL_MAXIMIZE, NotreWindow::
ID_TOTO, NotreWindow::
fct2),
[...]
FXMAPFUNC(SEL_MOTION , NotreWindow::
ID_TATA, NotreWindow::
fct4)
}
;
Vous pouvez trouver la liste des types des messages ici.
Une fois la liste établie, il faut appeler la seconde macro qui permet d'implémenter notre liste d'associations :
FXIMPLEMENT(NotreWindow,FXMainWindow,NotreWindowMap,ARRAYNUMBER(NotreWindowMap))
Reste maintenant à informer la classe NotreWindow de l'existence de ces types de messages, en rajoutant un type énuméré en attribut de classe ainsi que les prototypes des fonctions associées.
class
NotreWindow: public
FXMainWindow{
FXDECLARE(NotreWindow);
private
:
...
public
:
...
enum
{
ID_ZERO,
ID_NEW,
ID_END,
ID_FOX,
ID_TOTO,
ID_TATA
}
;
long
fct1(FXObject*
,FXSelector,void
*
);
long
fct2(FXObject*
,FXSelector,void
*
);
long
fct3(FXObject*
,FXSelector,void
*
);
long
fct4(FXObject*
,FXSelector,void
*
);
}
;
Les fonctions à appeler lors d'un évènement ont toujours le prototype suivant :
long
nom_fct(FXObject*
,FXSelector,void
*
);
où FXObject* est un pointeur sur l'objet qui a émis le message, FXSelector est le type du message et void* est un pointeur sur une structure représentant l'évènement qui a amené l'émission du message.
Vous en mourez d'envie, voici un petit exemple pour illustrer l'utilisation des évènements. Implémentons un label dont le texte change au clic sur un bouton.
#ifndef __NotreWindow_h__
#define __NotreWindow_h__
class
NotreWindow : public
FXMainWindow {
FXDECLARE(NotreWindow);
private
:
NotreWindow(){}
FXLabel *
lab;
FXButton *
b1,*
b2;
public
:
NotreWindow(FXApp *
);
void
create();
enum
{
ID_ZERO,
ID_CHANG,
ID_DEVEL
}
;
long
onChangez(FXObject*
,FXSelector,void
*
);
long
onDeveloppez(FXObject*
,FXSelector,void
*
);
}
;
#endif
#include
<fox/fx.h>
#include
"NotreWindow.h"
FXDEFMAP(NotreWindow) NotreWindowMap[]={
FXMAPFUNC(SEL_COMMAND, NotreWindow::
ID_CHANG , NotreWindow::
onChangez),
FXMAPFUNC(SEL_COMMAND, NotreWindow::
ID_DEVEL , NotreWindow::
onDeveloppez)
}
;
FXIMPLEMENT(NotreWindow,FXMainWindow,NotreWindowMap,ARRAYNUMBER(NotreWindowMap))
NotreWindow::
NotreWindow(FXApp *
a):FXMainWindow(a,"Ma fenêtre FOX"
,NULL
,NULL
,DECOR_ALL,50
,100
,250
,80
){
int
buttonStyle =
FRAME_THICK|
FRAME_RAISED|
LAYOUT_FILL_X|
LAYOUT_TOP|
LAYOUT_LEFT;
lab=
new
FXLabel( this
, "hello world"
,NULL
,JUSTIFY_CENTER_X|
LAYOUT_FILL_X );
b1 =
new
FXButton( this
, "Changez"
,NULL
,this
,ID_CHANG,buttonStyle,0
,0
,0
,0
,10
,10
,5
,5
);
b2 =
new
FXButton( this
, "Developpez"
,NULL
,this
,ID_DEVEL,buttonStyle,0
,0
,0
,0
,10
,10
,5
,5
);
}
void
NotreWindow::
create(){
FXMainWindow::
create();
show(PLACEMENT_VISIBLE);
}
long
NotreWindow::
onDeveloppez(FXObject*
,FXSelector,void
*
){
lab->
setText("developpez"
);
return
0
;
}
long
NotreWindow::
onChangez(FXObject*
,FXSelector,void
*
){
lab->
setText("changez"
);
return
0
;
}
II-B. Explications du code▲
J'ai déclaré deux pointeurs sur des boutons (FXButton), créés dans le constructeur public de notre classe. On remarquera la présence du premier argument this qui définit l'objet graphique contenant notre bouton. Le second argument positionné à this correspond à un pointeur sur l'objet à qui envoyer les messages, et dans notre cas, nous envoyons les messages à la fenêtre. On remarquera aussi les arguments ID_CHANG et ID_DEVEL décrivant le type des messages que les boutons émettront.
Comme la fenêtre sera amenée à recevoir des messages, elle doit connaitre les associations entre les messages et les fonctions à appeler, ceci est fait via la macro FXDEFMAP :
FXMAPFUNC(SEL_COMMAND, NotreWindow::
ID_CHANG , NotreWindow::
onChangez),
FXMAPFUNC(SEL_COMMAND, NotreWindow::
ID_DEVEL , NotreWindow::
onDeveloppez)
Pour éviter les erreurs de résolution de symbole et les références indéfinies, il ne faut pas oublier de créer les méthodes onChangez et onDeveloppez :
long
NotreWindow::
onDeveloppez(FXObject*
,FXSelector,void
*
){
lab->
setText("developpez"
);
return
0
;
}
long
NotreWindow::
onChangez(FXObject*
,FXSelector,void
*
){
lab->
setText("changez"
);
return
0
;
}
ainsi que les types de messages ID_CHANG et ID_DEVEL :
class
NotreWindow : public
FXMainWindow {
...
public
:
...
enum
{
ID_ZERO,
ID_CHANG,
ID_DEVEL
}
;
...
}
;
Attention à ne pas oublier de déclarer un premier item non utilisé pour le type énuméré. En effet, le premier item est géré comme étant 0, valeur ne convenant pas pour les message Fox. En ne déclarant pas le premier item, vous prenez le risque d'obtenir des comportements indéfinis.