DI RECENTE ACCOMAZZI...
CERCA
» Ricerca avanzata
MAILING LIST

Se vuoi iscriverti alla mailing list di Luca Accomazzi inserisci qui la tua mail:

Vuoi ricevere i messaggi immediatamente (50 invii / giorno) o in differita e in gruppo
(due invii / giorno)?

» Vuoi saperne di più?

Dietro alla finestra

Cosa sarebbe un computer Apple senza il tipico display a finestre? Questa puntata della nostra serie dedicata al toolbox tratterà dei tre manager che provvedono i dettagli esteriori dell'interfaccia utente Apple: Control, Menu e Window manager.
In generale, i tre manager non presentano difficoltà insormontabili per il programmatore che desideri utilizzarli: anzi, sono tra i più facilmente maneggevoli dell'intero toolbox.


In basso, control manager

Dei tre tool, il Control manager è quello di livello più basso: esso fornisce le procedure necessarie per trattare con i bottoni, le sbarre di scorrimento e in generale tutti i controlli manipolabili con il mouse.
Control manager viene normalmente utilizzato in modo indiretto, perchè i controlli che si trovano nelle finestre e nelle finestre di dialogo sono gestiti automaticamente quando si utilizzano Window manager e Dialog manager. Va notato che il Control manager viene comunque utilizzato, e pertanto va sempre inizializzato dal programmatore.


Un menu principesco

Occupiamoci dunque del Menu manager. Si tratta di un tool estremamente semplice: le chiamate fornite permettono di aggiungere un menu, toglierlo, aggiungere una voce a un menu, toglierla, abilitare o disabilitare una voce, e qualcos'altro nello stesso genere.
Normalmente i menu vengono tutti inseriti nella sbarra del menu quando il programma inizia la sua esecuzione e vengono rimossi quando il programma termina: pertanto, ben poche chiamate arrivano al Menu manager durante l'esecuzione. In particolare, è da considerarsi scorretto l'inserimento o la rimozione di un menu mentre una applicazione sta girando.
Questo non significa che non sia possibile manipolare diversamente i menu; come sempre quando si parla di toolbox, le possibilità esistono, ma non vanno usate (se non in casi specialissimi) in modo da mantenere la coerenza e la facilità d'uso che caratterizzano l'interfaccia utente Apple. Per esempio, alcuni accessori di scrivania aggiungono un menu alla sbarra dei menu quando vengono attivati: questo è accettabile. (E, per inciso, questo significa che ogni applicazione è tenuta a non occupare per intero lo spazio della sbarra dei menu. E' necessario che vi sia dello spazio libero per l'uso degli accessori, del menu Tastiera e del menu delle applicaizoni aperte).
Non è necessario specificare le dimensioni orizzontali e verticali in punti del menu: queste vengono calcolate automaticamente dal Menu manager quando il programmatore esegue la chiamata CalcMenuSize - e per di più questa chiamata viene eseguita automaticamente quando la sbarra dei menu viene disegnata. Non è neppure necessario memorizzare il contenuto della porzione di schermo che verrà coperta da un menu che si srotola: Menu manager ci pensa da solo, e provvederà a rinfrescare quella zona di schermo quando il menu viene risollevato.

Normalmente una voce di un menu contiene una scritta, ma è possibile con poco sforzo utilizzare una icona (un simbolo grafico) al suo posto.
Ad un prezzo di poco maggiore, il Menu manager permette l'uso di qualunque cosa dentro un menu, grazie alla possibilità di definire un menu custom (questo termine inglese significa "fatto su misura"). Per esempio, lo speciale menu del Finder che contiene i colori con cui etichettiamo le cartelle è un menu custom. Nella figura 1, che spiegheremo nel prossimo paragrafo, sono presenti quasi tutte le capacità innate (disponibili senza bisogno di definire un menu custom) del Menu manager.

Internamente, i menu sono identificati con dei numeri. E' normale indicare il menu mela con il numero 256, e i menu successivi con 257, 258, 259 e così via. Le voci dei menu sono normalmente numerate a partire da uno, e così via sino alla ultima voce del menu. Pertanto, la voce "About" del menu mela che vi da le informazioni sull'applicazione che state usando è l'elemento uno del menu 256. Segue tradizionalmente il menu Archivio (File), che prende il numero 257, e il menu Composizione (Edit). Quest'ultimo contiene le voci Annulla (Undo, numero 1), Taglia (Cut, numero 3), Copia (Copy, numero 4) e Incolla (Copy, numero 5). E il numero due che fine ha fatto? Il numero due corrisponde alla riga che separa Disfa da Taglia, ed è permanentemente disattivato.

