Questo esercizio è stato il primo che ho fatto dopo aver imparato a creare le finestre con l’SDK di Windows e mi è sembrato incredibile poter sviluppare delle finestre con dei buchi, si avete capito bene, dei buchi dai quali poter vedere sotto. Tutto questo è possibile grazie ad una semplice funzione
int SetWindowRgn(HWND hWnd, HRGN hRgn, BOOL bRedraw);
che permette di associare ad una finestra, e non ad un DC, un’area di clipping, questo significa che è possibile definire un’area di qualsiasi forma contenuta in un HRGN, assegnarla alla finestra e vedere tutto l’output della finestra stessa tagliato entro i limiti della regione. Prendiamo allora il codice di una semplice finestra ed inseriamogli le modifiche per bucarla, in particolare inseriremo un buco che assomiglia a quello delle chiavi, così possiamo sbirciare tutto quello che sta sotto di essa.
// File di intestazione CorsoVC.h #include <windows.h> #define APP_NAME "Finestra con il buco" #define IDM_EXIT 102 #define IDM_ABOUT 103 LRESULT CALLBACK AppWndProc(HWND, UINT, WPARAM, LPARAM); // File sorgente CorsoVC.cpp #define WIN32_LEAN_AND_MEAN #include "CorsoVC.h" static HWND m_hWndMain; static HINSTANCE m_hInst; static HRGN m_hRgnView; static RECT m_rcHole; static int InitApplication(HINSTANCE); static int InitInstance(HINSTANCE); static int SetClipArea(HWND); int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hPreInst, LPSTR lpszCmdLine, int iShowCmd) { MSG msg; if(!InitApplication(hInst)) return FALSE; if (!InitInstance(hInst)) return FALSE; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } if (m_hRgnView) DeleteObject(m_hRgnView); return TRUE; } LRESULT CALLBACK AppWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch(uMsg) { case WM_COMMAND: switch(LOWORD(wParam)) { case IDM_EXIT: SendMessage(m_hWndMain, WM_CLOSE, 0, 0L); break; } break; case WM_SIZE: case WM_MOVE: DefWindowProc(hWnd, uMsg, wParam, lParam); SetClipArea(hWnd); return 0; case WM_CLOSE: DestroyWindow(m_hWndMain); break; case WM_PAINT: { PAINTSTRUCT ps; BeginPaint(hWnd, &ps); DrawEdge(ps.hdc, &m_rcHole, EDGE_SUNKEN, BF_RECT); EndPaint(hWnd, &ps); } break; case WM_DESTROY: PostQuitMessage(0); break; } return DefWindowProc(hWnd, uMsg, wParam, lParam); } int InitApplication(HINSTANCE hInst) { WNDCLASS wc; wc.style = CS_HREDRAW|CS_VREDRAW; wc.lpfnWndProc = (WNDPROC)AppWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst; wc.hIcon = NULL; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1); wc.lpszClassName = "HOLE_CLASS"; wc.lpszMenuName = NULL; return RegisterClass(&wc); } int InitInstance(HINSTANCE hInst) { m_hInst = hInst; m_hWndMain = CreateWindowEx(0, "HOLE_CLASS", APP_NAME, WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_THICKFRAME, CW_USEDEFAULT, CW_USEDEFAULT, 500, 500, NULL, NULL, hInst, NULL); if (!m_hWndMain) return FALSE; SetClipArea(m_hWndMain); ShowWindow(m_hWndMain, SW_SHOW); UpdateWindow(m_hWndMain); return TRUE; } int SetClipArea(HWND hWnd) { RECT rcHole, rcArea, rcCirc; POINT pt[3]; HRGN hRgnHole, hRgnWnd, hRgnCirc, hRgnTriangle; POINT ptOffset; GetWindowRect(hWnd, &rcArea); rcArea.right -= rcArea.left; rcArea.bottom -= rcArea.top; rcArea.left = 0; rcArea.top = 0; CopyRect(&rcHole, &rcArea); rcHole.top += GetSystemMetrics(SM_CYCAPTION); ptOffset.x = GetSystemMetrics(SM_CXSIZEFRAME) + GetSystemMetrics(SM_CXEDGE); ptOffset.y = GetSystemMetrics(SM_CYSIZEFRAME) + GetSystemMetrics(SM_CYEDGE); InflateRect(&rcHole, ptOffset.x, ptOffset.y); InflateRect(&rcHole, -rcArea.right / 6, -rcArea.bottom / 8); m_hRgnView = CreateRectRgn(0, 0, 10, 10); hRgnWnd = CreateRectRgnIndirect(&rcArea); hRgnHole = CreateRectRgnIndirect(&rcHole); CopyRect(&rcCirc, &rcHole); InflateRect(&rcCirc, - (rcHole.right - rcHole.left) / 4, 0); rcCirc.bottom = rcHole.top + (rcHole.bottom - rcHole.top) / 3; hRgnCirc = CreateEllipticRgnIndirect(&rcCirc); pt[0].x = rcCirc.left + (rcHole.right - rcHole.left) / 4; pt[0].y = rcCirc.top + (rcCirc.bottom - rcCirc.top) / 2; pt[1].x = rcHole.right; pt[1].y = rcHole.bottom; pt[2].x = rcHole.left; pt[2].y = rcHole.bottom; hRgnTriangle = CreatePolygonRgn((LPPOINT)&pt, 3, ALTERNATE); CombineRgn(hRgnHole, hRgnCirc, hRgnTriangle, RGN_OR); CombineRgn(m_hRgnView, hRgnWnd, hRgnHole, RGN_DIFF); DeleteObject(hRgnHole); DeleteObject(hRgnCirc); DeleteObject(hRgnTriangle); DeleteObject(hRgnWnd); SetWindowRgn(hWnd, m_hRgnView, TRUE); return TRUE; }
Occorre creare una regione di forma ellittica, un’altra triangolare e sommarle, quindi sottrarre la regione della chiave e richiamare la funzione SetWindowRgn per fare in modo che la parte interna non ci sia nell’area di clipping della finestra, il risultato è una finestra con un buco al centro a forma di chiave.
In questi esempi che vi propongo cerco di semplificare la programmazione di applicazioni anche complicate, ma l’SDK di Windows è talmente vasto che non bastano 10 anni per conoscerlo tutto, occorre sapere a grandi linee il compito delle funzioni e poi andarsele a cercare nella guida in linea del compilatore. Se comunque avete bisogno di chiarimenti o fare delle semplici domande potete iscrivervi al forum, nella sezione dedicata alla programmazione.