现在需要实现窗口屏幕边缘吸附的效果,现在只考虑屏幕右边缘。思路如下:
当窗口右侧与屏幕右侧距离小于某个值(假设是20px)的时候,改变窗口的水平位置,使窗口右边框紧贴屏幕右侧。
这个动作时通过WM_MOVING消息实现的,当窗口移动时,窗口会收到这个消息,我们可以在这个消息中通过参数修改窗口位置。
这样实现的话问题就出现了,拖动窗口到右侧可以看到吸附效果,可是向左拽不动窗口了。
将屏幕最右侧20px像素矩形区域作为一个特殊区域(就叫做吸附区吧),只要窗口右侧落到这个区域内,就移动窗口至屏幕右边缘。如果想要拖动窗口离开边缘,需要下面两个状态值:
- 窗口是否已经停靠在右侧
- 鼠标是否是向左拖动窗口
很明显,如果上面两个状态均满足的话将窗口移出吸附区。
可以用一个bool值保存窗口是否停靠在右侧的状态。可是怎么判断鼠标是否是向左拖动窗口呢?
既然要拖动窗口,必然要用鼠标拖动窗口的标题栏(或者是非客户区),我们可以处理WM_NCMOUSEMOVE消息捕获到鼠标在非客户区移动时的坐标,并记录下来,然后再WM_MOVING中判断当前鼠标位置x坐标相对于刚才捕获位置的x坐标。这个就可以知道是否是向左拖拽窗口了。
(疑问:上面这个是借鉴的a note的源码逻辑。按照这个思路,窗口在吸附区时水平向右拖动窗口,窗口仍然可以收到WM_MOVING消息,那么是不是可以这样:通过在WM_MOVING处理函数里获取当前窗口位置,跟屏幕最右侧坐标相比,进而得到时候在用鼠标向右拖拽,这样的话更加简单了。)
这样程序基本上就实现了,代码如下:
1 void CMovingTestDlg::OnNcMouseMove(UINT nHitTest, CPoint point) 2 { 3 // m_dockedPnt = point; 4 if(m_bDockedLeft || m_bDockedRight){ 5 m_dockedPnt.x = point.x; 6 } 7 if(m_bDockedTop || m_bDockedBottom){ 8 m_dockedPnt.y = point.y; 9 } 10 CDialogEx::OnNcMouseMove(nHitTest, point); 11 }1 void CMovingTestDlg::OnMoving(UINT fwSide, LPRECT pRect) 2 { 3 CDialogEx::OnMoving(fwSide, pRect); 4 5 const int DOCK_WIDTH = 50; 6 CRect rectScreen; 7 SystemParametersInfo(SPI_GETWORKAREA,0,&rectScreen,0); 8 POINT curPnt; 9 GetCursorPos(&curPnt); 10 if(m_bDockedRight ){ 11 if(curPnt.x < m_dockedPnt.x - 3){ 12 pRect->left = rectScreen.right - (pRect->right-pRect->left)- DOCK_WIDTH; 13 pRect->right = rectScreen.right - DOCK_WIDTH; 14 m_bDockedRight = false; 15 } 16 else{ 17 pRect->left = rectScreen.right - (pRect->right-pRect->left); 18 pRect->right = rectScreen.right; 19 } 20 } 21 else if(!m_bDockedRight && pRect->right>rectScreen.right-DOCK_WIDTH){ 22 pRect->left = rectScreen.right - (pRect->right-pRect->left); 23 pRect->right = rectScreen.right; 24 m_bDockedRight = true; 25 m_dockedPnt = curPnt; 26 } 27 }