Ogni voce di un menu può venire caratterizzata in svariati modi; come abbiamo già osservato, essa può venire disattivata (resa grigia) e riattivata, ma vi sono molte altre possibilità. E' possibile usare uno stile a scelta tra i sei tradizionali (normale, neretto, corsivo, sottolineato, struttura, ombreggiato) ed usare un segno di selezionamento che è tipicamente la spuntatura: ž. É inoltre possibile l'uso delle scorciatoie di tastiera: il programmatore può associare ai comandi di menu più frequentemente usati la combinazione del tasto Mela con un altro tasto; per esempio, l'azione "Copia" del menu composizione ha sempre associato l'equivalente Mela-C. Trovate in figura 1 un esempio che mostra come vengono rese graficamente le opzioni.


Questo menu, che non ha nessuno scopo reale, mostra l'aspetto di molte opzioni fornite da Manu manager.

Menu e risorse

Nessun buon programmatore Mac inserirà in un programma il testo che vediamo nei menu (come la parola “Archivio”). I programmi risulterebbero intraducibili, perchè la scritta sarebbe inserita nel codice eseguibile prodotto, e non potrebbe venire sostituita da una scritta più corta o più lunga. E' per questo motivo che, lo ricorderete dalla scorsa puntata, si fa uso del Resource manager.
Il modo corretto di procedere consiste nel definire i menu in una risorsa, e poi leggere quella risorsa usando la combinazione del Resource manager con il Menu manager. In pratica, si definisce un menu come vediamo in figura due, e poi è sufficiente scrivere:
InsertMenu (GetMenu (257),0);
La chiamata GetMenu, che si sostituisce alla NewMenu e alla AppendMenu, esegue una funzione equivalente, ma legge i dati dalle risorse del programma.


Ecco una finestra dissezionata; notate che i nomi dei componenti differiscono dalle (maltradotte) convenzioni italiane. Talvolta i nomi usati nei manuali utente differiscono da quelli usati nei manuali per programmatori: qui abbiamo adottato la seconda gr

Stiamo alla finestra

A questo punto passiamo al Window manager.
Nella figura tre potete osservare una tipica finestra, dove abbiamo indicato i nomi corretti di tutte le componenti: ecco sintetizzata la finestra per quel che riguarda l'utente. Dal punto di vista del programmatore, una finestra è semplicemente un GrafPort, e questo è tutto.
Non appena si saranno calmate le scene di panico tra i lettori, però, daremo qualche altro dettaglio...
Il concetto di GrafPort è stato accennato nei numeri 85 ed 86 di Bit: in parole estremamente semplici, si tratta di uno spazio non necessariamente rettangolare con una serie molto complessa di attributi, tra cui il font corrente, il suo stile e le sue dimensioni; le dimensioni, il colore, la posizione e il pattern della penna, la regione di clipping e il boundsRect; e, ovviamente, il contenuto. Il boundsRect non è una pietanza tedesca: il termine indica il più piccolo rettangolo che può contenere il GrafPort.
Se prescindiamo dai dettagli tecnici, vediamo che ogni area in cui è possibile disegnare è vista dal toolbox Apple come un GrafPort . Lo schermo del computer è un GrafPort, per esempio, ma un GrafPort non deve necessariamente essere visibile: per esempio MacPaint e GsPaint mantengono in permanenza un GrafPort di lavoro ausiliario, dove viene memorizzato il disegno nella forma in cui si trovava prima del'ultima operazione. Se l'utente sceglie l'opzione Annulla (Undo) il contenuto del GrafPort ausiliario viene riversato nella finestra di lavoro, ripristinando così la situazione precedente l'ultima operazione di disegno. Analogamente, per una applicazione è molto semplice fare una stampa di un documento: si tratta semplicemente di disegnare in un GrafPort fornito dal Print manager. Sarà quest'ultimo in seguito a passare il contenuto del Grafport alla stampante secondo le idiosincrasie della stampante stessa.

In ogni dato momento, QuickDraw sa di star lavorando su un GrafPort (il cosidetto GrafPort corrente, o CurrentPort). Pertanto, per disegnare un rettangolo colorato nella finestra Pippo il programmatore scrive:
SetPort (Pippo);
PaintRect (Rettangolo);
Notate che ogni GrafPort dispone di un proprio sistema interno di coordinate: l'istruzione necessaria per disegnare un rettangolo nell'angolo in alto a destra di una finestra è identica a prescindere dalla posizione della finestra sullo schermo. Inoltre, se la finestra Pippo è troppo piccola per contenere il rettangolo, non è necessario preoccuparsi: il disegno non traboccherà nelle zone circostanti, perchè verrà clipped, cioé vincolato, allo spazio disponibile, e ne verrà disegnata solo la parte necessaria.

