欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Qt无边框窗体模仿Win32标准窗体鼠标拖拽效果

程序员文章站 2022-03-02 10:29:36
...

本文介绍如何针对Qt的无边框窗体(即setWindowFlags(Qt::FramelessWindowHint);)实现鼠标拖拽标题栏移动窗体位置、鼠标拖拽窗体四周边框改变窗体大小的行为。

一、为所有控件添加MouseTracking

在Qt中,控件默认只有在至少有一个鼠标按键被按下的情况下,控件才能捕获鼠标的移动事件。针对我们想要模拟“鼠标拖拽窗体四周边框改变窗体大小的行为”,需要能够实时获得鼠标的移动事件,以便在鼠标移动到窗体四周时能做出相应的处理。虽然我们可以对所有控件都添加上“鼠标追踪”属性(setMouseTracking(true);),但一个个控件添加太麻烦,下面的setAllWidgetMouseTracking函数,可以递归地为父widget下的所有控件添加“鼠标追踪”属性。一般情况我们只需要将centralWidget传给setAllWidgetMouseTracking函数即可。

void BaseMainWindow::setAllWidgetMouseTracking(QWidget *widget) {
	if(!widget)
		return;
	widget->setMouseTracking(true);
	QObjectList list = widget->children();
	foreach(QObject *obj, list) {
		if(obj->metaObject()->className() == QStringLiteral("QWidget")) {
			QWidget* w = (QWidget*)obj;
			qDebug() << w->objectName();
			w->setMouseTracking(true);
			setAllWidgetMouseTracking(w);
		}
	}
}

二、实现原理

  1. 模拟鼠标拖拽标题栏移动窗体位置
    通过void setTitleWidget(const QString &widgetName);接口设置一个widget名称,当鼠标左键在该widget区域按下,并保持着按下状态,就会启用鼠标移动则窗体位置跟着移动的效果。
  2. 模拟鼠标拖拽窗体四周边框改变窗体大小
    通过void setEnableResize(bool b);接口来启动该功能。
    鼠标移动到窗体边缘附件时(具体值由m_iResizeRegionPadding来设置),鼠标形状会变成Qt::SizeVerCursor,此时用户再按下鼠标左键,移动鼠标就可以改变窗体大小。

BaseMainWindow .h

#pragma once

#include <QtWidgets/QMainWindow>
#include <QtWidgets/QWidget>

class BaseMainWindow : public QMainWindow {
	Q_OBJECT
public:
	enum Direction {
		UP = 0,
		DOWN = 1,
		LEFT,
		RIGHT,
		LEFTTOP,
		LEFTBOTTOM,
		RIGHTBOTTOM,
		RIGHTTOP,
		NONE
	};

	BaseMainWindow(QWidget *parent = Q_NULLPTR);
	virtual ~BaseMainWindow();
	void setTitleWidget(const QString &widgetName);
	void setEnableResize(bool b);
	void setAllWidgetMouseTracking(QWidget *widget);
	
protected:
    bool eventFilter(QObject *target, QEvent *event);
    void mouseDoubleClickEvent(QMouseEvent *event) override;
	void mousePressEvent(QMouseEvent *event) override;
	void mouseMoveEvent(QMouseEvent *event) override;
	void mouseReleaseEvent(QMouseEvent *event) override;
protected:
	void region(const QPoint &cursorGlobalPoint);
protected:
	bool m_bLeftPressed;

	Direction m_Direction;
	const int m_iResizeRegionPadding;


	bool m_bEnableResize;
	QPoint m_DragPos;
	QString m_strTitleWidget;
};

BaseMainWindow.cpp

#include "stdafx.h"
#include "BaseMainWindow.h"


BaseMainWindow::BaseMainWindow(QWidget *parent) : QMainWindow(parent),
m_bLeftPressed(false),
m_bEnableResize(false),
m_Direction(Direction::NONE),
m_iResizeRegionPadding(2) {

}

BaseMainWindow::~BaseMainWindow() {
}

void BaseMainWindow::setTitleWidget(const QString &widgetName) {
	m_strTitleWidget = widgetName;
}

void BaseMainWindow::setEnableResize(bool b) {
	m_bEnableResize = b;
}

void BaseMainWindow::mousePressEvent(QMouseEvent *event) {
	if(event->button() == Qt::LeftButton) {
		m_bLeftPressed = true;
		if(m_Direction != Direction::NONE) {
			this->mouseGrabber();
		}
		else if(m_strTitleWidget.length() > 0) {
			QWidget *action = QApplication::widgetAt(event->globalPos());
			if(action) {
				if(action->objectName() == m_strTitleWidget) {
					m_DragPos = event->globalPos() - this->frameGeometry().topLeft();
				}
			}
		}
	}
	return QWidget::mousePressEvent(event);
}

