From a8ccbfa407c6081a27adf3e3406a1ed5eda5e688 Mon Sep 17 00:00:00 2001 From: Uwe Kindler Date: Fri, 3 Mar 2017 13:42:41 +0100 Subject: [PATCH] Implemented support for dropping into a dock are --- .../src/v2/DockAreaWidget.cpp | 102 ++++++++--- AdvancedDockingSystem/src/v2/DockAreaWidget.h | 14 ++ .../src/v2/DockContainerWidget.cpp | 172 ++++++++++++++---- .../src/v2/DockWidgetTitleBar.cpp | 7 +- .../src/v2/FloatingDockContainer.cpp | 21 ++- .../src/v2/FloatingDockContainer.h | 10 +- 6 files changed, 252 insertions(+), 74 deletions(-) diff --git a/AdvancedDockingSystem/src/v2/DockAreaWidget.cpp b/AdvancedDockingSystem/src/v2/DockAreaWidget.cpp index 9f96274..0df2d45 100644 --- a/AdvancedDockingSystem/src/v2/DockAreaWidget.cpp +++ b/AdvancedDockingSystem/src/v2/DockAreaWidget.cpp @@ -52,6 +52,7 @@ namespace ads { static const char* const INDEX_PROPERTY = "index"; static const char* const ACTION_PROPERTY = "action"; +static const int APPEND = -1; /** * Custom scroll bar implementation for dock area tab bar @@ -62,12 +63,13 @@ class CTabsScrollArea : public QScrollArea { private: QPoint m_DragStartMousePos; - CDockAreaWidget* DockArea; + CDockAreaWidget* m_DockArea; + CFloatingDockContainer* m_FloatingWidget = nullptr; public: CTabsScrollArea(CDockAreaWidget* parent) : QScrollArea(parent), - DockArea(parent) + m_DockArea(parent) { setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Ignored); setFrameStyle(QFrame::NoFrame); @@ -79,6 +81,7 @@ public: protected: virtual void wheelEvent(QWheelEvent* Event) override { + std::cout << "CTabsScrollArea::wheelEvent" << std::endl; Event->accept(); const int direction = Event->angleDelta().y(); if (direction < 0) @@ -105,6 +108,22 @@ protected: QScrollArea::mousePressEvent(ev); } + /** + * Stores mouse position to detect dragging + */ + virtual void mouseReleaseEvent(QMouseEvent* ev) override + { + if (ev->button() == Qt::LeftButton) + { + std::cout << "CTabsScrollArea::mouseReleaseEvent" << std::endl; + ev->accept(); + m_FloatingWidget = nullptr; + m_DragStartMousePos = QPoint(); + return; + } + QScrollArea::mouseReleaseEvent(ev); + } + /** * Starts floating the complete docking area including all dock widgets, * if it is not the last dock area in a floating widget @@ -117,10 +136,16 @@ protected: return; } + if (m_FloatingWidget) + { + m_FloatingWidget->moveFloating(); + return; + } + // If this is the last dock area in a dock container it does not make // sense to move it to a new floating widget and leave this one // empty - if (DockArea->dockContainer()->isFloating() && DockArea->dockContainer()->dockAreaCount() == 1) + if (m_DockArea->dockContainer()->isFloating() && m_DockArea->dockContainer()->dockAreaCount() == 1) { return; } @@ -128,11 +153,8 @@ protected: if (!this->geometry().contains(ev->pos())) { std::cout << "CTabsScrollArea::startFloating" << std::endl; - QSize Size = DockArea->size(); - CFloatingDockContainer* FloatingWidget = new CFloatingDockContainer(DockArea); - FloatingWidget->startFloating(m_DragStartMousePos, Size); - - auto Overlay = DockArea->dockManager()->containerOverlay(); + startFloating(m_DragStartMousePos); + auto Overlay = m_DockArea->dockManager()->containerOverlay(); Overlay->setAllowedAreas(OuterDockAreas); } @@ -144,9 +166,18 @@ protected: */ virtual void mouseDoubleClickEvent(QMouseEvent *event) override { - QSize Size = DockArea->size(); - CFloatingDockContainer* FloatingWidget = new CFloatingDockContainer(DockArea); - FloatingWidget->startFloating(event->pos(), Size); + startFloating(event->pos()); + } + + /** + * Starts floating + */ + void startFloating(const QPoint& Pos) + { + QSize Size = m_DockArea->size(); + CFloatingDockContainer* FloatingWidget = new CFloatingDockContainer(m_DockArea); + FloatingWidget->startFloating(Pos, Size); + m_FloatingWidget = FloatingWidget; } }; // class CTabsScrollArea @@ -201,7 +232,7 @@ struct DockAreaWidgetPrivate * member. If menu is a valid menu pointer, the entry will be added to * the given menu */ - void addTabsMenuEntry(CDockWidget* DockWidget, QMenu* menu = 0); + void addTabsMenuEntry(CDockWidget* DockWidget, int Index = -1, QMenu* menu = 0); /** * Returns the tab action of the given dock widget @@ -310,10 +341,19 @@ void DockAreaWidgetPrivate::updateTabBar() //============================================================================ void DockAreaWidgetPrivate::addTabsMenuEntry(CDockWidget* DockWidget, - QMenu* menu) + int Index, QMenu* menu) { menu = menu ? menu : TabsMenuButton->menu(); - auto Action = menu->addAction(DockWidget->windowTitle()); + QAction* Action; + if (Index >= 0 && Index < menu->actions().count()) + { + Action = new QAction(DockWidget->windowTitle()); + menu->insertAction(menu->actions().at(Index), Action); + } + else + { + Action = menu->addAction(DockWidget->windowTitle()); + } QVariant vAction = QVariant::fromValue(Action); DockWidget->setProperty(ACTION_PROPERTY, vAction); } @@ -327,7 +367,7 @@ void DockAreaWidgetPrivate::updateTabsMenu() for (int i = 0; i < ContentsLayout->count(); ++i) { CDockWidget* DockWidget = dockWidgetAt(i); - addTabsMenuEntry(dockWidgetAt(i), menu); + addTabsMenuEntry(dockWidgetAt(i), APPEND, menu); } } @@ -388,20 +428,26 @@ CDockContainerWidget* CDockAreaWidget::dockContainer() const //============================================================================ void CDockAreaWidget::addDockWidget(CDockWidget* DockWidget) { - d->ContentsLayout->addWidget(DockWidget); + insertDockWidget(d->ContentsLayout->count(), DockWidget); + +} + + +//============================================================================ +void CDockAreaWidget::insertDockWidget(int index, CDockWidget* DockWidget, + bool Activate) +{ + d->ContentsLayout->insertWidget(index, DockWidget); DockWidget->titleBar()->setDockAreaWidget(this); auto TitleBar = DockWidget->titleBar(); - d->TabsLayout->insertWidget(d->TabsLayout->count() - d->TabsLayoutInitCount, - TitleBar); + d->TabsLayout->insertWidget(index, TitleBar); connect(TitleBar, SIGNAL(clicked()), this, SLOT(onDockWidgetTitleClicked())); - // if this is the first tab, then activate it - if (d->ContentsLayout->count() == 1) + DockWidget->setProperty(INDEX_PROPERTY, index); + if (Activate) { - setCurrentIndex(0); + setCurrentIndex(index); } - - DockWidget->setProperty(INDEX_PROPERTY, d->ContentsLayout->count() - 1); - d->addTabsMenuEntry(DockWidget); + d->addTabsMenuEntry(DockWidget, index); } @@ -413,6 +459,7 @@ void CDockAreaWidget::removeDockWidget(CDockWidget* DockWidget) auto TitleBar = DockWidget->titleBar(); d->TabsLayout->removeWidget(TitleBar); disconnect(TitleBar, SIGNAL(clicked()), this, SLOT(onDockWidgetTitleClicked())); + setCurrentIndex(d->ContentsLayout->currentIndex()); d->updateTabsMenu(); if (d->ContentsLayout->isEmpty()) @@ -481,6 +528,13 @@ void CDockAreaWidget::setCurrentIndex(int index) } +//============================================================================ +int CDockAreaWidget::currentIndex() const +{ + return d->ContentsLayout->currentIndex(); +} + + //============================================================================ QRect CDockAreaWidget::titleAreaGeometry() const { diff --git a/AdvancedDockingSystem/src/v2/DockAreaWidget.h b/AdvancedDockingSystem/src/v2/DockAreaWidget.h index 8c3dc14..b5d86e9 100644 --- a/AdvancedDockingSystem/src/v2/DockAreaWidget.h +++ b/AdvancedDockingSystem/src/v2/DockAreaWidget.h @@ -77,6 +77,15 @@ public: */ CDockContainerWidget* dockContainer() const; + /** + * Inserts a dock widget into dock area. + * All dockwidgets in the dock area tabified in a stacked layout with tabs. + * The index indicates the index of the new dockwidget in the tabbar and + * in the stacked layout. If the Activate parameter is true, the new + * DockWidget will be the active one in the stacked layout + */ + void insertDockWidget(int index, CDockWidget* DockWidget, bool Activate = true); + /** * Add a new dock widget to dock area. * All dockwidgets in the dock area tabified in a stacked layout with tabs @@ -129,6 +138,11 @@ public: */ void reorderDockWidget(int fromIndex, int toIndex); + /** + * Returns the index of the current active dock widget + */ + int currentIndex() const; + public slots: /** * This sets the index position of the current tab page. diff --git a/AdvancedDockingSystem/src/v2/DockContainerWidget.cpp b/AdvancedDockingSystem/src/v2/DockContainerWidget.cpp index e583712..29e62b4 100644 --- a/AdvancedDockingSystem/src/v2/DockContainerWidget.cpp +++ b/AdvancedDockingSystem/src/v2/DockContainerWidget.cpp @@ -117,7 +117,12 @@ struct DockContainerWidgetPrivate * Drop floating widget into dock area */ void dropIntoSection(CFloatingDockContainer* FloatingWidget, - CDockAreaWidget* targetSection, DockWidgetArea area); + CDockAreaWidget* TargetArea, DockWidgetArea area); + + /** + * Adds new dock areas to the internal dock area list + */ + void addDockAreasToList(const QList NewDockAreas); }; // struct DockContainerWidgetPrivate @@ -133,19 +138,17 @@ DockContainerWidgetPrivate::DockContainerWidgetPrivate(CDockContainerWidget* _pu void DockContainerWidgetPrivate::dropIntoContainer(CFloatingDockContainer* FloatingWidget, DockWidgetArea area) { - QSplitter* OldSplitter = _this->findChild(QString(), Qt::FindDirectChildrenOnly); auto InsertParam = internal::dockAreaInsertParameters(area); auto NewDockAreas = FloatingWidget->dockContainer()->findChildren( QString(), Qt::FindChildrenRecursively); + QSplitter* OldSplitter = nullptr; - if (DockAreas.isEmpty()) + // If the container already contains dock areas, then we need to ensure that + // we have a splitter with an orientation that matches the orientation of + // the current drop action + if (!DockAreas.isEmpty()) { - auto Widget = FloatingWidget->dockContainer()->findChild(QString(), Qt::FindDirectChildrenOnly); - Layout->addWidget(Widget, 0, 0); - } - else - { - QSplitter* OldSplitter = _this->findChild(QString(), Qt::FindDirectChildrenOnly); + OldSplitter = _this->findChild(QString(), Qt::FindDirectChildrenOnly); // First replace the dock widget with a splitter if (DockAreas.count() == 1) { @@ -162,32 +165,121 @@ void DockContainerWidgetPrivate::dropIntoContainer(CFloatingDockContainer* Float NewSplitter->addWidget(OldSplitter); OldSplitter = NewSplitter; } + } + // Now we can insert the floating widget content into this container + auto Widget = FloatingWidget->dockContainer()->findChild(QString(), Qt::FindDirectChildrenOnly); + auto FloatingSplitter = dynamic_cast(Widget); + if (DockAreas.isEmpty()) + { auto Widget = FloatingWidget->dockContainer()->findChild(QString(), Qt::FindDirectChildrenOnly); - auto FloatingSplitter = dynamic_cast(Widget); - - if (!FloatingSplitter) + Layout->addWidget(Widget, 0, 0); + } + else if (!FloatingSplitter) + { + insertWidgetIntoSplitter(OldSplitter, Widget, InsertParam.append()); + } + else if (FloatingSplitter->orientation() == InsertParam.orientation()) + { + while (FloatingSplitter->count()) { - insertWidgetIntoSplitter(OldSplitter, Widget, InsertParam.append()); + insertWidgetIntoSplitter(OldSplitter, FloatingSplitter->widget(0), InsertParam.append()); } - else if (FloatingSplitter->orientation() == InsertParam.orientation()) + } + else + { + insertWidgetIntoSplitter(OldSplitter, FloatingSplitter, InsertParam.append()); + } + + addDockAreasToList(NewDockAreas); + FloatingWidget->deleteLater(); +} + + +//============================================================================ +void DockContainerWidgetPrivate::dropIntoSection(CFloatingDockContainer* FloatingWidget, + CDockAreaWidget* TargetArea, DockWidgetArea area) +{ + CDockContainerWidget* FloatingContainer = FloatingWidget->dockContainer(); + if (area == CenterDockWidgetArea) + { + auto NewDockWidgets = FloatingContainer->findChildren( + QString(), Qt::FindChildrenRecursively); + for (auto DockWidget : NewDockWidgets) { - while (FloatingSplitter->count()) - { - insertWidgetIntoSplitter(OldSplitter, FloatingSplitter->widget(0), InsertParam.append()); - } + TargetArea->insertDockWidget(0, DockWidget, false); + } + TargetArea->setCurrentIndex(0); // make the topmost widget active + FloatingWidget->deleteLater(); + TargetArea->updateDockArea(); + return; + } + + auto InsertParam = internal::dockAreaInsertParameters(area); + auto NewDockAreas = FloatingWidget->dockContainer()->findChildren( + QString(), Qt::FindChildrenRecursively); + QSplitter* TargetAreaSplitter = internal::findParent(TargetArea); + + if (!TargetAreaSplitter) + { + QSplitter* Splitter = internal::newSplitter(InsertParam.orientation()); + Layout->replaceWidget(TargetArea, Splitter); + Splitter->addWidget(TargetArea); + TargetAreaSplitter = Splitter; + } + + int AreaIndex = TargetAreaSplitter->indexOf(TargetArea); + auto Widget = FloatingWidget->dockContainer()->findChild(QString(), Qt::FindDirectChildrenOnly); + auto FloatingSplitter = dynamic_cast(Widget); + + if (TargetAreaSplitter->orientation() == InsertParam.orientation()) + { + if (!FloatingSplitter || FloatingSplitter->orientation() != InsertParam.orientation()) + { + TargetAreaSplitter->insertWidget(AreaIndex + InsertParam.insertOffset(), Widget); } else { - insertWidgetIntoSplitter(OldSplitter, FloatingSplitter, InsertParam.append()); + int InsertIndex = AreaIndex + InsertParam.insertOffset(); + while (FloatingSplitter->count()) + { + TargetAreaSplitter->insertWidget(InsertIndex++, FloatingSplitter->widget(0)); + } } } + else + { + QSplitter* NewSplitter = internal::newSplitter(InsertParam.orientation()); + if (!FloatingSplitter || FloatingSplitter->orientation() != InsertParam.orientation()) + { + NewSplitter->addWidget(Widget); + } + else + { + while (FloatingSplitter->count()) + { + NewSplitter->addWidget(FloatingSplitter->widget(0)); + } + } + + insertWidgetIntoSplitter(NewSplitter, TargetArea, !InsertParam.append()); + TargetAreaSplitter->insertWidget(AreaIndex, NewSplitter); + } + + FloatingWidget->deleteLater(); + addDockAreasToList(NewDockAreas); +} - std::cout << "Adding " << NewDockAreas.count() << " dock areas" << std::endl; +//============================================================================ +void DockContainerWidgetPrivate::addDockAreasToList(const QList NewDockAreas) +{ int CountBefore = DockAreas.count(); int NewAreaCount = NewDockAreas.count(); DockAreas.append(NewDockAreas); + + // We need to ensure, that the dock area title bar is visible. The title bar + // is invisible, if the dock are is a single dock area in a floating widget. if (1 == CountBefore) { DockAreas.at(0)->updateDockArea(); @@ -197,15 +289,6 @@ void DockContainerWidgetPrivate::dropIntoContainer(CFloatingDockContainer* Float { DockAreas.last()->updateDockArea(); } - FloatingWidget->deleteLater(); -} - - -//============================================================================ -void DockContainerWidgetPrivate::dropIntoSection(CFloatingDockContainer* FloatingWidget, - CDockAreaWidget* targetSection, DockWidgetArea area) -{ - } @@ -281,20 +364,31 @@ CDockAreaWidget* DockContainerWidgetPrivate::dockWidgetIntoDockArea(DockWidgetAr CDockAreaWidget* NewDockArea = new CDockAreaWidget(DockManager, _this); NewDockArea->addDockWidget(Dockwidget); auto InsertParam = internal::dockAreaInsertParameters(area); - QSplitter* TargetAreaSplitter = internal::findParent(TargetDockArea); - int index = TargetAreaSplitter ->indexOf(TargetDockArea); - if (TargetAreaSplitter->orientation() == InsertParam.orientation()) + if (DockAreas.count() == 1) { - std::cout << "TargetAreaSplitter->orientation() == InsertParam.orientation()" << std::endl; - TargetAreaSplitter->insertWidget(index + InsertParam.insertOffset(), NewDockArea); + QSplitter* Splitter = internal::newSplitter(InsertParam.orientation()); + auto DockArea = dynamic_cast(Layout->itemAtPosition(0, 0)->widget()); + Layout->replaceWidget(DockArea, Splitter); + Splitter->addWidget(DockArea); + insertWidgetIntoSplitter(Splitter, NewDockArea, InsertParam.append()); } else { - std::cout << "TargetAreaSplitter->orientation() != InsertParam.orientation()" << std::endl; - QSplitter* NewSplitter = internal::newSplitter(InsertParam.orientation()); - NewSplitter->addWidget(TargetDockArea); - insertWidgetIntoSplitter(NewSplitter, NewDockArea, InsertParam.append()); - TargetAreaSplitter->insertWidget(index, NewSplitter); + QSplitter* TargetAreaSplitter = internal::findParent(TargetDockArea); + int index = TargetAreaSplitter ->indexOf(TargetDockArea); + if (TargetAreaSplitter->orientation() == InsertParam.orientation()) + { + std::cout << "TargetAreaSplitter->orientation() == InsertParam.orientation()" << std::endl; + TargetAreaSplitter->insertWidget(index + InsertParam.insertOffset(), NewDockArea); + } + else + { + std::cout << "TargetAreaSplitter->orientation() != InsertParam.orientation()" << std::endl; + QSplitter* NewSplitter = internal::newSplitter(InsertParam.orientation()); + NewSplitter->addWidget(TargetDockArea); + insertWidgetIntoSplitter(NewSplitter, NewDockArea, InsertParam.append()); + TargetAreaSplitter->insertWidget(index, NewSplitter); + } } DockAreas.append(NewDockArea); diff --git a/AdvancedDockingSystem/src/v2/DockWidgetTitleBar.cpp b/AdvancedDockingSystem/src/v2/DockWidgetTitleBar.cpp index 1f7478d..7151e0d 100644 --- a/AdvancedDockingSystem/src/v2/DockWidgetTitleBar.cpp +++ b/AdvancedDockingSystem/src/v2/DockWidgetTitleBar.cpp @@ -73,6 +73,7 @@ struct DockWidgetTitleBarPrivate bool IsActiveTab = false; CDockAreaWidget* DockArea = nullptr; eDragState DragState = DraggingInactive; + CFloatingDockContainer* FloatingWidget = nullptr; /** * Private data constructor @@ -193,6 +194,7 @@ bool DockWidgetTitleBarPrivate::startFloating(const QPoint& GlobalPos) FloatingWidget->startFloating(DragStartMousePosition, Size); auto Overlay = DockWidget->dockManager()->containerOverlay(); Overlay->setAllowedAreas(OuterDockAreas); + this->FloatingWidget = FloatingWidget; return true; } @@ -233,6 +235,7 @@ void CDockWidgetTitleBar::mousePressEvent(QMouseEvent* ev) //============================================================================ void CDockWidgetTitleBar::mouseReleaseEvent(QMouseEvent* ev) { + std::cout << "CDockWidgetTitleBar::mouseReleaseEvent" << std::endl; // End of tab moving, change order now if (d->isDraggingState(DraggingTab) && d->DockArea) { @@ -255,8 +258,6 @@ void CDockWidgetTitleBar::mouseReleaseEvent(QMouseEvent* ev) d->DragStartMousePosition = QPoint(); d->DragState = DraggingInactive; - //mcw->m_SectionDropOverlay->hideDropOverlay(); - //mcw->hideContainerOverlay(); QFrame::mouseReleaseEvent(ev); } @@ -275,6 +276,8 @@ void CDockWidgetTitleBar::mouseMoveEvent(QMouseEvent* ev) if (d->isDraggingState(DraggingFloatingWidget)) { + std::cout << "DraggingFloatingWidget" << std::endl; + d->FloatingWidget->moveFloating(); QFrame::mouseMoveEvent(ev); return; } diff --git a/AdvancedDockingSystem/src/v2/FloatingDockContainer.cpp b/AdvancedDockingSystem/src/v2/FloatingDockContainer.cpp index a45997b..6f051da 100644 --- a/AdvancedDockingSystem/src/v2/FloatingDockContainer.cpp +++ b/AdvancedDockingSystem/src/v2/FloatingDockContainer.cpp @@ -189,6 +189,9 @@ CFloatingDockContainer::CFloatingDockContainer(CDockManager* DockManager) : l->addWidget(d->DockContainer); DockManager->registerFloatingWidget(this); + // We install an event filter to detect mouse release events because we + // do not receive mouse release event if the floating widget is behind + // the drop overlay cross qApp->installEventFilter(this); } @@ -300,14 +303,7 @@ bool CFloatingDockContainer::eventFilter(QObject *watched, QEvent *event) std::cout << "FloatingWidget::eventFilter QEvent::MouseButtonRelease" << std::endl; d->titleMouseReleaseEvent(); } - else if ((event->type() == QEvent::MouseMove) && d->DraggingActive) - { - QMouseEvent* MouseEvent = dynamic_cast(event); - int BorderSize = (frameSize().width() - size().width()) / 2; - const QPoint moveToPos = QCursor::pos() - d->DragStartMousePosition - QPoint(BorderSize, 0); - move(moveToPos); - return true; - } + return false; } @@ -322,6 +318,15 @@ void CFloatingDockContainer::startFloating(const QPoint& Pos, const QSize& Size) show(); d->DragStartMousePosition = Pos; } + + +//============================================================================ +void CFloatingDockContainer::moveFloating() +{ + int BorderSize = (frameSize().width() - size().width()) / 2; + const QPoint moveToPos = QCursor::pos() - d->DragStartMousePosition - QPoint(BorderSize, 0); + move(moveToPos); +} } // namespace ads //--------------------------------------------------------------------------- diff --git a/AdvancedDockingSystem/src/v2/FloatingDockContainer.h b/AdvancedDockingSystem/src/v2/FloatingDockContainer.h index 0a7d086..0fd0157 100644 --- a/AdvancedDockingSystem/src/v2/FloatingDockContainer.h +++ b/AdvancedDockingSystem/src/v2/FloatingDockContainer.h @@ -85,9 +85,17 @@ public: CDockContainerWidget* dockContainer() const; /** - * Starts floating at the given global position + * Starts floating at the given global position. + * Use moveToGlobalPos() to move the widget to a new position + * depending on the start position given in Pos parameter */ void startFloating(const QPoint& Pos, const QSize& Size = QSize()); + + /** + * Moves the widget to a new position relative to the position given when + * startFloating() was called + */ + void moveFloating(); }; // class FloatingDockContainer } // namespace ads