Sin qui, abbiamo solo rinfrescato alcuni concetti tipici di QuickDraw; aggiungiamo che, se in seguito la finestra verrà allargata, il Window manager provvederà a far disegnare la parte del rettangolo che prima non era visibile. Come? Vediamo un po'.
Nel numero di aprile abbiamo parlato di Event manager: se prendete nuovamente in mano quella puntata noterete che tra gli eventi predefiniti spiccano gli eventi di tipo Update e Activate, che in quella occasione avevamo soltanto accennato.
Supponiamo di avere sullo schermo due finestre, disposte come in figura 4 a sinistra: a questo punto noi clicchiamo sulla finestra B, quella parzialmente nascosta, giungendo alla situazione della figura 4 a destra. Dal punto di vista dell'Event manager, questo corrisponde a tre eventi.
Innanzitutto si ha l'evento di Activate della finestra B, che diviene la finestra attiva: le sbarre di scorrimento, i bottoni e la sbarra del titolo vengono attivati. Si ha poi un secondo evento Activate leggermente differente, questa volta per la finestra A; dovremmo parlare in realtà di evento di deattivazione, perchè quella finestra diviene inattiva, ma è abituale adottare anche in questo caso il termine "attivazione".
Infine, abbiamo un evento di Update (aggiornamento, in italiano), relativo alla finestra B; questo evento rende conto del fatto che va disegnato un pezzetto della finestra B, il pezzetto che era in precedenza nascosto dalla finestra A. Il pezzo che va disegnato (nel nostro esempio è un rettangolo, ma se ci pensate potete immaginare casi in cui ha forme più complesse) prende il nome di Update region, regione da aggiornare.

Il disegno (e quindi anche il rinfresco) delle finestre può avvenire in due modi molto simili ma concettualmente distinti. Il primo modo consiste nel definire una Window content definition procedure, o per dirla in italiano, una procedura che definisce il contenuto della finestra. Si tratta di una procedura dell'applicazione, che viene presentata a Window manager quando l'applicazione crea per la prima volta la finestra. Questa procedura viene passata al Window manager come se fosse un normale parametro, insieme agli altri requisiti più mondani come il titolo della finestra o le sue dimensioni. Se la Window content definition procedure esiste, il Window manager la chiama ogni qual volta è necessario effettuare lo update della finestra; in questo modo il resto dell'applicazione non si rende neppure conto che è stato necessario intervenire sulla finestra. Questo metodo non è sempre utilizzabile, perchè spesso il disegno della finestra è una azione complessa che richiede l'uso di gran parte dell'applicazione; se però la finestra contiene solo, per esempio, un disegno o una scritta, il metodo funziona perfettamente ed è più veloce e preferibile al metodo seguente.

Il secondo modo è quello usato più di frequente. L'evento di Update viene notificato dallo Event manager all'applicazione non appena l'applicazione effettua la chiamata GetNextEvent nel suo ciclo eventi (questo avviene quasi istantaneamente perchè, come ricorderete, una applicazione passa tutto il suo tempo nel ciclo eventi ad aspettare che accada un evento).
A questo punto l'applicazione può compiere i passi necessari per rinfrescare la Update region.

In ogni caso, il disegno della regione di update avviene secondo uno schema di clipping (rieccolo!). Le istruzioni di disegno (notate che il termine "disegno" non vuole significare che la finestra deve contenere un grafico: tutto il discorso vale anche per testi, icone, controlli e qualunque altro contenuto possibile) vengono incapsulate tra due istruzioni speciali del Window manager, così:
BeginUpdate (FinestraB);
{ Istruzioni di disegno }
EndUpdate (FinestraB);
Le due istruzioni citate mettono in funzione il clipping, ovvero fanno in modo che il disegno avvenga solo nella update region. Le istruzioni che provocherebbero un cambiamento al di fuori di questa regione vengono ignorate da QuickDraw.


Il Window Manager risolve il problema della sovrapposizione di finestre con la collaborazione di QuickDraw, che effettua il clipping, e dello Event manager.

Questo articolo fa parte di uno dei miei percorsi. Se vuoi saperne di più su questo argomento, visita il resto del percorso cliccando qui.