diff --git a/src/timeline/CommunitiesModel.cpp b/src/timeline/CommunitiesModel.cpp
index b04fd7a9..3c09d747 100644
--- a/src/timeline/CommunitiesModel.cpp
+++ b/src/timeline/CommunitiesModel.cpp
@@ -22,6 +22,7 @@ CommunitiesModel::CommunitiesModel(QObject *parent)
, hiddenTagIds_{UserSettings::instance()->hiddenTags()}
, mutedTagIds_{UserSettings::instance()->mutedTags()}
{
+ instance_ = this;
}
QHash<int, QByteArray>
diff --git a/src/timeline/CommunitiesModel.h b/src/timeline/CommunitiesModel.h
index a90fa6a2..d0841f4b 100644
--- a/src/timeline/CommunitiesModel.h
+++ b/src/timeline/CommunitiesModel.h
@@ -6,6 +6,7 @@
#include <QAbstractListModel>
#include <QHash>
+#include <QQmlEngine>
#include <QSortFilterProxyModel>
#include <QString>
#include <QStringList>
@@ -21,6 +22,8 @@ class CommunitiesModel;
class FilteredCommunitiesModel final : public QSortFilterProxyModel
{
Q_OBJECT
+ QML_ELEMENT
+ QML_UNCREATABLE("Use Communities.filtered() to create a FilteredCommunitiesModel")
public:
explicit FilteredCommunitiesModel(CommunitiesModel *model, QObject *parent = nullptr);
@@ -73,6 +76,9 @@ public:
class CommunitiesModel final : public QAbstractListModel
{
Q_OBJECT
+ QML_NAMED_ELEMENT(Communities)
+ QML_SINGLETON
+
Q_PROPERTY(QString currentTagId READ currentTagId WRITE setCurrentTagId NOTIFY
currentTagIdChanged RESET resetCurrentTagId)
Q_PROPERTY(QStringList tags READ tags NOTIFY tagsChanged)
@@ -149,6 +155,26 @@ public:
};
CommunitiesModel(QObject *parent = nullptr);
+
+ static CommunitiesModel *create(QQmlEngine *qmlEngine, QJSEngine *)
+ {
+ // The instance has to exist before it is used. We cannot replace it.
+ Q_ASSERT(instance_);
+
+ // The engine has to have the same thread affinity as the singleton.
+ Q_ASSERT(qmlEngine->thread() == instance_->thread());
+
+ // There can only be one engine accessing the singleton.
+ static QJSEngine *s_engine = nullptr;
+ if (s_engine)
+ Q_ASSERT(qmlEngine == s_engine);
+ else
+ s_engine = qmlEngine;
+
+ QJSEngine::setObjectOwnership(instance_, QJSEngine::CppOwnership);
+ return instance_;
+ }
+
QHash<int, QByteArray> roleNames() const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override
{
@@ -221,4 +247,6 @@ private:
mtx::responses::UnreadNotifications dmUnreads{};
friend class FilteredCommunitiesModel;
+
+ inline static CommunitiesModel *instance_ = nullptr;
};
diff --git a/src/timeline/DelegateChooser.h b/src/timeline/DelegateChooser.h
index c27f2c43..ac227382 100644
--- a/src/timeline/DelegateChooser.h
+++ b/src/timeline/DelegateChooser.h
@@ -19,6 +19,7 @@ class QQmlAdaptorModel;
class DelegateChoice : public QObject
{
Q_OBJECT
+ QML_ELEMENT
Q_CLASSINFO("DefaultProperty", "delegate")
public:
@@ -45,6 +46,7 @@ private:
class DelegateChooser : public QQuickItem
{
Q_OBJECT
+ QML_ELEMENT
Q_CLASSINFO("DefaultProperty", "choices")
public:
diff --git a/src/timeline/InputBar.h b/src/timeline/InputBar.h
index f03e6019..3cd65524 100644
--- a/src/timeline/InputBar.h
+++ b/src/timeline/InputBar.h
@@ -7,11 +7,13 @@
#include <QIODevice>
#include <QImage>
#include <QObject>
+#include <QQmlEngine>
#include <QSize>
#include <QStringList>
#include <QTimer>
#include <QUrl>
#include <QVariantList>
+
#include <deque>
#include <memory>
@@ -43,6 +45,10 @@ enum class MarkdownOverride
class MediaUpload final : public QObject
{
Q_OBJECT
+
+ QML_ELEMENT
+ QML_UNCREATABLE("")
+
Q_PROPERTY(int mediaType READ type NOTIFY mediaTypeChanged)
// https://stackoverflow.com/questions/33422265/pass-qimage-to-qml/68554646#68554646
Q_PROPERTY(QUrl thumbnail READ thumbnailDataUrl NOTIFY thumbnailChanged)
diff --git a/src/timeline/PresenceEmitter.h b/src/timeline/PresenceEmitter.h
index e89fb316..09ad1301 100644
--- a/src/timeline/PresenceEmitter.h
+++ b/src/timeline/PresenceEmitter.h
@@ -5,6 +5,7 @@
#pragma once
#include <QObject>
+#include <QQmlEngine>
#include <vector>
@@ -15,10 +16,33 @@ class PresenceEmitter final : public QObject
{
Q_OBJECT
+ QML_NAMED_ELEMENT(Presence)
+ QML_SINGLETON
+
public:
PresenceEmitter(QObject *p = nullptr)
: QObject(p)
{
+ instance_ = this;
+ }
+
+ static PresenceEmitter *create(QQmlEngine *qmlEngine, QJSEngine *)
+ {
+ // The instance has to exist before it is used. We cannot replace it.
+ Q_ASSERT(instance_);
+
+ // The engine has to have the same thread affinity as the singleton.
+ Q_ASSERT(qmlEngine->thread() == instance_->thread());
+
+ // There can only be one engine accessing the singleton.
+ static QJSEngine *s_engine = nullptr;
+ if (s_engine)
+ Q_ASSERT(qmlEngine == s_engine);
+ else
+ s_engine = qmlEngine;
+
+ QJSEngine::setObjectOwnership(instance_, QJSEngine::CppOwnership);
+ return instance_;
}
void sync(const std::vector<mtx::events::Event<mtx::events::presence::Presence>> &presences);
@@ -28,4 +52,7 @@ public:
signals:
void presenceChanged(QString userid);
+
+private:
+ inline static PresenceEmitter *instance_ = nullptr;
};
diff --git a/src/timeline/RoomlistModel.cpp b/src/timeline/RoomlistModel.cpp
index ec41cc12..8d8d2977 100644
--- a/src/timeline/RoomlistModel.cpp
+++ b/src/timeline/RoomlistModel.cpp
@@ -909,6 +909,8 @@ FilteredRoomlistModel::FilteredRoomlistModel(RoomlistModel *model, QObject *pare
: QSortFilterProxyModel(parent)
, roomlistmodel(model)
{
+ instance_ = this;
+
this->sortByImportance = UserSettings::instance()->sortByImportance();
this->sortByAlphabet = UserSettings::instance()->sortByAlphabet();
setSourceModel(model);
diff --git a/src/timeline/RoomlistModel.h b/src/timeline/RoomlistModel.h
index c06ab67d..34bf3f9a 100644
--- a/src/timeline/RoomlistModel.h
+++ b/src/timeline/RoomlistModel.h
@@ -167,12 +167,36 @@ private:
class FilteredRoomlistModel final : public QSortFilterProxyModel
{
Q_OBJECT
+
+ QML_NAMED_ELEMENT(Rooms)
+ QML_SINGLETON
+
Q_PROPERTY(
TimelineModel *currentRoom READ currentRoom NOTIFY currentRoomChanged RESET resetCurrentRoom)
Q_PROPERTY(RoomPreview currentRoomPreview READ currentRoomPreview NOTIFY currentRoomChanged
RESET resetCurrentRoom)
public:
FilteredRoomlistModel(RoomlistModel *model, QObject *parent = nullptr);
+
+ static FilteredRoomlistModel *create(QQmlEngine *qmlEngine, QJSEngine *)
+ {
+ // The instance has to exist before it is used. We cannot replace it.
+ Q_ASSERT(instance_);
+
+ // The engine has to have the same thread affinity as the singleton.
+ Q_ASSERT(qmlEngine->thread() == instance_->thread());
+
+ // There can only be one engine accessing the singleton.
+ static QJSEngine *s_engine = nullptr;
+ if (s_engine)
+ Q_ASSERT(qmlEngine == s_engine);
+ else
+ s_engine = qmlEngine;
+
+ QJSEngine::setObjectOwnership(instance_, QJSEngine::CppOwnership);
+ return instance_;
+ }
+
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override;
bool filterAcceptsRow(int sourceRow, const QModelIndex &) const override;
@@ -249,4 +273,6 @@ private:
FilterBy filterType = FilterBy::Nothing;
QStringList hiddenTags, hiddenSpaces;
bool hideDMs = false;
+
+ inline static FilteredRoomlistModel *instance_ = nullptr;
};
diff --git a/src/timeline/TimelineFilter.h b/src/timeline/TimelineFilter.h
index 1c92c89a..658a8c57 100644
--- a/src/timeline/TimelineFilter.h
+++ b/src/timeline/TimelineFilter.h
@@ -4,6 +4,7 @@
#pragma once
+#include <QQmlEngine>
#include <QSortFilterProxyModel>
#include <QString>
@@ -14,6 +15,7 @@
class TimelineFilter : public QSortFilterProxyModel
{
Q_OBJECT
+ QML_ELEMENT
Q_PROPERTY(QString filterByThread READ filterByThread WRITE setThreadId NOTIFY threadIdChanged)
Q_PROPERTY(QString filterByContent READ filterByContent WRITE setContentFilter NOTIFY
diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h
index a232b4ee..fd1a4396 100644
--- a/src/timeline/TimelineModel.h
+++ b/src/timeline/TimelineModel.h
@@ -11,6 +11,7 @@
#include <QTimer>
#include <QVariant>
+#include <mtx/responses/common.hpp>
#include <mtxclient/http/errors.hpp>
#include "CacheCryptoStructs.h"
@@ -36,6 +37,7 @@ struct RelatedInfo;
namespace qml_mtx_events {
Q_NAMESPACE
+QML_NAMED_ELEMENT(MtxEvent)
enum EventType
{
@@ -193,6 +195,9 @@ class TimelineViewManager;
class TimelineModel final : public QAbstractListModel
{
Q_OBJECT
+ QML_NAMED_ELEMENT(Room)
+ QML_UNCREATABLE("")
+
Q_PROPERTY(int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged)
Q_PROPERTY(std::vector<QString> typingUsers READ typingUsers WRITE updateTypingUsers NOTIFY
typingUsersChanged)
diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp
index a3b91ce7..2f6553e5 100644
--- a/src/timeline/TimelineViewManager.cpp
+++ b/src/timeline/TimelineViewManager.cpp
@@ -96,29 +96,21 @@ TimelineViewManager::userColor(QString id, QColor background)
TimelineViewManager::TimelineViewManager(CallManager *, ChatPage *parent)
: QObject(parent)
, rooms_(new RoomlistModel(this))
+ , frooms_(new FilteredRoomlistModel(this->rooms_))
, communities_(new CommunitiesModel(this))
, verificationManager_(new VerificationManager(this))
, presenceEmitter(new PresenceEmitter(this))
{
- static auto self = this;
- qmlRegisterSingletonInstance("im.nheko", 1, 0, "TimelineManager", self);
- qmlRegisterSingletonType<RoomlistModel>(
- "im.nheko", 1, 0, "Rooms", [](QQmlEngine *, QJSEngine *) -> QObject * {
- auto ptr = new FilteredRoomlistModel(self->rooms_);
+ instance_ = this;
- connect(self->communities_,
- &CommunitiesModel::currentTagIdChanged,
- ptr,
- &FilteredRoomlistModel::updateFilterTag);
- connect(self->communities_,
- &CommunitiesModel::hiddenTagsChanged,
- ptr,
- &FilteredRoomlistModel::updateHiddenTagsAndSpaces);
- return ptr;
- });
- qmlRegisterSingletonInstance("im.nheko", 1, 0, "Communities", self->communities_);
- qmlRegisterSingletonInstance("im.nheko", 1, 0, "VerificationManager", verificationManager_);
- qmlRegisterSingletonInstance("im.nheko", 1, 0, "Presence", presenceEmitter);
+ connect(this->communities_,
+ &CommunitiesModel::currentTagIdChanged,
+ frooms_,
+ &FilteredRoomlistModel::updateFilterTag);
+ connect(this->communities_,
+ &CommunitiesModel::hiddenTagsChanged,
+ frooms_,
+ &FilteredRoomlistModel::updateHiddenTagsAndSpaces);
updateColorPalette();
diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h
index 303e2af2..a4bc6c41 100644
--- a/src/timeline/TimelineViewManager.h
+++ b/src/timeline/TimelineViewManager.h
@@ -34,6 +34,9 @@ class TimelineViewManager final : public QObject
{
Q_OBJECT
+ QML_NAMED_ELEMENT(TimelineManager)
+ QML_SINGLETON
+
Q_PROPERTY(
bool isInitialSync MEMBER isInitialSync_ READ isInitialSync NOTIFY initialSyncChanged)
Q_PROPERTY(bool isConnected READ isConnected NOTIFY isConnectedChanged)
@@ -41,6 +44,25 @@ class TimelineViewManager final : public QObject
public:
TimelineViewManager(CallManager *callManager, ChatPage *parent = nullptr);
+ static TimelineViewManager *create(QQmlEngine *qmlEngine, QJSEngine *)
+ {
+ // The instance has to exist before it is used. We cannot replace it.
+ Q_ASSERT(instance_);
+
+ // The engine has to have the same thread affinity as the singleton.
+ Q_ASSERT(qmlEngine->thread() == instance_->thread());
+
+ // There can only be one engine accessing the singleton.
+ static QJSEngine *s_engine = nullptr;
+ if (s_engine)
+ Q_ASSERT(qmlEngine == s_engine);
+ else
+ s_engine = qmlEngine;
+
+ QJSEngine::setObjectOwnership(instance_, QJSEngine::CppOwnership);
+ return instance_;
+ }
+
void sync(const mtx::responses::Sync &sync_);
VerificationManager *verificationManager() { return verificationManager_; }
@@ -123,6 +145,7 @@ private:
bool isConnected_ = true;
RoomlistModel *rooms_ = nullptr;
+ FilteredRoomlistModel *frooms_ = nullptr;
CommunitiesModel *communities_ = nullptr;
// don't move this above the rooms_
@@ -130,4 +153,6 @@ private:
PresenceEmitter *presenceEmitter = nullptr;
QHash<QPair<QString, quint64>, QColor> userColors;
+
+ inline static TimelineViewManager *instance_ = nullptr;
};
|