窗口靠边自动隐藏
用过qq的同学都知道,qq主窗口在靠近界面边缘时会自动隐藏,而当鼠标再一次进入的时候会自动弹出,接下来我将记录下自己实现的类似同样的功能,支持多屏幕靠边隐藏。文章末尾我提供了demo的下载地址,这个样例我是从网上下载下来,并自己进行了优化,主要针对边界判断和多屏幕支持。
下面是我做的效果展示,由于录屏软件录制屏幕顶端不好录制,所以录制了屏幕左侧,我自己测试的结果是:屏幕左、屏幕上和屏幕右都没有问题。如果发现问题的同学可以联系我。
效果预览
接下来我将每一步的实现代码分别做以解释:
一、窗口移动
做windows桌面程序的同学,应该都会这个功能,不过我还是贴一下代码吧,虽然不是特别复杂。要实现这个功能只需要重写3个方法,分别是:mousePressEvent、mouseMoveEvent和mouseReleaseEvent。
1、mousePressEvent
1 void FloatingWidget::mousePressEvent(QMouseEvent *e) 2 { 3 if (e->button() == Qt::LeftButton) 4 { 5 m_dragPosition = e->globalPos() - frameGeometry().topLeft(); 6 e->accept(); 7 } 8 }
2、mouseMoveEvent
1 void FloatingWidget::mouseMoveEvent(QMouseEvent * event) 2 { 3 if (event->buttons() & Qt::LeftButton) 4 { 5 QPoint pos = event->globalPos() - m_dragPosition; 6 7 QDesktopWidget * desktop = qApp->desktop(); 8 QRect rect = desktop->screenGeometry(QCursor::pos()); 9 QRect frameRect = frameGeometry(); 10 if (rect.top() >= pos.y())//修正顶端位置 11 { 12 pos.setY(rect.top()); 13 } 14 15 if (rect.left()>= pos.x())//修正左侧位置 16 { 17 int leftScreenNumber = desktop->screenNumber(pos - QPoint(width(), 0)); 18 if (desktop->screenGeometry(leftScreenNumber).contains((pos - QPoint(width(), 0))) == false) 19 { 20 pos.setX(rect.left()); 21 } 22 } 23 24 if (rect.right() <= pos.x() + frameRect.width())//修正右侧位置 25 { 26 int rightScreenNumber = desktop->screenNumber(pos + QPoint(width(), 0)); 27 if (desktop->screenGeometry(rightScreenNumber).contains((pos + QPoint(width(), 0))) == false) 28 { 29 pos.setX(rect.right() - frameRect.width()); 30 } 31 } 32 move(pos); 33 34 event->accept(); 35 } 36 }
这个函数里有3个位置修正判断,主要实现了在鼠标拖拽时,保证窗口不移出屏幕。
3、mouseReleaseEvent
1 void FloatingWidget::mouseReleaseEvent(QMouseEvent * event) 2 { 3 QWidget::mouseReleaseEvent(event); 4 }
通过上述3个方式的重写,就实现了窗口的拖拽,并且我们的窗口是不能拖拽到屏幕意外
二、屏幕边界自动隐藏
窗口移动到屏幕边界时,自动隐藏我们的窗口,首先我们来考虑这么几个问题:
1、什么时候需要隐藏窗口
2、什么时候需要显示窗口
3、检测窗口是否需要隐藏
4、窗口显示时怎么回到起始位置
问题1:隐藏窗口在我们鼠标移除窗口的时候,这个时候需要注意,鼠标在屏幕边界时不能隐藏
问题2:鼠标进入到我们的窗口时
问题3:窗口的边界和屏幕对应(比如:窗口左边界对应屏幕左边界)边界如果距离小于我们制定的边界检测宽度则可以隐藏
问题4:记录平路隐藏时的位置,方便回显回去
搞清楚上述4个问题后,我们就来贴代码吧
1、首先是窗口隐藏
1 void FloatingWidget::leaveEvent(QEvent * e) 2 { 3 QPoint mousePos = mapFromGlobal(QCursor::pos()); 4 if (rect().contains(mousePos) == false 5 && mousePos.x() != rect().width()) 6 { 7 HideDockWidget(); 8 } 9 else 10 { 11 if (m_timer == nullptr) 12 { 13 m_timer = new QTimer(this); 14 connect(m_timer, &QTimer::timeout, this, [this]{ 15 QPoint mousePos = mapFromGlobal(QCursor::pos()); 16 if (this->rect().contains(mousePos) == false 17 && mousePos.x() != rect().width()) 18 { 19 HideDockWidget(); 20 } 21 }); 22 } 23 m_timer->start(500); 24 } 25 26 QWidget::leaveEvent(e); 27 }
鼠标移出窗口时,需要隐藏窗口,首先判断鼠标是否还在窗口内,包括窗口边界,如果在窗口内,则开启定时器,每隔500毫秒检测鼠标是否还在屏幕内,因为leaveEvent事件和enterEvent是成对出现的,当leaveEvent发生一次后,除非enterEvent触发,否则不会触发;如果不在窗口内,直接去隐藏窗口。
2、显示窗口
显示窗口就比较好理解了,只需要在鼠标进入窗口时,如果窗口之前隐藏了,则把窗口恢复到之前隐藏的位置
1 void FloatingWidget::enterEvent(QEvent * e) 2 { 3 if (m_timer && m_timer->isActive()) 4 { 5 m_timer->stop(); 6 } 7 8 ShowDockWidget(); 9 10 QWidget::enterEvent(e); 11 }
3、检测窗口是否需要隐藏
1 void FloatingWidget::HideDockWidget()
2 {
3 if (m_IsVisible == false)
4 {
5 return;
6 }
7
8 m_IsVisible = false;
9
10 int curHeight = height();
11 int curWidth = width();
12
13 QDesktopWidget * desktop = qApp->desktop();
14 QRect rect = desktop->screenGeometry(this);
15
16 if (frameGeometry().left() - CHECK_BORDER <= rect.top()
17 && TEST_BIT(m_feature, LeftArea))
18 {
19 MoveWindow(pos(), pos() - QPoint(curWidth - HIDE_BORDER, 0));
20 }
21 else if (frameGeometry().right() + CHECK_BORDER >= rect.right()
22 && TEST_BIT(m_feature, RightArea))
23 {
24 MoveWindow(pos(), pos() + QPoint(curWidth - HIDE_BORDER, 0));
25 }
26 else if (frameGeometry().top() - CHECK_BORDER <= rect.top()
27 && TEST_BIT(m_feature, TopArea))
28 {
29 MoveWindow(pos(), pos() - QPoint(0, curHeight - HIDE_BORDER));
30 }
31 else
32 {
33 m_IsVisible = true;
34 }
35
36 if (m_IsVisible == false)
37 {
38 if (m_timer && m_timer->isActive())
39 {
40 m_timer->stop();
41 }
42 }
43 }
4、窗口回显到起始位置
1 void FloatingWidget::ShowDockWidget()
2 {
3 if (m_IsVisible)
4 {
5 return;
6 }
7
8 m_IsVisible = true;
9
10 int curHeight = height();
11 int curWidth = width();
12
13 QDesktopWidget * desktop = qApp->desktop();
14 QRect rect = desktop->screenGeometry(this);
15 QRect frameRect = frameGeometry();
16
17 if (frameRect.left() == m_RecoverPosition.x() - (curWidth - HIDE_BORDER)
18 && TEST_BIT(m_feature, LeftArea))
19 {
20 MoveWindow(pos(), m_RecoverPosition);
21 }
22 else if (frameRect.left() == m_RecoverPosition.x() + (curWidth - HIDE_BORDER)
23 && TEST_BIT(m_feature, RightArea))
24 {
25 MoveWindow(pos(), m_RecoverPosition);
26 }
27 else if (frameRect.top() == m_RecoverPosition.y() - (curHeight - HIDE_BORDER)
28 && TEST_BIT(m_feature, TopArea))
29 {
30 MoveWindow(pos(), m_RecoverPosition);
31 }
32 else
33 {
34 m_IsVisible = false;
35 }
36 }
5、最后是窗口移动算法
1 void FloatingWidget::MoveWindow(const QPoint & start, const QPoint & end, unsigned int step) 2 { 3 QPoint distance = end - start; 4 QPoint stepPos, stepOne; 5 if (end.x() == start.x()) 6 { 7 stepOne.setY(step * (distance.y() > 0 ? 1 : -1)); 8 } 9 else 10 { 11 stepOne.setX(step * (distance.x() > 0 ? 1 : -1)); 12 } 13 stepPos = stepOne; 14 15 int disLenght = distance.manhattanLength(); 16 while (stepPos.manhattanLength() <= disLenght) 17 { 18 move(start + stepPos); 19 stepPos += stepOne; 20 } 21 22 move(end); 23 24 m_RecoverPosition = start; 25 }
参数分别是:起始位置、终点位置和每次移动距离
demo下载链接:http://download.csdn.net/detail/qq_30392343/9644654