Obligement - L'Amiga au maximum

Vendredi 06 juin 2025 - 12:46  

Translate

En De Nl Nl
Es Pt It Nl


Rubriques

Actualité (récente)
Actualité (archive)
Comparatifs
Dossiers
Entrevues
Matériel (tests)
Matériel (bidouilles)
Points de vue
En pratique
Programmation
Reportages
Quizz
Tests de jeux
Tests de logiciels
Tests de compilations
Trucs et astuces
Articles divers

Articles in English


Réseaux sociaux

Suivez-nous sur X




Liste des jeux Amiga

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


Trucs et astuces

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


Glossaire

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


Galeries

Menu des galeries

BD d'Amiga Spécial
Caricatures Dudai
Caricatures Jet d'ail
Diagrammes de Jay Miner
Images insolites
Fin de jeux (de A à E)
Fin de Jeux (de F à O)
Fin de jeux (de P à Z)
Galerie de Mike Dafunk
Logos d'Obligement
Pubs pour matériels
Systèmes d'exploitation
Trombinoscope Alchimie 7
Vidéos


Téléchargement

Documents
Jeux
Logiciels
Magazines
Divers


Liens

Associations
Jeux
Logiciels
Matériel
Magazines et médias
Pages personnelles
Réparateurs
Revendeurs
Scène démo
Sites de téléchargement
Divers


Partenaires

Annuaire Amiga

Amedia Computer

Relec


A Propos

A propos d'Obligement

A Propos


Contact

David Brunet

Courriel

 


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.


[Retour en haut] / [Retour aux articles] [Article précédent]