qt的无边框窗口支持拖拽、Aero Snap、窗口阴影等特性

news/2025/1/22 17:51:52/文章来源:https://www.cnblogs.com/Yzi321/p/18512010

环境:Desktop Qt 5.4.1 MSVC2013 32bit

需要的库:dwmapi.libuser32.lib

需要头文件:<dwmapi.h><windowsx.h>

只显示重要代码

1、去除原边框、加上阴影、Aero Snap以及其他动画特效

(1)头文件

#include "Windows.h"
#include "uxtheme.h"
#include "dwmapi.h"
#include "titlebar.h"//自定义类

(2)去除标题、原边框

初始化,去除边框

void MainWindow::init()
{setWindowFlags(Qt::Window | Qt::FramelessWindowHint);#ifdef Q_OS_WINHWND hwnd = reinterpret_cast<HWND>(this->winId());const LONG style = ( WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CLIPCHILDREN );SetWindowLongPtr(hwnd, GWL_STYLE, style);const MARGINS shadow = {1, 1, 1, 1};DwmExtendFrameIntoClientArea(hwnd, &shadow);SetWindowPos(hwnd, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE);
#endif// 标题拖动、双击事件MyTitleBar *title = new MyTitleBar(this);qobject_cast<QBoxLayout *>(ui->centralwidget->layout())->insertWidget(0, title);
}

同时在最大化时,增加了边界,可以自行删除

