Corso di AmigaOS

Torna all'elenco delle lezioniPer dubbi, consigli o richieste, potete mandare un'e-mail ad Andrea Carolfi.
Ringraziamo Amiga Transactor Mailing List per questo tangibile contributo!


La GadTools library (Undicesima lezione)

In questa lezione cominceremo a vedere le funzioni della gadtools.library relative ai bottoni, successivamente nella prossima lezione parleremo dei menù per ritornare a descrivere le procedure della gadtools.library che aiutano il programmatore a creare i menù.

Struttura NewGadget

Prima di tutto occorre introdurre la struttura NewGadget, necessaria ad inizializzare alcuni aspetti di un bottone gadtools prima di crearlo:

/* Generic NewGadget used by several of the gadget classes: */

struct NewGadget
{
         WORD ng_LeftEdge, ng_TopEdge;   /* gadget position */
         WORD ng_Width, ng_Height;    /* gadget size */
         UBYTE *ng_GadgetText;     /* gadget label */
         struct TextAttr *ng_TextAttr;   /* desired font for gadget label */
         UWORD ng_GadgetID;        /* gadget ID */
         ULONG ng_Flags;        /* see below */
         APTR ng_VisualInfo;       /* Set to retval of GetVisualInfo() */
         APTR ng_UserData;         /* gadget UserData */
};

I campi degni di nota, sono: ng_Flags, ng_VisualInfo e ng_TextAttr, ritengo gli altri di facile comprensione per tutti.
I possibili flags attualmente definiti agiscono solamente sulla posizione della etichetta del bottone.

/* ng_Flags control certain aspects of the gadget.  The first five control
 * the placement of the descriptive text.  Each gadget kind has its default,
 * which is usually PLACETEXT_LEFT.  Consult the autodocs for details.
 */

#define PLACETEXT_LEFT  0x0001   /* Right-align text on left side */
#define PLACETEXT_RIGHT 0x0002   /* Left-align text on right side */
#define PLACETEXT_ABOVE 0x0004   /* Center text above */
#define PLACETEXT_BELOW 0x0008   /* Center text below */
#define PLACETEXT_IN 0x0010   /* Center text on */

#define NG_HIGHLABEL 0x0020   /* Highlight the label */

Il campo ng_VisualInfo, serve alla gadtools, per sapere con quali colori disegnare i bottoni. Infatti, la funzione:

APTR GetVisualInfoA( struct Screen *screen, struct TagItem *taglist );
APTR GetVisualInfo( struct Screen *screen, Tag tag1, ... );

ritorna il puntatore ad una struttura particolare, utilizzata dalla libreria per sapere quali colori della tavolozza deve utilizzare per il disegno dei bottoni (tipo, bordo superiore bianco, bordo inferiore nero, testo nero, ecc. ecc.).
Infatti, se vogliamo usare uno schermo con una nostra palette, ed abbiamo intenzione di creare dei bottoni gadtools su di esso, possiamo (e dovremmo) fare in modo che la struttura VisualInfo del nostro schermo sia coerente, inzializzando un array di "penne" che indentificano il numero di colore da usare per una determinata "penna".
Nel file intuition/screens.h vengono chiamate rendering pen number indexes. Sono 9 per le versioni di sistema operativo precedenti la 39, 12 per le versioni superiori:

/* rendering pen number indexes into DrawInfo.dri_Pens[] */
#define DETAILPEN  (0x0000)   /* compatible Intuition rendering pens */
#define BLOCKPEN   (0x0001)   /* compatible Intuition rendering pens */
#define TEXTPEN       (0x0002)   /* text on background         */
#define SHINEPEN   (0x0003)   /* bright edge on 3D objects     */
#define SHADOWPEN  (0x0004)   /* dark edge on 3D objects    */
#define FILLPEN       (0x0005)   /* active-window/selected-gadget fill  */
#define FILLTEXTPEN   (0x0006)   /* text over FILLPEN       */
#define BACKGROUNDPEN    (0x0007)   /* always color 0       */
#define HIGHLIGHTTEXTPEN (0x0008)   /* special color text, on background   */
/* New for V39, only present if DRI_VERSION >= 2: */
#define BARDETAILPEN  (0x0009)   /* text/detail in screen-bar/menus */
#define BARBLOCKPEN   (0x000A)   /* screen-bar/menus fill */
#define BARTRIMPEN    (0x000B)   /* trim under screen-bar */

