diff --git a/examples/qtquick/customtabbar/MyTabBar.qml b/examples/qtquick/customtabbar/MyTabBar.qml index 58b7abea..c13e40f8 100644 --- a/examples/qtquick/customtabbar/MyTabBar.qml +++ b/examples/qtquick/customtabbar/MyTabBar.qml @@ -51,7 +51,7 @@ KDDW.TabBarBase { /// ##HERE## The list of tabs is stored in a C++ model. This repeater populates our tab bar Repeater { - model: root.groupCpp ? root.groupCpp.tabWidget.dockWidgetModel : 0 + model: root.groupCpp ? root.groupCpp.tabBar.dockWidgetModel : 0 Rectangle { id: tab height: parent.height diff --git a/src/qtquick/views/Stack_qtquick.cpp b/src/qtquick/views/Stack_qtquick.cpp index af1ec4bd..276125f6 100644 --- a/src/qtquick/views/Stack_qtquick.cpp +++ b/src/qtquick/views/Stack_qtquick.cpp @@ -15,27 +15,20 @@ #include "kddockwidgets/controllers/Stack.h" #include "kddockwidgets/controllers/Group.h" -#include "kddockwidgets/controllers/DockWidget_p.h" #include "kddockwidgets/controllers/TabBar.h" -#include -#include - using namespace KDDockWidgets; using namespace KDDockWidgets::Views; Stack_qtquick::Stack_qtquick(Controllers::Stack *controller, QQuickItem *parent) : View_qtquick(controller, Type::Stack, parent) , StackViewInterface(controller) - , m_dockWidgetModel(new DockWidgetModel(this)) { - connect(m_dockWidgetModel, &DockWidgetModel::countChanged, this, - [this] { Q_EMIT m_stack->tabBar()->countChanged(); }); } void Stack_qtquick::init() { - m_dockWidgetModel->m_tabBar = m_stack->tabBar(); + m_tabBarAutoHideChanged = m_stack->tabBarAutoHideChanged.connect([this] { Q_EMIT tabBarAutoHideChanged(); }); @@ -52,176 +45,7 @@ QObject *Stack_qtquick::tabBarViewObj() const return View_qt::asQObject(m_stack->tabBar()->view()); } -Controllers::DockWidget *Stack_qtquick::currentDockWidget() const -{ - return m_dockWidgetModel->currentDockWidget(); -} - bool Stack_qtquick::tabBarAutoHide() const { return m_stack->tabBarAutoHide(); } - -DockWidgetModel *Stack_qtquick::dockWidgetModel() const -{ - return m_dockWidgetModel; -} - -DockWidgetModel::DockWidgetModel(QObject *parent) - : QAbstractListModel(parent) -{ -} - -int DockWidgetModel::count() const -{ - return m_dockWidgets.size(); -} - -int DockWidgetModel::rowCount(const QModelIndex &parent) const -{ - return parent.isValid() ? 0 : m_dockWidgets.size(); -} - -QVariant DockWidgetModel::data(const QModelIndex &index, int role) const -{ - const int row = index.row(); - if (row < 0 || row >= m_dockWidgets.size()) - return {}; - - Controllers::DockWidget *dw = m_dockWidgets.at(row); - - switch (role) { - case Role_Title: - return dw->title(); - } - - return {}; -} - -Controllers::DockWidget *DockWidgetModel::dockWidgetAt(int index) const -{ - if (index < 0 || index >= m_dockWidgets.size()) { - // Can happen. Benign. - return nullptr; - } - - return m_dockWidgets[index]; -} - -bool DockWidgetModel::contains(Controllers::DockWidget *dw) const -{ - return m_dockWidgets.contains(dw); -} - -Controllers::DockWidget *DockWidgetModel::currentDockWidget() const -{ - return m_currentDockWidget; -} - -void DockWidgetModel::setCurrentDockWidget(Controllers::DockWidget *dw) -{ - if (m_currentDockWidget) - m_currentDockWidget->setVisible(false); - - m_currentDockWidget = dw; - setCurrentIndex(indexOf(dw)); - if (m_currentDockWidget) { - QScopedValueRollback guard(m_currentDockWidget->d->m_isSettingCurrent, true); - m_currentDockWidget->setVisible(true); - } -} - -QHash DockWidgetModel::roleNames() const -{ - return { { Role_Title, "title" } }; -} - -void DockWidgetModel::emitDataChangedFor(Controllers::DockWidget *dw) -{ - const int row = indexOf(dw); - if (row == -1) { - qWarning() << Q_FUNC_INFO << "Couldn't find" << dw; - } else { - QModelIndex index = this->index(row, 0); - Q_EMIT dataChanged(index, index); - } -} - -void DockWidgetModel::remove(Controllers::DockWidget *dw) -{ - QScopedValueRollback guard(m_removeGuard, true); - const int row = indexOf(dw); - - if (row == -1) { - if (!m_removeGuard) { - // can happen if there's reentrancy. Some user code reacting - // to the signals and call remove for whatever reason. - qWarning() << Q_FUNC_INFO << "Nothing to remove" - << static_cast(dw); // Print address only, as it might be deleted - // already - } - } else { - const auto connections = m_connections.take(dw); - for (const QMetaObject::Connection &conn : connections) - disconnect(conn); - - beginRemoveRows(QModelIndex(), row, row); - m_dockWidgets.removeOne(dw); - endRemoveRows(); - - Q_EMIT countChanged(); - Q_EMIT dockWidgetRemoved(); - } -} - -int DockWidgetModel::indexOf(const Controllers::DockWidget *dw) -{ - return m_dockWidgets.indexOf(const_cast(dw)); -} - -int DockWidgetModel::currentIndex() const -{ - if (!m_currentDockWidget) - return -1; - - const int index = m_dockWidgets.indexOf(m_currentDockWidget); - - if (index == -1) - qWarning() << Q_FUNC_INFO << "Unexpected null index for" << m_currentDockWidget << this - << "; count=" << count(); - - return index; -} - -void DockWidgetModel::setCurrentIndex(int index) -{ - Controllers::DockWidget *dw = dockWidgetAt(index); - - if (m_currentDockWidget != dw) { - setCurrentDockWidget(dw); - m_tabBar->setCurrentIndex(index); - } -} - -bool DockWidgetModel::insert(Controllers::DockWidget *dw, int index) -{ - if (m_dockWidgets.contains(dw)) { - qWarning() << Q_FUNC_INFO << "Shouldn't happen"; - return false; - } - - QMetaObject::Connection conn = connect(dw, &Controllers::DockWidget::titleChanged, this, - [dw, this] { emitDataChangedFor(dw); }); - - QMetaObject::Connection conn2 = - connect(dw, &QObject::destroyed, this, [dw, this] { remove(dw); }); - - m_connections[dw] = { conn, conn2 }; - - beginInsertRows(QModelIndex(), index, index); - m_dockWidgets.insert(index, dw); - endInsertRows(); - - Q_EMIT countChanged(); - return true; -} diff --git a/src/qtquick/views/Stack_qtquick.h b/src/qtquick/views/Stack_qtquick.h index 6e502cd8..edfa3495 100644 --- a/src/qtquick/views/Stack_qtquick.h +++ b/src/qtquick/views/Stack_qtquick.h @@ -23,7 +23,6 @@ #include "View_qtquick.h" #include "views/StackViewInterface.h" -#include #include #include "kdbindings/signal.h" @@ -37,20 +36,13 @@ class TabBar; namespace Views { - -class DockWidgetModel; - class DOCKS_EXPORT Stack_qtquick : public View_qtquick, public StackViewInterface { Q_OBJECT - Q_PROPERTY(DockWidgetModel *dockWidgetModel READ dockWidgetModel CONSTANT) Q_PROPERTY(QObject *tabBar READ tabBarViewObj NOTIFY tabBarChanged) public: explicit Stack_qtquick(Controllers::Stack *controller, QQuickItem *parent = nullptr); - DockWidgetModel *dockWidgetModel() const; - Controllers::DockWidget *currentDockWidget() const; - /// @brief Returns the tab bar as a QObject for QML. /// As the base class is not a QObject. QObject *tabBarViewObj() const; @@ -71,52 +63,9 @@ protected: private: Q_DISABLE_COPY(Stack_qtquick) - DockWidgetModel *const m_dockWidgetModel; KDBindings::ScopedConnection m_tabBarAutoHideChanged; }; -class DockWidgetModel : public QAbstractListModel -{ - Q_OBJECT - Q_PROPERTY(int count READ count NOTIFY countChanged) -public: - enum Role { - Role_Title = Qt::UserRole - }; - - explicit DockWidgetModel(QObject *parent); - int count() const; - int rowCount(const QModelIndex &parent) const override; - QVariant data(const QModelIndex &index, int role) const override; - Controllers::DockWidget *dockWidgetAt(int index) const; - Controllers::DockWidget *currentDockWidget() const; - void setCurrentDockWidget(Controllers::DockWidget *); - void remove(Controllers::DockWidget *); - int indexOf(const Controllers::DockWidget *); - bool insert(Controllers::DockWidget *dw, int index); - bool contains(Controllers::DockWidget *dw) const; - int currentIndex() const; - void setCurrentIndex(int index); - - // TODOm4: Move to private and make *const - Controllers::TabBar *m_tabBar = nullptr; - -protected: - QHash roleNames() const override; - -Q_SIGNALS: - void countChanged(); - void dockWidgetRemoved(); - -private: - void emitDataChangedFor(Controllers::DockWidget *); - QVector m_dockWidgets; - QHash> - m_connections; // To make it easy to disconnect from lambdas - bool m_removeGuard = false; - Controllers::DockWidget *m_currentDockWidget = nullptr; -}; - } } diff --git a/src/qtquick/views/TabBar_qtquick.cpp b/src/qtquick/views/TabBar_qtquick.cpp index b60d2861..59ed40cb 100644 --- a/src/qtquick/views/TabBar_qtquick.cpp +++ b/src/qtquick/views/TabBar_qtquick.cpp @@ -19,12 +19,15 @@ #include "TabBar_qtquick.h" #include "Stack_qtquick.h" +#include "kddockwidgets/controllers/DockWidget_p.h" #include "kddockwidgets/controllers/TabBar.h" #include "kddockwidgets/controllers/Stack.h" #include "Stack_qtquick.h" #include #include +#include +#include using namespace KDDockWidgets; using namespace KDDockWidgets::Views; @@ -32,7 +35,11 @@ using namespace KDDockWidgets::Views; TabBar_qtquick::TabBar_qtquick(Controllers::TabBar *controller, QQuickItem *parent) : View_qtquick(controller, Type::TabBar, parent) , TabBarViewInterface(controller) + , m_dockWidgetModel(new DockWidgetModel(this)) { + m_dockWidgetModel->m_tabBar = controller; + connect(m_dockWidgetModel, &DockWidgetModel::countChanged, this, + [controller] { Q_EMIT controller->countChanged(); }); } void TabBar_qtquick::init() @@ -150,7 +157,7 @@ Stack_qtquick *TabBar_qtquick::stackView() const void TabBar_qtquick::setCurrentIndex(int index) { - stackView()->dockWidgetModel()->setCurrentIndex(index); + m_dockWidgetModel->setCurrentIndex(index); } void TabBar_qtquick::renameTab(int index, const QString &) @@ -167,7 +174,7 @@ void TabBar_qtquick::changeTabIcon(int index, const QIcon &) void TabBar_qtquick::removeDockWidget(Controllers::DockWidget *dw) { - stackView()->dockWidgetModel()->remove(dw); + m_dockWidgetModel->remove(dw); } void TabBar_qtquick::insertDockWidget(int index, Controllers::DockWidget *dw, const QIcon &icon, @@ -176,5 +183,169 @@ void TabBar_qtquick::insertDockWidget(int index, Controllers::DockWidget *dw, co Q_UNUSED(title); // TODO Q_UNUSED(icon); // TODO - stackView()->dockWidgetModel()->insert(dw, index); + m_dockWidgetModel->insert(dw, index); +} + +DockWidgetModel *TabBar_qtquick::dockWidgetModel() const +{ + return m_dockWidgetModel; +} + +DockWidgetModel::DockWidgetModel(QObject *parent) + : QAbstractListModel(parent) +{ +} + +int DockWidgetModel::count() const +{ + return m_dockWidgets.size(); +} + +int DockWidgetModel::rowCount(const QModelIndex &parent) const +{ + return parent.isValid() ? 0 : m_dockWidgets.size(); +} + +QVariant DockWidgetModel::data(const QModelIndex &index, int role) const +{ + const int row = index.row(); + if (row < 0 || row >= m_dockWidgets.size()) + return {}; + + Controllers::DockWidget *dw = m_dockWidgets.at(row); + + switch (role) { + case Role_Title: + return dw->title(); + } + + return {}; +} + +Controllers::DockWidget *DockWidgetModel::dockWidgetAt(int index) const +{ + if (index < 0 || index >= m_dockWidgets.size()) { + // Can happen. Benign. + return nullptr; + } + + return m_dockWidgets[index]; +} + +bool DockWidgetModel::contains(Controllers::DockWidget *dw) const +{ + return m_dockWidgets.contains(dw); +} + +Controllers::DockWidget *DockWidgetModel::currentDockWidget() const +{ + return m_currentDockWidget; +} + +void DockWidgetModel::setCurrentDockWidget(Controllers::DockWidget *dw) +{ + if (m_currentDockWidget) + m_currentDockWidget->setVisible(false); + + m_currentDockWidget = dw; + setCurrentIndex(indexOf(dw)); + if (m_currentDockWidget) { + QScopedValueRollback guard(m_currentDockWidget->d->m_isSettingCurrent, true); + m_currentDockWidget->setVisible(true); + } +} + +QHash DockWidgetModel::roleNames() const +{ + return { { Role_Title, "title" } }; +} + +void DockWidgetModel::emitDataChangedFor(Controllers::DockWidget *dw) +{ + const int row = indexOf(dw); + if (row == -1) { + qWarning() << Q_FUNC_INFO << "Couldn't find" << dw; + } else { + QModelIndex index = this->index(row, 0); + Q_EMIT dataChanged(index, index); + } +} + +void DockWidgetModel::remove(Controllers::DockWidget *dw) +{ + QScopedValueRollback guard(m_removeGuard, true); + const int row = indexOf(dw); + + if (row == -1) { + if (!m_removeGuard) { + // can happen if there's reentrancy. Some user code reacting + // to the signals and call remove for whatever reason. + qWarning() << Q_FUNC_INFO << "Nothing to remove" + << static_cast(dw); // Print address only, as it might be deleted + // already + } + } else { + const auto connections = m_connections.take(dw); + for (const QMetaObject::Connection &conn : connections) + disconnect(conn); + + beginRemoveRows(QModelIndex(), row, row); + m_dockWidgets.removeOne(dw); + endRemoveRows(); + + Q_EMIT countChanged(); + Q_EMIT dockWidgetRemoved(); + } +} + +int DockWidgetModel::indexOf(const Controllers::DockWidget *dw) +{ + return m_dockWidgets.indexOf(const_cast(dw)); +} + +int DockWidgetModel::currentIndex() const +{ + if (!m_currentDockWidget) + return -1; + + const int index = m_dockWidgets.indexOf(m_currentDockWidget); + + if (index == -1) + qWarning() << Q_FUNC_INFO << "Unexpected null index for" << m_currentDockWidget << this + << "; count=" << count(); + + return index; +} + +void DockWidgetModel::setCurrentIndex(int index) +{ + Controllers::DockWidget *dw = dockWidgetAt(index); + + if (m_currentDockWidget != dw) { + setCurrentDockWidget(dw); + m_tabBar->setCurrentIndex(index); + } +} + +bool DockWidgetModel::insert(Controllers::DockWidget *dw, int index) +{ + if (m_dockWidgets.contains(dw)) { + qWarning() << Q_FUNC_INFO << "Shouldn't happen"; + return false; + } + + QMetaObject::Connection conn = connect(dw, &Controllers::DockWidget::titleChanged, this, + [dw, this] { emitDataChangedFor(dw); }); + + QMetaObject::Connection conn2 = + connect(dw, &QObject::destroyed, this, [dw, this] { remove(dw); }); + + m_connections[dw] = { conn, conn2 }; + + beginInsertRows(QModelIndex(), index, index); + m_dockWidgets.insert(index, dw); + endInsertRows(); + + Q_EMIT countChanged(); + return true; } diff --git a/src/qtquick/views/TabBar_qtquick.h b/src/qtquick/views/TabBar_qtquick.h index e65a9556..034fa939 100644 --- a/src/qtquick/views/TabBar_qtquick.h +++ b/src/qtquick/views/TabBar_qtquick.h @@ -24,6 +24,7 @@ #include "View_qtquick.h" #include "views/TabBarViewInterface.h" +#include #include #include @@ -36,6 +37,7 @@ class TabBar; namespace KDDockWidgets::Views { class DockWidget; +class DockWidgetModel; class TabWidget; class Stack_qtquick; @@ -45,10 +47,13 @@ class DOCKS_EXPORT TabBar_qtquick : public View_qtquick, public TabBarViewInterf Q_PROPERTY(QQuickItem *tabBarQmlItem READ tabBarQmlItem WRITE setTabBarQmlItem NOTIFY tabBarQmlItemChanged) Q_PROPERTY(bool tabBarAutoHide READ tabBarAutoHide NOTIFY tabBarAutoHideChanged) + Q_PROPERTY(DockWidgetModel *dockWidgetModel READ dockWidgetModel CONSTANT) public: explicit TabBar_qtquick(Controllers::TabBar *controller, QQuickItem *parent = nullptr); - int tabAt(QPoint localPos) const override; + DockWidgetModel *dockWidgetModel() const; + + int tabAt(QPoint localPos) const override; QQuickItem *tabBarQmlItem() const; void setTabBarQmlItem(QQuickItem *); @@ -78,9 +83,53 @@ protected: private: QQuickItem *tabAt(int index) const; + DockWidgetModel *const m_dockWidgetModel; QPointer m_tabBarQmlItem; KDBindings::ScopedConnection m_tabBarAutoHideChanged; }; + +class DockWidgetModel : public QAbstractListModel +{ + Q_OBJECT + Q_PROPERTY(int count READ count NOTIFY countChanged) +public: + enum Role { + Role_Title = Qt::UserRole + }; + + explicit DockWidgetModel(QObject *parent); + int count() const; + int rowCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role) const override; + Controllers::DockWidget *dockWidgetAt(int index) const; + Controllers::DockWidget *currentDockWidget() const; + void setCurrentDockWidget(Controllers::DockWidget *); + void remove(Controllers::DockWidget *); + int indexOf(const Controllers::DockWidget *); + bool insert(Controllers::DockWidget *dw, int index); + bool contains(Controllers::DockWidget *dw) const; + int currentIndex() const; + void setCurrentIndex(int index); + + // TODOm4: Move to private and make *const + Controllers::TabBar *m_tabBar = nullptr; + +protected: + QHash roleNames() const override; + +Q_SIGNALS: + void countChanged(); + void dockWidgetRemoved(); + +private: + void emitDataChangedFor(Controllers::DockWidget *); + QVector m_dockWidgets; + QHash> + m_connections; // To make it easy to disconnect from lambdas + bool m_removeGuard = false; + Controllers::DockWidget *m_currentDockWidget = nullptr; +}; + } #endif diff --git a/src/qtquick/views/qml/TabBar.qml b/src/qtquick/views/qml/TabBar.qml index 80955ce4..2d7082a9 100644 --- a/src/qtquick/views/qml/TabBar.qml +++ b/src/qtquick/views/qml/TabBar.qml @@ -99,7 +99,7 @@ TabBarBase { Repeater { /// The list of tabs is stored in a C++ model. This repeater populates our TabBar. - model: root.groupCpp ? root.groupCpp.tabWidget.dockWidgetModel : 0 + model: root.groupCpp ? root.groupCpp.tabBar.dockWidgetModel : 0 TabButton { readonly property int tabIndex: index text: title diff --git a/src/qtquick/views/qml/TabBarBase.qml b/src/qtquick/views/qml/TabBarBase.qml index 0aea1ad7..bcf49138 100644 --- a/src/qtquick/views/qml/TabBarBase.qml +++ b/src/qtquick/views/qml/TabBarBase.qml @@ -21,10 +21,10 @@ Item { readonly property QtObject groupCpp: parent.groupCpp /// This is our C++ TabBar_qtquick.cpp - readonly property QtObject tabBarCpp: groupCpp ? groupCpp.tabWidget.tabBar : null + readonly property QtObject tabBarCpp: groupCpp ? groupCpp.tabBar : null /// The number of tabs - readonly property int count: groupCpp ? groupCpp.tabWidget.dockWidgetModel.count : 0 + readonly property int count: tabBarCpp ? tabBarCpp.dockWidgetModel.count : 0 property int currentTabIndex: -1