diff --git a/examples/dockwidgets/MyMainWindow.cpp b/examples/dockwidgets/MyMainWindow.cpp index 9dfefd06..2c42f70e 100644 --- a/examples/dockwidgets/MyMainWindow.cpp +++ b/examples/dockwidgets/MyMainWindow.cpp @@ -29,6 +29,11 @@ #include #include +#if defined(DOCKS_DEVELOPER_MODE) +// Block just here for my own debugging +# include "../../src/private/MDILayoutWidget_p.h" +#endif + // clazy:excludeall=qstring-allocations,ctor-missing-parent-argument,detaching-member static MyWidget *newMyWidget() @@ -134,6 +139,19 @@ void MyMainWindow::createDockWidgets() for (int i = 0; i < numDockWidgets; i++) m_dockwidgets << newDockWidget(); +#if defined(DOCKS_DEVELOPER_MODE) + // This block is just for my testing + if (isMDI()) { + auto mdiArea = qobject_cast(layoutWidget()); + QPoint pt(10, 10); + for (KDDockWidgets::DockWidgetBase *dw : qAsConst(m_dockwidgets)) { + mdiArea->addDockWidget(dw, pt); + pt += QPoint(40, 40); + } + + return; + } +#endif // MainWindow::addDockWidget() attaches a dock widget to the main window: addDockWidget(m_dockwidgets.at(0), KDDockWidgets::Location_OnTop); diff --git a/examples/dockwidgets/main.cpp b/examples/dockwidgets/main.cpp index 43399899..ae74d7a5 100644 --- a/examples/dockwidgets/main.cpp +++ b/examples/dockwidgets/main.cpp @@ -126,11 +126,13 @@ int main(int argc, char **argv) QCommandLineOption noParentForFloating("no-parent-for-floating", QCoreApplication::translate("main", "(internal) FloatingWindows won't have a parent")); QCommandLineOption nativeTitleBar("native-title-bar", QCoreApplication::translate("main", "(internal) FloatingWindows a native title bar")); QCommandLineOption noDropIndicators("no-drop-indicators", QCoreApplication::translate("main", "(internal) Don't use any drop indicators")); + QCommandLineOption mdiLayout("mdi-layout", QCoreApplication::translate("main", "Main Window will use an MDI layout instead")); // TODO: Expose once stable parser.addOption(noQtTool); parser.addOption(noParentForFloating); parser.addOption(nativeTitleBar); parser.addOption(noDropIndicators); + parser.addOption(mdiLayout); # if defined(Q_OS_WIN) QCommandLineOption noAeroSnap("no-aero-snap", QCoreApplication::translate("main", "(internal) Disable AeroSnap")); @@ -160,6 +162,9 @@ int main(int argc, char **argv) options = parser.isSet(centralFrame) ? MainWindowOption_HasCentralFrame : MainWindowOption_None; + if (parser.isSet(mdiLayout)) + options |= MainWindowOption_MDI; // TODO: Move outside of developer mode once stable + if (parser.isSet(noQtTool)) internalFlags |= KDDockWidgets::Config::InternalFlag_DontUseQtToolWindowsForFloatingWindows; diff --git a/src/KDDockWidgets.h b/src/KDDockWidgets.h index 328f7b27..3e1a8976 100644 --- a/src/KDDockWidgets.h +++ b/src/KDDockWidgets.h @@ -49,7 +49,8 @@ namespace KDDockWidgets enum MainWindowOption { MainWindowOption_None = 0, ///> No option set - MainWindowOption_HasCentralFrame = 1 ///> Makes the MainWindow always have a central frame, for tabbing documents + MainWindowOption_HasCentralFrame = 1, ///> Makes the MainWindow always have a central frame, for tabbing documents + MainWindowOption_MDI = 2 ///> The layout will be MDI. DockWidgets can have arbitrary positions, not restricted by any layout }; Q_DECLARE_FLAGS(MainWindowOptions, MainWindowOption) diff --git a/src/MainWindowBase.cpp b/src/MainWindowBase.cpp index 0360dec9..0cdd859b 100644 --- a/src/MainWindowBase.cpp +++ b/src/MainWindowBase.cpp @@ -19,6 +19,7 @@ #include "MainWindowBase.h" #include "DockRegistry_p.h" +#include "MDILayoutWidget_p.h" #include "DropArea_p.h" #include "Frame_p.h" #include "Utils_p.h" @@ -32,13 +33,21 @@ using namespace KDDockWidgets; +static LayoutWidget* createLayoutWidget(MainWindowBase *mainWindow, MainWindowOptions options) +{ + if (options & MainWindowOption_MDI) + return new MDILayoutWidget(mainWindow); + + return new DropAreaWithCentralFrame(mainWindow, options); +} + class MainWindowBase::Private { public: explicit Private(MainWindowBase *mainWindow, MainWindowOptions options) : m_options(options) , q(mainWindow) - , m_layoutWidget(new DropAreaWithCentralFrame(mainWindow, options)) + , m_layoutWidget(createLayoutWidget(mainWindow, options)) { } @@ -555,6 +564,11 @@ bool MainWindowBase::anySideBarIsVisible() const return false; } +bool MainWindowBase::isMDI() const +{ + return d->m_options & MainWindowOption_MDI; +} + void MainWindowBase::setUniqueName(const QString &uniqueName) { if (uniqueName.isEmpty()) diff --git a/src/MainWindowBase.h b/src/MainWindowBase.h index e63b2bd9..9eb185ee 100644 --- a/src/MainWindowBase.h +++ b/src/MainWindowBase.h @@ -179,6 +179,10 @@ public: /// @brief Returns whether any side bar is visible bool anySideBarIsVisible() const; + /// @brief Returns whether this main window is using an MDI layout. + /// In other words, returns true if MainWindowOption_MDI was passed in the ctor. + bool isMDI() const; + protected: void setUniqueName(const QString &uniqueName); void onResized(QResizeEvent *); // Because QtQuick doesn't have resizeEvent() diff --git a/src/private/LayoutWidget_p.h b/src/private/LayoutWidget_p.h index 3ce8f470..f6b314ff 100644 --- a/src/private/LayoutWidget_p.h +++ b/src/private/LayoutWidget_p.h @@ -24,7 +24,7 @@ #pragma once -#include "docks_export.h" +#include "kddockwidgets/docks_export.h" #include "kddockwidgets/KDDockWidgets.h" #include "kddockwidgets/LayoutSaver.h" #include "kddockwidgets/QWidgetAdapter.h" diff --git a/src/private/MDILayoutWidget.cpp b/src/private/MDILayoutWidget.cpp index bfe3128d..14d483e8 100644 --- a/src/private/MDILayoutWidget.cpp +++ b/src/private/MDILayoutWidget.cpp @@ -11,6 +11,9 @@ #include "ItemFreeContainer_p.h" #include "MDILayoutWidget_p.h" +#include "DockWidgetBase_p.h" +#include "Config.h" +#include "FrameworkWidgetFactory.h" using namespace KDDockWidgets; @@ -24,3 +27,31 @@ MDILayoutWidget::MDILayoutWidget(QWidgetOrQuick *parent) MDILayoutWidget::~MDILayoutWidget() { } + +void MDILayoutWidget::addDockWidget(DockWidgetBase *dw, QPoint localPt) +{ + if (!dw) { + qWarning() << Q_FUNC_INFO << "Refusing to add null dock widget"; + return; + } + + auto frame = qobject_cast(dw->d->frame()); + if (itemForFrame(frame) != nullptr) { + // Item already exists, remove it. See also comment in MultiSplitter::addWidget(). + frame->QWidgetAdapter::setParent(nullptr); + frame->setLayoutItem(nullptr); + } + + Layouting::Item *newItem = new Layouting::Item(this); + if (frame) { + newItem->setGuestWidget(frame); + } else { + frame = Config::self().frameworkWidgetFactory()->createFrame(); + newItem->setGuestWidget(frame); + frame->addWidget(dw); + } + + Q_ASSERT(!newItem->geometry().isEmpty()); + m_rootItem->addDockWidget(newItem, localPt); + +} diff --git a/src/private/MDILayoutWidget_p.h b/src/private/MDILayoutWidget_p.h index 09c39e21..be989f4d 100644 --- a/src/private/MDILayoutWidget_p.h +++ b/src/private/MDILayoutWidget_p.h @@ -35,6 +35,9 @@ public: explicit MDILayoutWidget(QWidgetOrQuick *parent = nullptr); ~MDILayoutWidget() override; + /// @brief docks the dock widgets into this MDI area, at the specified position + void addDockWidget(DockWidgetBase *dw, QPoint localPt); + private: Layouting::ItemFreeContainer *const m_rootItem; }; diff --git a/src/private/MultiSplitter.cpp b/src/private/MultiSplitter.cpp index d0eff794..24a5f52d 100644 --- a/src/private/MultiSplitter.cpp +++ b/src/private/MultiSplitter.cpp @@ -113,14 +113,6 @@ void MultiSplitter::addWidget(QWidgetOrQuick *w, Location location, InitialOption option) { auto frame = qobject_cast(w); - qCDebug(addwidget) << Q_FUNC_INFO << w - << "; location=" << locationStr(location) - << "; relativeTo=" << relativeToWidget - << "; size=" << size() - << "; w.size=" << w->size() - << "; frame=" << frame - << "; options=" << option; - if (itemForFrame(frame) != nullptr) { // Item already exists, remove it. // Changing the frame parent will make the item clean itself up. It turns into a placeholder and is removed by unrefOldPlaceholders diff --git a/src/private/multisplitter/ItemFreeContainer.cpp b/src/private/multisplitter/ItemFreeContainer.cpp index 4a4a1bd3..42147719 100644 --- a/src/private/multisplitter/ItemFreeContainer.cpp +++ b/src/private/multisplitter/ItemFreeContainer.cpp @@ -27,6 +27,28 @@ ItemFreeContainer::~ItemFreeContainer() { } +void ItemFreeContainer::addDockWidget(Item *item, QPoint localPt) +{ + Q_ASSERT(item != this); + if (contains(item)) { + qWarning() << Q_FUNC_INFO << "Item already exists"; + return; + } + item->setIsVisible(true); // TODO: Use OptionStartHidden here too + + m_children.append(item); + item->setParentContainer(this); + item->setPos(localPt); + + Q_EMIT itemsChanged(); + + if (item->isVisible()) + Q_EMIT numVisibleItemsChanged(numVisibleChildren()); + + Q_EMIT numItemsChanged(); + +} + void ItemFreeContainer::clear() { qWarning() << Q_FUNC_INFO << "Implement me"; diff --git a/src/private/multisplitter/ItemFreeContainer_p.h b/src/private/multisplitter/ItemFreeContainer_p.h index 01457fd1..649136a7 100644 --- a/src/private/multisplitter/ItemFreeContainer_p.h +++ b/src/private/multisplitter/ItemFreeContainer_p.h @@ -34,6 +34,9 @@ public: explicit ItemFreeContainer(Widget *hostWidget); ~ItemFreeContainer(); + /// @brief adds the item to the specifed position + void addDockWidget(Item *item, QPoint localPt); + void clear() override; void removeItem(Item *, bool hardRemove = true) override; void restore(Item *child) override; diff --git a/src/private/multisplitter/Item_p.h b/src/private/multisplitter/Item_p.h index 2c1b786e..2bbd5d02 100644 --- a/src/private/multisplitter/Item_p.h +++ b/src/private/multisplitter/Item_p.h @@ -347,6 +347,7 @@ private Q_SLOTS: private: friend class ItemContainer; friend class ItemBoxContainer; + friend class ItemFreeContainer; void turnIntoPlaceholder(); bool eventFilter(QObject *o, QEvent *event) override; int m_refCount = 0;