bool MainWindow::nativeEvent(const QByteArray& eventType, void* message, qintptr* result)
{MSG* msg = (MSG*)message;switch (msg->message){case WM_NCCALCSIZE:{// this kills the window frame and title bar we added with WS_THICKFRAME and WS_CAPTION*result = 0;return true;}// 若full时边框不合适,可以去除此casecase WM_GETMINMAXINFO:{if (::IsZoomed(msg->hwnd)) {// 最大化时会超出屏幕,所以填充边框间距RECT frame = { 0, 0, 0, 0 };AdjustWindowRectEx(&frame, WS_OVERLAPPEDWINDOW, FALSE, 0);frame.left = abs(frame.left);frame.top = abs(frame.bottom);this->setContentsMargins(frame.left, frame.top, frame.right, frame.bottom);}else {this->setContentsMargins(0, 0, 0, 0);}*result = ::DefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam);return true;}break;default:return QMainWindow::nativeEvent(eventType, message, result);}
}

(3)支持手动修改窗口

需要实现一个QAbstractNativeEventFilter 类,内容如下:

头文件
#include <QAbstractNativeEventFilter>
#include <QWidget>
#include "Windows.h"
#define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp))
#define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp))class NativeEventFilter : public QAbstractNativeEventFilter
{
public:virtual bool nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result)Q_DECL_OVERRIDE;
};
cpp文件
bool NativeEventFilter::nativeEventFilter(const QByteArray &eventType, void *message, qintptr *result){
#ifdef Q_OS_WINif (eventType != "windows_generic_MSG")return false;MSG* msg = static_cast<MSG*>(message);QWidget* widget = QWidget::find(reinterpret_cast<WId>(msg->hwnd));if (!widget)return false;switch (msg->message) {case WM_NCHITTEST: {const LONG borderWidth = 9;RECT winrect;GetWindowRect(msg->hwnd, &winrect);long x = GET_X_LPARAM(msg->lParam);long y = GET_Y_LPARAM(msg->lParam);// bottom leftif (x >= winrect.left && x < winrect.left + borderWidth &&y < winrect.bottom && y >= winrect.bottom - borderWidth){*result = HTBOTTOMLEFT;return true;}// bottom rightif (x < winrect.right && x >= winrect.right - borderWidth &&y < winrect.bottom && y >= winrect.bottom - borderWidth){*result = HTBOTTOMRIGHT;return true;}// top leftif (x >= winrect.left && x < winrect.left + borderWidth &&y >= winrect.top && y < winrect.top + borderWidth){*result = HTTOPLEFT;return true;}// top rightif (x < winrect.right && x >= winrect.right - borderWidth &&y >= winrect.top && y < winrect.top + borderWidth){*result = HTTOPRIGHT;return true;}// leftif (x >= winrect.left && x < winrect.left + borderWidth){*result = HTLEFT;return true;}// rightif (x < winrect.right && x >= winrect.right - borderWidth){*result = HTRIGHT;return true;}// bottomif (y < winrect.bottom && y >= winrect.bottom - borderWidth){*result = HTBOTTOM;return true;}// topif (y >= winrect.top && y < winrect.top + borderWidth){*result = HTTOP;return true;}return false;}default:break;}return false;
#elsereturn false;
#endif
};
应用

然后在窗口创建之前,使用QApplication::installNativeEventFilter 方法把监听器注册给主程序。

int main(int argc, char *argv[])
{NativeEventFilter f;QApplication a(argc, argv);a.installNativeEventFilter(&f);//支持手动修改窗口大小MainWindow w;w.show();return a.exec();
}

2、自定义标题栏

  • 需要重载QWidget::mousePressEvent 方法
  • 保存 Window 句柄操作原窗口
class MyTitleBar : public QFrame  {Q_OBJECT
public:explicit MyTitleBar(QWidget *parent = nullptr);protected:void mousePressEvent(QMouseEvent* ev);private:QWidget *Window = nullptr; // 保存主窗口的指针QVBoxLayout *verticalLayout;QHBoxLayout *horizontalLayout;QSpacerItem *horizontalSpacer;QPushButton *pushButton_min;QPushButton *pushButton_normal;QPushButton *pushButton_max;QPushButton *pushButton_full;QSpacerItem *horizontalSpacer_6;QPushButton *pushButton_close;
};
创建ui

MyTitleBar::MyTitleBar(QWidget *parent): QFrame (parent), Window(parent)
{// 边缘贴合parent->setContentsMargins(0, 0, 0, 0);setObjectName("title");setMaximumHeight(50);this->setStyleSheet("#title{background-color: rgb(255, 255, 255);}");verticalLayout = new QVBoxLayout(this);verticalLayout->setObjectName("verticalLayout");verticalLayout->setContentsMargins(0, 0, 0, 0);horizontalLayout = new QHBoxLayout();horizontalLayout->setObjectName("horizontalLayout");horizontalSpacer = new QSpacerItem(40, 20, QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Minimum);horizontalLayout->addItem(horizontalSpacer);pushButton_min = new QPushButton(this);pushButton_min->setObjectName("pushButton_min");horizontalLayout->addWidget(pushButton_min);pushButton_normal = new QPushButton(this);pushButton_normal->setObjectName("pushButton_normal");horizontalLayout->addWidget(pushButton_normal);pushButton_max = new QPushButton(this);pushButton_max->setObjectName("pushButton_max");horizontalLayout->addWidget(pushButton_max);pushButton_full = new QPushButton(this);pushButton_full->setObjectName("pushButton_full");horizontalLayout->addWidget(pushButton_full);horizontalSpacer_6 = new QSpacerItem(40, 20, QSizePolicy::Policy::Expanding, QSizePolicy::Policy::Minimum);horizontalLayout->addItem(horizontalSpacer_6);pushButton_close = new QPushButton(this);pushButton_close->setObjectName("pushButton_close");horizontalLayout->addWidget(pushButton_close);horizontalLayout->setStretch(0, 1);verticalLayout->addLayout(horizontalLayout);pushButton_min->setText(QCoreApplication::translate("MainWindow", "MIN", nullptr));pushButton_normal->setText(QCoreApplication::translate("MainWindow", "NORMAL", nullptr));pushButton_max->setText(QCoreApplication::translate("MainWindow", "MAX", nullptr));pushButton_full->setText(QCoreApplication::translate("MainWindow", "FULL", nullptr));pushButton_close->setText(QCoreApplication::translate("MainWindow", "CLOSE", nullptr));
}
连接主窗口的变化

最大化和关闭按扭,正常调用QWidget::showMaximized()QWidget::close() 等Qt自带方法即可。

    connect(pushButton_min, &QPushButton::clicked, Window, &QWidget::showMinimized);connect(pushButton_close, &QPushButton::clicked, Window, &QWidget::close);connect(pushButton_normal, &QPushButton::clicked, Window, &QWidget::showNormal);connect(pushButton_full, &QPushButton::clicked, Window, &QWidget::showFullScreen);connect(pushButton_max, &QPushButton::clicked, Window, &QWidget::showMaximized);
重截QWidget::mousePressEvent 方法
#include "Windows.h"
void MyTitleBar::mousePressEvent(QMouseEvent* ev)
{QWidget::mousePressEvent(ev);if (Window == nullptr) return;if (!ev->isAccepted()) {if (ev->type() == QEvent::MouseButtonDblClick) {// Toggle maximize/restore on double-clickif (Window->isMaximized()) {Window->showNormal(); // Restore}else {Window->showMaximized(); // Maximize}return; // Prevent further processing}
#ifdef Q_OS_WINReleaseCapture();SendMessage(reinterpret_cast<HWND>(Window->winId()), WM_SYSCOMMAND, SC_MOVE + HTCAPTION, 0);
#endif}
}
添加
    // 标题拖动、双击事件MyTitleBar *title = new MyTitleBar(this);qobject_cast<QBoxLayout *>(ui->centralwidget->layout())->insertWidget(0, title);

3、最终实现效果


参考链接:https://github.com/deimos1877/BorderlessWindow

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/823323.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring 框架:Java 开发者的春天

Spring 框架是一个功能强大的开源框架,主要用于简化 Java 企业级应用的开发,由被称为“Spring 之父”的 Rod Johnson 于 2002 年提出并创立,并由Pivotal团队维护。一、关于Spring 1.1 简介 Spring 框架是一个功能强大的开源框架,主要用于简化 Java 企业级应用的开发,由被称…

Mooc下载器:中国大学mooc慕课视频课件课程下载工具,如何在电脑端下载中国大学mooc慕课视频课程课件资料到本地?

一. 安装中国大学mooc慕课课程下载器 1.获取学无止下载器 https://www.xuewuzhi.cn/icourse163_downloader 2.下载安装后,然后点击桌面快捷方式运行即可。 注意:杀毒软件可能会阻止外部exe文件运行,并将其当做成病毒,直接添加信任即可,本软件绝对没有木马病毒。 二. 使用…

千聊兴趣岛视频课件课程下载工具,如何在电脑端下载千聊兴趣岛视频课程课件到本地?

一. 安装千聊/兴趣岛课程下载器 1.获取学无止下载器 https://www.xuewuzhi.cn/qlchat_downloader 2.下载安装后,然后点击桌面快捷方式运行即可。 注意:杀毒软件可能会阻止外部exe文件运行,并将其当做成病毒,直接添加信任即可,本软件绝对没有木马病毒。 二. 使用说明 1.学无…

T241029

有限可加性和次可列可加性结合等价于可列可加性

一文搞懂会计科目设计原理及运维要点

在企业的财务管理中,会计科目的设计和维护是核心环节,它不仅关系到财务数据的准确性,还直接影响到企业的决策和合规性。这篇文章,我们一起来学习一下相关知识。会计科目是业务事项按会计准则要求的一种归纳或分类,是在会计要素基础上的一种细分,包括科目编码和科目名称;…

Ubuntu系统配置Zabbix前端

1.zabbix 登录1.访问zabbix的前端地址(10.0.0.6/zabbix/setup.php)进入欢迎界面点击下一步即可2.如下图所示,下拉进度条进行条件检查,如果所有的参数都是"OK",则可以进入下一步3.配置zabbix前端的数据库信息根据数据库信息填写即可4 .配置zabbix server信息5.将之前…

Blender 骨骼绑定记录

首先先从Free3D网站下载一个免费的Blender模型,将它导入Blender需要检查:模型原点是否在世界坐标原点 Location,Rotation,Scale是否归一化3D游标是否在世界坐标原点,不在可以按Shift+C在正视图下(1)创建根骨架修改骨架视图显示改到侧视图,并将它置平因为它不绑定权重,所…

Linux 中sed命令输出奇数行和偶数行的方法

001、[root@PC1 test]# ls a.txt [root@PC1 test]# cat a.txt ## 测试数据 01 02 03 04 05 06 07 08 09 10 11 12 [root@PC1 test]# sed -n n;p a.txt ## 输出偶数行 03 04 07 08 11 12 [root@PC1 test]# sed -n p;n a.txt …

Centos7中Could not resolve host: mirrorlist.centos.org; 未知的错误

报错内容:Could not retrieve mirrorlist http://mirrorlist.centos.org/?release=7&arch=x86_64&repo=os&infra=stock error was 14: curl#6 - "Could not resolve host: mirrorlist.centos.org; 未知的错误"One of the configured repositories fail…

【CodeForces训练记录】Educational Codeforces Round 171 (Rated for Div. 2)

训练情况赛后反思 B题二分没有考虑到细节情况 A题 我们构造垂直并且要最长,我们只需要取长宽的较小值,然后一个正方形,左上到右下,左下到右上即可。 #include <bits/stdc++.h> #define int long longusing namespace std;void solve(){int x,y,k; cin>>x>&g…

提示词工程

提示词(Prompt)是指在与大语言模型交互时,用户输入的一段文本或指令,用于引导模型生成相应的输出。它可以是一句话、一个问题、一段描述或更复杂的结构化信息。BiliBili概念 提示词提示词(Prompt)是指在与大语言模型交互时,用户输入的一段文本或指令,用于引导模型生成相…

数据结构 - 散列表,再探

散列表碰撞解决方案包括链式法、开放寻址法(含线性、平方、双重散列、随机探测)、再散列法。链式法用链表处理碰撞,开放寻址法通过探测解决,再散列法通过扩容并重计算位置。书接上回,我们继续来聊散列表。从上面的章节不难发现,无论散列函数怎么构建总会发生碰撞,最多只…