Dîtes "j'aime" en GFA



Par Rajha Lone/Queen Meka

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.


1. 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.


2. 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 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).


3. 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 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


4. 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-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...).


5. L'affichage


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.


6. 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 (fourni par WIND_GET(17)) et de la remetre à la fin.


7. Last but not least


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 !



[Retour au sommaire]