/* This file is part of KDDockWidgets. SPDX-FileCopyrightText: 2019-2022 Klarälvdalens Datakonsult AB, a KDAB Group company Author: Sérgio Martins SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only Contact KDAB at for commercial licensing options. */ #include "Stack_qtquick.h" #include "Config.h" #include "ViewFactory.h" #include "controllers/Frame.h" #include "controllers/TabBar.h" #include #include using namespace KDDockWidgets; using namespace KDDockWidgets::Views; Stack_qtquick::Stack_qtquick(Controllers::Stack *controller, Controllers::Frame *parent) : View_qtquick(controller, Type::Stack, Views::asQQuickItem(parent)) , m_dockWidgetModel(new DockWidgetModel(this)) , m_stack(controller) { connect(m_dockWidgetModel, &DockWidgetModel::countChanged, this, [this] { if (m_currentDockWidget && indexOfDockWidget(m_currentDockWidget) == -1) { // The current dock widget was removed, set the first one as current if (numDockWidgets() > 0) setCurrentDockWidget(0); } Q_EMIT m_stack->countChanged(); }); } void Stack_qtquick::init() { Q_EMIT tabBarChanged(); } Controllers::Stack *Stack_qtquick::stack() const { return m_stack; } void Stack_qtquick::setDocumentMode(bool) { qDebug() << "Not implemented"; } int Stack_qtquick::numDockWidgets() const { return m_dockWidgetModel->count(); } void Stack_qtquick::removeDockWidget(Controllers::DockWidget *dw) { m_dockWidgetModel->remove(dw); } int Stack_qtquick::indexOfDockWidget(const Controllers::DockWidget *dw) const { return m_dockWidgetModel->indexOf(dw); } bool Stack_qtquick::isPositionDraggable(QPoint p) const { Q_UNUSED(p); return true; } void Stack_qtquick::setCurrentDockWidget(int index) { Controllers::DockWidget *dw = dockwidgetAt(index); if (m_currentDockWidget != dw) { m_currentDockWidget = dw; Q_EMIT m_stack->currentDockWidgetChanged(dw); Q_EMIT m_stack->currentTabChanged(index); } } QObject *Stack_qtquick::tabBarViewObj() const { return m_stack->tabBar()->view()->asQObject(); } Controllers::DockWidget *Stack_qtquick::currentDockWidget() const { return m_currentDockWidget; } bool Stack_qtquick::insertDockWidget(int index, Controllers::DockWidget *dw, const QIcon &, const QString &title) { Q_UNUSED(title); // todo return m_dockWidgetModel->insert(dw, index); } void Stack_qtquick::setTabBarAutoHide(bool) { qWarning() << Q_FUNC_INFO << "Not implemented"; } void Stack_qtquick::renameTab(int index, const QString &) { Q_UNUSED(index); qWarning() << Q_FUNC_INFO << "Not implemented"; } void Stack_qtquick::changeTabIcon(int index, const QIcon &) { Q_UNUSED(index); qWarning() << Q_FUNC_INFO << "Not implemented"; } Controllers::DockWidget *Stack_qtquick::dockwidgetAt(int index) const { return m_dockWidgetModel->dockWidgetAt(index); } int Stack_qtquick::currentIndex() const { if (!m_currentDockWidget) return -1; const int index = indexOfDockWidget(m_currentDockWidget); if (index == -1) qWarning() << Q_FUNC_INFO << "Unexpected null index for" << m_currentDockWidget << this << "; count=" << m_dockWidgetModel->count(); return index; } 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); } 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(); } } int DockWidgetModel::indexOf(const Controllers::DockWidget *dw) { return m_dockWidgets.indexOf(const_cast(dw)); } 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; }