Qt中的无边框窗口

                最近正在学习Qt,已经学习到无边框窗口相关的内容了,还是记录一些学习心得,这些内容是基于Qt6.10版本的。

      1.无边框窗口(基于QWidget)

this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowMinimizeButtonHint);

通过这串代码我们可以构建出一个基本的无边框窗口,但是,它只是一个窗口,什么也做不了,甚至无法拖动和缩放,那么应该如何让窗口“动起来”呢?

首先是窗口的拖动,一般来讲,这个是加在标题栏上的,因为一般的应用都是标题栏处理拖动和一些基本的功能,所以这个可以放在标题栏类中作为功能,它一共有两种实现方式。

1.通过重写Qt中的void mouseMoveEvent(QMouse* event)和void mousePressEvent(QMouse* event)来实现通用的鼠标拖拽功能,代码实现如下:
 

#include <QMouseEvent>
#include <QtWidgets/QWidget>

class noBorderWindow : public QWidget
{
    QOBJECT
public:
    noBorderWindow(QWidget* parent = nullptr);
    ~noBorderWindow();
private:
    QPoint diff_pos;//这个是窗口左上角和鼠标的坐标差
    QPoint window_pos;//窗口坐标
    QPoint mouse_pos;//鼠标坐标
private:
    void mousePressEvent(QMouseEvent* event) override;
    void mouseMoveEvent(QMouseEvent* event) override;
}
#include "noBorderWindow.h"

noBorderWindow::noBorderWindow(QWidget* parent)
    : QWidget(parent)
{
    this->setWindowFlags(Qt::FramelessWindowHint | Qt::WindowMinimizeButtonHint);
}
noBorderWindow::~noBorderWindow()
{}
void hand_write_tecent::mousePressEvent(QMouseEvent* event)
{
    mouse_pos = event->globalPosition().toPoint();
    window_pos = this->pos();
    diff_pos = mouse_pos - window_pos;
}//先获取相关坐标,再获得diff_pos,否则重点击时会出现鼠标跳动到上次位置的情况

void hand_write_tecent::mouseMoveEvent(QMouseEvent* event)
{
    QPoint pos = event->globalPosition().toPoint();
    this->move(pos - diff_pos);
}

这样就可以实现窗口的移动效果了

2.通过windows中的接口实现简洁的窗口移动:
 

void noBorderWindow::mousePressEvent(QMouseEvent* event)
{
	if (ReleaseCapture())
	{
		QWidget* pWindow = this->window();
		if (pWindow->isWindow()) 
		{
			SendMessage(HWND(pWindow->winId()), WM_SYSCOMMAND, SC_MOVE + HTCAPTION, 0);
		}
	}
}

ReleaseCapture 是一个 Windows API 函数,释放鼠标捕获。如果一个窗口在捕获鼠标,用户点击其他窗口不会改变鼠标捕获目标。再通过this->window() 返回当前窗口部件的顶级窗口指针。如果此时pWindow是顶部指针,那么就会使用Windows中的SendMessage实现鼠标拖动窗口。其中:

  • pWindow->winId() 返回窗口的系统句柄,将其转换为 HWND
  • WM_SYSCOMMAND 是 Windows 消息,表示系统命令。
  • SC_MOVE + HTCAPTION 触发窗口的移动操作,将鼠标点击的事件视为标题栏的拖动操作。

这个代码不适用于跨平台的鼠标拖拽哦

接下来是窗口拉伸效果,这个实现了窗口的第二大基础功能,一般可以把它单独封装成一个类用来作为父窗口,Windows的实现一般是重写bool nativeEvent(const QByteArray& eventType, void* message, qintptr* result) override;方法

#pragma once

#include <QtWidgets/QWidget>
#include <QMouseEvent>
#include <windows.h>
#include <windowsx.h>

class CFrameLessWidgetBase: public QWidget
{
public:
	CFrameLessWidgetBase(QWidget* prant = nullptr);
	~CFrameLessWidgetBase();
protected:
	bool nativeEvent(const QByteArray& eventType, void* message, qintptr* result) override;
private:
	int m_nBorderWidth;//这个是当鼠标位于离窗口边缘多远时拉伸窗口
};

#include "CFrameLessWidgetBase.h"

CFrameLessWidgetBase::CFrameLessWidgetBase(QWidget* prant)
	:QWidget(prant),
	m_nBorderWidth(5)
{
    this->setWindowFlags(Qt::FramelessWindowHint);
    this->setAttribute(Qt::WA_Hover);
}

CFrameLessWidgetBase::~CFrameLessWidgetBase()
{
}

bool CFrameLessWidgetBase::nativeEvent(const QByteArray& eventType, void* message, qintptr* result)
{
    Q_UNUSED(eventType);
    MSG* param = static_cast<MSG*>(message);
    if (param->message == WM_NCHITTEST) {
        QScreen* screen = QGuiApplication::primaryScreen();
        qreal dpiScale = screen->devicePixelRatio();
        POINT pt = { GET_X_LPARAM(param->lParam), GET_Y_LPARAM(param->lParam) };
        QRect windowRect = this->geometry();
        int left = windowRect.x() * dpiScale;
        int top = windowRect.y() * dpiScale;
        int right = left + windowRect.width() * dpiScale;
        int bottom = top + windowRect.height() * dpiScale;
        int scaledBorderWidth = static_cast<int>(m_nBorderWidth * dpiScale);
        bool inLeft = (pt.x >= left && pt.x < left + scaledBorderWidth);
        bool inRight = (pt.x <= right && pt.x > right - scaledBorderWidth);
        bool inTop = (pt.y >= top && pt.y < top + scaledBorderWidth);
        bool inBottom = (pt.y <= bottom && pt.y > bottom - scaledBorderWidth);
        if (inLeft && inTop) {
            *result = HTTOPLEFT;
        }
        else if (inRight && inTop) {
            *result = HTTOPRIGHT;
        }
        else if (inLeft && inBottom) {
            *result = HTBOTTOMLEFT;
        }
        else if (inRight && inBottom) {
            *result = HTBOTTOMRIGHT;
        }
        else if (inLeft) {
            *result = HTLEFT;
        }
        else if (inRight) {
            *result = HTRIGHT;
        }
        else if (inTop) {
            *result = HTTOP;
        }
        else if (inBottom) {
            *result = HTBOTTOM;
        }
        else {
            return false;
        }
        return true;
    }
    return QWidget::nativeEvent(eventType, message, result);
}

这样就实现了无边框窗口的拉伸,可以把它作为主类的继承父类使用

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值