Nello scorso articolo abbiamo parlato di come bucare una finestra di Windows grazie ad una funzione presente nell’SDK, SetWindowRgn, questa volta vi mostro come, adottando la stessa funzione, è possibile creare delle finestre rotonde, ma nulla vieta di farle a stelle o con altre forme, che serviranno soprattutto come splash screen.
// File di intestazione CorsoVC.h #include <windows.h> #define APP_NAME "Finestra per Splash" #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" #include <stdlib.h> #include <math.h> static HWND m_hWndMain; static HINSTANCE m_hInst; static HRGN m_hRgnSplash; static int InitApplication(HINSTANCE); static int InitInstance(HINSTANCE); static void SetClipArea(HWND); static void ContextMenu(HWND); static void DrawSplash(HWND, LPCSTR); 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); } return TRUE; } LRESULT CALLBACK AppWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { long lVal; switch(uMsg) { case WM_COMMAND: switch(LOWORD(wParam)) { case IDM_EXIT: SendMessage(m_hWndMain, WM_CLOSE, 0, 0L); break; case IDM_ABOUT: MessageBox(m_hWndMain, "Lo splash della RGPSoft di Rossi Giampaolo", "Splash Window", MB_OK|MB_ICONINFORMATION); break; } break; case WM_CONTEXTMENU: ContextMenu(m_hWndMain); break; case WM_SIZE: DefWindowProc(hWnd, uMsg, wParam, lParam); SetClipArea(hWnd); return 0; case WM_NCHITTEST: lVal = DefWindowProc(hWnd, uMsg, wParam, lParam); if (lVal == HTCLIENT) { if (GetAsyncKeyState(VK_LBUTTON)) return HTCAPTION; else return lVal; } else return lVal; case WM_CLOSE: DestroyWindow(m_hWndMain); DeleteObject(m_hRgnSplash); break; case WM_PAINT: DrawSplash(m_hWndMain, "RGPSoft di Rossi Giampaolo"); 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 = "ROUND_CLASS"; wc.lpszMenuName = NULL; return RegisterClass(&wc); } int InitInstance(HINSTANCE hInst) { m_hInst = hInst; m_hWndMain = CreateWindowEx(0, "ROUND_CLASS", APP_NAME, WS_POPUP|WS_THICKFRAME, 100, 100, 750, 500, NULL, NULL, hInst, NULL); if (!m_hWndMain) return FALSE; SetClipArea(m_hWndMain); ShowWindow(m_hWndMain, SW_SHOW); UpdateWindow(m_hWndMain); return TRUE; } void SetClipArea(HWND hWnd) { RECT rcArea; GetWindowRect(hWnd, &rcArea); rcArea.right -= rcArea.left; rcArea.bottom -= rcArea.top; rcArea.left = 0; rcArea.top = 0; InflateRect(&rcArea, -GetSystemMetrics(SM_CXSIZEFRAME), -GetSystemMetrics(SM_CYSIZEFRAME)); m_hRgnSplash = CreateEllipticRgnIndirect(&rcArea); SetWindowRgn(hWnd, m_hRgnSplash, TRUE); } void ContextMenu(HWND hWnd) { HMENU hMenu; POINT pt; GetCursorPos(&pt); hMenu = CreatePopupMenu(); AppendMenu(hMenu, MF_STRING, IDM_ABOUT, "&Info"); AppendMenu(hMenu, MF_STRING, IDM_EXIT, "&Esci"); TrackPopupMenu(hMenu, 0, pt.x, pt.y, 0, hWnd, NULL); DestroyMenu(hMenu); } void DrawSplash(HWND hWnd, LPCSTR szText) { HDC hdc; HFONT hFont, hOldFont; HBRUSH hBrush, hOldBrush; HPEN hPen, hOldPen; LOGFONT lf; SIZE sz; RECT rc; HRGN hRgn1, hRgn2; int x, y; GetClientRect(hWnd, &rc); hdc = GetDC(hWnd); int iMode = SetBkMode(hdc, TRANSPARENT); hPen = CreatePen(PS_SOLID, 1, RGB(0, 255, 0)); hBrush = CreateSolidBrush(RGB(255, 0, 0)); memset(&lf, 0, sizeof(LOGFONT)); lf.lfHeight = 48; lf.lfWeight = FW_BOLD; lstrcpy(lf.lfFaceName, "Arial"); hFont = CreateFontIndirect(&lf); hOldFont = (HFONT)SelectObject(hdc, (HFONT)hFont); GetTextExtentPoint32(hdc, szText, lstrlen(szText), &sz); x = (rc.right - sz.cx) / 2; y = (rc.bottom - sz.cy) / 2; BeginPath(hdc); TextOut(hdc, x, y, szText, lstrlen(szText)); EndPath(hdc); hRgn1 = CreateRectRgn(x, y, x + sz.cx, y + sz.cy / 2); SelectClipRgn(hdc, hRgn1); hOldPen = (HPEN)SelectObject(hdc, (HPEN)hPen); hOldBrush = (HBRUSH)SelectObject(hdc, (HBRUSH)GetStockObject(WHITE_BRUSH)); StrokeAndFillPath(hdc); BeginPath(hdc); TextOut(hdc, x, y, szText, lstrlen(szText)); EndPath(hdc); hRgn2 = CreateRectRgn(x, y + sz.cy / 2, x + sz.cx, y + sz.cy); SelectClipRgn(hdc, hRgn2); SelectObject(hdc, (HPEN)GetStockObject(WHITE_PEN)); SelectObject(hdc, (HBRUSH)hBrush); StrokeAndFillPath(hdc); SelectClipRgn(hdc, (HRGN)NULL); SelectObject(hdc, (HPEN)hOldPen); SelectObject(hdc, (HFONT)hOldFont); SelectObject(hdc, (HBRUSH)hOldBrush); DeleteObject(hPen); DeleteObject(hBrush); DeleteObject(hFont); SetBkMode(hdc, iMode); ReleaseDC(hWnd, hdc); }
Il processo di creazione e disegno della finestra risulta anche più semplice rispetto a creare un foro, in questo caso occorre prendere le coordinate della finestra e renderle relative alla client area, poi dobbiamo anche eliminare i bordi. Il programma utilizza lo stile della finestra WS_POPUP, senza barra del titolo e senza menu. Lo stile utilizzato per uno splash screen non va molto bene, ma dato che in questo caso dovevamo creare una finestra che dobbiamo anche controllare con il mouse preferiamo WS_POPUP. Abbiamo inserito anche un menu che appare facendo click con il tasto destro del mouse sulla finestra stessa e che consente di visualizzare informazioni sull’autore del programma e soprattutto di terminare l’applicazione.
In questo caso il problema è consentire anche lo spostamento della finestra con il drag’n’drop, come possiamo fare se non abbiamo la caption bar? Semplicemente facciamo in modo che Windows capisca che occorre considerare l’intera finestra come una barra del titolo, per questo abbiamo intercettato il messaggio WM_NCHITTEST e facciamo ritornare HTCAPTION in modo da far capire che è stato premuto il tasto sinistro del mouse sulla barra del titolo. Potevamo disegnare un semplice testo, ma ho cercato di mostrare altri usi del disegno con l’SDK di Windows. Come abbiamo creato una finestra rotonda così potevamo creare degli splash screen di numerose forme tramite le operazioni tra region.