学习游戏基础编程2:Win32分割窗口
一直很不解分割窗口是什么原理。百度了老长时间发现这方面的资料甚少,但好歹也知道了个大概,今天就做个总结,免得到时候忘记。
当初很傻很天真的时候,真的以为简单只是把一个窗口像玻璃那样劈成2半,各做各的,其实是父窗口下有数个子窗口,父窗口被这些子窗口所遮挡,看起来就像是窗口分成好几个区域。因此第一步我们要创建好几个窗口,并调整好它们的位置。
HWND hwndMain;//主窗口
HWND hwndEdit;//编辑区域窗口
HWND hwndPreview;//预览图区域窗口
HWND hwndTitleimg;//显示图片块儿窗口
在WinMain()里hwndMain=CreateWindow();
在WindowProc()里的WM_CREATE里,分别hwndEdit/hwndPreview/hwndTitleimg=CreateWindow();在WM_SIZE里,MoveWindow()到预定的位置。
之后你就会看到左侧的hwndEdit窗口,右侧上方的hwndPreview窗口,下方的hwndTitleimg窗口。
第二步,鼠标按在子窗口空隙形成的分割线(条)的时候拖动,子窗口能任意调整彼此之间的大小。怎么做呢?简单的一种做法就是,在鼠标拖动的时候画出一条矩形阴影来模拟,等鼠标释放时重新调整大小,也就是WM_SIZE里根据鼠标的位置设置3个子窗口的大小。复杂点的做法,就是分割条也当作一个子窗口CreateWindow()出来,不过有点麻烦,暂且不提。
下面给出完整代码:
//全局变量
static TCHAR szAppName[]=TEXT("Win32分割窗口");
char szClassName[ ] = TEXT("WindowsApp");
HINSTANCE hInst;
HWND hwndMain;
HWND hwndEdit;
HWND hwndPreview;
HWND hwndTitleimg;
static int CLIENT_W = 800;//主窗口客户区宽
static int CLIENT_H = 600;//主窗口客户区高
static RECT cliRect = {0,0,CLIENT_W,CLIENT_H};//主窗口客户区矩形位置
static int spX=550;//水平分割条初始位置
static int spY=150;//垂直分隔条初始位置
static int spBar=5;//分隔条的厚度
static RECT rectSpVer={spX,0,spX+spBar,CLIENT_H};//垂直分隔条矩形位置
static RECT rectSpHor={spX+spBar,spY,CLIENT_W,spY+spBar};//水平分隔条矩形位置
bool fDragVer=false;bool fMoveVer=false;//垂直分隔条是否按下/拖动
bool fDragHor=false;bool fMoveHor=false;//水平分隔条是否按下/拖动
POINT pold;//注意 保存旧的鼠标POINT位置 这是一个要点 并没有WM_PAINT重画,所以我们要消除之前绘制的阴影,利用PATINVERT方式重新绘制一次
//全局函数
LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);//窗口过程函数
void InitHwndChild(HWND hwnd);//初始化三个子窗口所用 在WM_CREATE处被调用
void SizeWindowContents(int nWidth, int nHeight);//调整三个子窗口size所用 在WM_SIZE处调用 参数为客户区宽高
void Sp_LBDown(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);//分隔条区域鼠标按下 WM_LBUTTONDOWN处调用
void Sp_LBUp(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);//分隔条区域鼠标释放 WM_LBUTTONUP处调用
void Sp_MouseMove(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);//分隔条区域鼠标移动 WM_MOUSEMOVE处调用
void DrawSpBar(LPRECT lpRect);//绘制模拟分隔条的阴影 参数为要绘制的分隔条矩形位置
LPRECT GetVerSpBar();//根据鼠标位置求得新的垂直分割矩形 用于绘制分割条时
LPRECT GetHorSpBar();//根据鼠标位置求得新的水平分割矩形 用于绘制分隔条时
void SetWndCenter(HWND hwnd);//根据客户区大小设置主窗口大小并居中
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int iCmdShow)
{
HWND hwnd; /* This is the handle for our window */
MSG msg;
WNDCLASSEX wndClass;
wndClass.cbSize=sizeof(WNDCLASSEX);
wndClass.style=CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS;
wndClass.lpfnWndProc=WindowProcedure;
wndClass.cbClsExtra=0;
wndClass.cbWndExtra=0;
wndClass.hInstance=hInstance;
wndClass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
wndClass.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
wndClass.hCursor=LoadCursor(NULL,IDC_ARROW);
wndClass.hbrBackground=(HBRUSH)COLOR_BACKGROUND;
wndClass.lpszMenuName=NULL;
wndClass.lpszClassName=szAppName;
if(!RegisterClassEx(&wndClass))
{
MessageBox(NULL,TEXT("error"),szAppName,MB_ICONERROR|MB_OK);
return 0;
}
hInst=hInstance;
hwndMain=CreateWindowEx(0,szAppName,TEXT("The hello program"),WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,hInstance,NULL);
SetWndCenter(hwndMain);//设置窗口客户区大小并居中
ShowWindow(hwndMain,iCmdShow);
UpdateWindow(hwndMain);
while(GetMessage(&msg,NULL,0,0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WindowProcedure (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
switch (message) /* handle the messages */
{
case WM_CREATE:
InitHwndChild(hwnd);//init three HWND child
break;
case WM_SIZE:
SizeWindowContents(LOWORD(lParam), HIWORD(lParam));//set three child window positions
break;
case WM_LBUTTONDOWN:
Sp_LBDown(hwnd,message,wParam,lParam);
break;
case WM_LBUTTONUP:
Sp_LBUp(hwnd,message,wParam,lParam);
break;
case WM_MOUSEMOVE:
Sp_MouseMove(hwnd,message,wParam,lParam);
break;
case WM_PAINT:
hdc=BeginPaint(hwnd,&ps);
EndPaint(hwnd,&ps);
break;
case WM_DESTROY:
PostQuitMessage (0); /* send a WM_QUIT to the message queue */
break;
default: /* for messages that we don't deal with */
return DefWindowProc (hwnd, message, wParam, lParam);
}
return 0;
}
void InitHwndChild(HWND hwnd)
{
hwndEdit = CreateWindowEx(WS_EX_CLIENTEDGE,
"STATIC", "hwndEdit",
WS_VISIBLE|WS_CHILD,
0,0,0,0,hwnd, 0, hInst, 0);
//SetWindowLongPtr(hwndEdit,GWLP_WNDPROC,(LONG)hwndEditProc);
hwndPreview = CreateWindowEx(WS_EX_CLIENTEDGE,
"STATIC", "hwndPreview",
WS_VISIBLE|WS_CHILD,
0,0,0,0,hwnd, 0, hInst, 0);
hwndTitleimg = CreateWindowEx(WS_EX_CLIENTEDGE,
"STATIC", "hwndTitleimg",
WS_VISIBLE|WS_CHILD,
0,0,0,0,hwnd, 0, hInst, 0);
}
void SizeWindowContents(int nWidth, int nHeight)
{
MoveWindow(hwndEdit, 0, 0, spX, nHeight, true);
MoveWindow(hwndPreview, spX+spBar, 0, nWidth-spX-spBar, spY, true);
MoveWindow(hwndTitleimg, spX+spBar, spY+spBar, nWidth-spX-spBar, nWidth-spY-spBar, true);
}
void Sp_LBDown(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
SetCapture(hwndMain);
POINT p;
p.x=LOWORD(lParam);p.y=HIWORD(lParam);
if((fDragVer=PtInRect(GetVerSpBar(),p))) {DrawSpBar(GetVerSpBar());pold.x=spX;pold.y=spY;return;}
if((fDragHor=PtInRect(GetHorSpBar(),p))) {DrawSpBar(GetHorSpBar());pold.x=spX;pold.y=spY;return;}
//if(fDragVer||fDragVer) MessageBox(hwnd,"asd","asd",MB_OK);
}
void Sp_LBUp(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
POINT p;p.x=LOWORD(lParam);p.y=HIWORD(lParam);
RECT rect;GetClientRect(hwnd,&rect);
if(fDragVer)
{
DrawSpBar(GetVerSpBar());fDragVer=fMoveVer=false;
SizeWindowContents(rect.right, rect.bottom);ReleaseCapture();return;
}
if(fDragHor)
{
DrawSpBar(GetHorSpBar());fDragHor=fMoveHor=false;
SizeWindowContents(rect.right, rect.bottom);ReleaseCapture();return;
}
}
void Sp_MouseMove(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
POINT p;
p.x=LOWORD(lParam);p.y=HIWORD(lParam);
if(wParam & MK_LBUTTON)//only handle mouse LeftButton drag and moved
{
if(fDragVer)
{
fMoveVer=true;
spX=pold.x;DrawSpBar(GetVerSpBar());
spX=p.x;DrawSpBar(GetVerSpBar());
pold=p;return;
}
if(fDragHor)
{
fMoveHor=true;
spY=pold.y;DrawSpBar(GetHorSpBar());
spY=p.y;DrawSpBar(GetHorSpBar());
pold=p;return;
}
}
}
void DrawSpBar(LPRECT lpRect)
{
//InvalidateRect(hwndMain,NULL,false);
HDC hdc;
hdc = GetDC(hwndMain);
static WORD _dotPatternBmp[8] =
{
0x00aa, 0x0055, 0x00aa, 0x0055,
0x00aa, 0x0055, 0x00aa, 0x0055
};
HBITMAP hbm;
HBRUSH hbr, hbrushOld;
hbm = CreateBitmap(8, 8, 1, 1, _dotPatternBmp);
hbr = CreatePatternBrush(hbm);
SetBrushOrgEx(hdc, lpRect->left, lpRect->top, 0);
hbrushOld = (HBRUSH)SelectObject(hdc, hbr);
PatBlt(hdc, lpRect->left, lpRect->top, lpRect->right-lpRect->left, lpRect->bottom-lpRect->top, PATINVERT);
SelectObject(hdc, hbrushOld);
DeleteObject(hbr);
DeleteObject(hbm);
ReleaseDC(hwndMain,hdc);
}
LPRECT GetVerSpBar()
{
rectSpVer.left=spX;
rectSpVer.top=0;
rectSpVer.right=spX+spBar;
rectSpVer.bottom=CLIENT_H;
return &rectSpVer;
}
LPRECT GetHorSpBar()
{
rectSpHor.left=spX+spBar;
rectSpHor.top=spY;
rectSpHor.right=CLIENT_W;
rectSpHor.bottom=spY+spBar;
return &rectSpHor;
}
void SetWndCenter(HWND hwnd)
{
AdjustWindowRect(&cliRect,WS_OVERLAPPEDWINDOW,false);
int WIN_W = cliRect.right - cliRect.left;
int WIN_H = cliRect.bottom - cliRect.top;
cliRect.left = (GetSystemMetrics(SM_CXSCREEN)-WIN_W)/2;
cliRect.top = (GetSystemMetrics(SM_CYSCREEN)-WIN_H)/2;
cliRect.right = cliRect.left + WIN_W;
cliRect.bottom = cliRect.top + WIN_H;
SetWindowPos(hwnd,HWND_TOPMOST,cliRect.left,cliRect.top,WIN_W,WIN_H, SWP_SHOWWINDOW);
}
代码注释不是很全,而且还有很多要补充的地方,如:拖动分割条的限制,离窗口一定距离就不再拖动等
如有问题请留言,欢迎交流。