Suivez-nous sur X

|
|
|
0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z,
ALL
|
|
0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z
|
|
0,
A,
B,
C,
D,
E,
F,
G,
H,
I,
J,
K,
L,
M,
N,
O,
P,
Q,
R,
S,
T,
U,
V,
W,
X,
Y,
Z
|
|
A propos d'Obligement
|
|
David Brunet
|
|
|
|
Programmation : E - Programmation objet
(Article écrit par Olivier Adam - novembre 2001)
|
|
Un exemple pratique, un gadget d'iconification universel
Un concept souvent mal perçu en programmation est celui de programmation objet.
Mais il existe des domaines où certaines des spécificités des objets sont
fondamentales. Exec, le micronoyau de l'Amiga et Intuition, son
interface fenêtrée sont conçus fondamentalement pour
être extensibles. En fait, AmigaOS est entièrement objet,
particulièrement, son interface graphique est extensible et
adopte à cette fin le concept de BOOPSI (Système
simple de programmation orientée objet pour Intuition). Ce dernier est une
manière de programmer des briques logicielles, construisant chacune
une portion de l'interface, comme autant de modules (plugins).
Intuition ne contient pas de gadget d'iconification en standard
mais VisualPrefs, je suis sûr que vous connaissez ce correctif,
fournit une nouvelle classe BOOPSI : Images/titlebar.image. Classe
qui contient de quoi créer un gadget assez général
pour la barre de titre d'une fenêtre, et d'agir dessus. Notez
qu'il n'est pas nécessaire d'utiliser VisualPrefs, en effet,
il suffit d'installer cette bibliothèque, qu'on peut trouver sur Aminet.
Le but de cet article est l'implémentation d'un module
du générateur d'interfaces du E, EasyGUI, ayant pour
fonction de créer et gérer le gadget d'iconification
d'une application quelconque.
Rappel succinct sur les objets et méthodes du E
Ce chapitre illustre les fonctionnalités "objet" du E, bien entendu, et comme on dit toujours dans ces cas là,
je vous conseille de lire les sources consacrées à ce concept (e:src/afc par exemple).
Un objet est initialisé (on dit instantialisé) lorsqu'on
utilise la fonction NEW du E, cette fonction est nécessaire
pour initialiser les méthodes. A ce titre, NEW est le "constructeur"
des objets. Sa contrepartie est END, nécessaire en tant que
"destructeur".
Soit "obj" un objet:
OBJECT obj
adr
pipo
lulu
ENDOBJECT
|
Un objet est défini par ses attributs, ces attributs peuvent
être de types :
CHAR, INT, LONG, <object>
PTR TO CHAR, INT, LONG, <object>
ARRAY OF CHAR, INT, LONG, <object>
Bien que le E soit non typé, il est nécessaire
de typer les objets si tant est qu'on veuille rester compatible avec
le modèle du système lorsqu'on passe du C au E.
Par exemple:
MODULE 'intuition/intuitiion'
OBJECT obj
adr:PTR TO LONG
num:INT
win:PTR TO window
ENDOBJECT
|
Avec n:=obj.num par exemple, on accède à l'attribut num.
Avec w:=obj.win on pointe vers cette fenêtre.
Un objet peut posséder des méthodes, ces méthodes
sont des fonctions normales du E, sauf qu'elles se rapportent à
l'usage de ces objets.
OBJECT obj
champ
ENDOBJECT
PROC init(arg=NIL) OF obj
DEF resultat
/* initialise ceci-cela */
ENDPROC resultat
PROC main() HANDLE
/* On déclenche la méthode par :
*/
DEF o:PTR TO obj
NEW o.init()
/* On tue l'objet avec :
*/
EXCEPT DO
IF o THEN END o
ENDPROC
|
Un objet peut contenir des champs cachés : en alternant les
instructions EXPORT/PRIVATE on peut cacher certains des champs de nos
objets, au sein de nos modules.
Cela se nomme le data-hiding, l'objet est alors une "black-box",
car une fois le module compilé, on n'a plus accès qu'aux
champs publics. Par contre, les méthodes sont toujours toutes
visibles.
Exemple:
OPT MODULE
MODULE 'intuition/intuition'
EXPORT OBJECT obj
adr:PTR TO LONG
PRIVATE
num:INT
win:PTR TO window
ENDOBJECT
PROC do_it() OF obj IS EMPTY -> Dans ce cas la méthode est "vide"
|
Un objet peut appeler ses propres méthodes et attributs, il
lui suffit de les référer à la variable self :
OBJECT obj
adr
num
ENDOBJECT
PROC init() OF obj
DEF res
res:=self.num
-> Appelle obj.num
ENDPROC res
|
Un objet peut hériter d'objets déjà connus, étendant
alors les attributs et méthodes fournis initialement avec cet objet.
OBJECT obj
adr
num
ENDOBJECT
OBJECT bbb OF obj
nouveau_champ
ENDOBJECT
|
bbb devient donc :
OBJECT bbb
adr
num
nouveau_champ
ENDOBJECT
|
A présent concentrons-nous sur les spécifications objets de EasyGUI.
Les générateurs EasyGUI
EasyGUI est un module du E (emodules:tools/easygui) qui a pour fonction
de simplifier la création d'interfaces :
MODULE 'tools/easygui'
PROC main() IS easyguiA('titre',[SBUTTON,0,' Yea! '],NIL)
|
Les gadgets peuvent être par exemple BUTTON, TEXT, NUM, etc.
Mais il existe un type PLUGIN extensible, vide par défaut,
mais fourni avec quelques attributs utiles et une poignée de méthodes.
OBJECT plugin
x:INT
y:INT
xs:INT
ys:INT
gh:PTR TO guihandle
ENDOBJECT
|
Les méthodes de cet objet sont : (les types des variables de ces fonctions sont documentés
dans e:src/tools/easygui/docs/easygui.doc)
- min_size(a,b)
- will_resize()
- message_test(a,b)
- message_action(a,b,c,d)
- clear_render(a)
- render(a,b,c,d,e,f)
- appmessage(a,b)
- gtrender(a,b,c,d,e,f,g,h)
La plupart de ces méthodes servent un but lumineux : min_size
rend la taille minimum du module créé par exemple. will_resize
nous informe si ce module est retaillable ou pas.
Le truc, pour bien comprendre le fonctionnement, c'est de connaître
l'ordre dans lequel sont scrutées ces méthodes par le
générateur d'interface :
1) min_size (calcule la taille minimum).
2) will_resize (ces deux méthodes servent aussi aux initialisations).
3) render (dessine le composant graphique -s'il est graphique-).
4) message_test (boucle d'interception des actions sur le gadget (filtre)).
5) message_action (réaction du gadget).
6) clear_render (efface tout).
(se référer aux exemples du E: e:src/tools/easygui/plugins).
Notre module EasyGUI
Quel plaisir de créer des briques systèmes : en effet, celles-ci sont universelles et
permettent une conception totalement modulaire.
D'autre part, elles sont réutilisables et permettent aux développeurs d'éviter à avoir à réinventer
la roue.
L'organisation de notre projet est :
project
|
.build
|
|
tbi_test.e
|
modules
|
titelbar.m
|
images
|
titlebar.e
|
plugins
|
tbi.e
|
Le module "titlebar.m" a été créé à
partir du fichier titlebar_lib.fd trouvé dans l'archive titlebarimage.lha
et généré avec :
fd2module titlebar
Après une traduction des constantes et des structures
du C vers le E, on obtient le fichier images/titlebar.e, que je vous
livre ici in extenso :
OPT OSVERSION=39
OPT PREPROCESS
OPT MODULE
OPT EXPORT
/************************************************************/
/* Public definitions for the "titlebar image"
BOOPSI class */
/************************************************************/
/* This macro can be used to compute the correct
position for a gadget */
/* to be placed into the titlebar. "tbi" is
a pointer to a "tbiclass" */
/* instance and "num" is the number of gadgets
(zoom, depth...) that */
/* will be at the right side of the new gadget.
For instance, if your */
/* window has both a zoom gadget and a depth
gadget, you can compute */
/* the position of a new titlebar gadget with
TBI_RELPOS(tbi,2). */
/* If there's instead only a depth gadget, you'll
use TBI_RELPOS(tbi,1). */
/* Note: the new gadget MUST have the GFLG_RELRIGHT
flag set. */
/* w := tbi.width
*/
#define TBI_RELPOS(w,n) (1-((1+n)*(w-1)))
/* necessary to define here, because parts of
modules/plugins/tbi.m */
/* have to be globally private, for library
bases safe. */
/* i.e. module must be OPT EXPORT to share macros
*/
#define DEFICON 'env:sys/def_tool'
#define TITLEBAR PLUGIN
MODULE 'utility/tagitem'
/* Attributes defined by the "tbiclass" image
class */
#define TBIA_DUMMY (TAG_USER + 0x0B0000)
#define TBIA_CONTENTSBOX (TBIA_DUMMY + 0x0001)
/* Get inner size (V40.12) */
/* Types of titlebar gadget
images available */
ENUM POPUPIMAGE =101,
MUIIMAGE ,
SNAPSHOTIMAGE ,
ICONIFYIMAGE ,
PADLOCKIMAGE ,
TBFRAMEIMAGE
|
Fichier qui est compilé avec :
Ec images/titlebar
D'où le code du plugin :
OPT PREPROCESS
OPT MODULE
OPT OSVERSION=39
->types OF sys:classes/images/titlebar.image
->POPUPIMAGE,
->MUIIMAGE,
->SNAPSHOTIMAGE,
->ICONIFYIMAGE,
->PADLOCKIMAGE,
->TBFRAMEIMAGE ->fallback
->#define DEBUG
#define NODEBUG
#ifdef DEBUG
MODULE 'tools/easygui_debug'
#endif
#ifdef NODEBUG
MODULE 'tools/easygui'
#endif
-> Remarquez les différents chemins, absolus et relatifs.
-> Le compilateur utilise le répertoire courant du fichier
source, et non pas le répertoire courant au lancement
de la commande
MODULE 'icon','*/titlebar',
'*/images/titlebar',
'intuition/gadgetclass','intuition/imageclass',
'intuition/intuition','intuition/screens',
'libraries/gadtools',
'wb',
'workbench/startup','workbench/workbench'
MODULE 'libraries/gadtools'
ENUM TB_NO_ERROR,
TB_ERROR_LIBRARY,
TB_ERROR_DRAWINFO,
TB_ERROR_NEWOBJECT,
TB_ERROR_ICON,
TB_ERROR_PORT
-> On redéfinit des bases locales privées
pour ces bibliothèques.
DEF iconbase,titlebarimagebase,workbenchbase
-> Voici notre objet, dont le type et les méthodes
sont partagés
EXPORT OBJECT tbi OF plugin
type
PRIVATE
dri:PTR TO drawinfo
scr:PTR TO screen
i :PTR TO image
gad:PTR TO gadget
icon
label
relpos
ENDOBJECT
-> L'objet n'est pas retaillable par l'utilisateur
PROC will_resize() OF tbi IS FALSE
-> L'objet ne représente pas un gadget
à l'intérieur de la
fenêtre, il ne participe donc
pas au calcul de la taille générale
ce qui explique que sa taille soit
nulle.
PROC min_size(ta,fh) OF tbi IS 0,0
-> Méthode d'initialisation, selon Wouter,
on se doit de donner le nom de l'objet à
la méthode qui sert à le construire
de manière à être
cohérent et éviter les collisions.
PROC tbi(type=TBFRAMEIMAGE,relpos=3,label=NIL,icon=NIL) OF tbi
self.type:=type
self.relpos:=relpos
-> Soit VisualPrefs a déjà chargé
ce gadget, soit on peut trouver ce gadget sur le disque, c'est
pourquoi on ne quitte qu'au second test, sans oublier que
OpenLibrary scrute l'assignement multiple vers LIBS: (Cf. S:startup-sequence)
IF NIL=(titlebarimagebase:=OpenLibrary('titlebar.image',40))
IF NIL=(titlebarimagebase:=OpenLibrary('images/titlebar.image',40))
Raise(TB_ERROR_LIBRARY)
ENDIF
ENDIF
IF NIL=(iconbase:=OpenLibrary('icon.library',37))
THEN Raise(TB_ERROR_LIBRARY)
IF NIL=(workbenchbase:=OpenLibrary('workbench.library',39))
THEN Throw(TB_ERROR_LIBRARY,'workbench.library')
self.label:=IF label THEN label ELSE
''
self.icon:=icon
ENDPROC
-> La méthode de destruction s'appelle
naturellement end et est reconnue par le E comme
telle, c'est donc cette fonction que l'instruction
END appellera pour détruire l'objet.
PROC end() OF tbi
IF self.dri THEN FreeScreenDrawInfo(self.scr,self.dri)
IF iconbase THEN CloseLibrary(iconbase)
IF titlebarimagebase THEN CloseLibrary(titlebarimagebase)
IF workbenchbase THEN CloseLibrary(workbenchbase)
ENDPROC
-> On "dessine" notre gadget
PROC render(ta,x,y,xs,ys,win:PTR TO window) OF tbi HANDLE
DEF dri:PTR TO drawinfo,
gad:PTR TO gadget,
i:PTR TO image,
scr:PTR TO screen
scr:=win.wscreen
IF NIL=(dri:=GetScreenDrawInfo(scr))
THEN Raise(TB_ERROR_DRAWINFO)
self.dri:=dri ; self.scr:=scr
-> On crée un objet parmi ceux proposés
par titlebar.image
IF i:=NewObjectA(NIL,'tbiclass',[SYSIA_WHICH,self.type,SYSIA_DRAWINFO,dri,NIL])
self.i:=i
-> On place ce gadget dans un bouton de manière à ce qu'on
-> puisse cliquer dessus
IF gad:=NewObjectA(NIL,'buttongclass',[GA_RELRIGHT,TBI_RELPOS(i.width,self.relpos),
GA_TOP,0,
GA_WIDTH,i.width-1,
GA_HEIGHT,i.height,
GA_TOPBORDER,TRUE,
GA_IMAGE,i,
GA_RELVERIFY,TRUE,
NIL])
self.gad:=gad
-> On l'ajoute aux autres gadgets attachés
à cette fenêtre
AddGList(win,self.gad,0,1,NIL)
-> On valide le changement
RefreshGList(self.gad,win,NIL,1)
ELSE ; Raise(TB_ERROR_NEWOBJECT)
ENDIF
ELSE ; Raise(TB_ERROR_NEWOBJECT)
ENDIF
EXCEPT
ReThrow()
ENDPROC
-> Méthode d'effacement du gadget
PROC clear_render(win) OF tbi
IF self.i
IF self.gad
-> On retire le gadget de la liste
RemoveGList(win,self.gad,1)
-> Puis on retire le bouton
DisposeObject(self.gad)
ENDIF
-> Puis l'objet titlebar.image lui-même
DisposeObject(self.i)
ENDIF
ENDPROC
-> On vient de m'appuyer dessus, crie le gadget :
PROC message_test(imsg:PTR TO intuimessage,win) OF tbi
IF imsg.class THEN RETURN imsg.iaddress=self.gad
ENDPROC FALSE
-> Méthode de traitement en fonction du type d'image
-> En effet, puisqu'il y a plusieurs gadgets possibles (DOpus ou MUI
-> par exemple) il faut en profiter :
PROC message_action(class,code,qual,win) OF tbi HANDLE
DEF dobj=NIL:PTR TO diskobject,
myport=NIL,
appicon=NIL,
appmsg:PTR TO appmessage,
tp
tp:=self.type
-> Sélection du type de gadget utilisé,
à vous de créer les fonctions manquantes
SELECT tp
CASE ICONIFYIMAGE
-> Si c'est un gadget d'icônification
closewin(self.gh)
-> Fermer la fenêtre
-> Trouver ou créer l'icône
IF NIL=(dobj:=GetDiskObjectNew(self.icon)) THEN dobj:=GetDiskObjectNew(DEFICON)
dobj.type:=NIL
-> Créer un port pour les communications
avec l'icône
IF NIL=(myport:=CreateMsgPort()) THEN Raise(TB_ERROR_PORT)
-> Ajouter l'icône au Workbench
IF NIL=(appicon:=AddAppIconA(0,0,self.label,myport,NIL,dobj,NIL)) THEN Raise(TB_ERROR_ICON)
-> Attendre qu'on double-clique sur cet icône
WaitPort(myport)
CASE PADLOCKIMAGE
NOP ; NOP
ENDSELECT
EXCEPT DO
IF appicon THEN RemoveAppIcon(appicon)
IF myport
WHILE appmsg:=GetMsg(myport) DO ReplyMsg(appmsg)
DeleteMsgPort(myport)
ENDIF
IF dobj THEN FreeDiskObject(dobj)
IF self.type=ICONIFYIMAGE THEN openwin(self.gh)
-> Attention ! Fondamental !
-> Répondre VRAI lorsqu'on a
appuyé sur le gadget, puis qu'on a relâché le bouton
de la souris, c'est la stratégie
adoptée par les autres gadgets du système.
ENDPROC class=IDCMP_GADGETUP
|
Après compilation en tbi.m ceci entraîne la création
d'un exemple.
L'exemple :
OPT PREPROCESS
OPT OSVERSION=39
MODULE 'tools/easygui',
'*modules/images/titlebar',
'*modules/plugins/tbi'
-> Par paresse, je n'est pas ajouté
de test automatique de l'identité du programme appelant l'objet.
-> Il faut donc respecter le nom du fichier
exécutable.
#define APP_NAME 'PROGDIR:tbi_test'
#define APP_ICON 'coucou'
PROC main() HANDLE
DEF t:PTR TO tbi,t2:PTR TO tbi
-> On utilise l'objet par défaut
à gauche, position 3
-> Puis celui d'icônification
en position 2, il reste en effet
-> le gadget de retaillage en position
1 à droite.
NEW t.tbi(),t2.tbi(ICONIFYIMAGE,2,APP_ICON,APP_NAME)
easyguiA('titre',[ROWS,
[TITLEBAR,{cloc},t],
[TITLEBAR,{cloc},t2],
[EQCOLS,
-> Ces gadgets ne sont là que pour
-> créer de l'espace
[SBUTTON,{clac},'
yep ! '],
[SBUTTON,{clac},'
yop ! ']
],
[SPACE],[BAR]
],NIL)
EXCEPT DO
IF t THEN END t
IF t2 THEN END t2
IF exception THEN WriteF('error : \\d,\\n',exception)
ENDPROC
PROC cloc(i) IS WriteF('cloc\\n')
PROC clac(i) IS WriteF('clac\\n')
|
Conclusion
C'est ainsi qu'à présent vous êtes pourvus d'un gadget d'iconification très pratique pour cacher
vos fenêtres d'application, mais aussi lorsque l'on retouche le Workbench, et qu'il faut mettre à jour Intuition, ce gadget permettra
de ne pas avoir à quitter votre application. Vous pouvez vous servir du patron fourni par e:src/tools/easygui/plugins/ticker.e pour faire
vos propres gadgets. J'ai par exemple construit un objet qui sauve la position de la fenêtre automatiquement lorsqu'on la clôt.
Non seulement l'interface graphique (Intuition) est extensible au travers de BOOPSI (sys:classes/images) mais le générateur
EasyGUI est aussi extensible, à l'aide des modules. Chaque nouveau module vient ajouter une fonctionnalité. Pour rester simple,
il est nécessaire que peu de champs restent visibles (data-hiding), que les constantes aient des noms à rallonge, de manière
à éviter les collisions...
Les méthodes, si vous en ajoutez de nouvelles, doivent être un minimum, pour rester rapide, lors de message_test(), tâchez
de faire vite !
Ici, nous nous sommes simplement servis d'un objet existant. Pour ajouter une fonction au système lui-même, c'est une bibliothèque
qu'il faut compiler. Mais bon, on fera ça une autre fois.
|