前言 众所周知,停靠窗口可以实现任意拖动效果,本文重点在于如何利用Qt制作与Visual Studio相似的带有停靠方向标及停靠区域预览的的停靠窗口框架。 效果图 功能 1、鼠标在中间方向标:叠加窗口 2、鼠标在上下左右方向标:分割目标窗口,并紧挨着目标窗口周边位置添加新窗口 3、鼠标在内部最上下左右方向标:目标窗口所在的最上下左右位置添加新窗口 4、鼠标在外部最上下左右方向标:程序主窗口的最上下左右位置添加新窗口 5、鼠标在Tab位置上:在当前所在tab页位置插入新窗口 6、鼠标在Tab最右侧位置上:在tab页尾部添加新窗口 注释:Dock停靠优先级:某些情况下,外部最上下左右方向的方向标会和目标窗口方向标重叠,此时遵循 中间停靠优于外部停靠、方向标停靠优于tab页停靠 的原则。
部分头文件
#pragma once
#include <QWidget>
#include <QPaintEvent>
#include "QWHDockWidget.h"
class QMainWindow;
class QTabWidget;
class QDockWidget;
class QSplitter;
class QWHTabWidgetMask : public QWidget
{
Q_OBJECT
public:
enum Area
{
None,Top, Right, Bottom, Left, TopMore, RightMore, BottomMore, LeftMore, Center, TopMost, RightMost, BottomMost, LeftMost
};
QWHTabWidgetMask();
~QWHTabWidgetMask();
static QWHTabWidgetMask *getInstance();
// 设置程序主窗口
void setMainWindow(QMainWindow *mainWindow);
// 创建停靠窗口
QWHDockWidget *createDockWidget(QWHDockWidget::AreaMode areaMode, const QString &windowTitle = "");
// 创建分裂器(水平分裂)
QSplitter *createSplitter();
// 创建分裂器(由参数orientation决定分裂方向)
QSplitter *createSplitter(Qt::Orientation orientation);
// 设置程序主分裂器
void setMainSplitter(QSplitter *splitter);
// 设置目标窗口(接收方)
void setTargetWidget(QTabWidget *widget);
// 设置当前页索引(鼠标移入当前页 或 鼠标移入中心方向标)
void setCurTabIndex(int index);
// 设置鼠标按下的停靠窗口(准备移动的窗口)
void setMousePressed(QWHDockWidget *moveDockWidget);
// 设置鼠标释放
void setMouseReleased();
// 获取停靠窗口推荐最小尺寸
QSize minimumSizeHint() const override;
// 获取鼠标按下的停靠窗口(准备移动或正在移动的窗口)
QDockWidget *moveDockWidget();
// 获取程序主分裂器
QSplitter *mainSplitter();
// 获取程序主窗口
QMainWindow *mainWindow();
protected:
void paintEvent(QPaintEvent *event);
private:
// 获取指定索引的边界路径
QPainterPath tabWidgetBorderPath(QTabWidget *tabWidget, int tabIndex);
// 绘制主停靠窗口的指示器
void drawMainDockIndicator();
// 绘制次停靠窗口的指示器
void drawMinorDockIndicator();
// 检查鼠标所在方向标区域
Area checkArea(QPoint globalPos);
signals:
// 创建停靠窗口
void dockWidgetAdded(QWHDockWidget *newDockWidget);
private:
QMainWindow *m_mainWindow;
QSplitter *m_mainSplitter;
QWHDockWidget *m_moveDockWidget;
QTabWidget *m_targetWidget;
QList<QWHDockWidget *> m_listDockWidgets;
int m_tabIndex;
QColor m_borderColor;
QColor m_bgColor;
QRect m_centerRect; // 中心矩形
QRect m_topRect, m_rightRect, m_bottomRect, m_leftRect; // 四个方位矩形(紧挨着中心矩形)
QRect m_topMoreRect, m_rightMoreRect, m_bottomMoreRect, m_leftMoreRect; // 更加靠边四个方位矩形(紧挨着四个方位矩形)
QRect m_topMostRect, m_rightMostRect, m_bottomMostRect, m_leftMostRect; // 最靠边四个方向矩形(紧挨着主窗口四边)
QPixmap m_centerPixmap;
QPixmap m_topPixmap, m_rightPixmap, m_bottomPixmap, m_leftPixmap;
QPixmap m_topMostPixmap, m_rightMostPixmap, m_bottomMostPixmap, m_leftMostPixmap;
QPixmap m_centerPixmapHover;
QPixmap m_topPixmapHover, m_rightPixmapHover, m_bottomPixmapHover, m_leftPixmapHover;
QPixmap m_topMostPixmapHover, m_rightMostPixmapHover, m_bottomMostPixmapHover, m_leftMostPixmapHover;
};
测试代码
TestVSWindow::TestVSWindow(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
QWHTabWidgetMask::getInstance()->setMainWindow(this);
// 测试左侧停靠窗体
QWHDockWidget *dockWidget = QWHTabWidgetMask::getInstance()->createDockWidget(QWHDockWidget::Mode_Minor, "总tab");
QSplitter *splitter = QWHTabWidgetMask::getInstance()->createSplitter();
splitter->addWidget(dockWidget);
dockWidget->setFloating(false);
QWidget *widget1 = new QWidget();
widget1->setMinimumSize(200, 100);
widget1->setStyleSheet("background-color: green;");
dockWidget->tabWidget()->addTab(widget1, "第一页");
QWidget *widget2 = new QWidget();
widget2->setMinimumSize(200, 100);
widget2->setStyleSheet("background-color: green;");
dockWidget->tabWidget()->addTab(widget2, "第二页");
QWidget *widget3 = new QWidget();
widget3->setMinimumSize(200, 100);
widget3->setStyleSheet("background-color: green;");
dockWidget->tabWidget()->addTab(widget3, "第三页");
// 测试中间停靠窗体
QWHDockWidget *dockWidgetCenter = QWHTabWidgetMask::getInstance()->createDockWidget(QWHDockWidget::Mode_Main, "总tabCenter");
splitter->addWidget(dockWidgetCenter);
dockWidgetCenter->setFloating(false);
QWidget *widgetCenter1 = new QWidget();
widgetCenter1->setMinimumSize(200, 100);
widgetCenter1->setStyleSheet("background-color: rgb(255, 174, 201);");
dockWidgetCenter->tabWidget()->addTab(widgetCenter1, "第一页Center");
QWidget *widgetCenter2 = new QWidget();
widgetCenter2->setMinimumSize(200, 100);
widgetCenter2->setStyleSheet("background-color: rgb(255, 174, 201);");
dockWidgetCenter->tabWidget()->addTab(widgetCenter2, "第二页Center");
QWidget *widgetCenter3 = new QWidget();
widgetCenter3->setMinimumSize(200, 100);
widgetCenter3->setStyleSheet("background-color: rgb(255, 174, 201);");
dockWidgetCenter->tabWidget()->addTab(widgetCenter3, "第三页Center");
// 测试右侧停靠窗体
QWHDockWidget *dockWidget2 = QWHTabWidgetMask::getInstance()->createDockWidget(QWHDockWidget::Mode_Minor, "总tab2");
splitter->addWidget(dockWidget2);
dockWidget2->setFloating(false);
QWidget *widget12 = new QWidget();
widget12->setMinimumSize(200, 100);
widget12->setStyleSheet("background-color: gray;");
dockWidget2->tabWidget()->addTab(widget12, "第一页2");
QWidget *widget22 = new QWidget();
widget22->setMinimumSize(200, 100);
widget22->setStyleSheet("background-color: gray;");
dockWidget2->tabWidget()->addTab(widget22, "第二页2");
QWidget *widget32 = new QWidget();
widget32->setMinimumSize(200, 100);
widget32->setStyleSheet("background-color: gray;");
dockWidget2->tabWidget()->addTab(widget32, "第三页2");
QWHTabWidgetMask::getInstance()->setMainSplitter(splitter);
}
|