Qt自定义标题栏-移动窗口

本文最后更新于:2021年10月11日 上午

前情提要

众所周知,一个最简单的窗口也是有标题栏的

Windows默认提供的标题栏上有:

图标-窗口标题-Min-Max-Close按钮

但是,这未免太过局限

高自由度的自定义是极客(Geek)精神不可或缺的一部分

如果你想在标题栏上增加/减少控件,或改变布局、颜色、Size

就必须抛弃Windows提供的嗟来之食

蹴尔而与之,乞人不屑也

じゃあ、どうする

使用无边框窗口即可

1
setWindowFlags(Qt::FramelessWindowHint);//Qt

这样就可以去除默认的标题栏,取而代之的便是光秃秃的客户区

你问我接下来怎么办?

那自然是自己写一个标题栏,当然这不是重点

只是你很快就会发现:根本没法移动窗口

それは決まってんだろう(那可不废话)

衣来伸手饭来张口,吃久了五斗米都不知道怎么下地了

正题

前摇巨长,现在进入正题,重点讲讲如何移动窗口

有两种方式:

  • 监测鼠标动作
  • 响应Windows消息,伪造标题栏

手动模拟

第一种最直观,平时移动窗口的方式不就是:

在鼠标在标题栏按下左键并移动 就可以带着窗口一起飞嘛

那只要手动检测鼠标状态 并移动窗口即可

1
2
3
4
5
6
7
8
9
10
11
12
13
void Widget::mousePressEvent(QMouseEvent* event)//鼠标按下
{
curPos = event->screenPos().toPoint();
}

void Widget::mouseMoveEvent(QMouseEvent* event)//鼠标按着并移动
{
if (!(event->buttons() & Qt::LeftButton)) return;//左键按下
QPoint mousePos = event->screenPos().toPoint();
QPoint newPos = this->pos() + mousePos - curPos;
curPos = mousePos;
move(newPos);
}

每次鼠标移动都会触发Move消息,只要计算与上次移动的差值并同步移动窗口,即可

这里要注意几个细节:

  • 最好用函数自带的参数event,因为QCursor::pos()更新相对滞后

  • 最好用鼠标的全局坐标event->screenPos(),而不是相对窗口坐标event->pos(),因为后者在鼠标快速移动的情况下可能越界导致Bomb爆炸,窗口鬼畜

伪造消息

第二种,欺骗Windows,假装自定义标题栏就是原来的标题栏

响应Windows消息即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
bool Widget::nativeEvent(const QByteArray& eventType, void* message, long* result)
{
Q_UNUSED(eventType);
MSG* msg = (MSG*)message;
switch (msg->message) {
case WM_NCHITTEST://鼠标移动消息
int xPos = GET_X_LPARAM(msg->lParam) - this->frameGeometry().x();//解析鼠标相对坐标
int yPos = GET_Y_LPARAM(msg->lParam) - this->frameGeometry().y();
if (ui->label_title->geometry().contains(xPos, yPos)) //标题栏(伪)
*result = HTCAPTION;//POINT重点语句,通过result指针返回--------------------------------<-
else //其他部分不做处理,返回false,留给其他事件处理器处理
return false;
return true;
}
return false; //此处返回false,留给其他事件处理器处理
}

通过result指针,将结果返回给Windows,假装是标题栏(CAPTION)

Peace


Qt自定义标题栏-移动窗口
https://mrbeancpp.github.io/2021/10/09/Qt自定义标题栏-移动窗口/
作者
MrBeanC
发布于
2021年10月9日
许可协议