Qt停靠窗口
浮生卍流年 人气:0前言
众所周知,停靠窗口可以实现任意拖动效果,本文重点在于如何利用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); }
加载全部内容