diff --git a/examples/dockwidgets/MyMainWindow.cpp b/examples/dockwidgets/MyMainWindow.cpp index f1775acc..0a1caf8d 100644 --- a/examples/dockwidgets/MyMainWindow.cpp +++ b/examples/dockwidgets/MyMainWindow.cpp @@ -49,10 +49,11 @@ static MyWidget *newMyWidget() } MyMainWindow::MyMainWindow(const QString &uniqueName, KDDockWidgets::MainWindowOptions options, - bool dockWidget0IsNonClosable, bool restoreIsRelative, + bool dockWidget0IsNonClosable, bool nonDockableDockWidget9, bool restoreIsRelative, const QString &affinityName, QWidget *parent) : MainWindow(uniqueName, options, parent) , m_dockWidget0IsNonClosable(dockWidget0IsNonClosable) + , m_dockWidget9IsNonDockable(nonDockableDockWidget9) , m_restoreIsRelative(restoreIsRelative) { // qApp->installEventFilter(this); @@ -112,8 +113,11 @@ void MyMainWindow::createDockWidgets() { Q_ASSERT(m_dockwidgets.isEmpty()); + const int numDockWidgets = m_dockWidget9IsNonDockable ? 10 : 9; + + // Create 9 KDDockWidget::DockWidget and the respective widgets they're hosting (MyWidget instances) - for (int i = 0; i < 9; i++) + for (int i = 0; i < numDockWidgets; i++) m_dockwidgets << newDockWidget(); @@ -150,6 +154,9 @@ KDDockWidgets::DockWidgetBase *MyMainWindow::newDockWidget() if (count == 0 && m_dockWidget0IsNonClosable) options |= KDDockWidgets::DockWidget::Option_NotClosable; + if (count == 9 && m_dockWidget9IsNonDockable) + options |= KDDockWidgets::DockWidget::Option_NotDockable; + auto dock = new KDDockWidgets::DockWidget(QStringLiteral("DockWidget #%1").arg(count), options); dock->setAffinityName(affinityName()); // optional, just to show the feature. Pass -mi to the example to see incompatible dock widgets @@ -157,7 +164,14 @@ KDDockWidgets::DockWidgetBase *MyMainWindow::newDockWidget() dock->setIcon(QIcon::fromTheme(QStringLiteral("mail-message"))); auto myWidget = newMyWidget(); dock->setWidget(myWidget); - dock->setTitle(QStringLiteral("DockWidget #%1").arg(count)); + + + if (dock->options() & KDDockWidgets::DockWidget::Option_NotDockable) { + dock->setTitle(QStringLiteral("DockWidget #%1 (%2)").arg(count).arg("non dockable")); + } else { + dock->setTitle(QStringLiteral("DockWidget #%1").arg(count)); + } + dock->resize(600, 600); dock->show(); m_toggleMenu->addAction(dock->toggleAction()); diff --git a/examples/dockwidgets/MyMainWindow.h b/examples/dockwidgets/MyMainWindow.h index 94559c06..ec072c45 100644 --- a/examples/dockwidgets/MyMainWindow.h +++ b/examples/dockwidgets/MyMainWindow.h @@ -28,7 +28,7 @@ class MyMainWindow : public KDDockWidgets::MainWindow Q_OBJECT public: explicit MyMainWindow(const QString &uniqueName, KDDockWidgets::MainWindowOptions options, - bool dockWidget0IsNonClosable, bool restoreIsRelative, + bool dockWidget0IsNonClosable, bool nonDockableDockWidget9, bool restoreIsRelative, const QString &affinityName = {}, // Usually not needed. Just here to show the feature. QWidget *parent = nullptr); @@ -37,6 +37,7 @@ private: KDDockWidgets::DockWidgetBase* newDockWidget(); QMenu *m_toggleMenu = nullptr; const bool m_dockWidget0IsNonClosable; + const bool m_dockWidget9IsNonDockable; const bool m_restoreIsRelative; KDDockWidgets::DockWidget::List m_dockwidgets; }; diff --git a/examples/dockwidgets/main.cpp b/examples/dockwidgets/main.cpp index 3b015fa1..ffbc406b 100644 --- a/examples/dockwidgets/main.cpp +++ b/examples/dockwidgets/main.cpp @@ -76,6 +76,9 @@ int main(int argc, char **argv) QCommandLineOption doubleClickMaximize("x", QCoreApplication::translate("main", "Double clicking a title bar will maximize a floating window")); parser.addOption(doubleClickMaximize); + QCommandLineOption nonDockable("d", QCoreApplication::translate("main", "DockWidget #9 will be non-dockable")); + parser.addOption(nonDockable); + #if defined(DOCKS_DEVELOPER_MODE) QCommandLineOption noCentralFrame("c", QCoreApplication::translate("main", "No central frame")); parser.addOption(noCentralFrame); @@ -122,8 +125,9 @@ int main(int argc, char **argv) const bool nonClosableDockWidget0 = parser.isSet(nonClosableDockWidget); const bool restoreIsRelative = parser.isSet(relativeRestore); + const bool nonDockableDockWidget9 = parser.isSet(nonDockable); - MyMainWindow mainWindow(QStringLiteral("MyMainWindow"), options, nonClosableDockWidget0, restoreIsRelative); + MyMainWindow mainWindow(QStringLiteral("MyMainWindow"), options, nonClosableDockWidget0, nonDockableDockWidget9, restoreIsRelative); mainWindow.setWindowTitle("Main Window 1"); mainWindow.resize(1200, 1200); mainWindow.show(); @@ -135,7 +139,7 @@ int main(int argc, char **argv) : QString(); auto mainWindow2 = new MyMainWindow(QStringLiteral("MyMainWindow-2"), options, - nonClosableDockWidget0, restoreIsRelative, affinity); + nonClosableDockWidget0, nonDockableDockWidget9, restoreIsRelative, affinity); if (affinity.isEmpty()) mainWindow2->setWindowTitle("Main Window 2"); else diff --git a/src/DockWidgetBase.cpp b/src/DockWidgetBase.cpp index c71089cc..40a754c8 100644 --- a/src/DockWidgetBase.cpp +++ b/src/DockWidgetBase.cpp @@ -150,6 +150,12 @@ void DockWidgetBase::addDockWidgetAsTab(DockWidgetBase *other, AddingOption addi return; } + if ((other->options() & DockWidgetBase::Option_NotDockable) || + (options() & DockWidgetBase::Option_NotDockable)) { + qWarning() << Q_FUNC_INFO << "Refusing to dock non-dockable widget" << other; + return; + } + Frame *frame = this->frame(); if (frame) { @@ -184,6 +190,12 @@ void DockWidgetBase::addDockWidgetToContainingWindow(DockWidgetBase *other, Loca return; } + if ((other->options() & DockWidgetBase::Option_NotDockable) || + (options() & DockWidgetBase::Option_NotDockable)) { + qWarning() << Q_FUNC_INFO << "Refusing to dock non-dockable widget" << other; + return; + } + if (isWindow()) morphIntoFloatingWindow(); @@ -283,6 +295,11 @@ DockWidgetBase::Options DockWidgetBase::options() const void DockWidgetBase::setOptions(Options options) { + if ((d->options & Option_NotDockable) != (options & Option_NotDockable)) { + qWarning() << Q_FUNC_INFO << "Option_NotDockable not allowed to change. Pass via ctor only."; + return; + } + if (options != d->options) { d->options = options; Q_EMIT optionsChanged(options); diff --git a/src/DockWidgetBase.h b/src/DockWidgetBase.h index c401fad5..1442bfec 100644 --- a/src/DockWidgetBase.h +++ b/src/DockWidgetBase.h @@ -69,7 +69,8 @@ public: ///@brief DockWidget options to pass at construction time enum Option { Option_None = 0, ///< No option, the default - Option_NotClosable = 1 /// The DockWidget can't be closed on the [x], only programatically + Option_NotClosable = 1, /// The DockWidget can't be closed on the [x], only programatically + Option_NotDockable = 2 ///< The DockWidget can't be docked, it's always floating }; Q_DECLARE_FLAGS(Options, Option) @@ -180,7 +181,10 @@ public: Options options() const; /** - * @brief Setter for the options + * @brief Setter for the options. + * Only Option_NotClosable is allowed to change after construction. For the other options use + * the constructor only. + * * @sa options(), optionsChanged() */ void setOptions(Options); diff --git a/src/MainWindowBase.cpp b/src/MainWindowBase.cpp index 409202de..703ad0d3 100644 --- a/src/MainWindowBase.cpp +++ b/src/MainWindowBase.cpp @@ -81,6 +81,11 @@ void MainWindowBase::addDockWidgetAsTab(DockWidgetBase *widget) return; } + if (widget->options() & DockWidgetBase::Option_NotDockable) { + qWarning() << Q_FUNC_INFO << "Refusing to dock non-dockable widget" << widget; + return; + } + if (d->supportsCentralFrame()) { dropArea()->m_centralFrame->addWidget(widget); } else { @@ -90,6 +95,11 @@ void MainWindowBase::addDockWidgetAsTab(DockWidgetBase *widget) void MainWindowBase::addDockWidget(DockWidgetBase *dw, Location location, DockWidgetBase *relativeTo, AddingOption option) { + if (dw->options() & DockWidgetBase::Option_NotDockable) { + qWarning() << Q_FUNC_INFO << "Refusing to dock non-dockable widget" << dw; + return; + } + dropArea()->addDockWidget(dw, location, relativeTo, option); } diff --git a/src/private/DragController.cpp b/src/private/DragController.cpp index 12318cff..c6902593 100644 --- a/src/private/DragController.cpp +++ b/src/private/DragController.cpp @@ -197,6 +197,12 @@ bool StateDragging::handleMouseButtonRelease(QPoint globalPos) return true; } + if (floatingWindow->anyNonDockable()) { + qCDebug(state) << "StateDragging: Ignoring floating window with non dockable widgets"; + Q_EMIT q->dragCanceled(); + return true; + } + if (q->m_currentDropArea) { if (q->m_currentDropArea->drop(floatingWindow, globalPos)) { Q_EMIT q->dropped(); @@ -213,21 +219,36 @@ bool StateDragging::handleMouseButtonRelease(QPoint globalPos) bool StateDragging::handleMouseMove(QPoint globalPos) { - if (!q->m_windowBeingDragged->floatingWindow()) { + FloatingWindow *fw = q->m_windowBeingDragged->floatingWindow(); + if (!fw) { qCDebug(state) << "Canceling drag, window was deleted"; Q_EMIT q->dragCanceled(); return true; } if (!q->m_nonClientDrag) - q->m_windowBeingDragged->floatingWindow()->windowHandle()->setPosition(globalPos - q->m_offset); + fw->windowHandle()->setPosition(globalPos - q->m_offset); + + + if (fw->anyNonDockable()) { + qCDebug(state) << "StateDragging: Ignoring non dockable floating window"; + return true; + } DropArea *dropArea = q->dropAreaUnderCursor(); if (q->m_currentDropArea && dropArea != q->m_currentDropArea) q->m_currentDropArea->removeHover(); - if (dropArea) - dropArea->hover(q->m_windowBeingDragged->floatingWindow(), globalPos); + if (dropArea) { + if (FloatingWindow *targetFw = dropArea->floatingWindow()) { + if (targetFw->anyNonDockable()) { + qCDebug(state) << "StateDragging: Ignoring non dockable target floating window"; + return false; + } + } + + dropArea->hover(fw, globalPos); + } q->m_currentDropArea = dropArea; diff --git a/src/private/FloatingWindow.cpp b/src/private/FloatingWindow.cpp index 74e055ca..8ed20490 100644 --- a/src/private/FloatingWindow.cpp +++ b/src/private/FloatingWindow.cpp @@ -260,6 +260,15 @@ bool FloatingWindow::anyNonClosable() const return false; } +bool FloatingWindow::anyNonDockable() const +{ + for (Frame *frame : frames()) { + if (frame->anyNonDockable()) + return true; + } + return false; +} + bool FloatingWindow::hasSingleFrame() const { return frames().size() == 1; diff --git a/src/private/FloatingWindow_p.h b/src/private/FloatingWindow_p.h index 6f555758..2afce0df 100644 --- a/src/private/FloatingWindow_p.h +++ b/src/private/FloatingWindow_p.h @@ -63,6 +63,7 @@ public: TitleBar *titleBar() const { return m_titleBar; } bool anyNonClosable() const; + bool anyNonDockable() const; /** * @brief checks if this FloatingWindow only has one frame. diff --git a/src/private/Frame.cpp b/src/private/Frame.cpp index 7255c045..6c3f0534 100644 --- a/src/private/Frame.cpp +++ b/src/private/Frame.cpp @@ -333,6 +333,16 @@ bool Frame::anyNonClosable() const return false; } +bool Frame::anyNonDockable() const +{ + for (auto dw : dockWidgets()) { + if (dw->options() & DockWidgetBase::Option_NotDockable) + return true; + } + + return false; +} + void Frame::onDockWidgetShown(DockWidgetBase *w) { if (hasSingleDockWidget() && contains(w)) { // We have to call contains because it might be being in process of being reparented diff --git a/src/private/Frame_p.h b/src/private/Frame_p.h index 5f7f60aa..74a9d83d 100644 --- a/src/private/Frame_p.h +++ b/src/private/Frame_p.h @@ -164,7 +164,7 @@ public: FrameOptions options() const { return m_options; } bool anyNonClosable() const; - + bool anyNonDockable() const; ///@brief returns whether there's 0 dock widgets. If not persistent then the Frame will delete itself. bool isEmpty() const { return dockWidgetCount() == 0; }