Finestre con il Buco in Windows

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.

Finestra bucata

Una finestra con un buco 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.

Informazioni su Giampaolo Rossi

Sviluppatore di software gestionale da oltre 28 anni.
Questa voce è stata pubblicata in VC/C++, Windows. Contrassegna il permalink.