From c37495fae29cd93b1b03e4e3689c604cc5312a18 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Sun, 20 Oct 2019 12:39:47 +0200 Subject: [PATCH] Use a basic implementation of a DelegateChooser for compat with older Qt The interface is taken from Qt/KDE, but the implementation is different, because the Qt implementation depends on some Qt internals. --- CMakeLists.txt | 3 + resources/qml/RowDelegateChooser.qml | 52 +++++++++ resources/qml/TimelineView.qml | 46 +------- resources/res.qrc | 1 + src/timeline2/DelegateChooser.cpp | 160 ++++++++++++++++++++++++++ src/timeline2/DelegateChooser.h | 78 +++++++++++++ src/timeline2/TimelineViewManager.cpp | 4 + 7 files changed, 299 insertions(+), 45 deletions(-) create mode 100644 resources/qml/RowDelegateChooser.qml create mode 100644 src/timeline2/DelegateChooser.cpp create mode 100644 src/timeline2/DelegateChooser.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f659d91c..f6249831 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -193,6 +193,7 @@ set(SRC_FILES # Timeline src/timeline2/TimelineViewManager.cpp src/timeline2/TimelineModel.cpp + src/timeline2/DelegateChooser.cpp #src/timeline/TimelineViewManager.cpp #src/timeline/TimelineItem.cpp #src/timeline/TimelineView.cpp @@ -338,6 +339,7 @@ qt5_wrap_cpp(MOC_HEADERS # Timeline src/timeline2/TimelineViewManager.h src/timeline2/TimelineModel.h + src/timeline2/DelegateChooser.h #src/timeline/TimelineItem.h #src/timeline/TimelineView.h #src/timeline/TimelineViewManager.h @@ -410,6 +412,7 @@ set(COMMON_LIBS Qt5::Concurrent Qt5::Multimedia Qt5::Qml + Qt5::QmlPrivate Qt5::QuickControls2 nlohmann_json::nlohmann_json) diff --git a/resources/qml/RowDelegateChooser.qml b/resources/qml/RowDelegateChooser.qml new file mode 100644 index 00000000..b7b6bdf4 --- /dev/null +++ b/resources/qml/RowDelegateChooser.qml @@ -0,0 +1,52 @@ +import QtQuick 2.6 +import Qt.labs.qmlmodels 1.0 +import com.github.nheko 1.0 + +import "./delegates" + +DelegateChooser { + role: "type" + width: chat.width + roleValue: model.type + + DelegateChoice { + roleValue: MtxEvent.TextMessage + TimelineRow { view: chat; TextMessage { id: kid } } + } + DelegateChoice { + roleValue: MtxEvent.NoticeMessage + TimelineRow { view: chat; NoticeMessage { id: kid } } + } + DelegateChoice { + roleValue: MtxEvent.EmoteMessage + TimelineRow { view: chat; TextMessage { id: kid } } + } + DelegateChoice { + roleValue: MtxEvent.ImageMessage + TimelineRow { view: chat; ImageMessage { id: kid } } + } + DelegateChoice { + roleValue: MtxEvent.Sticker + TimelineRow { view: chat; ImageMessage { id: kid } } + } + DelegateChoice { + roleValue: MtxEvent.FileMessage + TimelineRow { view: chat; FileMessage { id: kid } } + } + DelegateChoice { + roleValue: MtxEvent.VideoMessage + TimelineRow { view: chat; PlayableMediaMessage { id: kid } } + } + DelegateChoice { + roleValue: MtxEvent.AudioMessage + TimelineRow { view: chat; PlayableMediaMessage { id: kid } } + } + DelegateChoice { + roleValue: MtxEvent.Redacted + TimelineRow { view: chat; Redacted { id: kid } } + } + DelegateChoice { + //roleValue: MtxEvent.Redacted + TimelineRow { view: chat; Placeholder { id: kid } } + } +} diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index d1ada3ea..e09b9ed3 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -3,7 +3,6 @@ import QtQuick.Controls 2.1 import QtQuick.Layouts 1.2 import QtGraphicalEffects 1.0 import QtQuick.Window 2.2 -import Qt.labs.qmlmodels 1.0 import com.github.nheko 1.0 @@ -91,50 +90,7 @@ Rectangle { onMovementEnded: updatePosition() spacing: 4 - delegate: DelegateChooser { - role: "type" - DelegateChoice { - roleValue: MtxEvent.TextMessage - TimelineRow { view: chat; TextMessage { id: kid } } - } - DelegateChoice { - roleValue: MtxEvent.NoticeMessage - TimelineRow { view: chat; NoticeMessage { id: kid } } - } - DelegateChoice { - roleValue: MtxEvent.EmoteMessage - TimelineRow { view: chat; TextMessage { id: kid } } - } - DelegateChoice { - roleValue: MtxEvent.ImageMessage - TimelineRow { view: chat; ImageMessage { id: kid } } - } - DelegateChoice { - roleValue: MtxEvent.Sticker - TimelineRow { view: chat; ImageMessage { id: kid } } - } - DelegateChoice { - roleValue: MtxEvent.FileMessage - TimelineRow { view: chat; FileMessage { id: kid } } - } - DelegateChoice { - roleValue: MtxEvent.VideoMessage - TimelineRow { view: chat; PlayableMediaMessage { id: kid } } - } - DelegateChoice { - roleValue: MtxEvent.AudioMessage - TimelineRow { view: chat; PlayableMediaMessage { id: kid } } - } - DelegateChoice { - roleValue: MtxEvent.Redacted - TimelineRow { view: chat; Redacted { id: kid } } - } - DelegateChoice { - //roleValue: MtxEvent.Redacted - TimelineRow { view: chat; Placeholder { id: kid } } - } - } - + delegate: RowDelegateChooser {} section { property: "section" diff --git a/resources/res.qrc b/resources/res.qrc index 11a20e54..4816ffad 100644 --- a/resources/res.qrc +++ b/resources/res.qrc @@ -116,6 +116,7 @@ qml/TimelineView.qml + qml/RowDelegateChooser.qml qml/Avatar.qml qml/StatusIndicator.qml qml/EncryptionIndicator.qml diff --git a/src/timeline2/DelegateChooser.cpp b/src/timeline2/DelegateChooser.cpp new file mode 100644 index 00000000..ddde93e1 --- /dev/null +++ b/src/timeline2/DelegateChooser.cpp @@ -0,0 +1,160 @@ +#include "DelegateChooser.h" + +#include "Logging.h" + +// uses private API, which moved between versions +#include +#include +#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) +#include +#else +#include +#endif + +QQmlComponent * +DelegateChoice::delegate() const +{ + return delegate_; +} + +void +DelegateChoice::setDelegate(QQmlComponent *delegate) +{ + if (delegate != delegate_) { + delegate_ = delegate; + emit delegateChanged(); + emit changed(); + } +} + +QVariant +DelegateChoice::roleValue() const +{ + return roleValue_; +} + +void +DelegateChoice::setRoleValue(const QVariant &value) +{ + if (value != roleValue_) { + roleValue_ = value; + emit roleValueChanged(); + emit changed(); + } +} + +QVariant +DelegateChooser::roleValue() const +{ + return roleValue_; +} + +void +DelegateChooser::setRoleValue(const QVariant &value) +{ + if (value != roleValue_) { + roleValue_ = value; + recalcChild(); + emit roleValueChanged(); + } +} + +QQmlListProperty +DelegateChooser::choices() +{ + return QQmlListProperty(this, + this, + &DelegateChooser::appendChoice, + &DelegateChooser::choiceCount, + &DelegateChooser::choice, + &DelegateChooser::clearChoices); +} + +QString +DelegateChooser::role() const +{ + return role_; +} + +void +DelegateChooser::setRole(const QString &role) +{ + if (role != role_) { + role_ = role; + emit roleChanged(); + } +} + +QQmlComponent * +DelegateChooser::delegate(QQmlAdaptorModel *adaptorModel, int row, int column) const +{ + auto value = adaptorModel->value(adaptorModel->indexAt(row, column), role_); + + for (const auto choice : choices_) { + auto choiceValue = choice->roleValue(); + if (!value.isValid() || choiceValue == value) { + nhlog::ui()->debug("Returned delegate for {}", role_.toStdString()); + return choice->delegate(); + } + } + + nhlog::ui()->debug("Returned null delegate"); + return nullptr; +} + +void +DelegateChooser::appendChoice(QQmlListProperty *p, DelegateChoice *c) +{ + DelegateChooser *dc = static_cast(p->object); + dc->choices_.append(c); + // dc->recalcChild(); +} + +int +DelegateChooser::choiceCount(QQmlListProperty *p) +{ + return static_cast(p->object)->choices_.count(); +} +DelegateChoice * +DelegateChooser::choice(QQmlListProperty *p, int index) +{ + return static_cast(p->object)->choices_.at(index); +} +void +DelegateChooser::clearChoices(QQmlListProperty *p) +{ + static_cast(p->object)->choices_.clear(); +} + +void +DelegateChooser::recalcChild() +{ + for (const auto choice : choices_) { + auto choiceValue = choice->roleValue(); + if (!roleValue_.isValid() || !choiceValue.isValid() || choiceValue == roleValue_) { + nhlog::ui()->debug("Returned delegate for {}", role_.toStdString()); + + if (child) { + // delete child; + child = nullptr; + } + + child = dynamic_cast( + choice->delegate()->create(QQmlEngine::contextForObject(this))); + child->setParentItem(this); + connect(this->child, &QQuickItem::heightChanged, this, [this]() { + this->setHeight(this->child->height()); + }); + this->setHeight(this->child->height()); + return; + } + } +} + +void +DelegateChooser::componentComplete() +{ + QQuickItem::componentComplete(); + recalcChild(); +} + diff --git a/src/timeline2/DelegateChooser.h b/src/timeline2/DelegateChooser.h new file mode 100644 index 00000000..d2a1cf59 --- /dev/null +++ b/src/timeline2/DelegateChooser.h @@ -0,0 +1,78 @@ +// A DelegateChooser like the one, that was added to Qt5.12 (in labs), but compatible with older Qt versions +// see KDE/kquickitemviews +// see qtdeclarative/qqmldelagatecomponent + +#pragma once + +#include +#include +#include +#include +#include + +class QQmlAdaptorModel; + +class DelegateChoice : public QObject +{ + Q_OBJECT + Q_CLASSINFO("DefaultProperty", "delegate") + +public: + Q_PROPERTY(QVariant roleValue READ roleValue WRITE setRoleValue NOTIFY roleValueChanged) + Q_PROPERTY(QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) + + QQmlComponent *delegate() const; + void setDelegate(QQmlComponent *delegate); + + QVariant roleValue() const; + void setRoleValue(const QVariant &value); + +signals: + void delegateChanged(); + void roleValueChanged(); + void changed(); + +private: + QVariant roleValue_; + QQmlComponent *delegate_ = nullptr; +}; + +class DelegateChooser : public QQuickItem +{ + Q_OBJECT + Q_CLASSINFO("DefaultProperty", "choices") + +public: + Q_PROPERTY(QQmlListProperty choices READ choices CONSTANT) + Q_PROPERTY(QString role READ role WRITE setRole NOTIFY roleChanged) + Q_PROPERTY(QVariant roleValue READ roleValue WRITE setRoleValue NOTIFY roleValueChanged) + + QQmlListProperty choices(); + + QString role() const; + void setRole(const QString &role); + + QVariant roleValue() const; + void setRoleValue(const QVariant &value); + + QQmlComponent *delegate(QQmlAdaptorModel *adaptorModel, int row, int column = 0) const; + + void recalcChild(); + void componentComplete() override; + +signals: + void roleChanged(); + void roleValueChanged(); + +private: + QString role_; + QVariant roleValue_; + QList choices_; + QQuickItem *child; + + static void appendChoice(QQmlListProperty *, DelegateChoice *); + static int choiceCount(QQmlListProperty *); + static DelegateChoice *choice(QQmlListProperty *, int index); + static void clearChoices(QQmlListProperty *); +}; + diff --git a/src/timeline2/TimelineViewManager.cpp b/src/timeline2/TimelineViewManager.cpp index 057f03de..a054bc78 100644 --- a/src/timeline2/TimelineViewManager.cpp +++ b/src/timeline2/TimelineViewManager.cpp @@ -8,6 +8,7 @@ #include #include "ChatPage.h" +#include "DelegateChooser.h" #include "Logging.h" #include "MxcImageProvider.h" #include "UserSettingsPage.h" @@ -57,6 +58,9 @@ TimelineViewManager::TimelineViewManager(QWidget *parent) 0, "MtxEvent", "Can't instantiate enum!"); + qmlRegisterType("com.github.nheko", 1, 0, "DelegateChoice"); + qmlRegisterType("com.github.nheko", 1, 0, "DelegateChooser"); + view = new QQuickView(); container = QWidget::createWindowContainer(view, parent); container->setMinimumSize(200, 200);