|
Вы наверное заметили, что вертикальная прокрутка, организованная в предыдущей программе, не очень эффективна. На данном уроке мы улучшим вертикальную прокрутку и добавим горизонтальную.
Вот пример программы:
//файл sm.h остался прежним, поэтому он здесь не приводится
//файл main.cpp
#include <windows.h>
#include "sm.h"
//описание оконной процедуры
LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
//Это главная функция программы.
int WINAPI WinMain(HINSTANCE hInst,
HINSTANCE hPrevInst,
PSTR szCmdLine,
int iCmdShow)
{
HWND hwnd;
MSG msg;
WNDCLASSEX w;
static CHAR *szAppName={"TextOutExample"};
w.cbSize=sizeof(w);
w.style=CS_HREDRAW|CS_VREDRAW;
w.lpfnWndProc=WndProc;
w.cbClsExtra=0;
w.cbWndExtra=0;
w.hInstance=hInst;
w.hIcon=LoadIcon(NULL,IDI_APPLICATION);
w.hCursor=LoadCursor(NULL,IDC_ARROW);
w.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
w.lpszMenuName=NULL;
w.lpszClassName=szAppName;
w.hIconSm=w.hIcon;
RegisterClassEx(&w);
hwnd=CreateWindow(
szAppName,
"System metrics",
WS_OVERLAPPEDWINDOW|WS_VSCROLL,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL,
NULL,
hInst,
NULL);
ShowWindow(hwnd,iCmdShow);
UpdateWindow(hwnd);
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
//Оконная процедура
LRESULT CALLBACK WndProc(HWND hwnd, UINT imsg,
WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
TEXTMETRIC tm;
static int cxChar,cyChar,iVscrollPos=0,iVscrollMax,cyClient,cxClient,
iMaxWidth,iHscrollMax,iHscrollPos;
int iPaintBeg,iPaintEnd,iVscrollInc,iHscrollInc,i,x,y;
CHAR szBuffer[10];
switch(imsg)
{
case WM_CREATE:
hdc=GetDC(hwnd);
GetTextMetrics(hdc,&tm);
cxChar=tm.tmAveCharWidth;
cyChar=tm.tmHeight+tm.tmExternalLeading;
iMaxWidth=120*cxChar;
ReleaseDC(hwnd,hdc);
return 0;
case WM_SIZE:
cxClient=LOWORD(lParam);
cyClient=HIWORD(lParam);
iVscrollMax=max(0, N_LINES+2-cyClient/cyChar);
iVscrollPos=min(iVscrollPos, iVscrollMax);
SetScrollRange(hwnd,SB_VERT,0,iVscrollMax,FALSE);
SetScrollPos(hwnd,SB_VERT,iVscrollPos,TRUE);
iHscrollMax=max(0, 2+(iMaxWidth-cxClient)/cxChar);
iHscrollPos=min(iHscrollPos,iHscrollMax);
SetScrollRange(hwnd,SB_HORZ,0,iHscrollMax,FALSE);
SetScrollPos(hwnd,SB_HORZ,iHscrollPos,TRUE);
return 0;
case WM_PAINT:
hdc=BeginPaint(hwnd, &ps);
iPaintBeg=max(0,iVscrollPos+ps.rcPaint.top/cyChar-1);
iPaintEnd=min(N_LINES,iVscrollPos+ps.rcPaint.bottom/cyChar);
for(i=iPaintBeg; i<iPaintEnd; i++)
{
x=cxChar*(1-iHscrollPos);
y=cyChar*(1-iVscrollPos+i);
TextOut(hdc,x,y,sm[i].szLabel,
lstrlen(sm[i].szLabel));
wsprintf(szBuffer,"%5d",
GetSystemMetrics(sm[i].i));
TextOut(hdc,x+22*cxChar,y,szBuffer,
lstrlen(szBuffer));
TextOut(hdc,x+24*cxChar+8*cxChar,y,
sm[i].szDesc,lstrlen(sm[i].szDesc));
}
EndPaint(hwnd, &ps);
return 0;
case WM_VSCROLL:
switch(LOWORD(wParam))
{
case SB_TOP:
iVscrollInc = -iVscrollPos; break;
case SB_BOTTOM:
iVscrollInc = iVscrollMax-iVscrollPos; break;
case SB_LINEUP:
iVscrollInc = -1;
break;
case SB_LINEDOWN:
iVscrollInc = 1;
break;
case SB_PAGEUP:
iVscrollInc = min(-1,-cyClient/cyChar);
break;
case SB_PAGEDOWN:
iVscrollInc = max(1,cyClient/cyChar);
break;
case SB_THUMBTRACK:
iVscrollInc=HIWORD(wParam)-iVscrollPos;
break;
default: iVscrollInc=0;
}
iVscrollInc=max(-iVscrollPos,
min(iVscrollInc,iVscrollMax-iVscrollPos));
if(iVscrollInc!=0)
{
iVscrollPos += iVscrollInc;
ScrollWindow(hwnd,0,-cyChar*iVscrollInc,NULL,NULL);
SetScrollPos(hwnd,SB_VERT,iVscrollPos,TRUE);
UpdateWindow(hwnd);
}
return 0;
case WM_HSCROLL:
switch(LOWORD(wParam))
{
case SB_LINEUP:
iHscrollInc=-1; break;
case SB_LINEDOWN:
iHscrollInc=1; break;
case SB_PAGEUP:
iHscrollInc=-8; break;
case SB_PAGEDOWN:
iHscrollInc=8; break;
case SB_THUMBPOSITION:
iHscrollInc=HIWORD(wParam)-iHscrollPos;
break;
default: iHscrollInc=0;
}
iHscrollInc=max(-iHscrollPos,
min(iHscrollInc,iHscrollMax-iHscrollPos));
if(iHscrollInc!=0)
{
iHscrollPos+=iHscrollInc;
ScrollWindow(hwnd,-cxChar*iHscrollInc,0,NULL,NULL);
SetScrollPos(hwnd,SB_HORZ,iHscrollPos,TRUE);
}
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd,imsg,wParam,lParam);
}
В этой программе есть небольшие улучшения:
- Вы не сможете прокрутить окно так, чтобы последняя строка оказалась в самом верху. Прокрутка продолжается только до тех пор, пока появится последняя строка. Для того, чтобы сделать это, программа расчитывает диапазон полосы прокрутки и положение бегунка при обработке сообщения WM_SIZE. (Сообщение WM_SIZE посылается окну тогда, когда изменяется его размеры). Диапазон прокрутки вычисляется на основании числа строк текста (N_LINES) и размера рабочей области (cyClient).
Если рабочая область будет достаточно велика, чтобы в ней поместились все строки, тогда минимальное и максимальное положение диапазона прокрутки будет равно нулю. Это приведет к тому, что Windows удалит полосу прокрутки, т.к. она уже больше не нужна.
Аналогично, если ширина рабочей области будет достаточна для отображения 120 символов (см. WM_CREATE: ... iMaxWidth=120*cxChar;), то исчезнет и горизонтальная полоса прокрутки.
- Перед тем, как прокрутить окно, расчитывается приращение текущей позиции прокрутки - iVscrollInc (для вертикальной прокрутки), iHscrollInc (для горизонтальной прокрутки). Затем выполняется прокрутка окна с помощью функции
ScrollWindow(hwnd, xInc, yInc, pRect, pClipRect);
Параметры xInc и yInc задают величину прокрутки. Параметры pRect и pClipRect устанавливаются в NULL для того, чтобы прокрутить всю рабочую область.
В результате вызова функции ScrollWindow рабочая область, открываемая после прокрутки, делается недействительной, что заставляет Windows послать окну сообщение WM_PAINT. При обработке этого сообщения перерисовываются те строки, которых не было видно до прокрутки окна (а не все строки, как в предыдущей версии программы).
- Поскольку сообщение WM_PAINT обрабатывается быстрее, чем в предыдущей версии программы (т.к. перерисовываются не все строки, а только вновь открытые), теперь программа обрабатывает сообщение SB_THUMBTRACK вместо SB_THUMBPOSITION, т.е. теперь вертикальная прокрутка выполняется при движении ползунка, а не при отпускании левой клавиши мыши, как в предыдущей версии.
|