diff --git a/README.md b/README.md index a0c72b1d..44a47e6d 100644 --- a/README.md +++ b/README.md @@ -202,6 +202,7 @@ Also state which KDDW sha1, branch or version you're using, and which operating KDAB will happily accept external contributions; however, **all** contributions require a signed [Copyright Assignment Agreement](docs/KDDockWidgets-CopyrightAssignmentForm.pdf). + This is needed so we can continue to dual-license it. Contact info@kdab.com for more information. diff --git a/src/DockWidgetBase.cpp b/src/DockWidgetBase.cpp index 1a74825b..cac5fa31 100644 --- a/src/DockWidgetBase.cpp +++ b/src/DockWidgetBase.cpp @@ -39,7 +39,6 @@ using namespace KDDockWidgets; - DockWidgetBase::DockWidgetBase(const QString &name, Options options, LayoutSaverOptions layoutSaverOptions) : QWidgetAdapter(nullptr, Qt::Tool) diff --git a/src/MainWindowBase.cpp b/src/MainWindowBase.cpp index af364209..1d368f84 100644 --- a/src/MainWindowBase.cpp +++ b/src/MainWindowBase.cpp @@ -489,16 +489,25 @@ void MainWindowBase::toggleOverlayOnSideBar(DockWidgetBase *dw) } } -void MainWindowBase::clearSideBarOverlay() +void MainWindowBase::clearSideBarOverlay(bool deleteFrame) { if (!d->m_overlayedDockWidget) return; Frame *frame = d->m_overlayedDockWidget->frame(); - d->m_overlayedDockWidget->setParent(nullptr); - Q_EMIT d->m_overlayedDockWidget->isOverlayedChanged(false); - d->m_overlayedDockWidget = nullptr; - delete frame; + frame->unoverlay(); + + if (deleteFrame) { + d->m_overlayedDockWidget->setParent(nullptr); + Q_EMIT d->m_overlayedDockWidget->isOverlayedChanged(false); + d->m_overlayedDockWidget = nullptr; + delete frame; + } else { + // No cleanup, just unset. When we drag the overlay it becomes a normal floating window + // meaning we reuse Frame. Don't delete it. + Q_EMIT d->m_overlayedDockWidget->isOverlayedChanged(false); + d->m_overlayedDockWidget = nullptr; + } } SideBar *MainWindowBase::sideBarForDockWidget(const DockWidgetBase *dw) const diff --git a/src/MainWindowBase.h b/src/MainWindowBase.h index c9158519..df68d17d 100644 --- a/src/MainWindowBase.h +++ b/src/MainWindowBase.h @@ -159,7 +159,7 @@ public: void toggleOverlayOnSideBar(DockWidgetBase *); /// @brief closes any overlayed dock widget. The sidebar still displays them as button. - void clearSideBarOverlay(); + void clearSideBarOverlay(bool deleteFrame = true); /// @brief Returns the sidebar this dockwidget is in. nullptr if not in any. SideBar *sideBarForDockWidget(const DockWidgetBase *) const; diff --git a/src/private/DockWidgetBase_p.h b/src/private/DockWidgetBase_p.h index 0dab8fcf..67cd5da2 100644 --- a/src/private/DockWidgetBase_p.h +++ b/src/private/DockWidgetBase_p.h @@ -53,6 +53,14 @@ public: } Q_EMIT q->isFloatingChanged(checked); + + // When floating, we remove from the sidebar + if (checked && q->isOpen()) { + if (SideBar *sb = DockRegistry::self()->sideBarForDockWidget(q)) { + sb->mainWindow()->clearSideBarOverlay(/* deleteFrame=*/false); + sb->removeDockWidget(q); + } + } }); toggleAction->setCheckable(true); diff --git a/src/private/Frame.cpp b/src/private/Frame.cpp index 02aa7f3d..0af4aa4f 100644 --- a/src/private/Frame.cpp +++ b/src/private/Frame.cpp @@ -585,6 +585,11 @@ bool Frame::isOverlayed() const return m_options & FrameOption_IsOverlayed; } +void Frame::unoverlay() +{ + m_options &= ~FrameOption_IsOverlayed; +} + bool Frame::isFloating() const { if (isInMainWindow()) diff --git a/src/private/Frame_p.h b/src/private/Frame_p.h index 78e0eda7..6e328d62 100644 --- a/src/private/Frame_p.h +++ b/src/private/Frame_p.h @@ -131,6 +131,10 @@ public: ///@brief Returns whether this frame is overlayed on top of the MainWindow (auto-hide feature); bool isOverlayed() const; + ///@brief clears the FrameOption_IsOverlayed flag. + /// For example, if you drag a side-bar overlay, then it becomes a normal floating window + void unoverlay(); + /** * @brief Returns whether this frame is floating. A floating frame isn't attached to any other MainWindow, * and if it's attached to a FloatingWindow then it's considered floating if it's the only frame in that Window. @@ -318,7 +322,7 @@ private: bool event(QEvent *) override; DropArea *m_dropArea = nullptr; - const FrameOptions m_options; + FrameOptions m_options = FrameOption_None; QPointer m_layoutItem; bool m_updatingTitleBar = false; bool m_beingDeleted = false; diff --git a/tests/tst_docks.cpp b/tests/tst_docks.cpp index 012c9223..2ef4aff0 100644 --- a/tests/tst_docks.cpp +++ b/tests/tst_docks.cpp @@ -264,6 +264,7 @@ private Q_SLOTS: void tst_toggleActionOnSideBar(); void tst_deleteOnCloseWhenOnSideBar(); void tst_sidebarOverlayGetsHiddenOnClick(); + void tst_floatRemovesFromSideBar(); // And fix these void tst_floatingWindowDeleted(); @@ -5271,6 +5272,38 @@ void TestDocks::tst_sidebarOverlayGetsHiddenOnClick() } } +void TestDocks::tst_floatRemovesFromSideBar() +{ + EnsureTopLevelsDeleted e; + KDDockWidgets::Config::self().setFlags(KDDockWidgets::Config::Flag_AutoHideSupport); + + auto m1 = createMainWindow(QSize(1000, 1000), MainWindowOption_None, "MW1"); + auto dw1 = new DockWidgetType(QStringLiteral("1")); + m1->addDockWidget(dw1, Location_OnBottom); + + m1->moveToSideBar(dw1); + m1->overlayOnSideBar(dw1); + + QVERIFY(dw1->isOverlayed()); + QVERIFY(!dw1->isFloating()); + QVERIFY(dw1->isInMainWindow()); + + dw1->setFloating(true); + QVERIFY(!dw1->isOverlayed()); + QVERIFY(dw1->isFloating()); + QVERIFY(!dw1->isInMainWindow()); + + QCOMPARE(dw1->sideBarLocation(), SideBarLocation::None); + + // Also test a crash I got + m1->addDockWidget(dw1, Location_OnBottom); + + auto tb = dw1->titleBar(); + QVERIFY(tb->isVisible()); + + tb->onFloatClicked(); +} + void TestDocks::tst_embeddedMainWindow() { EnsureTopLevelsDeleted e;