第七章 鼠标(CHECKER2)

CHECKER2程序包含一个键盘接口,内容与CHECKER1完全相同。利用←、→、↑、↓四个方向键可以在25个矩形之间移动鼠标指针。Home键把鼠标指针移动到左上角的矩形;End键使鼠标指针落到右下角的矩形。空格键和回车键都可以切换X形标记。

  1 /*---------------------------------------------
  2 CHECKER2.C -- Mouse Hit-Test Demo Program No.2
  3               (c) Charles Petzold, 1998
  4 ---------------------------------------------*/
  5 
  6 #include <Windows.h>
  7 
  8 #define DIVISIONS 5
  9 
 10 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
 11 
 12 int WINAPI WinMain( __in HINSTANCE hInstance
 13                     , __in_opt HINSTANCE hPrevInstance
 14                     , __in LPSTR lpCmdLine
 15                     , __in int nShowCmd )
 16 {
 17     static TCHAR szAppName[] = TEXT("Checker2");
 18     HWND hwnd;
 19     MSG msg;
 20     WNDCLASS wndclass;
 21 
 22     wndclass.style = CS_HREDRAW | CS_VREDRAW;
 23     wndclass.lpfnWndProc = WndProc;
 24     wndclass.cbClsExtra = 0;
 25     wndclass.cbWndExtra = 0;
 26     wndclass.hInstance = hInstance;
 27     wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
 28     wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
 29     wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
 30     wndclass.lpszMenuName = NULL;
 31     wndclass.lpszClassName = szAppName;
 32 
 33     if (!RegisterClass(&wndclass))
 34     {
 35         MessageBox(NULL, TEXT("Program requires Windows NT!")
 36             , szAppName, MB_ICONERROR);
 37         return 0;
 38     }
 39 
 40     hwnd = CreateWindow(szAppName, TEXT("Checker2 Mouse Hit-Test Demo")
 41         , WS_OVERLAPPEDWINDOW
 42         , CW_USEDEFAULT, CW_USEDEFAULT
 43         , CW_USEDEFAULT, CW_USEDEFAULT
 44         , NULL, NULL, hInstance, NULL);
 45 
 46     ShowWindow(hwnd, nShowCmd);
 47     UpdateWindow(hwnd);
 48 
 49     while (GetMessage(&msg, NULL, 0, 0))
 50     {
 51         TranslateMessage(&msg);
 52         DispatchMessage(&msg);
 53     }
 54 
 55     return msg.wParam;
 56 }
 57 
 58 LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
 59 {
 60     static BOOL fState[DIVISIONS][DIVISIONS];
 61     static int cxBlock, cyBlock;
 62     HDC hdc;
 63     int x, y;
 64     PAINTSTRUCT ps;
 65     POINT point;
 66     RECT rect;
 67 
 68     switch (message)
 69     {
 70     case WM_SIZE:
 71         cxBlock = LOWORD(lParam) / DIVISIONS;
 72         cyBlock = HIWORD(lParam) / DIVISIONS;
 73         return 0;
 74 
 75     case WM_SETFOCUS:
 76         ShowCursor(TRUE);
 77         return 0;
 78 
 79     case WM_KILLFOCUS:
 80         ShowCursor(FALSE);
 81         return 0;
 82 
 83     case WM_KEYDOWN:
 84         GetCursorPos(&point);
 85         ScreenToClient(hwnd, &point);
 86 
 87         x = max(0, min(DIVISIONS - 1, point.x / cxBlock));
 88         y = max(0, min(DIVISIONS - 1, point.y / cyBlock));
 89 
 90         switch (wParam)
 91         {
 92         case VK_UP:
 93             --y;
 94             break;
 95 
 96         case VK_DOWN:
 97             ++y;
 98             break;
 99 
100         case VK_LEFT:
101             --x;
102             break;
103 
104         case VK_RIGHT:
105             ++x;
106             break;
107 
108         case VK_HOME:
109             x = y = 0;
110             break;
111 
112         case VK_END:
113             x = y = DIVISIONS - 1;
114             break;
115 
116         case VK_RETURN:
117         case VK_SPACE:
118             SendMessage(hwnd, WM_LBUTTONDOWN, MK_LBUTTON, MAKELONG(x * cxBlock, y * cyBlock));
119             break;
120         }
121 
122         x = (x + DIVISIONS) % DIVISIONS;
123         y = (y + DIVISIONS) % DIVISIONS;
124 
125         point.x = x * cxBlock + cxBlock / 2;
126         point.y = y * cyBlock + cyBlock / 2;
127 
128         ClientToScreen(hwnd, &point);
129         SetCursorPos(point.x, point.y);
130         return 0;
131 
132     case WM_LBUTTONDOWN:
133         x = LOWORD(lParam) / cxBlock;
134         y = HIWORD(lParam) / cyBlock;
135 
136         if (x < DIVISIONS && y < DIVISIONS)
137         {
138             fState[x][y] ^= 1;
139 
140             rect.left = x * cxBlock;
141             rect.top = y * cyBlock;
142             rect.right = (x + 1) * cxBlock;
143             rect.bottom = (y + 1) * cyBlock;
144 
145             InvalidateRect(hwnd, &rect, FALSE);
146         }
147         else
148             MessageBeep(0);
149         return 0;
150 
151     case WM_PAINT:
152         hdc = BeginPaint(hwnd, &ps);
153 
154         for (x = 0; x < DIVISIONS; ++x)
155             for (y = 0; y < DIVISIONS; ++y)
156             {
157                 Rectangle(hdc, x * cxBlock, y * cyBlock
158                     , (x + 1) * cxBlock, (y + 1) * cyBlock);
159                 
160                 if (fState[x][y])
161                 {
162                     MoveToEx(hdc, x * cxBlock, y * cyBlock, NULL);
163                     LineTo(hdc, (x + 1) * cxBlock, (y + 1) * cyBlock);
164                     MoveToEx(hdc, x * cxBlock, (y + 1) * cyBlock, NULL);
165                     LineTo(hdc, (x + 1) * cxBlock, y * cyBlock);
166                 }
167             }
168 
169         EndPaint(hwnd, &ps);
170         return 0;
171 
172     case WM_DESTROY:
173         PostQuitMessage(0);
174         return 0;
175     }
176 
177     return DefWindowProc(hwnd, message, wParam, lParam);
178 }
CHECKER2.C

在CHECKER2程序中,处理WM_KEYDOWN时利用GetCursorPos判断指针的位置,并利用ScreenToClient将屏幕坐标转换成客户区坐标,然后将坐标值除以矩形块的宽和高,得到x和y。这些x和y的值表示了矩形在5*5数组中的位置。当按下某个键时,鼠标指针可能在客户区也可能不在客户区内,因此x和y必须包含在min和max的宏处理中,保证它们的范围处于0和4之间。

对于方向键,CHECKER2程序相应的增加或减少x和y的值。若按下回车键或空格键,CHECKER2程序调用SendMessage给自己发送一个WM_LBUTTONDOWN消息。最后,WM_KEYDOWN处理逻辑计算得到指向矩形中心的客户区坐标,并调用ClientToScreen将其转换成屏幕坐标,最后调用SetCursorPos设置指针的位置。

posted @ 2018-05-09 12:38  web1013  阅读(288)  评论(0编辑  收藏  举报