STemWin自定义回调实现窗口的缩放
实现思路
按下窗口的边缘,记录下当前按下的坐标,利用WM_SetCapture()函数来使窗口捕获所有的鼠标和触屏信息。坐标有改动既会捕获取坐标信息,判断当前和记录下来的坐标,然后利用WM_Resizewindow()来缩放窗口。但此函数和缩放是依据窗口左上角的原点,所以当我们拖动原点的边(以下简称原边)的时候,需要先把原点的位置先移动到我们鼠标的位置 WM_MoveWindow然后再缩放,而非“原边”便可根据当前的鼠标位置和之前的位置来直接缩放。
note:传入窗口的坐标信息都是以窗口的原点为相对位置的,而非是以桌面窗口。
实现过程
所需要用到的关键元素
WM_TOUCH :当窗口被触屏到便会在回调中产生此事件
GUI_PID_STATE:此结构体 包含着 int x,y坐标,是否被按下等消息。
在收到WM_TOUCH事件后GUI_PID_STATE消息应当被传递到回调的参数中
GUI_PID_STATE* pState;
pState = (GUI_PID_STATE*)pMsg->Data.p;
就可以从回调参数中得到窗口被Touch的具体坐标。
WM_SetCapture(hWin, 1); 第一个参数是句柄,第二个参数如果为1的表示:当用户释放触摸时结束。该函数的主要目的就是把所有的鼠标和触摸消息都发送给指定窗口。也让是让窗口捕获触摸屏的输入直到用户释放。
int WM_HasCaptured(WM_HWIN hWin); 此函数是否捕获到触屏的输入。
开始实现
1建立窗口的回调
这里是建立的一个 FRAMEWIN的小工具
_hFrame =_CreateEx(60, 80, 200, 120, WM_HBKWIN, WM_CF_SHOW, 0, 0, "Notepad", 0);
_pcbFrameWin = WM_SetCallback(_hFrame, _cbFrameWin);
为什么要把_hFrame回调函数的上一个给保存起来呢?这里的目的就是在回调函数中我们只利用一部分消息通知来达成我们的目的,且这部分的消息还是需要让默认的回调来处理。否则就会程序异常!如下一小节的代码所示。
2利用回调触摸事件产生相应的行为
2.1窗口回调
static void _cbFrameWin(WM_MESSAGE* pMsg) {
switch(pMsg->MsgId) {
case WM_TOUCH:
if (FRAMEWIN_IsMinimized(pMsg->hWin) == 0) {
if (_OnTouch(pMsg->hWin, pMsg)) {
return;
}
}
break;
case WM_DELETE:
_hFrame = 0;
_hClient = 0;
_hMEdit = 0;
_hWrapButton = 0;
break;
}
if (_pcbFrameWin) {
(*_pcbFrameWin)(pMsg);
}
}
如上面这段代码一样,最终我们仍然要是用默认的回调来处理处理回调消息。
如果收到了WM_TOUCH 消息事件那么这里用了一个_OnTouch()函数来处理 如果是WM_DELETE事件我们需要把相应的标记给清零。
2.2触摸事件的处理
#define RESIZE_X (1<<0)
#define RESIZE_Y (1<<1)
#define REPOS_X (1<<2)
#define REPOS_Y (1<<3)
static int _OnTouch(FRAMEWIN_Handle hWin, WM_MESSAGE* pMsg) { //由上传入的是Frame窗口的包柄
if (pMsg->Data.p) { // 如果是Touch事件这里结构体里应该有坐标,按下或释放。标记
GUI_PID_STATE* pState;
pState = (GUI_PID_STATE*)pMsg->Data.p;
if (pState->Pressed) {
int x, y;
x = pState->x;
y = pState->y;
if (WM_HasCaptured(hWin) == 0) { //Frame窗口未捕获取鼠标或触摸屏输入
GUI_RECT Rect;
int Mode = 0;
int BorderSize = 10;
WM_GetClientRectEx(hWin, &Rect);//获取到Frame窗口的区域大小
//以下代码判断点击的是否是边缘如果是原边的x,y话需要做上标记,缩放时做不同处理
if (x > (Rect.x1 - BorderSize)) {
Mode |= RESIZE_X;
} else if (x < BorderSize) {
Mode |= RESIZE_X | REPOS_X;
}
if (y > (Rect.y1 - BorderSize)) {
Mode |= RESIZE_Y;
} else if (y < BorderSize) {
Mode |= RESIZE_Y | REPOS_Y;
}
if (Mode) {
WM_SetFocus(hWin);
WM_BringToTop(hWin);
_SetCapture(hWin, x, y, Mode);
return 1;
}
} else if (_HasCaptured) {
_ChangeWindowPosSize(hWin, &x, &y);
_SetCapture(hWin, x, y, 0);
return 1;
}
}
}
_HasCaptured = 0;
return 0;
}
在这段代码里首先是判断消息中的数据结构体是否为空,正常情况下不应当不空。然后检查是否按键按下并取出x,y坐标。和窗口有没有捕获WM_HasCaptured(hWin);如果没有捕获(第一次进入回调)则记录下x,y的坐标值,并设置捕获。如果已捕获(第二次和以后进入回调)那么可直接_ChangeWindowPosSize更改窗口大小更改后再调用_SetCapture记录下位置
2.3缩放和移动窗口处理
static void _ChangeWindowPosSize(FRAMEWIN_Handle hWin, int* px, int* py) {
int dx;
int dy;
GUI_RECT Rect;
dx = 0;
dy = 0;
WM_GetClientRectEx(hWin, &Rect);
//注1
if (_HasCaptured & RESIZE_X) {
dx = (_HasCaptured & REPOS_X) ? (_CaptureX - *px) : (*px - _CaptureX);
}
if (_HasCaptured & RESIZE_Y) {
dy = (_HasCaptured & REPOS_Y) ? (_CaptureY - *py) : (*py - _CaptureY);
}
if ((Rect.x1 + dx + 1) < MIN_SIZE_X) {
dx = MIN_SIZE_X - (Rect.x1 + 1); //如果当前的尺寸缩放了尺寸后小于最小尺寸那么就只缩小到最小就好了
*px = _CaptureX; //并且把当前的值改为之前的值
}
if ((Rect.y1 + dy + 1) < MIN_SIZE_Y) {
dy = MIN_SIZE_Y - (Rect.y1 + 1);
*py = _CaptureY;
}
//如果是原边的话那么是需要移动窗口来达到让窗口跟随鼠标的
if (_HasCaptured & REPOS_X) {
WM_MoveWindow(hWin, -dx, 0);
}
if (_HasCaptured & REPOS_Y) {
WM_MoveWindow(hWin, 0, -dy);
}
//缩放窗口
WM_ResizeWindow(hWin, dx, dy);
}
_HasCaptured保存的是 是否在捕获状态 不在既为0 如果在那么 其值应当记录了是否要移动原边的信息。
注1:判断x,y方向是否需要更改,且如果是从原边缩放的话(注意:坐标都是以本窗口的原点相对位置),当前鼠标往原边的负方向移动则表示放大,正方向移动表示缩小。如果是从非原边缩放 正方向移动放大负方向移动是缩小。
原边的缩放,每次都是以移动窗口的方式来跟随鼠标,然后再缩放大小,更改完成后每次的目标坐标都会变成原坐标。
而非原边的缩放,每次只需要与上次记录的坐标对应如果是大了就放大如果是小了就变小
所以如果是原边的缩放应当是(_CaptureX - *px) 对应非原边的缩放(*px - _CaptureX)。上面的代码其它部分都好理解。为什么要往负方向移动呢?如果从原边开始缩放那么放大的话dx应该为正数,也应该往负方向增长
2.4缩放处理相关标记处理和设置捕获
static void _SetCapture(FRAMEWIN_Handle hWin, int x, int y, int Mode) {
if ((_HasCaptured & REPOS_X) == 0) { //如果移动的非原边每次调整过窗口后都需要记录上次位置而原边调整的时候原点会更改到鼠标位置所发原边不需要记录也不能记录,否则原点位置更改后上一个值将是一个错误的值。
_CaptureX = x;
}
if ((_HasCaptured & REPOS_Y) == 0) {
_CaptureY = y;
}
if (!_HasCaptured) {
WM_SetCapture(hWin, 1); //将所有鼠标和触摸屏消息发送到给定窗口 且捕获操作应当在释放时结束
_HasCaptured = Mode;
}
}
以上代码主要是在原边移动和非原边移动上做了一定的处理(非原边移动时需要每次移动后都记录位置)。而且初次调用时需要开启捕获。
以上便是所有了。如有误希望能指正出来。谢谢!