DITES "J'AIME" EN GFA
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.
One goal, one soul and WERCS.
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.
GFA mon amour.
Première astuce, utilisons des tableaux pour gérer les
fenêtres en formulaire (on dira win-dial par la suite):
- win!() pour leur existence,
- cp_win%() pour leur composantes,
- hand_win%() pour le handle de la fenêtre,
- adtree%() pour l'adresse du formulaire,
- 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),
...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êts? Je vous les laisse deviner. Avec
ceci, nous indiquerons à votre win-dial fraîchement
créé la formule magique:
WIND_SET(hand_win%(x),24,1,0,0,0)
Ça sert à rendre notre 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 l'habitude de
traiter: je pense en particulier à WM_ONTOP (code 31: activation
spontanée par le GEM).
Ossature.
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 interrongeons 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%=1 to ?
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 le bit 1 de evnt& est à 1, 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 le bit 0 de 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
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
Petit retour en arrière.
...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-cis 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 (voire 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...).
L'affichage.
Avec nos win-dials 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
fenêtres. Pourtant, il 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é), 10 (activé: décalage) et 9+10
(background). Après moultes essais et prises de tête, je ne me
suis occupé que du 9. 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.
Variantes.
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 (adresse fournie par WIND_GET(17)) et de la remettre à la fin.
Last but not least.
Sauvez votre source au format LST, puis faites New, et rechargez par merge
votre source. Intérêt? Epurer 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.
Ensuite, en mode Direct, faites 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.
Rajah Lone / Queen Meka
le 11 Septembre 1997