Introduzione alle API Win32

Capitolo 25: ri-creare una finestra

Come promesso alla fine del capitolo precedente, proseguiamo a mostrare subito un possibile approccio alla ri-creazione di una finestra, anche se, alla fine, dovremo ancora chiarire un paio di cosette...:

// // ri-crea una window, eguale in tutto a quella // passata; quest'ultima viene invece distrutta // HWND ricreaWindow(HWND hwnd, void* lpParam) { // le classi non hanno mai nomi > 255 caratteri char className[256]; GetClassName(hwnd, className, 256); // il testo della window ha lunghezza qualsiasi char *winText; int nChars = GetWindowTextLength(hwnd); // cast per compatibilità C++ (innocuo in C): winText = (char*)malloc(1+nChars); GetWindowText(hwnd, winText, 1+nChars); // stile, stile esteso, genitrice o proprietaria DWORD dwStyle = GetWindowLong(hwnd, GWL_STYLE); DWORD dwExStyle = GetWindowLong(hwnd, GWL_EXSTYLE); HWND hwndParent = GetParent(hwnd); // geometria e menu-o-ID RECT r; GetWindowRect(hwnd, &r); HMENU hMenu; if(dwStyle & WS_CHILD) { // finestra-figlia: coordinate relative! MapWindowPoints(HWND_DESKTOP, hwndParent, (POINT*)&r, 2); hMenu = (HMENU)GetDlgCtrlID(hwnd); } else { // non-figlia: evitiamo incidenti sul menu hMenu = GetMenu(hwnd); SetMenu(hwnd, 0); } // la HINSTANCE HINSTANCE hInst = (HINSTANCE) GetWindowLong(hwnd, GWL_HINSTANCE); // il font WPARAM wFont = SendMessage(hwnd, WM_GETFONT, 0, 0); // via la finestra vecchia! DestroyWindow(hwnd); // ed ecco la nuova...! hwnd = CreateWindowEx( dwExStyle, className, winText, dwStyle, r.left, r.top, r.right-r.left, r.bottom-r.top, hwndParent, hMenu, hInst, lpParam); // eventuale settaggio del font if(wFont) SendMessage(hwnd, WM_SETFONT, wFont, MAKELPARAM(TRUE, 0)); return hwnd; }

Alcuni dei dettagli ancora da chiarire sono piccoli; ad esempio, il messaggio WM_SETFONT prende l'HFONT come wParam, mentre l'lParam, come l'abbiamo costruito, dice alla finestra-bersaglio di "ridipingersi" subito per riflettere il "nuovo" font settato; la malloc, che non richiederebbe un cast per l'uso in C (ove void* può essere assegnato a qualsiasi puntatore) lo richiede invece in C++, quindi lo poniamo, per totale compatibilità -- minuzie.

Le cosette più interessanti sono invece quelle di geometria. Anzitutto, è importante notare che r.right-r.left è la giusta espressione per dare la larghezza della finestra -- non c'è, come si potrebbe pensare, un errore "off by one"; e questo, perchè Windows, assai comodamente, ci ha dato come right uno in più -- cioè, non la coordinata dell'ultimo punto a destra, che è ancora dentro la finestra, ma del primo che ne è fuori!

È questo un esempio di un idioma fondamentale del C, presentato da A. Koenig in "C Traps and Pitfalls": rappresentare un "range", non, come ingenuamente si potrebbe pensare, con "il primo, e l'ultimo" (questo impedisce di rappresentare comodamente un range vuoto, e inoltre causa spesso problemi di off-by-1), bensì con "il primo, e il primo successivo all'ultimo" (i lettori che usano C++ sanno bene, d'altra parte, come questo stesso concetto sia usato estesamente nella libreria standard C++, ad esempio con i vari membri end() dei contenitori standard).

Ho anche infilato nel trattamento della geometria un'API che non avevo ancora nominato... la

MapWindowPoints(HWND hFrom, HWND hTo, POINT* pPoints, int nPoints); che serve a "mappare" delle coordinate in pixel da una finestra all'altra, usando la pseudo-HWND HWND_DESKTOP per esprimere "coordinate di schermo". La GetWindowRect ci dà, appunto, sempre le coordinate di schermo, mentre, per creare una finestra-figlia, ci servono invece quelle relative alla finestra-genitrice; così "mappando", risolviamo il problema, approfittando anche del comodo fatto che un RECT è identico ad un array di due POINT (quello in alto a sinistra, poi quello "subito dopo" quello in basso a destra), anche se occorre un cast per convincere di ciò il nostro compilatore. Questo, e le sue ovvie varianti, è un idioma frequentissimo quando ci si occupa di geometria di finestre, e punti e regioni entro le finestre.

 

Avendo, dunque, a disposizione la nostra nuova funzione ricreaWindow, sarà facile modificare il nostro programmino sperimentale: basta ricreare l'edit a ogni modifica al suo stile, oppure, volendo, avere un altro bottone separato -- un pushbutton, che causi la ri-creazione, ovvero una checkbox che la renda facoltativa ("ri-crea a ogni modifica"). Questo tipo di scelte di progetto, così come la relativa implementazione, è ormai sicuramente in grado di farle e sperimentarle il lettore, e noi possiamo tornare, nel prossimo capitolo, ad esaminare altri aspetti dei controlli di edit.


Capitolo 24: creazione di finestre
Capitolo 26: interazioni con gli EDIT
Elenco dei capitoli