From fac566128039c6ce3d74555becb5cd8391f07ad0 Mon Sep 17 00:00:00 2001 From: Uwe Kindler Date: Thu, 2 Mar 2017 11:43:48 +0100 Subject: [PATCH] Improved support when moving widgets out of FloatingWidget --- .../src/v2/DockAreaWidget.cpp | 59 +- AdvancedDockingSystem/src/v2/DockAreaWidget.h | 5 + .../src/v2/DockContainerWidget.cpp | 20 + .../src/v2/DockContainerWidget.h | 10 + AdvancedDockingSystem/src/v2/DockManager.cpp | 4 +- AdvancedDockingSystem/src/v2/DockOverlay.cpp | 502 +++++++++++++++++- AdvancedDockingSystem/src/v2/DockOverlay.h | 127 ++++- .../src/v2/DockWidgetTitleBar.cpp | 40 +- .../src/v2/FloatingDockContainer.cpp | 30 +- AdvancedDockingSystem/src/v2/ads_globals.h | 1 + 10 files changed, 710 insertions(+), 88 deletions(-) diff --git a/AdvancedDockingSystem/src/v2/DockAreaWidget.cpp b/AdvancedDockingSystem/src/v2/DockAreaWidget.cpp index abd3cee..9f96274 100644 --- a/AdvancedDockingSystem/src/v2/DockAreaWidget.cpp +++ b/AdvancedDockingSystem/src/v2/DockAreaWidget.cpp @@ -43,6 +43,8 @@ #include "DockWidget.h" #include "DockWidgetTitleBar.h" #include "FloatingDockContainer.h" +#include "DockManager.h" +#include "DockOverlay.h" #include @@ -104,7 +106,8 @@ protected: } /** - * Starts floating the complete docking area including all dock widgets + * Starts floating the complete docking area including all dock widgets, + * if it is not the last dock area in a floating widget */ virtual void mouseMoveEvent(QMouseEvent* ev) override { @@ -113,11 +116,24 @@ protected: { 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) + { + return; + } + 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(); + Overlay->setAllowedAreas(OuterDockAreas); } return; @@ -142,6 +158,7 @@ struct DockAreaWidgetPrivate { CDockAreaWidget* _this; QBoxLayout* Layout; + QFrame* TitleBar; QBoxLayout* TopLayout; QStackedLayout* ContentsLayout; QScrollArea* TabsScrollArea; @@ -207,6 +224,12 @@ struct DockAreaWidgetPrivate * been removed */ void updateTabsMenu(); + + /** + * Updates the tab bar visibility depending on the number of dock widgets + * in this area + */ + void updateTabBar(); }; // struct DockAreaWidgetPrivate @@ -222,10 +245,12 @@ DockAreaWidgetPrivate::DockAreaWidgetPrivate(CDockAreaWidget* _public) : //============================================================================ void DockAreaWidgetPrivate::createTabBar() { + TitleBar = new QFrame(_this); TopLayout = new QBoxLayout(QBoxLayout::LeftToRight); TopLayout->setContentsMargins(0, 0, 0, 0); TopLayout->setSpacing(0); - Layout->addLayout(TopLayout); + TitleBar->setLayout(TopLayout); + Layout->addWidget(TitleBar); TabsScrollArea = new CTabsScrollArea(_this); TopLayout->addWidget(TabsScrollArea, 1); @@ -263,6 +288,26 @@ void DockAreaWidgetPrivate::createTabBar() } +//============================================================================ +void DockAreaWidgetPrivate::updateTabBar() +{ + CDockContainerWidget* Container = _this->dockContainer(); + if (!Container) + { + return; + } + + if (Container->isFloating() && (Container->dockAreaCount() == 1) && (_this->count() == 1)) + { + TitleBar->setVisible(false); + } + else + { + TitleBar->setVisible(true); + } +} + + //============================================================================ void DockAreaWidgetPrivate::addTabsMenuEntry(CDockWidget* DockWidget, QMenu* menu) @@ -376,6 +421,8 @@ void CDockAreaWidget::removeDockWidget(CDockWidget* DockWidget) dockContainer()->removeDockArea(this); this->deleteLater(); } + + d->updateTabBar(); } @@ -540,6 +587,14 @@ void CDockAreaWidget::onTabsMenuActionTriggered(QAction* Action) int Index = d->TabsMenuButton->menu()->actions().indexOf(Action); setCurrentIndex(Index); } + + +//============================================================================ +void CDockAreaWidget::updateDockArea() +{ + d->updateTabBar(); +} + } // namespace ads //--------------------------------------------------------------------------- diff --git a/AdvancedDockingSystem/src/v2/DockAreaWidget.h b/AdvancedDockingSystem/src/v2/DockAreaWidget.h index 984c998..8c3dc14 100644 --- a/AdvancedDockingSystem/src/v2/DockAreaWidget.h +++ b/AdvancedDockingSystem/src/v2/DockAreaWidget.h @@ -134,6 +134,11 @@ public slots: * This sets the index position of the current tab page. */ void setCurrentIndex(int index); + + /** + * Updates the dock area layout and components visibility + */ + void updateDockArea(); }; // class DockAreaWidget } // namespace ads diff --git a/AdvancedDockingSystem/src/v2/DockContainerWidget.cpp b/AdvancedDockingSystem/src/v2/DockContainerWidget.cpp index d662d99..944462c 100644 --- a/AdvancedDockingSystem/src/v2/DockContainerWidget.cpp +++ b/AdvancedDockingSystem/src/v2/DockContainerWidget.cpp @@ -39,6 +39,7 @@ #include "DockManager.h" #include "DockAreaWidget.h" #include "DockWidget.h" +#include "FloatingDockContainer.h" #include "ads_globals.h" #include @@ -72,6 +73,7 @@ struct DockContainerWidgetPrivate unsigned int zOrderIndex = 0; QList DockAreas; QGridLayout* Layout = nullptr; + bool isFloating = false; /** * Private data constructor @@ -170,6 +172,7 @@ void DockContainerWidgetPrivate::addDockArea(CDockAreaWidget* NewDockArea, DockW } DockAreas.append(NewDockArea); + NewDockArea->updateDockArea(); } @@ -212,6 +215,8 @@ CDockContainerWidget::CDockContainerWidget(CDockManager* DockManager, QWidget *p QFrame(parent), d(new DockContainerWidgetPrivate(this)) { + d->isFloating = dynamic_cast(parent) != 0; + //setStyleSheet("background: green;"); d->DockManager = DockManager; if (DockManager != this) @@ -306,6 +311,7 @@ void CDockContainerWidget::addDockArea(CDockAreaWidget* DockAreaWidget, //============================================================================ void CDockContainerWidget::removeDockArea(CDockAreaWidget* area) { + std::cout << "CDockContainerWidget::removeDockArea" << std::endl; d->DockAreas.removeAll(area); QSplitter* Splitter = internal::findParent(area); area->setParent(0); @@ -345,6 +351,20 @@ CDockAreaWidget* CDockContainerWidget::dockAreaAt(const QPoint& GlobalPos) const return 0; } + + +//============================================================================ +bool CDockContainerWidget::isFloating() const +{ + return d->isFloating; +} + + +//============================================================================ +int CDockContainerWidget::dockAreaCount() const +{ + return d->DockAreas.count(); +} } // namespace ads //--------------------------------------------------------------------------- diff --git a/AdvancedDockingSystem/src/v2/DockContainerWidget.h b/AdvancedDockingSystem/src/v2/DockContainerWidget.h index 0f44bbe..75fc00a 100644 --- a/AdvancedDockingSystem/src/v2/DockContainerWidget.h +++ b/AdvancedDockingSystem/src/v2/DockContainerWidget.h @@ -104,6 +104,16 @@ public: * dock area at this position */ CDockAreaWidget* dockAreaAt(const QPoint& GlobalPos) const; + + /** + * Returns the number of dock areas in this container + */ + int dockAreaCount() const; + + /** + * This function returns true, if this container is in a floating widget + */ + bool isFloating() const; }; // class DockContainerWidget } // namespace ads //----------------------------------------------------------------------------- diff --git a/AdvancedDockingSystem/src/v2/DockManager.cpp b/AdvancedDockingSystem/src/v2/DockManager.cpp index 77ec09b..2d18043 100644 --- a/AdvancedDockingSystem/src/v2/DockManager.cpp +++ b/AdvancedDockingSystem/src/v2/DockManager.cpp @@ -77,8 +77,8 @@ CDockManager::CDockManager(QWidget *parent) : MainWindow->setCentralWidget(this); } - d->DockAreaOverlay = new CDockOverlay(this); - d->ContainerOverlay = new CDockOverlay(this); + d->DockAreaOverlay = new CDockOverlay(this, CDockOverlay::ModeDockAreaOverlay); + d->ContainerOverlay = new CDockOverlay(this, CDockOverlay::ModeContainerOverlay); d->Containers.append(this); } diff --git a/AdvancedDockingSystem/src/v2/DockOverlay.cpp b/AdvancedDockingSystem/src/v2/DockOverlay.cpp index 8a871f7..f80fe57 100644 --- a/AdvancedDockingSystem/src/v2/DockOverlay.cpp +++ b/AdvancedDockingSystem/src/v2/DockOverlay.cpp @@ -17,61 +17,185 @@ ******************************************************************************/ -//============================================================================ -/// \file DropOverlay.cpp -/// \author Uwe Kindler -/// \date 01.03.2017 -/// \brief Implementation of CDropOverlay class -//============================================================================ - - //============================================================================ // INCLUDES //============================================================================ #include "DockOverlay.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + #include namespace ads { +//============================================================================ +static QPixmap createDropIndicatorPixmap(const QPalette& pal, const QSizeF& size, DockWidgetArea DockWidgetArea) +{ + const QColor borderColor = pal.color(QPalette::Active, QPalette::Highlight); + const QColor backgroundColor = pal.color(QPalette::Active, QPalette::Base); + const QColor areaBackgroundColor = pal.color(QPalette::Active, QPalette::Highlight).lighter(150); + + QPixmap pm(size.width(), size.height()); + pm.fill(QColor(0, 0, 0, 0)); + + QPainter p(&pm); + QPen pen = p.pen(); + QRectF baseRect(pm.rect()); + + // Fill + p.fillRect(baseRect, backgroundColor); + + // Drop area rect. + p.save(); + QRectF areaRect; + QLineF areaLine; + QLinearGradient gradient; + switch (DockWidgetArea) + { + case TopDockWidgetArea: + areaRect = QRectF(baseRect.x(), baseRect.y(), baseRect.width(), baseRect.height() * .5f); + areaLine = QLineF(areaRect.bottomLeft(), areaRect.bottomRight()); + gradient.setStart(areaRect.topLeft()); + gradient.setFinalStop(areaRect.bottomLeft()); + break; + case RightDockWidgetArea: + areaRect = QRectF(baseRect.width() * .5f, baseRect.y(), baseRect.width() * .5f, baseRect.height()); + areaLine = QLineF(areaRect.topLeft(), areaRect.bottomLeft()); + gradient.setStart(areaRect.topLeft()); + gradient.setFinalStop(areaRect.topRight()); + break; + case BottomDockWidgetArea: + areaRect = QRectF(baseRect.x(), baseRect.height() * .5f, baseRect.width(), baseRect.height() * .5f); + areaLine = QLineF(areaRect.topLeft(), areaRect.topRight()); + gradient.setStart(areaRect.topLeft()); + gradient.setFinalStop(areaRect.bottomLeft()); + break; + case LeftDockWidgetArea: + areaRect = QRectF(baseRect.x(), baseRect.y(), baseRect.width() * .5f, baseRect.height()); + areaLine = QLineF(areaRect.topRight(), areaRect.bottomRight()); + gradient.setStart(areaRect.topLeft()); + gradient.setFinalStop(areaRect.topRight()); + break; + default: + break; + } + if (areaRect.isValid()) + { + gradient.setColorAt(0.f, areaBackgroundColor); + gradient.setColorAt(1.f, areaBackgroundColor.lighter(120)); + p.fillRect(areaRect, gradient); + + pen = p.pen(); + pen.setColor(borderColor); + pen.setStyle(Qt::DashLine); + p.setPen(pen); + p.drawLine(areaLine); + } + p.restore(); + + p.save(); + pen = p.pen(); + pen.setColor(borderColor); + pen.setWidth(1); + + p.setPen(pen); + p.drawRect(baseRect.adjusted(0, 0, -pen.width(), -pen.width())); + p.restore(); + return pm; +} + + +//============================================================================ +QWidget* createDropIndicatorWidget(DockWidgetArea DockWidgetArea) +{ + QLabel* l = new QLabel(); + l->setObjectName("DockWidgetAreaLabel"); + + const qreal metric = static_cast(l->fontMetrics().height()) * 2.f; + const QSizeF size(metric, metric); + + l->setPixmap(createDropIndicatorPixmap(l->palette(), size, DockWidgetArea)); + l->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint); + l->setAttribute(Qt::WA_TranslucentBackground); + return l; +} + + /** - * Private data class of CDropOverlay class (pimpl) + * Private data class of CDockOverlay */ struct DockOverlayPrivate { CDockOverlay* _this; + DockWidgetAreas AllowedAreas = InvalidDockWidgetArea; + CDockOverlayCross* Cross; + QPointer TargetWidget; + QRect TargetRect; + DockWidgetArea LastLocation = InvalidDockWidgetArea; /** * Private data constructor */ - DockOverlayPrivate(CDockOverlay* _public); + DockOverlayPrivate(CDockOverlay* _public) : _this(_public) {} }; -// struct DropOverlayPrivate -//============================================================================ -DockOverlayPrivate::DockOverlayPrivate(CDockOverlay* _public) : - _this(_public) +/** + * Private data of CDockOverlayCross class + */ +struct DockOverlayCrossPrivate { + CDockOverlayCross* _this; + CDockOverlay::eMode Mode = CDockOverlay::ModeDockAreaOverlay; + CDockOverlay* DockOverlay; + QHash DropIndicatorWidgets; + QGridLayout* GridLayout; + + /** + * Private data constructor + */ + DockOverlayCrossPrivate(CDockOverlayCross* _public) : _this(_public) {} + + /** + * + * @param area + * @return + */ + QPoint areaGridPosition(const DockWidgetArea area); +}; -} //============================================================================ -CDockOverlay::CDockOverlay(QWidget *parent) : +CDockOverlay::CDockOverlay(QWidget* parent, eMode Mode) : QFrame(parent), d(new DockOverlayPrivate(this)) { - setStyleSheet("ads--CDockOverlay {background: palette(highlight);}"); + d->Cross = new CDockOverlayCross(this); setWindowFlags(Qt::Tool | Qt::FramelessWindowHint); setWindowOpacity(0.2); setWindowTitle("DockOverlay"); - //setAttribute(Qt::WA_NoSystemBackground); - //setAttribute(Qt::WA_TranslucentBackground); + setAttribute(Qt::WA_NoSystemBackground); + setAttribute(Qt::WA_TranslucentBackground); - setAttribute(Qt::WA_TransparentForMouseEvents); - setWindowFlags(windowFlags() | Qt::WindowTransparentForInput); + QBoxLayout* l = new QBoxLayout(QBoxLayout::TopToBottom); + l->setSpacing(0); + setLayout(l); + + d->Cross->setupOverlayCross(Mode); + d->Cross->setVisible(false); setVisible(false); } + //============================================================================ CDockOverlay::~CDockOverlay() { @@ -79,22 +203,344 @@ CDockOverlay::~CDockOverlay() } +//============================================================================ +void CDockOverlay::setAllowedAreas(DockWidgetAreas areas) +{ + if (areas == d->AllowedAreas) + return; + d->AllowedAreas = areas; + d->Cross->reset(); +} + + +//============================================================================ +DockWidgetAreas CDockOverlay::allowedAreas() const +{ + return d->AllowedAreas; +} + + +//============================================================================ +DockWidgetArea CDockOverlay::dropAreaUnderCursor() const +{ + return d->Cross->cursorLocation(); +} + + //============================================================================ DockWidgetArea CDockOverlay::showOverlay(QWidget* target) { - std::cout << "CDockOverlay::showOverlay" << std::endl; + //std::cout << "CDockOverlay::showDockOverlay(QWidget* target)" << std::endl; + if (d->TargetWidget == target) + { + qInfo() << "_target == target"; + // Hint: We could update geometry of overlay here. + DockWidgetArea da = dropAreaUnderCursor(); + if (da != d->LastLocation) + { + qInfo() << "repaint()"; + repaint(); + d->LastLocation = da; + } + return da; + } + + d->TargetWidget = target; + d->TargetRect = QRect(); + d->LastLocation = InvalidDockWidgetArea; + + // Move it over the target. resize(target->size()); move(target->mapToGlobal(target->rect().topLeft())); - this->show(); - return NoDockWidgetArea; + show(); + return dropAreaUnderCursor(); } + +//============================================================================ +void CDockOverlay::showOverlay(QWidget* target, const QRect& targetAreaRect) +{ + qInfo() << "CDockOverlay::showDockOverlay(QWidget* target, const QRect& targetAreaRect)"; + if (d->TargetWidget == target && d->TargetRect == targetAreaRect) + { + return; + } + //hideDockOverlay(); + d->TargetWidget = target; + d->TargetRect = targetAreaRect; + d->LastLocation = InvalidDockWidgetArea; + + // Move it over the target's area. + resize(targetAreaRect.size()); + move(target->mapToGlobal(QPoint(targetAreaRect.x(), targetAreaRect.y()))); + show(); + return; +} + + //============================================================================ void CDockOverlay::hideOverlay() { - this->hide(); + qInfo() << "hideDockOverlay() _fullAreaDrop = false"; + hide(); + d->TargetWidget.clear(); + d->TargetRect = QRect(); + d->LastLocation = InvalidDockWidgetArea; } -} // namespace ads -//--------------------------------------------------------------------------- -// EOF DropOverlay.cpp + +//============================================================================ +void CDockOverlay::paintEvent(QPaintEvent*) +{ + // Draw rect based on location + QRect r = rect(); + const DockWidgetArea da = dropAreaUnderCursor(); + std::cout << "CursorLocation: " << dropAreaUnderCursor() << std::endl; + switch (da) + { + case TopDockWidgetArea: r.setHeight(r.height() / 2); break; + case RightDockWidgetArea: r.setX(r.width() / 2); break; + case BottomDockWidgetArea: r.setY(r.height() / 2); break; + case LeftDockWidgetArea: r.setWidth(r.width() / 2); break; + case CenterDockWidgetArea: r = rect();break; + default: return; + } + QPainter painter(this); + QColor Color = palette().color(QPalette::Active, QPalette::Highlight); + painter.fillRect(r, QBrush(Color, Qt::Dense4Pattern)); + painter.setBrush(QBrush(Color)); + painter.drawRect(r); +} + + +//============================================================================ +void CDockOverlay::showEvent(QShowEvent*) +{ + d->Cross->show(); + QWidget* w = parentWidget() ? parentWidget() : d->TargetWidget.data(); + QRect WidgetRect = w->rect(); + QPoint Pos(WidgetRect.left(), WidgetRect.center().y()); +} + + +//============================================================================ +void CDockOverlay::hideEvent(QHideEvent*) +{ + d->Cross->hide(); +} + + + +//============================================================================ +void CDockOverlay::resizeEvent(QResizeEvent* e) +{ + qInfo() << "CDockOverlay::resizeEvent" << e->size(); + d->Cross->resize(e->size()); +} + + +//============================================================================ +void CDockOverlay::moveEvent(QMoveEvent* e) +{ + qInfo() << "CDockOverlay::moveEvent" << e->pos(); + d->Cross->move(e->pos()); +} + + +//============================================================================ +static int areaAlignment(const DockWidgetArea area) +{ + switch (area) + { + case TopDockWidgetArea: return (int) Qt::AlignHCenter | Qt::AlignBottom; + case RightDockWidgetArea: return (int) Qt::AlignLeft | Qt::AlignVCenter; + case BottomDockWidgetArea: return (int) Qt::AlignHCenter | Qt::AlignTop; + case LeftDockWidgetArea: return (int) Qt::AlignRight | Qt::AlignVCenter; + case CenterDockWidgetArea: return (int) Qt::AlignCenter; + default: return Qt::AlignCenter; + } +} + +//============================================================================ +// DockOverlayCrossPrivate +//============================================================================ +QPoint DockOverlayCrossPrivate::areaGridPosition(const DockWidgetArea area) +{ + if (CDockOverlay::ModeDockAreaOverlay == Mode) + { + switch (area) + { + case TopDockWidgetArea: return QPoint(1, 2); + case RightDockWidgetArea: return QPoint(2, 3); + case BottomDockWidgetArea: return QPoint(3, 2); + case LeftDockWidgetArea: return QPoint(2, 1); + case CenterDockWidgetArea: return QPoint(2, 2); + default: return QPoint(); + } + } + else + { + switch (area) + { + case TopDockWidgetArea: return QPoint(0, 2); + case RightDockWidgetArea: return QPoint(2, 4); + case BottomDockWidgetArea: return QPoint(4, 2); + case LeftDockWidgetArea: return QPoint(2, 0); + case CenterDockWidgetArea: return QPoint(2, 2); + default: return QPoint(); + } + } +} + + +//============================================================================ +CDockOverlayCross::CDockOverlayCross(CDockOverlay* overlay) : + QWidget(overlay->parentWidget()), + d(new DockOverlayCrossPrivate(this)) +{ + d->DockOverlay = overlay; + setWindowFlags(Qt::Tool | Qt::FramelessWindowHint); + setWindowTitle("DockOverlayCross"); + setAttribute(Qt::WA_TranslucentBackground); + + d->GridLayout = new QGridLayout(); + d->GridLayout->setSpacing(6); + setLayout(d->GridLayout); +} + + +//============================================================================ +CDockOverlayCross::~CDockOverlayCross() +{ + delete d; +} + + +//============================================================================ +void CDockOverlayCross::setupOverlayCross(CDockOverlay::eMode Mode) +{ + d->Mode = Mode; + + QHash areaWidgets; + areaWidgets.insert(TopDockWidgetArea, createDropIndicatorWidget(TopDockWidgetArea)); + areaWidgets.insert(RightDockWidgetArea, createDropIndicatorWidget(RightDockWidgetArea)); + areaWidgets.insert(BottomDockWidgetArea, createDropIndicatorWidget(BottomDockWidgetArea)); + areaWidgets.insert(LeftDockWidgetArea, createDropIndicatorWidget(LeftDockWidgetArea)); + areaWidgets.insert(CenterDockWidgetArea, createDropIndicatorWidget(CenterDockWidgetArea)); + + setAreaWidgets(areaWidgets); +} + + +//============================================================================ +void CDockOverlayCross::setAreaWidgets(const QHash& widgets) +{ + // Delete old widgets. + QMutableHashIterator i(d->DropIndicatorWidgets); + while (i.hasNext()) + { + i.next(); + QWidget* widget = i.value(); + d->GridLayout->removeWidget(widget); + delete widget; + i.remove(); + } + + // Insert new widgets into grid. + d->DropIndicatorWidgets = widgets; + QHashIterator i2(d->DropIndicatorWidgets); + while (i2.hasNext()) + { + i2.next(); + const DockWidgetArea area = i2.key(); + QWidget* widget = i2.value(); + QPoint p = d->areaGridPosition(area); + d->GridLayout->addWidget(widget, p.x(), p.y(), (Qt::Alignment) areaAlignment(area)); + } + + if (CDockOverlay::ModeDockAreaOverlay == d->Mode) + { + d->GridLayout->setContentsMargins(0, 0, 0, 0); + d->GridLayout->setRowStretch(0, 1); + d->GridLayout->setRowStretch(1, 0); + d->GridLayout->setRowStretch(2, 0); + d->GridLayout->setRowStretch(3, 0); + d->GridLayout->setRowStretch(4, 1); + + d->GridLayout->setColumnStretch(0, 1); + d->GridLayout->setColumnStretch(1, 0); + d->GridLayout->setColumnStretch(2, 0); + d->GridLayout->setColumnStretch(3, 0); + d->GridLayout->setColumnStretch(4, 1); + } + else + { + d->GridLayout->setContentsMargins(4, 4, 4, 4); + d->GridLayout->setRowStretch(0, 0); + d->GridLayout->setRowStretch(1, 1); + d->GridLayout->setRowStretch(2, 1); + d->GridLayout->setRowStretch(3, 1); + d->GridLayout->setRowStretch(4, 0); + + d->GridLayout->setColumnStretch(0, 0); + d->GridLayout->setColumnStretch(1, 1); + d->GridLayout->setColumnStretch(2, 1); + d->GridLayout->setColumnStretch(3, 1); + d->GridLayout->setColumnStretch(4, 0); + } + reset(); +} + + +//============================================================================ +DockWidgetArea CDockOverlayCross::cursorLocation() const +{ + const QPoint pos = mapFromGlobal(QCursor::pos()); + QHashIterator i(d->DropIndicatorWidgets); + while (i.hasNext()) + { + i.next(); + if (d->DockOverlay->allowedAreas().testFlag(i.key()) + && i.value() + && i.value()->isVisible() + && i.value()->geometry().contains(pos)) + { + return i.key(); + } + } + return InvalidDockWidgetArea; +} + + +//============================================================================ +void CDockOverlayCross::showEvent(QShowEvent*) +{ + resize(d->DockOverlay->size()); + move(d->DockOverlay->pos()); +} + + +//============================================================================ +void CDockOverlayCross::reset() +{ + QList allAreas; + allAreas << TopDockWidgetArea << RightDockWidgetArea + << BottomDockWidgetArea << LeftDockWidgetArea << CenterDockWidgetArea; + const DockWidgetAreas allowedAreas = d->DockOverlay->allowedAreas(); + + // Update visibility of area widgets based on allowedAreas. + for (int i = 0; i < allAreas.count(); ++i) + { + QPoint p = d->areaGridPosition(allAreas.at(i)); + QLayoutItem* item = d->GridLayout->itemAtPosition(p.x(), p.y()); + QWidget* w = nullptr; + if (item && (w = item->widget()) != nullptr) + { + w->setVisible(allowedAreas.testFlag(allAreas.at(i))); + } + } +} + +} // namespace ads +//---------------------------------------------------------------------------- + diff --git a/AdvancedDockingSystem/src/v2/DockOverlay.h b/AdvancedDockingSystem/src/v2/DockOverlay.h index 40651ad..47c639a 100644 --- a/AdvancedDockingSystem/src/v2/DockOverlay.h +++ b/AdvancedDockingSystem/src/v2/DockOverlay.h @@ -19,58 +19,139 @@ ******************************************************************************/ -//============================================================================ -/// \file DockOverlay.h -/// \author Uwe Kindler -/// \date 01.03.2017 -/// \brief Declaration of CDockOverlay class -//============================================================================ - - //============================================================================ // INCLUDES //============================================================================ +#include +#include +#include #include +class QGridLayout; #include "ads_globals.h" namespace ads { struct DockOverlayPrivate; +class CDockOverlayCross; -/** +/*! * DockOverlay paints a translucent rectangle over another widget. The geometry - * of the rectangle is based on drop area at the mouse location. + * of the rectangle is based on the mouse location. */ class CDockOverlay : public QFrame { Q_OBJECT private: - DockOverlayPrivate* d; ///< private data (pimpl) + DockOverlayPrivate* d; //< private data class friend class DockOverlayPrivate; -protected: + friend class DockOverlayCross; + public: - /** - * Default Constructor - */ - CDockOverlay(QWidget* parent = 0); + enum eMode + { + ModeDockAreaOverlay, + ModeContainerOverlay + }; /** - * Virtual Destructor + * Creates a dock overlay + */ + CDockOverlay(QWidget* parent, eMode Mode = ModeDockAreaOverlay); + + /** + * Virtual destructor */ virtual ~CDockOverlay(); /** - * Shows the dock overlay for the given target widget + * Configures the areas that are allowed for docking + */ + void setAllowedAreas(DockWidgetAreas areas); + + /** + * Returns flags with all allowed drop areas + */ + DockWidgetAreas allowedAreas() const; + + /** + * Returns the drop area under the current cursor location + */ + DockWidgetArea dropAreaUnderCursor() const; + + /** + * Show the drop overly for the given target widget */ DockWidgetArea showOverlay(QWidget* target); /** - * Hides this verlay + * Show drop overlay for the given target widget and the given rectangle + */ + void showOverlay(QWidget* target, const QRect& targetAreaRect); + + /** + * Hides the overlay */ void hideOverlay(); -}; // class DockOverlay -} - // namespace ads -//----------------------------------------------------------------------------- + +protected: + virtual void paintEvent(QPaintEvent *e) override; + virtual void showEvent(QShowEvent* e) override; + virtual void hideEvent(QHideEvent* e) override; + virtual void resizeEvent(QResizeEvent* e) override; + virtual void moveEvent(QMoveEvent* e) override; +}; + + +struct DockOverlayCrossPrivate; +/*! + * DockOverlayCross shows a cross with 5 different drop area possibilities. + * I could have handled everything inside DockOverlay, but because of some + * styling issues it's better to have a separate class for the cross. + */ +class CDockOverlayCross : public QWidget +{ + Q_OBJECT +private: + DockOverlayCrossPrivate* d; + friend class DockOverlayCrossPrivate; + friend class CDockOverlay; + +public: + /** + * Creates an overlay corss for the given overlay + */ + CDockOverlayCross(CDockOverlay* overlay); + + /** + * Virtual destructor + */ + virtual ~CDockOverlayCross(); + + /** + * Returns the dock widget area depending on the current cursor location. + * The function checks, if the mouse cursor is inside of any drop indicator + * widget and returns the corresponding DockWidgetArea. + */ + DockWidgetArea cursorLocation() const; + + /** + * Sets up the overlay cross for the given overlay mode + */ + void setupOverlayCross(CDockOverlay::eMode Mode); + + /** + * Resets and updates the + */ + void reset(); + +protected: + virtual void showEvent(QShowEvent* e) override; + void setAreaWidgets(const QHash& widgets); + +private: + +}; // CDockOverlayCross + +} // namespace ads #endif // DockOverlayH diff --git a/AdvancedDockingSystem/src/v2/DockWidgetTitleBar.cpp b/AdvancedDockingSystem/src/v2/DockWidgetTitleBar.cpp index 2873121..275ddec 100644 --- a/AdvancedDockingSystem/src/v2/DockWidgetTitleBar.cpp +++ b/AdvancedDockingSystem/src/v2/DockWidgetTitleBar.cpp @@ -43,6 +43,8 @@ #include "DockWidget.h" #include "DockAreaWidget.h" #include "FloatingDockContainer.h" +#include "DockOverlay.h" +#include "DockManager.h" namespace ads { @@ -107,8 +109,10 @@ struct DockWidgetTitleBarPrivate /** * Starts floating of the dock widget that belongs to this title bar + * Returns true, if floating has been started and false if floating + * is not possible for any reason */ - void startFloating(const QPoint& GlobalPos); + bool startFloating(const QPoint& GlobalPos); }; // struct DockWidgetTitleBarPrivate @@ -153,8 +157,21 @@ void DockWidgetTitleBarPrivate::moveTab(QMouseEvent* ev) //============================================================================ -void DockWidgetTitleBarPrivate::startFloating(const QPoint& GlobalPos) +bool DockWidgetTitleBarPrivate::startFloating(const QPoint& GlobalPos) { + std::cout << "isFloating " << DockWidget->dockContainer()->isFloating() << std::endl; + std::cout << "areaCount " << DockWidget->dockContainer()->dockAreaCount() << std::endl; + std::cout << "widgetCount " << DockWidget->dockAreaWidget()->count() << std::endl; + // if this is the last dock widget inside of this floating widget, + // then it does not make any sense, to make if floating because + // it is already floating + if (DockWidget->dockContainer()->isFloating() + && (DockWidget->dockContainer()->dockAreaCount() == 1) + && (DockWidget->dockAreaWidget()->count() == 1)) + { + return false; + } + std::cout << "startFloating" << std::endl; DragState = DraggingFloatingWidget; QSize Size = DockArea->size(); @@ -166,6 +183,7 @@ void DockWidgetTitleBarPrivate::startFloating(const QPoint& GlobalPos) } else { + std::cout << "DockWidgetTitleBarPrivate::startFloating DockArea" << std::endl; // If section widget has only one content widget, we can move the complete // section widget into floating widget auto splitter = internal::findParent(DockArea); @@ -173,13 +191,9 @@ void DockWidgetTitleBarPrivate::startFloating(const QPoint& GlobalPos) } FloatingWidget->startFloating(DragStartMousePosition, Size); - - /* - * DropOverlay* ContainerDropOverlay = cw->dropOverlay(); - ContainerDropOverlay->setAllowedAreas(OuterAreas); - ContainerDropOverlay->showDropOverlay(this); - ContainerDropOverlay->raise(); - */ + auto Overlay = DockWidget->dockManager()->containerOverlay(); + Overlay->setAllowedAreas(OuterDockAreas); + return true; } @@ -188,6 +202,7 @@ CDockWidgetTitleBar::CDockWidgetTitleBar(CDockWidget* DockWidget, QWidget *paren QFrame(parent), d(new DockWidgetTitleBarPrivate(this)) { + setAttribute(Qt::WA_NoMousePropagation, true); d->DockWidget = DockWidget; d->createLayout(); } @@ -271,9 +286,12 @@ void CDockWidgetTitleBar::mouseMoveEvent(QMouseEvent* ev) if (!MouseInsideTitleArea) { d->startFloating(ev->globalPos()); - return; + // do not delegate handling of mouse move event base class to prevent + // move events for the title area + return; } - else if ((ev->pos() - d->DragStartMousePosition).manhattanLength() >= QApplication::startDragDistance()) // Wait a few pixels before start moving + else if (d->DockArea->count() > 1 + && (ev->pos() - d->DragStartMousePosition).manhattanLength() >= QApplication::startDragDistance()) // Wait a few pixels before start moving { d->DragState = DraggingTab; return; diff --git a/AdvancedDockingSystem/src/v2/FloatingDockContainer.cpp b/AdvancedDockingSystem/src/v2/FloatingDockContainer.cpp index d1c462e..2d7d476 100644 --- a/AdvancedDockingSystem/src/v2/FloatingDockContainer.cpp +++ b/AdvancedDockingSystem/src/v2/FloatingDockContainer.cpp @@ -84,16 +84,16 @@ FloatingDockContainerPrivate::FloatingDockContainerPrivate(CFloatingDockContaine void FloatingDockContainerPrivate::titleMouseReleaseEvent() { setDraggingActive(false); - /*if (!m_DropContainer) + if (!DropContainer) { return; } std::cout << "Dropped" << std::endl; - CMainContainerWidget* MainContainerWidget = mainContainerWidget(); - m_DropContainer->dropFloatingWidget(this, QCursor::pos()); - MainContainerWidget->dropOverlay()->hideDropOverlay(); - MainContainerWidget->sectionDropOverlay()->hideDropOverlay();*/ + /*CMainContainerWidget* MainContainerWidget = mainContainerWidget(); + m_DropContainer->dropFloatingWidget(this, QCursor::pos());*/ + DockManager->containerOverlay()->hideOverlay(); + DockManager->dockAreaOverlay()->hideOverlay(); } @@ -123,7 +123,6 @@ void FloatingDockContainerPrivate::updateDropOverlays(const QPoint& GlobalPos) QPoint MappedPos = ContainerWidget->mapFromGlobal(GlobalPos); if (ContainerWidget->rect().contains(MappedPos)) { - std::cout << "Container " << ContainerWidget << " contains mousepos" << std::endl; if (!TopContainer || ContainerWidget->isInFrontOf(TopContainer)) { TopContainer = ContainerWidget; @@ -148,7 +147,7 @@ void FloatingDockContainerPrivate::updateDropOverlays(const QPoint& GlobalPos) auto DockArea = TopContainer->dockAreaAt(GlobalPos); if (DockArea) { - //SectionOverlay->setAllowedAreas(AllAreas); + DockAreaOverlay->setAllowedAreas(AllDockAreas); DockAreaOverlay->showOverlay(DockArea); } else @@ -172,22 +171,7 @@ void FloatingDockContainerPrivate::updateDropOverlays(const QPoint& GlobalPos) //============================================================================ void FloatingDockContainerPrivate::setDraggingActive(bool Active) { - if (DraggingActive == Active) - { - return; - } - DraggingActive = Active; - if (Active) - { - std::cout << "FloatingWidget:: InstallEventFilter" << std::endl; - qApp->installEventFilter(_this); - } - else - { - std::cout << "FloatingWidget:: RemoveEventFilter" << std::endl; - qApp->removeEventFilter(_this); - } } @@ -205,6 +189,8 @@ CFloatingDockContainer::CFloatingDockContainer(CDockManager* DockManager) : d->DockContainer = new CDockContainerWidget(DockManager, this); l->addWidget(d->DockContainer); DockManager->registerFloatingWidget(this); + + qApp->installEventFilter(this); } diff --git a/AdvancedDockingSystem/src/v2/ads_globals.h b/AdvancedDockingSystem/src/v2/ads_globals.h index d9b8183..a1aea46 100644 --- a/AdvancedDockingSystem/src/v2/ads_globals.h +++ b/AdvancedDockingSystem/src/v2/ads_globals.h @@ -45,6 +45,7 @@ enum DockWidgetArea BottomDockWidgetArea = 0x08, CenterDockWidgetArea = 0x10, + InvalidDockWidgetArea = NoDockWidgetArea, OuterDockAreas = TopDockWidgetArea | LeftDockWidgetArea | RightDockWidgetArea | BottomDockWidgetArea, AllDockAreas = OuterDockAreas | CenterDockWidgetArea };