diff --git a/AdvancedDockingSystem/src/v2/DockAreaWidget.cpp b/AdvancedDockingSystem/src/v2/DockAreaWidget.cpp index 2508336..b90de5e 100644 --- a/AdvancedDockingSystem/src/v2/DockAreaWidget.cpp +++ b/AdvancedDockingSystem/src/v2/DockAreaWidget.cpp @@ -37,6 +37,7 @@ #include #include #include +#include #include "DockContainerWidget.h" #include "DockWidget.h" @@ -46,6 +47,9 @@ namespace ads { +static const char* const INDEX_PROPERTY = "index"; +static const char* const ACTION_PROPERTY = "action"; + /** * Custom scroll bar implementation for dock area tab bar */ @@ -104,6 +108,43 @@ struct DockAreaWidgetPrivate * Creates the layout for top area with tabs and close button */ void createTabBar(); + + /** + * Returns the dock widget with the given index + */ + CDockWidget* dockWidgetAt(int index) + { + return dynamic_cast(ContentsLayout->widget(index)); + } + + /** + * Convenience function to ease title widget access by index + */ + CDockWidgetTitleBar* titleWidgetAt(int index) + { + return dockWidgetAt(index)->titleBar(); + } + + /** + * Adds a tabs menu entry for the given dock widget + */ + void addTabsMenuEntry(CDockWidget* DockWidget); + + /** + * Returns the tab action of the given dock widget + */ + QAction* dockWidgetTabAction(CDockWidget* DockWidget) const + { + return qvariant_cast(DockWidget->property(ACTION_PROPERTY)); + } + + /** + * Returns the index of the given dock widget + */ + int dockWidgetIndex(CDockWidget* DockWidget) const + { + return DockWidget->property(INDEX_PROPERTY).toInt(); + } }; // struct DockAreaWidgetPrivate @@ -142,7 +183,10 @@ void DockAreaWidgetPrivate::createTabBar() TabsMenuButton->setFlat(true); TabsMenuButton->setIcon(_this->style()->standardIcon(QStyle::SP_TitleBarUnshadeButton)); TabsMenuButton->setMaximumWidth(TabsMenuButton->iconSize().width()); + TabsMenuButton->setMenu(new QMenu(TabsMenuButton)); TopLayout->addWidget(TabsMenuButton, 0); + _this->connect(TabsMenuButton->menu(), SIGNAL(triggered(QAction*)), + SLOT(onTabsMenuActionTriggered(QAction*))); CloseButton = new QPushButton(); CloseButton->setObjectName("closeButton"); @@ -156,6 +200,16 @@ void DockAreaWidgetPrivate::createTabBar() TabsLayoutInitCount = TabsLayout->count(); } + +//============================================================================ +void DockAreaWidgetPrivate::addTabsMenuEntry(CDockWidget* DockWidget) +{ + auto Action = TabsMenuButton->menu()->addAction(DockWidget->windowTitle()); + QVariant vAction = QVariant::fromValue(Action); + DockWidget->setProperty(ACTION_PROPERTY, vAction); +} + + //============================================================================ CDockAreaWidget::CDockAreaWidget(CDockManager* DockManager, CDockContainerWidget* parent) : QFrame(parent), @@ -204,6 +258,7 @@ CDockContainerWidget* CDockAreaWidget::dockContainerWidget() const void CDockAreaWidget::addDockWidget(CDockWidget* DockWidget) { d->ContentsLayout->addWidget(DockWidget); + DockWidget->titleBar()->setDockAreaWidget(this); auto TitleBar = DockWidget->titleBar(); d->TabsLayout->insertWidget(d->TabsLayout->count() - d->TabsLayoutInitCount, TitleBar); @@ -213,6 +268,9 @@ void CDockAreaWidget::addDockWidget(CDockWidget* DockWidget) { setCurrentIndex(0); } + + DockWidget->setProperty(INDEX_PROPERTY, d->ContentsLayout->count() - 1); + d->addTabsMenuEntry(DockWidget); } @@ -259,7 +317,7 @@ void CDockAreaWidget::setCurrentIndex(int index) TitleWidget->setActiveTab(true); d->TabsScrollArea->ensureWidgetVisible(TitleWidget); auto Features = TitleWidget->dockWidget()->features(); - d->CloseButton->setEnabled(Features.testFlag(CDockWidget::DockWidgetClosable)); + d->CloseButton->setVisible(Features.testFlag(CDockWidget::DockWidgetClosable)); } else { @@ -269,6 +327,114 @@ void CDockAreaWidget::setCurrentIndex(int index) d->ContentsLayout->setCurrentIndex(index); } + + +//============================================================================ +QRect CDockAreaWidget::titleAreaGeometry() const +{ + return d->TopLayout->geometry(); +} + +//============================================================================ +QRect CDockAreaWidget::contentAreaGeometry() const +{ + return d->ContentsLayout->geometry(); +} + + +//============================================================================ +int CDockAreaWidget::tabIndex(CDockWidget* DockWidget) +{ + return d->ContentsLayout->indexOf(DockWidget); +} + + +//============================================================================ +QList CDockAreaWidget::dockWidgets() const +{ + QList DockWidgetList; + for (int i = 0; i < d->ContentsLayout->count(); ++i) + { + DockWidgetList.append(dockWidget(i)); + } + return DockWidgetList; +} + + +//============================================================================ +int CDockAreaWidget::indexOfContentByTitlePos(const QPoint& p, QWidget* exclude) const +{ + for (int i = 0; i < d->ContentsLayout->count(); ++i) + { + auto TitleWidget = d->titleWidgetAt(i); + if (TitleWidget->geometry().contains(p) && (!exclude || TitleWidget != exclude)) + { + return i; + } + } + return -1; +} + + +//============================================================================ +int CDockAreaWidget::count() const +{ + return d->ContentsLayout->count(); +} + + +//============================================================================ +CDockWidget* CDockAreaWidget::dockWidget(int Index) const +{ + return dynamic_cast(d->ContentsLayout->widget(Index)); +} + + +//============================================================================ +void CDockAreaWidget::reorderDockWidget(int fromIndex, int toIndex) +{ + if (fromIndex >= d->ContentsLayout->count() || fromIndex < 0 + || toIndex >= d->ContentsLayout->count() || toIndex < 0 || fromIndex == toIndex) + { + qDebug() << "Invalid index for tab movement" << fromIndex << toIndex; + d->TabsLayout->update(); + return; + } + + CDockWidget* DockWidget = dockWidget(fromIndex); + + // reorder tabs menu action to match new order of contents + auto Menu = d->TabsMenuButton->menu(); + auto TabsAction = d->dockWidgetTabAction(DockWidget); + Menu->removeAction(TabsAction); + if (toIndex >= Menu->actions().count()) + { + Menu->addAction(TabsAction); + } + else + { + Menu->insertAction(Menu->actions().at(toIndex), TabsAction); + } + + + // now reorder contents and title bars + QLayoutItem* liFrom = nullptr; + liFrom = d->TabsLayout->takeAt(fromIndex); + d->TabsLayout->insertItem(toIndex, liFrom); + liFrom = d->ContentsLayout->takeAt(fromIndex); + d->ContentsLayout->insertWidget(toIndex, liFrom->widget()); + delete liFrom; + + //Menu->removeAction() +} + + +//============================================================================ +void CDockAreaWidget::onTabsMenuActionTriggered(QAction* Action) +{ + int Index = d->TabsMenuButton->menu()->actions().indexOf(Action); + setCurrentIndex(Index); +} } // namespace ads //--------------------------------------------------------------------------- diff --git a/AdvancedDockingSystem/src/v2/DockAreaWidget.h b/AdvancedDockingSystem/src/v2/DockAreaWidget.h index cbf7cd9..3c74347 100644 --- a/AdvancedDockingSystem/src/v2/DockAreaWidget.h +++ b/AdvancedDockingSystem/src/v2/DockAreaWidget.h @@ -53,6 +53,7 @@ private: private slots: void onDockWidgetTitleClicked(); + void onTabsMenuActionTriggered(QAction* Action); public: /** @@ -77,6 +78,47 @@ public: */ void addDockWidget(CDockWidget* DockWidget); + /** + * Returns the rectangle of the title area + */ + QRect titleAreaGeometry() const; + + /** + * Returns the rectangle of the content + */ + QRect contentAreaGeometry() const; + + /** + * Returns the tab index of the given DockWidget + */ + int tabIndex(CDockWidget* DockWidget); + + /** + * Returns the index of contents of the title widget that is located at + * mouse position pos + */ + int indexOfContentByTitlePos(const QPoint& pos, QWidget* exclude = nullptr) const; + + /** + * Returns a list of all dock widgets in this dock area + */ + QList dockWidgets() const; + + /** + * Returns the number of dock widgets in this area + */ + int count() const; + + /** + * Returns a dock widget by its index + */ + CDockWidget* dockWidget(int Index) const; + + /** + * Reorder the index position of DockWidget at fromIndx to toIndex. + */ + void reorderDockWidget(int fromIndex, int toIndex); + public slots: /** * This sets the index position of the current tab page. diff --git a/AdvancedDockingSystem/src/v2/DockContainerWidget.h b/AdvancedDockingSystem/src/v2/DockContainerWidget.h index b28a57d..cbff95e 100644 --- a/AdvancedDockingSystem/src/v2/DockContainerWidget.h +++ b/AdvancedDockingSystem/src/v2/DockContainerWidget.h @@ -43,7 +43,7 @@ class CDockManager; /** * Container that manages a number of dock areas with single dock widgets - * or tabyfied dock widtes in each area + * or tabyfied dock widgets in each area */ class CDockContainerWidget : public QFrame { diff --git a/AdvancedDockingSystem/src/v2/DockWidget.cpp b/AdvancedDockingSystem/src/v2/DockWidget.cpp index db46657..9d0c0a9 100644 --- a/AdvancedDockingSystem/src/v2/DockWidget.cpp +++ b/AdvancedDockingSystem/src/v2/DockWidget.cpp @@ -124,6 +124,7 @@ CDockWidget::DockWidgetFeatures CDockWidget::features() const { return d->Features; } + } // namespace ads //--------------------------------------------------------------------------- diff --git a/AdvancedDockingSystem/src/v2/DockWidgetTitleBar.cpp b/AdvancedDockingSystem/src/v2/DockWidgetTitleBar.cpp index 88ea940..3438429 100644 --- a/AdvancedDockingSystem/src/v2/DockWidgetTitleBar.cpp +++ b/AdvancedDockingSystem/src/v2/DockWidgetTitleBar.cpp @@ -34,8 +34,12 @@ #include #include #include +#include + +#include #include "DockWidget.h" +#include "DockAreaWidget.h" namespace ads { @@ -50,6 +54,8 @@ struct DockWidgetTitleBarPrivate QLabel* TitleLabel; QPoint DragStartMousePosition; bool IsActiveTab = false; + bool TabMoving = false; + CDockAreaWidget* DockArea = nullptr; /** * Private data constructor @@ -60,6 +66,11 @@ struct DockWidgetTitleBarPrivate * Creates the complete layout including all controls */ void createLayout(); + + /** + * Moves the tab depending on the position in the given mouse event + */ + void moveTab(QMouseEvent* ev); }; // struct DockWidgetTitleBarPrivate @@ -90,6 +101,18 @@ void DockWidgetTitleBarPrivate::createLayout() TitleLabel->setText(DockWidget->windowTitle()); } +//============================================================================ +void DockWidgetTitleBarPrivate::moveTab(QMouseEvent* ev) +{ + ev->accept(); + int left, top, right, bottom; + _this->getContentsMargins(&left, &top, &right, &bottom); + QPoint moveToPos = _this->mapToParent(ev->pos()) - DragStartMousePosition; + moveToPos.setY(0); + _this->move(moveToPos); + _this->raise(); +} + //============================================================================ CDockWidgetTitleBar::CDockWidgetTitleBar(CDockWidget* DockWidget, QWidget *parent) : @@ -124,19 +147,57 @@ void CDockWidgetTitleBar::mousePressEvent(QMouseEvent* ev) //============================================================================ void CDockWidgetTitleBar::mouseReleaseEvent(QMouseEvent* ev) { + // End of tab moving, change order now + if (d->TabMoving && d->DockArea) + { + // Find tab under mouse + QPoint pos = d->DockArea->mapFromGlobal(ev->globalPos()); + int fromIndex = d->DockArea->tabIndex(d->DockWidget); + int toIndex = d->DockArea->indexOfContentByTitlePos(pos, this); + if (-1 == toIndex) + { + toIndex = d->DockArea->count() - 1; + } + std::cout << "Move tab from " << fromIndex << " to " << toIndex << std::endl; + d->DockArea->reorderDockWidget(fromIndex, toIndex); + } + if (!d->DragStartMousePosition.isNull()) { emit clicked(); } d->DragStartMousePosition = QPoint(); + d->TabMoving = false; + //mcw->m_SectionDropOverlay->hideDropOverlay(); + //mcw->hideContainerOverlay(); + QFrame::mouseReleaseEvent(ev); } //============================================================================ void CDockWidgetTitleBar::mouseMoveEvent(QMouseEvent* ev) { + if (!(ev->buttons() & Qt::LeftButton)) + { + QFrame::mouseMoveEvent(ev); + return; + } + // move tab + if (d->TabMoving) + { + d->moveTab(ev); + } + + if ((ev->pos() - d->DragStartMousePosition).manhattanLength() >= QApplication::startDragDistance() // Wait a few pixels before start moving + && d->DockArea->titleAreaGeometry().contains(d->DockArea->mapFromGlobal(ev->globalPos()))) + { + d->TabMoving = true; + return; + } + + QFrame::mouseMoveEvent(ev); } @@ -171,6 +232,13 @@ CDockWidget* CDockWidgetTitleBar::dockWidget() const { return d->DockWidget; } + + +//============================================================================ +void CDockWidgetTitleBar::setDockAreaWidget(CDockAreaWidget* DockArea) +{ + d->DockArea = DockArea; +} } // namespace ads //--------------------------------------------------------------------------- diff --git a/AdvancedDockingSystem/src/v2/DockWidgetTitleBar.h b/AdvancedDockingSystem/src/v2/DockWidgetTitleBar.h index 9afc673..8198afc 100644 --- a/AdvancedDockingSystem/src/v2/DockWidgetTitleBar.h +++ b/AdvancedDockingSystem/src/v2/DockWidgetTitleBar.h @@ -35,6 +35,7 @@ namespace ads { class CDockWidget; +class CDockAreaWidget; struct DockWidgetTitleBarPrivate; /** @@ -82,6 +83,12 @@ public: */ CDockWidget* dockWidget() const; + /** + * Sets the dock area widget the dockWidget returned by dockWidget() + * function belongs to. + */ + void setDockAreaWidget(CDockAreaWidget* DockArea); + signals: void activeTabChanged(); void clicked(); diff --git a/AdvancedDockingSystemDemo_v2/src/mainwindow.cpp b/AdvancedDockingSystemDemo_v2/src/mainwindow.cpp index c1292a7..c35ea9b 100644 --- a/AdvancedDockingSystemDemo_v2/src/mainwindow.cpp +++ b/AdvancedDockingSystemDemo_v2/src/mainwindow.cpp @@ -92,7 +92,9 @@ void MainWindow::createContent() m_DockManager->addDockWidget(ads::LeftDockWidgetArea, createLongTextLabelDockWidget(m_DockManager)); m_DockManager->addDockWidget(ads::BottomDockWidgetArea, createFileSystemTreeDockWidget(m_DockManager)); auto DockArea = m_DockManager->addDockWidget(ads::TopDockWidgetArea, createFileSystemTreeDockWidget(m_DockManager)); - m_DockManager->addDockWidget(ads::CenterDockWidgetArea, createCalendarDockWidget(m_DockManager), DockArea); + DockWidget = createCalendarDockWidget(m_DockManager); + DockWidget->setFeatures(DockWidget->features().setFlag(ads::CDockWidget::DockWidgetClosable, false)); + m_DockManager->addDockWidget(ads::CenterDockWidgetArea, DockWidget, DockArea); }