#define NUMDRIPENS    (0x000C)

A noi quindi basta dichiarare un array:

UWORD penne[NUMDRIPENS] = { colore x, colore y, ... };

e passarlo come argomento alla:

scr = OpenScreenTags(NULL,
        [...]

        SA_Pens,    penne,

        [...]
        TAG_DONE);

in questo modo, ogni requester Intuition o bottoni e menù gadtools che useremo sul nostro schermo manterranno un aspetto simile a quando appaiono sul workbench (a patto ovviamente di avere colori compatibili).

Infine, il campo struct TextAttr (dichiarato in graphics/text.h) serve per indicare alla gadtools quale carattere intendiamo usare per i nostri bottoni. Ovviamente questo carattere deve essere aperto, altrimenti la funzione chiamata fallirà.
Quindi, se vogliamo usare il carattere di sistema, basta mettere questo puntatore a NULL, altrimenti basta dichiarare:

struct TextAttr ta = {"helvetica.font",13,FS_NORMAL,FPF_DISKFONT};

possiamo ovviamente utilizzare anche un altro font con una dimensione diversa (a partire dalla versione 2.0 del SO, se viene specificata un'altezza inesistente, viene creata in realtime riscalando quella più vicina) e uno stile diverso. Oltre a FPF_DISKFONT esiste FPF_ROMFONT. In ROM esiste solo il carattere topaz ad altezza 8 e 9. Tutti gli altri sono su disco e devono essere aperti prima di essere utilizzati con la funzione della diskfont.library:

struct TextFont *OpenDiskFont( struct TextAttr *textAttr );

ed usare la procedura della graphics.library:

void CloseFont( struct TextFont *textFont );

per chiuderla quando non serve più.

Esempio di codice

Vediamo quindi come utilizzare la gadtools.library:

#include 
#include 
#include 
#include 
#include 
#include 
#define WIDTH     120
#define HEIGHT    20
#define OFFSET    5

/* Inizializzazione struttura NewGadget */
struct NewGadget ngad = {OFFSET,OFFSET,WIDTH,HEIGHT,"Premi quì",NULL,1,
        PLACETEXT_IN,NULL,NULL};

/* Puntatore al bottone che verrà creato */
struct Gadget *gad = NULL;

void main(void)
{
        struct Screen *scr;
        struct Window *win;
        struct IntuiMessage *msg;
        BOOL finito = FALSE;
        APTR vinfo;

        /* Apriamo lo schermo clonandolo da quello del WB */
        if((scr = OpenScreenTags(NULL,
                SA_LikeWorkbench, TRUE,
                SA_Title,         "Schermo di prova",
                TAG_DONE)))
        {

                /* Otteniamo le informazioni necessarie per il rendering dei bottoni */
                if((vinfo = GetVisualInfoA(scr,NULL)))
                {
                        /* Valorizziamo il puntatore nella struttura NewGadget */
                        ngad.ng_VisualInfo = vinfo;

                        /* Apriamo la finestra */
                        if((win = OpenWindowTags(NULL,
                                WA_Top,        scr -> BarHeight + scr -> BarVBorder,
                                WA_Height,     scr -> Height - scr -> BarHeight - scr -> BarVBorder,
                                WA_IDCMP,      IDCMP_CLOSEWINDOW | IDCMP_GADGETUP |
                                                                        IDCMP_REFRESHWINDOW,
                                WA_Flags,      WFLG_CLOSEGADGET | WFLG_SIZEGADGET |
                                                                        WFLG_DRAGBAR | WFLG_DEPTHGADGET,
                                WA_Title,      "Finestra di prova",
                                WA_PubScreen,  scr,
                                TAG_DONE)))
                        {
                                /* Aggiusto in base alla dimensione del font */
                                ngad.ng_LeftEdge += win -> BorderLeft;
                                ngad.ng_TopEdge += win -> BorderTop;

                                /* Più piccola di così no */
                                WindowLimits(win,
                                        ngad.ng_LeftEdge + ngad.ng_Width + win -> BorderRight + OFFSET,
                                        ngad.ng_TopEdge + ngad.ng_Height + win -> BorderBottom + OFFSET,0,0);

                                /* Creo il bottone */
                                if((gad = CreateGadget(BUTTON_KIND,CreateContext(&gad),&ngad,TAG_DONE)))
                                {
                                        /* Aggiungo il bottone alla finestra */
                                        AddGList(win,gad,0,1,NULL);

                                        /* Rinfresco il bottone tramite Intuition */
                                        RefreshGList(gad,win,NULL,1);

                                        /* Rinfresco il bottone tramite GadTools */
                                        GT_RefreshWindow(win,NULL);

                                        while(!finito)
                                        {
                                                WaitPort(win -> UserPort);

                                                /* Finchè ci sono messaggi */
                                                while((msg = GT_GetIMsg(win -> UserPort)))
                                                {
                                                        switch(msg -> Class)
                                                        {
                                                                case IDCMP_CLOSEWINDOW:
                                                                        finito = TRUE;
                                                                break;
                                                                case IDCMP_GADGETUP:
                                                                        Printf("Bottone premuto!\n");
                                                                break;

                                                                /* Rinfresco i bottoni */
                                                                case IDCMP_REFRESHWINDOW:
                                                                        GT_BeginRefresh(win);
                                                                        GT_EndRefresh(win,TRUE);
                                                                break;
                                                        }

                                                        /* Replico il messaggio */
                                                        GT_ReplyIMsg(msg);
                                                }
                                        }
                                }
                                CloseWindow(win);
                        }

                        /* Dopo aver chiuso la finestra libero i bottoni ed il VisualInfo */
                        FreeGadgets(gad);
                        FreeVisualInfo(vinfo);
                }
                /* Chiudo lo schermo */
                CloseScreen(scr);
        }
}

La funzione:

struct Gadget *CreateContext( struct Gadget **glistptr );

deve essere chiamata prima di ogni successiva CreateGadget. Serve per allocare ed inizializzare alcune strutture private che serviranno in seguito alla libreria per creare i bottoni. Può essere utilizzata come mostrato o chiamandola come ogni altra funzione in un suo costrutto if. Se questa funzione fallisce, tutte le successive CreateGadget falliranno e se i controlli sono stati fatti bene, il programma terminerà senza problemi.

La funzione:

struct Gadget *CreateGadgetA( unsigned long kind, struct Gadget *gad,
        struct NewGadget *ng, struct TagItem *taglist );
struct Gadget *CreateGadget( unsigned long kind, struct Gadget *gad,
        struct NewGadget *ng, Tag tag1, ... );

serve per creare i vari tipi di gadget supportati dalla gadtools. Negli autodoc forniti vengono ampiamente documentati per ogni tipo, quali tag possono essere passati per determinare svariati aspetti dei bottoni. Ad esempio, specificando GA_Disable a TRUE, creerà un bottone disabilitato.
Il parametro gad, che può essere interpretato erroneamente, è il puntatore al gadget creato precedentemente. Serve prima di tutto per creare la lista di gadget. Inoltre, in questo modo, chiamando in cascata le varie CreateGadget, sarà sufficente controllare l'esito dell'ultima per sapere se la creazione dei gadget è andata a buon fine o no.
Dato che questa procedura fallisce se il parametro gad è NULL.

La procedura:

void FreeGadgets( struct Gadget *gad );

serve per liberare la lista di gadget e deve essere chiamata DOPO la chiusura della finestra.

La funzione e la procedura:

struct IntuiMessage *GT_GetIMsg( struct MsgPort *iport );
void GT_ReplyIMsg( struct IntuiMessage *imsg );

sono le corrispettive gadtools di quelle exec. Questo perchè i messaggi inviati dai bottoni gadtools sono leggermente diversi da quelli soliti ed è necessario utilizzare queste funzioni.

Infine, queste tre procedure:

void GT_RefreshWindow( struct Window *win, struct Requester *req );
void GT_BeginRefresh( struct Window *win );
void GT_EndRefresh( struct Window *win, long complete );

Servono per ridisegnare il contenuto "gadtools" della finestra. Contenuto "gadtools" perchè se nella finestra avessimo della grafica non creata con la gadtools, dovremo preoccuparci di ridisegnarla a mano o utilizzare una finestra con SMART_REFRESH come tipo di refresh.

Listato di questa lezione

Lezione precedente Indice delle lezioni Lezione successiva
Copyright AMiWoRLD Ph0ton