XFORMS - THE FORMS LIBRARY
Toolkit per la costruzione di interfacce grafiche (GUI) nel sistema X
Window.
Principali vantaggi:
- Software di pubblico dominio, disponibile su diverse piattaforme
- Si basa su libreria Xlib
- Molte funzionalita'
- Semplice e intuitivo
- Comprende un Form Designer per la creazione interattiva di GUI
A differenza di altri toolkit (per es. Motif), nasconde completamente
X al programmatore: non e' necessario conoscere X per programmare in
XForms.
+---------------+ +---------------+
|Applicazione | |Applicazione |
+----------+--+-+ +---------------+
|Motif | | | | |
+----------+ | | |XForms |
|Xt intrinsics| | | |
+-------------+ | +---------------+
|Xlib | |Xlib |
+---------------+ +---------------+
Concetti base (Terminologia):
- Form: finestra-contenitore in cui sono sistemati vari oggetti.
Una form e' una finestra X situata a top-level e gestita dal window
manager.
- Oggetti: elementi di interfaccia contenuti in una form,
attraverso i quali si svolge l'interazione tra utente e applicazione
(es: bottoni, campi di inserimento, ecc.).
Un oggetto e' una sotto-finestra X avente un certo aspetto distintivo
e certe capacita' di interazione (sensibile a certi eventi).
XForms non adopera il termine "widget".
Classi di oggetti
Oggetti statici
Non hanno capacita' di interazione, servono solo per visualizzare.
- Box: area rettangolare contenente testo
- Frame: cornice che racchiude un'area rettangolare (puo' racchiudere altri
oggetti)
- Text: testo alfanumerico
- Bitmap: immagine raster B/N (matrice di bit)
- Pixmap: immagine raster a colori o a livelli di grigio
- Clock: orologio analogico o digitale
- Chart: area rettangolare destinata a contenere grafici a linea, a barre,
a torta, ecc.
Bottoni
Caratterizzati da interazione col mouse tramite click.
- Button: varie classi e tipi di bottoni che si
differenziano per aspetto e comportamento
(ved. piu' avanti)
Quantificatori
Interazione col mouse mediante click o trascinamento,
permettono di introdurre un valore numerico.
- Slider: vari tipi di potenziometri lineari
- Dial: vari tipi di potenziometri rotanti
- Counter: potenziometro digitale
- Positioner: fornisce non un singolo valore, ma una coppia di coordinate
(x,y) in un'area rettangolare indicata
Campi di input
Attivazione col mouse, input da tastiera.
- Input: campo di una o piu' linee contenente un cursore
Selezionatori
Interazione col mouse tramite click, permettono di immettere
una scelta tra un insieme predefinito.
- Menu: diversi tipi di menu'
(ved. piu' avanti)
- Choice: ha un unico bottone che mostra
in ogni momento la scelta corrente, la scelta corrente puo' essere
cambiata ciclando sull'insieme delle scelte possibili
- Browser: si usa per lunghe liste di scelte, permette di muoversi
attraverso la lista e selezionare elementi attraverso un cursore
(scrollbar)
Altri oggetti
- Timer: contatore alla rovescia
- XYPlot: consente di disegnare e modificare agevolmente funzioni
- Canvas: sottofinestra utilizzabile per output grafico; in un canvas
posso utilizzare X direttamente, scavalcando XForms; in particolare,
posso usare altri pacchetti grafici operanti sotto X.
Noi useremo un canvas per fare grafica in OpenGL.
Struttura di un'applicazione in XForms
Un programma applicativo che utilizza XForms deve contenere le seguenti
parti:
-
Inizializzazione:
stabilisce connessione tra programma e X-server,
e inizializza strutture interne di XForms.
-
Definizione delle form: creazione delle
(una o piu') form necessarie al programma, con tutti i loro oggetti.
Comprende eventuale associazione di callback agli oggetti.
-
Visualizzazione delle form per rendere visibile la GUI.
-
Ciclo degli eventi (main loop):
gestione dell'interazione con l'utente
e delle operazioni che il programma deve compiere di conseguenza.
Nota: XForms si pone sempre come "cuscinetto" tra applicazione e Xlib,
Xlib rimane trasparente al programmatore.
Vediamo le funzioni di XForms relative alle 4 fasi.
NOTA: i programmi forniti in esempio si compilano con il
makefile digitando
make PROG=nomefile.c
per generare l'eseguibile nomefile.
Inizializzazione
fl_initialize (&argc, argv, titolo, 0, 0);
argc, argv = argomenti passati al main da command line
titolo = titolo dell'applicazione (char *)
Definizione delle form
FL_FORM * fl_bgn_form(tipo, w, h);
void fl_end_form();
Delimitano la definizione di una form.
Tra fl_bgn_form e fl_end_form
trovano posto le definizioni di tutti gli oggetti contenuti nella form.
tipo = tipo di form (costante simbolica), si riferisce
all'apparenza del rettangolo che costituisce la finestra, es:
FL_UP_BOX (in rilievo), FL_DOWN_BOX (rientrante), FL_BORDER_BOX,
FL_FLAT_BOX...
w, h = larghezza e altezza (in pixel)
FL_OBJECT * fl_add_XXXX(tipo, x,y, w,h, label);
Posto fra fl_bgn_form e fl_end_form,
definisce un oggetto che viene aggiunto alla form corrente.
XXXX = classe dell'oggetto (es: bottone, slider...)
tipo = tipo di oggetto all'interno della sua
classe, di solito si riferisce ad una caratteristica di aspetto o
comportamento particolare
x,y = posizione del punto di aggancio dell'oggetto (angolo in
alto a sinistra) nel sistema di coordinate della form
w,h = larghezza e altezza dell'oggetto in pixel
Associazione di callback
Ogni oggetto e' sensibile a certi eventi predefiniti, dipendenti dalla
classe di oggetto.
Una callback e' una procedura del programma applicativo che viene
associata ad un oggetto in fase di definizione e che viene eseguita
automaticamente da XForms ogni volta che in tale oggetto si genera un evento.
Un oggetto ha una sola callback per tutti gli eventi a cui e'
sensibile.
La procedura callback deve interpretare l'evento che si e' verificato
interrogando l'oggetto che lo ha prodotto con apposite funzioni, in modo
da poter decidere che cosa fare.
Definire una funzione di callback
Una funzione di callback deve essere dichiarata del tipo:
f(FL_OBJECT * obj, long arg);
obj = sara' l'oggetto dove si e' generato l'evento (oggetto
che avra' questa funzione come sua callback )
arg = valore numerico deciso dal programmatore quando associa
la callback all'oggetto
Associarla ad un oggetto
fl_set_object_callback(FL_OBJECT * obj, function, long arg);
obj = l'oggetto a cui la associo (ritornato da fl_add_XXXX
quando ho aggiunto l'oggetto alla form)
function = la callback
arg = valore numerico che verra' passato dal sistema in
argomento alla callback.
Il valore di arg viene usato quando la stessa funzione e'
associata come callback ad oggetti diversi.
Nel corpo della funzione uso il valore di arg per discriminare.
La callback di un oggetto puo' essere cambiata in qualsiasi momento.
Chiamata della callback
La callback di un oggetto e' chiamata da XForms nel ciclo degli eventi,
quando si verifica un evento in tale oggetto.
Quando il sistema chiama la callback, le passa in argomento
l'oggetto dove l'evento e' accaduto e il valore di arg
che ho specificato al momento di associare la callback all'oggetto.
Posso anche chiamare una callback "artificialmente" da programma,
senza che nessun evento accada.
fl_call_object_callback(FL_OBJECT *);
Invoca la callback di un oggetto.
Visualizzazione delle form
fl_show_form(FL_FORM * form, place, border, title);
form = la form da visualizzare (ritornata da
fl_bgn_form quando ho definito la form)
place = dove deve essere posizionata di preferenza la form
(costante simbolica), es:
FL_PLACE_FREE, FL_PLACE_MOUSE, FL_PLACE_FULLSCREEEN,
FL_PLACE_ICONIC...
border = che tipo di bordo deve essere aggiunto di preferenza
alla form (costante simbolica)
title = titolo della form, che apparira' come titolo della finestra
Ciclo degli eventi (Main loop)
FL_OBJECT * fl_do_forms();
Implementa il ciclo degli eventi con tipo di interazione bloccante.
-
Tiene il controllo da quando viene invocata a quando si verifica un
evento proveniente da un oggetto.
-
Se a tale oggetto il programmatore ha associato una callback,
fl_do_forms chiama automaticamente la callback.
Terminata l'esecuzione
della callback, fl_do_forms riprende il controllo.
-
Se tale oggetto non ha una callback
associata, fl_do_forms esce, restituendo l'oggetto.
Il controllo passa al programma applicativo.
Il programma applicativo in base all'oggetto ritornato da
fl_do_forms fara' qualcosa. Poi, tipicamente, chiamera' di nuovo
fl_do_forms.
Esempio di programma senza callback
Vedere file yesno.c.
Crea una form con un titolo testuale "Do you want to quit?" e due
bottoni "Yes" e "No".
Visualizza la form, poi chiama fl_do_forms. Quando uno dei due
bottoni viene premuto, fl_do_forms esce e lo ritorna. Se il
bottone premuto e' "Yes", il programma termina, altrimenti chiama
nuovamente fl_do_forms, e cosi' via.
#include "forms.h"
FL_FORM *form;
FL_OBJECT *yes, *no, *but;
main (int argc, char *argv[])
{
/*** Inizializzazione ***/
fl_initialize ( &argc, argv, "FormDemo", 0, 0);
/*** Definizione forms ***/
form = fl_bgn_form(FL_UP_BOX, 320, 120);
/* crea form con aspetto di box tridimensionale sporgente da schermo,
larga 320 pixel e alta 120 */
fl_add_box(FL_NO_BOX, 160, 40, 0, 0, "Do you want to quit?");
/* aggiunge alla form che sto definendo un box contenente la
scritta "Do you want to quit?" */
yes = fl_add_button(FL_NORMAL_BUTTON, 40, 70, 80, 30, "Yes");
/* aggiunge alla form un bottone di etichetta "Yes", posizionato con
l'angolo in alto a sinistra nel punto 40,70 della form, largo 80
pixel e alto 30 */
no = fl_add_button(FL_NORMAL_BUTTON, 200, 70, 80, 30, "No");
/* analogo per bottone "No" */
fl_end_form();
/*** Visualizzazione forms ***/
fl_show_form(form, FL_PLACE_MOUSE,FL_TRANSIENT,"Question");
/* FL_TRANSIENT causa un tipo di bordo adatto a finestre che
devono stare visualizzate per poco tempo */
/*** Main loop ***/
while ( (but = fl_do_forms()) != yes ) ;
/* fintanto che il bottone cliccato non e' yes, continua a ciclare */
/*** Terminazione ***/
fl_hide_form(form);
return 0;
}
Stesso esempio riscritto usando callback
Vedere file yncallbk.c.
Crea una form come prima. Entrambi i bottoni hanno callback: quella di
"Yes" termina il programma, quella di "No" non fa nulla.
Il programma visualizza la form, poi chiama fl_do_forms.
Quando uno dei due bottoni viene premuto, fl_do_forms
chiama la callback corrispondente.
#include "forms.h"
FL_FORM *form;
void yes_callback(FL_OBJECT *ob, long user_data)
{
printf("Yes is pushed\n");
fl_finish();
exit(0);
}
void no_callback(FL_OBJECT *ob, long user_data)
{
printf("No is pushed\n");
}
main (int argc, char *argv[])
{
FL_OBJECT *obj;
/*** Inizializzazione ***/
fl_initialize ( &argc, argv, "FormDemo", 0, 0);
/*** Definizione forms ***/
form = fl_bgn_form(FL_UP_BOX, 320, 120);
/* crea form con aspetto di box tridimensionale sporgente da schermo,
larga 320 pixel e alta 120 */
fl_add_box(FL_NO_BOX, 160, 40, 0, 0, "Do you want to quit?");
/* aggiunge alla form che sto definendo un box contenente la
scritta "Do you want to quit?" */
obj = fl_add_button(FL_NORMAL_BUTTON, 40, 70, 80, 30, "Yes");
/* aggiunge alla form un bottone di etichetta "Yes", posizionato con
l'angolo in alto a sinistra nel punto 40,70 della form, largo 80
pixel e alto 30 */
fl_set_object_callback(obj, yes_callback, 0);
/* assegna al bottone "Yes" la callback */
obj = fl_add_button(FL_NORMAL_BUTTON, 200, 70, 80, 30, "No");
fl_set_object_callback(obj, no_callback, 0);
/* analogo per bottone "No" */
fl_end_form();
/*** Visualizzazione forms ***/
fl_show_form(form, FL_PLACE_MOUSE,FL_TRANSIENT,"Question");
/* FL_TRANSIENT causa un tipo di bordo adatto a finestre che
devono stare visualizzate per poco tempo */
/*** Main loop ***/
fl_do_forms();
/* poiche' ogni oggetto capace di catturare eventi ha una callback,
fl_do_forms() non esce mai, la terminazione del programma avviene
dentro la callback di "Yes" */
}
Schemi di programma nelle due modalita'
- Interazione semplice:
- inizializzazione
- definizione forms
- visualizzazione forms
- while (...)
{
obj = fl_do_forms();
gestione evento avvenuto in obj
}
- Interazione con callback:
- inizializzazione
- definizione forms e associazione callback ai loro oggetti
- visualizzazione forms
- fl_do_forms();
Interazione non bloccante
Serve nei casi in cui l'applicazione deve fare qualcosa anche in assenza di
eventi o tra un evento e l'altro (es: animazione grafica).
FL_OBJECT * fl_check_forms();
Guarda se si e' verificato un evento in un oggetto.
- Se si e' verificato:
-
se l'oggetto ha una callback associata,
esegue tale callback e poi torna a vedere se c'e' un altro evento
-
se l'oggetto non ha callback, esce ritornando l'oggetto; sara' il
programma ad agire di conseguenza all'evento
- Se non si e' verificato alcun evento, allora esce ritornano NULL.
Il programma prende il controllo e puo' eseguire una procedura
indipendente da eventi (es: andare avanti un passo in un'animazione),
e poi tipicamente chiama di nuovo fl_check_forms
Da usare come alternativa a fl_do_forms solo se veramente
necessario (consuma CPU).
Aggiungere oggetti ad una form
Questa parte copre le classi principali di oggetti XForms.
E' solo un'idea di quello che c'e', per dettagli vedere il manuale.
Oggetti statici
Box
Riquadro con etichetta all'interno.
FL_OBJECT * fl_add_box(tipo, x,y, w,h, label);
tipo = uno fra FL_UP_BOX, FL_DOWN_BOX, FL_FLAT_BOX,
FL_SHADOW_BOX...
Frame
"Cornice", riquadro vuoto con etichetta sul bordo.
FL_OBJECT * fl_add_frame(tipo, x,y, w,h, label);
tipo = uno fra FL_NO_FRAME, FL_UP_FRAME, FL_DOWN_FRAME,
FL_BORDER_FRAME, FL_ENGRAVED_FRAME...
Text
Semplice etichetta senza decorazioni, di default allineata a sinistra
in un box invisibile.
FL_OBJECT * fl_add_text(tipo, x,y, w,h, label);
tipo = sempre FL_NORMAL_TEXT
Bottoni
Si differenziano per aspetto (controllato dalla classe) e
comportamento (controllato dal tipo).
FL_OBJECT * fl_add_XXXXbutton(tipo, x,y, w,h, label);
Aggiunge bottone di classe XXXX e di tipo tipo.
Classi di bottoni:
button, roundbutton, checkbutton, lightbutton...
Tipi di bottoni:
- FL_NORMAL_BUTTON
- FL_PUSH_BUTTON: resta schiacciato finche' non clicco di nuovo
- FL_RETURN_BUTTON: mostra freccetta accanto all'etichetta,
sensibile anche a pressione del "return" della tastiera
- FL_INOUT_BUTTON: ritorna valore sia quando premuto che
quando rilasciato
- ecc.
Alcune funzioni utili sui bottoni
-
int fl_get_button(FL_OBJECT *);
Ritorna se bottone e' premuto o no.
-
int fl_get_button_numb(FL_OBJECT *);
Ritorna il tasto del mouse con cui ho premuto il bottone (1,2, o 3).
-
int fl_set_button(FL_OBJECT *, int);
Cambia lo stato di un bottone, argomento = 1 (premuto) o = 0 (rilasciato).
-
fl_set_button_shortcut
Assegna un insieme di tasti della tastiera come "scorciatoia" per
attivare il bottone in alternativa ad usare il mouse (ved. manuale).
Gruppi di oggetti
FL_OBJECT * fl_bgn_group();
FL_OBJECT * fl_end_group();
Delimitano la definizione di oggetti che sono visti come un unico oggetto.
Utile per poter assegnare attributi, attivare/disattivare,
nascondere/mostrare tutto il gruppo in una istruzione sola.
E' vietato annidare le definizioni di gruppi.
Esiste un tipo di bottone, FL_RADIO_BUTTON, dove la pressione di un
bottone del gruppo provoca automaticamente il rilascio di tutti gli
altri bottoni del gruppo. Gruppi di bottoni FL_RADIO_BUTTON servono a
implementare scelte mutuamente esclusive.
Quantificatori
Slider
Barra con cursore mobile.
La barra corrisponde ad un intervallo di valori (per default tra 0 e 1),
il cursore ad un valore corrente nell'intervallo (per default inizializzato
a 0).
FL_OBJECT * fl_add_slider(tipo, x,y, w,h, label);
FL_OBJECT * fl_add_valslider(tipo, x,y, w,h, label);
Nella seconda variante lo slider ha accanto un'etichetta che mostra il
valore corrente.
tipo = definisce direzione (orizzontale, verticale) della
barra e aspetto del cursore:
FL_VERT_SLIDER, FL_HOR_SLIDER, FL_VERT_FILL_SLIDER, FL_HOR_FILL_SLIDER,
FL_VERT_NICE_SLIDER, FL_HOR_NICE_SLIDER
Restituzione di eventi da slider
Per default, lo slider cattura un evento ogni volta che l'utente
muove il cursore. Per cambiare politica si usa:
void fl_set_slider_return(FL_OBJECT *, int);
Possibili valori del parametro intero:
FL_RETURN_CHANGED (ogni volta che il cursore si sposta),
FL_RETURN_END_CHANGED (quando l'utente rilascia il cursore dopo
averlo spostato) ...
Alcune funzioni utili sugli slider
- fl_set_slider_value, fl_get_slider_value assegna, legge il
valore corrente (posizione del cursore), inizialmente per default = 0
- fl_set_slider_bounds assegna
l'intervallo di valori coperto dalla barra (per default tra 0 e 1)
Esempio: File demo05.c.
Usa interazione semplice.
Visualizza una form contenente uno slider, un box la cui etichetta mostra
il valore corrente dello slider (aggiornato da programma ogni volta che
l'utente interagisce con lo slider), e un bottone "Exit".
Dial
Analogo allo slider ma circolare con una lancetta rotante.
Le funzioni sui dial sono simili a quelle sugli slider (vedere manuale).
Positioner
Svolge la funzione di uno slider bidimensionale.
Permette di inserire una coppia di coordinate (x,y).
Si presenta come un riquadro con una retta orizzontale ed una verticale che
si incrociano.
L'incrocio delle due rette denota il valore corrente.
Le funzioni sui positioner sono analoghe a quelle sugli slider ma
doppie (per x e per y, vedere manuale).
Counter
Mostra il valore corrente in un riquadro e frecce per modificare
tale valore in crescendo e in diminuendo.
FL_OBJECT * fl_add_counter(tipo, x,y, w,h, label);
tipo = uno fra FL_NORMAL_COUNTER (quattro frecce,
per ciascuna direzione
una freccia con passo piccolo ed una con passo grande) e
FL_SIMPLE_COUNTER (solo due frecce).
Alcune funzioni utili sui counter
- fl_set_counter_value, fl_get_counter_value
assegna / legge il valore corrente (per default il valore iniziale e' 0)
- fl_set_counter_bounds
assegna l'intervallo di variabilita' (per default tra -10000000 e +1000000)
- fl_set_counter_step assegna i passi di
incremento / decremento delle frecce (per default 0.1 e 1)
Input Fields
Riquadro contenente testo, che puo' essere editato usando la tastiera.
FL_OBJECT * fl_add_input(tipo, x,y, w,h, label);
tipo = FL_NORMAL_INPUT...
L'etichetta viene mostrata a lato del campo di inserimento.
Per default, il campo di inserimento all'inizio e' vuoto.
Funzioni utili sui text field
- fl_set_input(FL_OBJECT *, char *);
assegna il contenuto del campo
- char * fl_get_input(FL_OBJECT *);
legge la stringa inserita
Menu'
Appare come una box con un'etichetta (titolo del menu') al centro.
Quando l'utente si posiziona col mouse su
tale box (ed eventualente clicca un tasto) appare una tendina contenente
le voci del menu', e l'utente puo' selezionarne una.
FL_OBJECT * fl_add_menu(tipo, x,y, w,h, label);
tipo = uno tra:
- FL_PUSH_MENU: la tendina del menu' appare quando l'utente preme
un tasto del mouse sulla box, e appare sollevata dallo sfondo e
proiettante un'ombra
- FL_PULLDOWN_MENU: la tendina del menu' appare quando l'utente
preme un tasto del mouse sulla box (come nel caso push down)
ma la tendina appare giacente sullo sfondo, senza ombra
- FL_TOUCH_MENU: la tendina del menu' appare quando l'utente si
muove col mouse nella box (non e' necessario premere tasti)
Definire le voci di un menu'
fl_add_menu non specifica quali sono le voci del menu'. Queste
si assegnano con:
fl_set_menu(menu', stringa);
stringa contiene le voci del menu' una di seguito all'altra
separate da '|', es: "prima|seconda|terza"
Alcune funzioni utili sui menu'
- int fl_get_menu(FL_OBJECT * menu);
da chiamare nella gestione di un evento da menu',
ritorna il numero d'ordine della voce selezionata
- fl_get_menu_text ritorna la stringa titolo del menu'
- fl_get_menu_item_text
ritorna la stringa che etichetta la voce k-esima
- fl_get_menu_maxitems ritorna il numero di voci nel menu
- funzioni per aggiungere/rimuovere una voce, sostituirne l'etichetta.
- funzioni per cambiare l'apparenza e comportamento delle voci
(ved. dopo).
Esempio
Vedere file menu.c.
#include "forms.h"
#include
void menu_cb(FL_OBJECT *ob, long user_data)
{
printf("%s\n",fl_get_menu_item_text(ob,fl_get_menu(ob)));
switch(fl_get_menu(ob))
{
case 1: /* azione corrispondente a prima voce */ break;
case 2: /* azione corrispondente a seconda voce */ break;
case 3: /* azione corrispondente a terza voce */ break;
case 4: exit(0);
}
}
int main(int argc,char *argv[])
{
FL_FORM *form;
FL_OBJECT *menu;
fl_initialize(&argc, argv, "FormDemo", 0, 0);
form = fl_bgn_form(FL_UP_BOX,140,100);
menu = fl_add_menu(FL_PULLDOWN_MENU,20,20,100,60,"This is a menu");
fl_set_object_boxtype(menu,FL_UP_BOX);
fl_set_menu(menu,"one option|another option|a third option|exit");
fl_set_object_callback(menu,menu_cb,0);
fl_end_form();
fl_show_form(form, FL_PLACE_MOUSE, FL_TRANSIENT,"FormDemo");
fl_do_forms();
}
Modo di una voce di menu'
Ogni voce ha un modo tra:
- FL_PUP_NONE = default
- FL_PUP_GREY = voce non selezionabile, etichetta visualizzata in grigio
- FL_PUP_BOX = con quadratino vuoto a fianco (vedere dopo)
- FL_PUP_CHECK = con quadratino pieno a fianco (vedere dopo)
- FL_PUP_RADIO = con rombetto, inizialmente vuoto, a fianco (vedere dopo)
Mediante FL_PUP_BOX e FL_PUP_CHECK si realizzano voci che restano
selezionate o disselezionate tra un'apertura e l'altra della tendina.
Mediante FL_PUP_RADIO si fa cosa analoga per voci mutuamente
esclusive.
Per assegnare / leggere il modo corrente di una voce di menu':
fl_set_menu_item_mode(menu, numero voce, modo);
fl_get_menu_item_mode(menu, numero voce);
Uso di FL_PUP_BOX e FL_PUP_CHECK:
Se una voce ha un quadtratino (vuoto o pieno) a fianco, ogni volta che
seleziono la voce il quadratino passa da vuoto a pieno e viceversa
(ovvero i modi FL_PUP_BOX e FL_PUP_CHECK si scambiano tra loro).
Uso di FL_PUP_RADIO:
Le voci con un rombetto (FL_PUP_RADIO) costituiscono un gruppo "radio":
in ogni istante solo una puo' avere il rombetto pieno, se ne seleziono
una il suo rombetto si riempie, e si svuota quello di tutte le altre.
Analogo ai gruppi di radio buttons visti in precedenza.
Choice
Si presenta come un box che mostra al suo interno la scelta corrente.
L'utente puo' ciclare tra le scelte possibili usando
il tasto sinistro o centrale del mouse, oppure ottenere la lista completa
in forma di menu' usando il tasto destro.
Le funzioni per oggetti choice sono molto simili a quelle dei menu'.
Vedere manuale.
Browser
Oggetto che puo' contenere un numero qualsiasi di linee di testo. Se ne
contiene piu' di quante stiano nella dimensione dell'oggetto, XForms
aggiunge automaticamente una scroll bar.
FL_OBJECT * fl_add_menu(tipo, x,y, w,h, label);
tipo = uno fra:
- FL_NORMAL_BROWSER: visualizza semplicemente testo (utile per es. per
mostrare messaggi di help all'utente
- FL_SELECT_BROWSER: permette di selezionare una linea singola
- FL_HOLD_BROWSER: come sopra ma la linea resta selezionata fino a che
l'utente non ne seleziona un'altra
- FL_MULTIBROWSER: permette di selezionare piu' linee contemporaneamente
E' possibile anche inizializzare il contenuto testuale di un browser
caricandolo da un file, questo permette ad es. di visualizzare schermate
di help caricate da file.
Per maggiori dettagli si rimanda al manuale.
Form di utilita'
XForms fornisce alcune form predefinite per compiti particolari:
- fl_show_message fa apparire
form contenente messaggio e bottone di "ok"
- fl_show_question fa apparire
form contenente messaggio (domanda) e bottoni "ok" e "no"
- fl_show_choice fa apparire
form contenente messaggio (titolo) e fino a tre bottoni di scelta
- fl_show_input fa apparire
form contenente messaggio e campo di inserimento testuale
- fl_show_fselector fa apparire
form per scelta di nome file (con possibilita' di scorrere
directory corrente, cambiare directory...)
Per dettagli vedere il manuale.
Attributi degli oggetti
Ogni classe di oggetti ha certi attributi, che posso leggere o impostare
con apposite funzioni.
Alcuni attributi sono comuni a tutte le classi di oggetti:
colore, geometria, etichetta.
Colore
- fl_set_object_color imposta i due colori che controllano un
oggetto, questi colori hanno un significato diverso a seconda della classe
di oggetto (es: per un bottone sono il colore del bottone quando
e' premuto e quando e' rilasciato)
I colori sono espressi comeindici interi in una color map. Esistono costanti
predefinite: FL_BLACK, FL_RED...
Esempio: File colors4.c.
Visualizza una form con un bottone in alto, etichettato "color",
e 4 bottoni ognuno di un colore diverso.
Cliccare uno dei 4 bottoni provoca il cambiamento di colore del bottone
in alto, che diventa uguale a quello del colore selezionato.
Cliccare il bottone in alto provoca l'uscita dal programma.
I 4 bottoni condividono la stessa callback, ma con un diverso valore
come secondo argomento (quelle di tipo "long") che e' uguale al colore
del bottone, e che viene usato all'interno della callback per cambiare
il colore del bottone in alto.
Il bottone in alto non ha callback, cosi' che cliccare su di esso provoca
l'uscita da fl_do_forms.
Geometria
- fl_set_object_position imposta posizione
(coordinate del punto di aggancio dell'oggetto all'interno della form)
- fl_set_object_size imposta dimensioni
(larghezza e altezza in pixel)
- fl_set_object_geometry imposta posizione e dimensione
- fl_set_object_boxtype imposta tipo di box
(costante simbolica con stesso significato come il "tipo" per oggetti di
classe box)
Etichetta
- fl_set_object_lcol cambia colore dell'etichetta
- fl_set_object_label cambia l'etichetta
- fl_set_object_lsize cambia la dimensione del carattere
- fl_set_object_lstyle cambia lo stile (bold,italic,...)
- fl_set_object_lalign cambia l'allineamento
Simboli come etichette di oggetti
Finora abbiamo visto oggetti con etichette testuali. L'etichetta di
un oggetto puo' essere un simbolo.
XForms ha un insieme di simboli predefiniti.
Quando l'etichetta di un simbolo inizia con il carattere '@', XForms
capisce che deve essere generato un simbolo e non una stringa. Il resto
della stringa etichetta (dopo '@') dice che simbolo deve essere generato,
esempi:
- "@->" : genera freccia che punta a destra
- "@>" : genera triangolo che punta a destra
- "@>>" : genera doppio triangolo che punta a destra
- eccetera
Posso anche specificare che un simbolo deve essere ruotato (es:
sono predefinite solo le frecce a dx e sx, per ottenere
quelle verso l'alto e il basso devo adoperare rotazioni.
Esempio: File sym.c.
Il programmatore puo' anche creare nuovi simboli per esempio usando pixmap,
o usando funzioni di disegno fornite da XForms.
Nascondere / disattivare parti di un'interfaccia
Non tutte le parti di un'interfaccia sono necessariamente visualizzate
sempre.
Alcune form (form secondarie) appaiono in seguito ad azioni
dell'utente. Una form puo' essere visualizzata e nascosta a varie volte.
Inoltre una form puo' essere visualizzata ma disabilitata a ricevere eventi.
Analogamente per un oggetto all'interno di una form.
Posso nascondere temporaneamente oggetti presenti in una form e
successivamente mostrarli di nuovo, oppure solo
disabilitare la cattura di eventi da parte di un oggetto.
E' utile disattivare oggetti corrispondenti
ad opzioni non valide (non selezionabili) nello stato attuale.
- fl_show_form, fl_hide_form
Visualizza / nasconde una form.
- fl_activate_form, fl_deactivate_form
Attiva / disattiva la capacita' di interagire con l'utente (catturare eventi)
di una form.
- fl_show_object, fl_hide_object
Mostra / nasconde un oggetto nella form che lo contiene.
-
fl_activate_object, fl_deactivate_object
Attiva / disattiva la capacita' di interagire di un oggetto.
E' possibile anche aggiungere / cancellare oggetti in una form
esistente (o in un gruppo all'interno di una form). Vedere il manuale.
Canvas
Un canvas e' una sottofinestra di una form gestita da X direttamente.
Abbiamo detto che XForms si frappone tra Xlib e il programma applicativo
rendendo Xlib trasparente a quest'ultimo.
Si puo' pensare la canvas come un "buco" in XForms attraverso il quale
il programma applicativo puo' vedere Xlib direttamente.
Una canvas e' una finestra X. Vi sono funzioni per settare le
caratteristiche di questa finestra (colormap, profondita', ecc.).
Una volta che la form contenente la canvas e' stata mostrata, posso
ottenere l'identificatore della finestra X che realizza la canvas e
usare questa come argomento di routine fornite da Xlib.
Noi non gestiremo una canvas direttamente con Xlib, ma con OpenGL
(pacchetto per grafica 3D, internamente e' implementato in X).
Perche' vi possa operare OpenGL, una canvas (ovvero la finestra X che
la realizza) deve avere certe caratteristiche.
XForms fornisce una routine apposita che aggiunge ad una form una canvas
adatta ad essere gestita da OpenGL.
Definizione di una canvas OpenGL
FL_OBJECT * fl_add_glcanvas(tipo, x,y, w,h);
tipo = GL_NORMAL_CANVAS (in futuro anche GL_SCROLLED_CANVAS)
Per default crea una canvas costituita da una finestra X in RGBA mode
(colori specificati come terne di valori rosso/verde/blu), dotata di
depth buffer (per eliminazione superfici nascoste) e double buffer.
Double buffer:
ha un front buffer = quello visualizzato sullo schermo, e un
back buffer = in memoria ma non visualizzato; io disegno nel back
buffer e poi con un'istruzione scambio i buffer in modo da visualizzare
istantaneamente quanto disegnato; invece nelle finestre con single
buffer disegno direttamente nel front buffer, e siccome disegnare
richiede un certo tempo, si ha un effetto visivo sgradevole).
Una volta aperta una canvas OpenGL, vi si puo' disegnare dentro
usando le funzioni di OpenGL.
Gestione di eventi nella canvas
I comandi di disegno OpenGL vanno inviati ogni qual volta la canvas
necessita di ridisegnamento (per esempio quando viene visualizzata
per la prima volta, quando viene rivisualizzata dopo essere stata
coperta da un'altra finestra, ecc.).
Infatti, se XForms si occupa di ridisegnare i suoi oggetti in questi
casi, non lo fa per le canvas (per definizione, una canvas e' una
sottofinestra che sfugge alla gestione di XForms).
Per ottenere il redisegmanento automatico del contenuto della canvas,
devo mettere i comandi necessari all'interno di una funzione che
assegno come event-handler della canvas,
collegata all'evento X Expose.
Expose e' un evento X che viene generato
quando una finestra (in questo caso la canvas) diventa visibile.
Un event-handler e' una funzione chiamata da XForms automaticamente
quando si verifica un certo evento X all'interno dell' oggetto canvas.
E' analoga a una callback, ma per eventi non predefiniti come catturabili
dalla classe di oggetti in esame.
Differenza tra event-handler di una canvas e callback degli
altri oggetti Xforms:
- ogni tipo di oggetto XForms (non canvas) e' sensibile a un certo insieme
predefinito di eventi per i quali XForms prevede una callback. Gli eventi
X sono filtrati da XForms, e non devono essere gestiti dal programmatore.
- per una canvas, e' il programmatore che deve stabilire quali
eventi X vuole gestire, e per questi eventi definisce un handler.
Definire un event handler
Un event handler e' una funzione del tipo:
int f(FL_OBJECT *ob, Window win, int w,int h, XEvent *ev, void* user_data);
gli argomenti (generati automaticamente da XForms quando
chiama si verifica l'evento), sono:
- ob = l'oggetto interessato (la canvas)
- win = la finestra X che realizza l'oggetto (necessaria per
agire su di essa con le funzioni Xlib, visto che la canvas e'
gestita con Xlib)
- w,h = dimensioni in pixel della finestra
- ev = evento X (contenente parametri al suo interno)
- user_data = argomento passato dal programma quando ha associato
la funzione all'evento (ved. sotto)
Associare un event handler
void fl_add_canvas_handler(canvas, evento X, f, user_data);
Event handler per canvas OpenGL
In teoria, bisogna sapere programmare con Xlib per scrivere un
event-handler (chiamare sulla finestra win opportune funzioni Xlib
a seconda dei valori dei parametri portati dall'evento ev).
In pratica, siccome noi stiamo usando una glcanvas, non usiamo
Xlib ma OpenGL per scrivere l'event-handler.
Il nostro event-handler per l'evento Expose conterra' le funzioni
OpenGL necessarie a ridisegnare la scena in OpenGL:
int canvas_expose(FL_OBJECT *ob, Window win,
int width, int height, XEvent *xev, void *user_data)
{
istruzioni OpenGL necessarie a disegnare la scena;
return 0;
}
Inoltre e' utile definire anche un event handler per l'evento
ConfigureNotify (evento generato quando le dimensioni della finestra
che realizza la canvas vengono modificate).
Se l'evento ConfigureNotify non e' gestito, OpenGL continua a utilizzare
come dimensioni di viewport quelle precedenti, e la scena appare
o in un piccolo sottopezzo della finestra attuale (se le dimensioni
sono aumentate) o appare tagliata (se le dimensioni sono state ridotte).
L'event-handler per ConfigureNotify riassegna le dimensioni della
viewport di OpenGL uguali alle nuove dimensioni della canvas.
int canvas_reshape(FL_OBJECT *ob, Window win,
int width, int height, XEvent *xev, void *user_data)
{
glViewport(0, 0, (GLint)width, (GLint)height); /* assegna viewport */
istruzioni OpenGL necessarie a disegnare la scena;
return 0;
}