void BaseMainWindow::mouseMoveEvent(QMouseEvent *event) {
	QPoint globalPoint = event->globalPos();
	if(m_bLeftPressed) {
		if(m_Direction != NONE) {
			QRect rect = this->rect();
			QPoint tl = mapToGlobal(rect.topLeft());
			QPoint rb = mapToGlobal(rect.bottomRight());

			QRect rMove(tl, rb);

			switch(m_Direction) {
				case LEFT:
					if(rb.x() - globalPoint.x() <= this->minimumWidth())
						rMove.setX(tl.x());
					else
						rMove.setX(globalPoint.x());
					break;
				case RIGHT:
					rMove.setWidth(globalPoint.x() - tl.x());
					break;
				case UP:
					if(rb.y() - globalPoint.y() <= this->minimumHeight())
						rMove.setY(tl.y());
					else
						rMove.setY(globalPoint.y());
					break;
				case DOWN:
					rMove.setHeight(globalPoint.y() - tl.y());
					break;
				case LEFTTOP:
					if(rb.x() - globalPoint.x() <= this->minimumWidth())
						rMove.setX(tl.x());
					else
						rMove.setX(globalPoint.x());
					if(rb.y() - globalPoint.y() <= this->minimumHeight())
						rMove.setY(tl.y());
					else
						rMove.setY(globalPoint.y());
					break;
				case RIGHTTOP:
					rMove.setWidth(globalPoint.x() - tl.x());
					rMove.setY(globalPoint.y());
					break;
				case LEFTBOTTOM:
					rMove.setX(globalPoint.x());
					rMove.setHeight(globalPoint.y() - tl.y());
					break;
				case RIGHTBOTTOM:
					rMove.setWidth(globalPoint.x() - tl.x());
					rMove.setHeight(globalPoint.y() - tl.y());
					break;
				default:

					break;
			}
			this->setGeometry(rMove);
		}
		else {
			this->move(event->globalPos() - m_DragPos);
			event->accept();
		}
	}
	else {
		region(globalPoint);
	}
   
	return QWidget::mouseMoveEvent(event);
}

void BaseMainWindow::region(const QPoint &cursorGlobalPoint) {
	if(!m_bEnableResize)
		return;
	QRect rect = this->rect();
	QPoint tl = mapToGlobal(rect.topLeft());
	QPoint rb = mapToGlobal(rect.bottomRight());
	int x = cursorGlobalPoint.x();
	int y = cursorGlobalPoint.y();

	if(tl.x() + m_iResizeRegionPadding >= x && tl.x() <= x && tl.y() + m_iResizeRegionPadding >= y && tl.y() <= y) {
		// 左上角
		m_Direction = Direction::LEFTTOP;
		this->setCursor(QCursor(Qt::SizeFDiagCursor));
	}
	else if(x >= rb.x() - m_iResizeRegionPadding && x <= rb.x() && y >= rb.y() - m_iResizeRegionPadding && y <= rb.y()) {
		// 右下角
		m_Direction = Direction::RIGHTBOTTOM;
		this->setCursor(QCursor(Qt::SizeFDiagCursor));
	}
	else if(x <= tl.x() + m_iResizeRegionPadding && x >= tl.x() && y >= rb.y() - m_iResizeRegionPadding && y <= rb.y()) {
		//左下角
		m_Direction = Direction::LEFTBOTTOM;
		this->setCursor(QCursor(Qt::SizeBDiagCursor));
	}
	else if(x <= rb.x() && x >= rb.x() - m_iResizeRegionPadding && y >= tl.y() && y <= tl.y() + m_iResizeRegionPadding) {
		// 右上角
		m_Direction = Direction::RIGHTTOP;
		this->setCursor(QCursor(Qt::SizeBDiagCursor));
	}
	else if(x <= tl.x() + m_iResizeRegionPadding && x >= tl.x()) {
		// 左边
		m_Direction = Direction::LEFT;
		this->setCursor(QCursor(Qt::SizeHorCursor));
	}
	else if(x <= rb.x() && x >= rb.x() - m_iResizeRegionPadding) {
		// 右边
		m_Direction = Direction::RIGHT;
		this->setCursor(QCursor(Qt::SizeHorCursor));
	}
	else if(y >= tl.y() && y <= tl.y() + m_iResizeRegionPadding) {
		// 上边
		m_Direction = Direction::UP;
		this->setCursor(QCursor(Qt::SizeVerCursor));
	}
	else if(y <= rb.y() && y >= rb.y() - m_iResizeRegionPadding) {
		// 下边
		m_Direction = Direction::DOWN;
		this->setCursor(QCursor(Qt::SizeVerCursor));
	}
	else {
		// 默认
		m_Direction = Direction::NONE;
		this->setCursor(QCursor(Qt::ArrowCursor));
	}
}

void BaseMainWindow::mouseReleaseEvent(QMouseEvent *event) {
	m_bLeftPressed = false;
	if(m_Direction != NONE) {
		this->releaseMouse();
		this->setCursor(QCursor(Qt::ArrowCursor));
	}
	return QWidget::mouseReleaseEvent(event);
}

void BaseMainWindow::setAllWidgetMouseTracking(QWidget *widget) {
	if(!widget)
		return;
	widget->setMouseTracking(true);
	QObjectList list = widget->children();
	foreach(QObject *obj, list) {
		if(obj->metaObject()->className() == QStringLiteral("QWidget")) {
			QWidget* w = (QWidget*)obj;
			qDebug() << w->objectName();
			w->setMouseTracking(true);
			setAllWidgetMouseTracking(w);
		}
	}
}
相关标签: qt