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 @@
</qresource>
<qresource prefix="/">
<file>qml/TimelineView.qml</file>
+ <file>qml/RowDelegateChooser.qml</file>
<file>qml/Avatar.qml</file>
<file>qml/StatusIndicator.qml</file>
<file>qml/EncryptionIndicator.qml</file>
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 <QQmlEngine>
+#include <QtGlobal>
+#if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
+#include <QtQmlModels/private/qqmladaptormodel_p.h>
+#else
+#include <QtQml/private/qqmladaptormodel_p.h>
+#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<DelegateChoice>
+DelegateChooser::choices()
+{
+ return QQmlListProperty<DelegateChoice>(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<DelegateChoice> *p, DelegateChoice *c)
+{
+ DelegateChooser *dc = static_cast<DelegateChooser *>(p->object);
+ dc->choices_.append(c);
+ // dc->recalcChild();
+}
+
+int
+DelegateChooser::choiceCount(QQmlListProperty<DelegateChoice> *p)
+{
+ return static_cast<DelegateChooser *>(p->object)->choices_.count();
+}
+DelegateChoice *
+DelegateChooser::choice(QQmlListProperty<DelegateChoice> *p, int index)
+{
+ return static_cast<DelegateChooser *>(p->object)->choices_.at(index);
+}
+void
+DelegateChooser::clearChoices(QQmlListProperty<DelegateChoice> *p)
+{
+ static_cast<DelegateChooser *>(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<QQuickItem *>(
+ 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 <QQmlComponent>
+#include <QQmlListProperty>
+#include <QQuickItem>
+#include <QtCore/QObject>
+#include <QtCore/QVariant>
+
+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<DelegateChoice> 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<DelegateChoice> 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<DelegateChoice *> choices_;
+ QQuickItem *child;
+
+ static void appendChoice(QQmlListProperty<DelegateChoice> *, DelegateChoice *);
+ static int choiceCount(QQmlListProperty<DelegateChoice> *);
+ static DelegateChoice *choice(QQmlListProperty<DelegateChoice> *, int index);
+ static void clearChoices(QQmlListProperty<DelegateChoice> *);
+};
+
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 <QStandardPaths>
#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<DelegateChoice>("com.github.nheko", 1, 0, "DelegateChoice");
+ qmlRegisterType<DelegateChooser>("com.github.nheko", 1, 0, "DelegateChooser");
+
view = new QQuickView();
container = QWidget::createWindowContainer(view, parent);
container->setMinimumSize(200, 200);
|