Des fenêtres non activables, des formulaires non bloquants, deux
ou trois expériences à faire partager, et tout en GFA... ça vous
dit ? Attention ici, car on va parler GEM pur et dur; pas de
bidouilles géniales, ni de savant inline. Il faudra connaître vos
fonctions GEM sur le bout de vos dix doigts et on supputera, dans
la foulée, que vous avez lu tous les articles de notre gourou à
tous: l'excellent Claude Attard. Votre serviteur ici présent s'est
beuvré sans compter de ses paroles, tout en étant incapable de
récupérer ses routines : mon satané GFA 3.03 ne reconnaissait même
pas le format GFA... J'ai donc réinventé le fil à couper le beurre
et commis Blaise.
Construire des formulaires, placer des textes, des boutons : ça
s'appelle dessiner, même si c'est dans un éditeur de ressources.
On laisse nos talents d'artiste et notre humanité s'exprimer.
Ainsi, on prévoit l'ergonomie du futur chef d'oeuvre, on élimine les
options type "usine à gaz", on se représente la structure du
programme. Quand ça commence à prendre forme, on peut passer à
l'étape suivante.
Première astuce, utilisons des tableaux pour gérer les fenêtres en
formulaire (on dira win^dial par la suite): win!() pour leur existance,
cp_win%() pour leur composantes, hand_win%() pour le handle
de la fenêtre, adtree%() pour l'adresse du formulaire, sans
oublier les wx&(), wy&(), wl&(), wh&() (coordonnées externes : la
fenêtre entière), les xd&(), yd&(), ld&(), hd&() (coordonnées
internes : la zone de travail de la fenêtre et/ou du formulaire),
etc. et arrangeons-nous pour que leurs numéros correspondent au
numéro de formulaire dans le ressource et que nous allons mettre
dans la fenêtre. Ainsi, le hand_win%(1) est le handle du win^dial
1, cette fenêtre hébergeant le formulaire 1 qui se trouve dans mon
ressource.
C'est bête à dire, mais ça simplifie les choses, et par là-même
votre programmation. Le seul hic, c'est que le numéro ne correspond
pas au handle, et qu'il faudra toujours passer par un IF
handle_obtenu%=hand_win%(x) AND win!(x)=TRUE si l'on veut
connaître quel win_dial est concerné.
Deuxième astuce : créons un formulaire "spécial titres" dans le
ressource, où nous plaçons les chaînes de titre pour les win^dial,
sous forme de string. Arrangeons-nous pour que le numéro d'objet
de chaque string corresponde au numéro du win_dial. La string
ayant le numéro objet 1 sera donc le titre de mon win^dial 1.
Lors de la création de votre fenêtre, au lieu d'indiquer l'adresse
d'une chaîne terminée par un chr$(0) pour le WIND_SET(2), chaîne
qui se trouve dans la mémoire interne du GFA, nous donnerons
l'adresse de cette string grâce à un gentil OB_SPEC. Intérêt ? Je vous
le laisse deviner. Avec ceci, nous indiquerons à votre win_dial
fraîchement créé la formule magique:
WIND_SET(hand_win%(x),24,1,...)
Ça sert à rendre notre de fenêtre non activable (si l'AES
le permet, bien sûr). Attention ici, car tout ce qui devra être affiché
(et modifié) dedans, devra l'être en passant par la liste des
rectangles GEM (vous savez ? les redraws avec WIND_GET(11 et 12) et
RC_INTERSECT). De plus il faudra prendre en compte certains
événements messages que nous n'avions pas eu l'habitude de traiter : je
pense en particulier à WM_ONTOP (code 31: activation spontanée par
le GEM).
Pas de surprise, après l'initilisation et dans notre boucle sans
fin, trône le sempiternel evnt&=EVNT_MULTI(&x10011,2,1,1,...), on
récupère au passage les infos clavier, les coordonnées et l'état
de la souris. Puis vient une série d'aiguillonnages.
Un premier SELECT CASE permet de diriger le traitement des
événements messages. En DEFAULT, nous interrogeons evnt&.
S'il s'agit d'un redraw, d'un redimensionnement, d'une fermeture,
etc, rappelons que le handle lu dans le buffer d'événements
messages (m_fenetre&) ne correspond pas au numéro du win^dial, nous
devons donc déterminer quel win^dial est concerné :
FOR i%=x to y IF m_fenetre&=hand_win%(i%) AND win!(i%)=TRUE ' traitement du win^dial i% ENDIF NEXT i%
Bien sûr, il faut s'arranger dans les différents cas pour ne
tester que les win^dials qui ne sont susceptibles d'être concernés.
Par exemple, on ne va pas tester dans une boucle for^next traitant
un WM_FULL, un win_dial qui ne peut être "fullé".
Si evnt& est à 2, nous demandons le handle de la fenêtre en
premier plan, testons quel win^dial est concerné, et nous allons
gérer les événements clavier.
Si evnt& est à 1, nous sélectionnons selon le type de clic souris
et demandons quel win_dial a été cliqué grâce à WIND_FIND et aux
coordonnées de la souris. Nous obtenons un handle: clic_win%,
demandons une pause par EVNT_TIMER(75) et entrons dans une nouvelle
procédure qui a cette structure :
PROCEDURE boucle_secondaire IF clic_win%=hand_win%(1) and win!(1)=TRUE gere_win_dial_1 ENDIF ' et on répète ça pour chaque win^dial RETURN
Vient ensuite la gestion du win^dial proprement^dite. On demande
quel objet a été cliqué grâce à OBJC_FIND et aux coordonnées de la
souris. Un SELECT CASE aiguillonne ensuite.
PROCEDURE gere_win_dial_1 object%=OBJC_FIND(adtree%(1)... SELECT object% CASE x black_white(1,x,actif) ' traitement de l'option correspondante à x black_white(1,x,desactif) ... DEFAULT ' activer_win_dial_1 ' ou gérer le clic si le formulaire ' n'occupe pas toute la fenêtre ENDSELECT RETURN
...lors de la création de votre win^dial. Vous avez sans doute
remarqué que Let Them Fly interceptait FORM_CENTER. Seulement, le
hic, c'est que maintenant nos formulaires sont en fenêtres. Nous
pouvons toujours faire FORM_CENTER pour placer correctement notre
win^dial. Mais si la souris se trouve tout en haut, elle empiétera
sur la barre de menu. Et là : c'est pas beau.
Donc, après le WIND_CREATE, les différents WIND_SET, FORM_CENTER
pour obtenir les coordonnées internes du win^dial, nous faisons un
WIND_CALC(0) pour avoir les coordonnées externes. Il faudra ensuite
corriger celles-ci en faisant des MAX, MIN, puis réajuster les
coordonnées internes par un WIND_CALC(1) inverse.
Ce WIND_CALC(1), intégrons-le dans une procédure à part: move_win,
qui a pour paramètre le numéro du win^dial et les coordonnées
externes de celui-ci. WIND_CALC(1) calcule les coordonnées internes
puis corrige par OB_Y(adtree%(x),0)=yd&(x), OB_X (voir OB_W, OB_H)
la position du formulaire.
Cette procédure servira à tous les changements de position du
win^dial (donc le full, size, move...).
Avec nos win^dial non activables, tout doit passer par la liste
des rectangles, redraw avec clipping VDI (ou OBJC_DRAW en indiquant
les coordonnées du clipping). Vous êtes adeptes du GEM et
vous la connaissez donc par coeur.
Blaise utilise deux types de procédures. Une qui traite les
redraws en général, qui sert à tout, et qui récupère
les coordonnées de la zone à dessiner dans le buffer message.
Et une plus spécifique, qui ne sert que pour un seul objet dans le win^dial,
nommée black_white. Elle récupère les coordonnées pour le redraw
par un WIND_GET(4) et attend trois paramètres : le numéro du
win^dial, le numéro de l'objet à dessiner, et un code spécial.
Servons-nous de ce code pour modifier par OB_STATE (et OB_FLAGS
s'il y a 3D) l'état de l'objet, puis demandons au GEM de le dessiner
en utilisant la liste des rectangles.
Petite précision : certains objets ne possèdent pas de masque, en
particulier les images, les strings et les titres. Si l'on change ces objets
et qu'on les redessine, ça peut donner des trucs bizarres. Un moyen d'y
remédier est de les dessiner deux fois: une fois avant de changer
l'objet (j'ai remarqué que ça l'effaçait), puis
une seconde fois après l'avoir modifié.
Vous avez remarqué que Blaise utilisait la 3D GEM dans certaines
boîtes de dialogue ? [NdTB : désolé, mais il manquait pas mal de
mots dans ce passage alors j'ai comblé avec ce que j'ai pu :) ].
Cela a été totalement dessiné avec WERCS. Comment c'est y
possible, ça ? Eh bien, j'ai pu introduire cette fameuse 3D grâce à
OB_FLAGS, lors de l'initialisation : ça concerne les bits 9 (3D
surélevée), 10 (activé : décalage) et 11 (background). Après
moultes essais et prises de tête, je ne me suis occupé que du 9 et
11. Précision importante : il y a des petits réglages à faire au
niveau du tramage, de la couleur, de l'épaisseur du cadre... dans
votre éditeur de ressource: en général, on choisit pas de trame et
blanc. A vous d'essayer les différentes possibilités.
Un formulaire n'a pas besoin d'occuper toute la fenêtre (ça
s'appelle alors une toolbar), ou le formulaire peut être remplacé par
un menu. De même, un objet peut servir de pop up menu. L'ossature
ne change pas, le point de départ est toujours le traitement de
l'objet à partir de la procédure gere_win_dial_x. Il faudra juste
jongler avec les coordonnées internes si c'est une toolbar ou
appeler une nouvelle procédure si c'est un menu ou un pop up.
Dans ces derniers, nous nous baserons sur un evnt&=EVNT_MULTI
(&x10011,2,0,1,...) et un blocage de l'AES avec les WIND_UPDATE.
L'EVNT_MULTI ne doit pas être bloquant pour pouvoir gérer les
redessins des objets. N'oublions pas aussi de sauver la zone image
qui se trouve en dessous en utilisant le buffer écran de l'AES
(fourni par WIND_GET(17)) et de la remetre à la fin.
Sauvez votre source au format LST, puis faites New, et rechargez
par merge votre source. Intérêt? Épurer votre source des variables
encombrantes, car figurez vous que le GFA (3.03 du moins) garde
celles que vous n'utilisez plus dans le format GFA [NdTB : la version
3.5e a le même défaut]. Ensuite, en mode Direct, faîtes :
DUMP "*.*" TO "D:\VAR.TXT".
Toutes vos variables seront listées dans ce fichier. Intérêt ?
Corriger vos fôtes et par la même certains bugs. Au revoir !