mirror of https://github.com/Nheko-Reborn/nheko
parent
797dadd7e9
commit
4d8b8c3b81
@ -0,0 +1,244 @@ |
||||
// SPDX-FileCopyrightText: Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include "EventDelegateChooser.h" |
||||
#include "TimelineModel.h" |
||||
|
||||
#include "Logging.h" |
||||
|
||||
#include <QQmlEngine> |
||||
#include <QtGlobal> |
||||
|
||||
// privat qt headers to access required properties
|
||||
#include <QtQml/private/qqmlincubator_p.h> |
||||
#include <QtQml/private/qqmlobjectcreator_p.h> |
||||
|
||||
QQmlComponent * |
||||
EventDelegateChoice::delegate() const |
||||
{ |
||||
return delegate_; |
||||
} |
||||
|
||||
void |
||||
EventDelegateChoice::setDelegate(QQmlComponent *delegate) |
||||
{ |
||||
if (delegate != delegate_) { |
||||
delegate_ = delegate; |
||||
emit delegateChanged(); |
||||
emit changed(); |
||||
} |
||||
} |
||||
|
||||
QList<int> |
||||
EventDelegateChoice::roleValues() const |
||||
{ |
||||
return roleValues_; |
||||
} |
||||
|
||||
void |
||||
EventDelegateChoice::setRoleValues(const QList<int> &value) |
||||
{ |
||||
if (value != roleValues_) { |
||||
roleValues_ = value; |
||||
emit roleValuesChanged(); |
||||
emit changed(); |
||||
} |
||||
} |
||||
|
||||
QQmlListProperty<EventDelegateChoice> |
||||
EventDelegateChooser::choices() |
||||
{ |
||||
return QQmlListProperty<EventDelegateChoice>(this, |
||||
this, |
||||
&EventDelegateChooser::appendChoice, |
||||
&EventDelegateChooser::choiceCount, |
||||
&EventDelegateChooser::choice, |
||||
&EventDelegateChooser::clearChoices); |
||||
} |
||||
|
||||
void |
||||
EventDelegateChooser::appendChoice(QQmlListProperty<EventDelegateChoice> *p, EventDelegateChoice *c) |
||||
{ |
||||
EventDelegateChooser *dc = static_cast<EventDelegateChooser *>(p->object); |
||||
dc->choices_.append(c); |
||||
} |
||||
|
||||
qsizetype |
||||
EventDelegateChooser::choiceCount(QQmlListProperty<EventDelegateChoice> *p) |
||||
{ |
||||
return static_cast<EventDelegateChooser *>(p->object)->choices_.count(); |
||||
} |
||||
EventDelegateChoice * |
||||
EventDelegateChooser::choice(QQmlListProperty<EventDelegateChoice> *p, qsizetype index) |
||||
{ |
||||
return static_cast<EventDelegateChooser *>(p->object)->choices_.at(index); |
||||
} |
||||
void |
||||
EventDelegateChooser::clearChoices(QQmlListProperty<EventDelegateChoice> *p) |
||||
{ |
||||
static_cast<EventDelegateChooser *>(p->object)->choices_.clear(); |
||||
} |
||||
|
||||
void |
||||
EventDelegateChooser::componentComplete() |
||||
{ |
||||
QQuickItem::componentComplete(); |
||||
// eventIncubator.reset(eventIndex);
|
||||
} |
||||
|
||||
void |
||||
EventDelegateChooser::DelegateIncubator::setInitialState(QObject *obj) |
||||
{ |
||||
auto item = qobject_cast<QQuickItem *>(obj); |
||||
if (!item) |
||||
return; |
||||
|
||||
item->setParentItem(&chooser); |
||||
|
||||
auto roleNames = chooser.room_->roleNames(); |
||||
QHash<QByteArray, int> nameToRole; |
||||
for (const auto &[k, v] : roleNames.asKeyValueRange()) { |
||||
nameToRole.insert(v, k); |
||||
} |
||||
|
||||
QHash<int, int> roleToPropIdx; |
||||
std::vector<QModelRoleData> roles; |
||||
|
||||
// Workaround for https://bugreports.qt.io/browse/QTBUG-98846
|
||||
QHash<QString, RequiredPropertyKey> requiredProperties; |
||||
for (const auto &[propKey, prop] : |
||||
QQmlIncubatorPrivate::get(this)->requiredProperties()->asKeyValueRange()) { |
||||
requiredProperties.insert(prop.propertyName, propKey); |
||||
} |
||||
|
||||
// collect required properties
|
||||
auto mo = obj->metaObject(); |
||||
for (int i = 0; i < mo->propertyCount(); i++) { |
||||
auto prop = mo->property(i); |
||||
// nhlog::ui()->critical("Found prop {}", prop.name());
|
||||
// See https://bugreports.qt.io/browse/QTBUG-98846
|
||||
if (!prop.isRequired() && !requiredProperties.contains(prop.name())) |
||||
continue; |
||||
|
||||
if (auto role = nameToRole.find(prop.name()); role != nameToRole.end()) { |
||||
roleToPropIdx.insert(*role, i); |
||||
roles.emplace_back(*role); |
||||
|
||||
nhlog::ui()->critical("Found prop {}, idx {}, role {}", prop.name(), i, *role); |
||||
} else { |
||||
nhlog::ui()->critical("Required property {} not found in model!", prop.name()); |
||||
} |
||||
} |
||||
|
||||
nhlog::ui()->debug("Querying data for id {}", currentId.toStdString()); |
||||
chooser.room_->multiData(currentId, forReply ? chooser.eventId_ : QString(), roles); |
||||
|
||||
QVariantMap rolesToSet; |
||||
for (const auto &role : roles) { |
||||
const auto &roleName = roleNames[role.role()]; |
||||
nhlog::ui()->critical("Setting role {}, {}", role.role(), roleName.toStdString()); |
||||
|
||||
mo->property(roleToPropIdx[role.role()]).write(obj, role.data()); |
||||
rolesToSet.insert(roleName, role.data()); |
||||
|
||||
if (const auto &req = requiredProperties.find(roleName); req != requiredProperties.end()) |
||||
QQmlIncubatorPrivate::get(this)->requiredProperties()->remove(*req); |
||||
} |
||||
|
||||
// setInitialProperties(rolesToSet);
|
||||
|
||||
auto update = |
||||
[this, obj, roleToPropIdx = std::move(roleToPropIdx)](const QList<int> &changedRoles) { |
||||
std::vector<QModelRoleData> rolesToRequest; |
||||
|
||||
if (changedRoles.empty()) { |
||||
for (auto role : roleToPropIdx.keys()) |
||||
rolesToRequest.emplace_back(role); |
||||
} else { |
||||
for (auto role : changedRoles) { |
||||
if (roleToPropIdx.contains(role)) { |
||||
rolesToRequest.emplace_back(role); |
||||
} |
||||
} |
||||
} |
||||
|
||||
if (rolesToRequest.empty()) |
||||
return; |
||||
|
||||
auto mo = obj->metaObject(); |
||||
chooser.room_->multiData( |
||||
currentId, forReply ? chooser.eventId_ : QString(), rolesToRequest); |
||||
for (const auto &role : rolesToRequest) { |
||||
mo->property(roleToPropIdx[role.role()]).write(obj, role.data()); |
||||
} |
||||
}; |
||||
|
||||
if (!forReply) { |
||||
auto row = chooser.room_->idToIndex(currentId); |
||||
connect(chooser.room_, |
||||
&QAbstractItemModel::dataChanged, |
||||
obj, |
||||
[row, update](const QModelIndex &topLeft, |
||||
const QModelIndex &bottomRight, |
||||
const QList<int> &changedRoles) { |
||||
if (row < topLeft.row() || row > bottomRight.row()) |
||||
return; |
||||
|
||||
update(changedRoles); |
||||
}); |
||||
} |
||||
} |
||||
|
||||
void |
||||
EventDelegateChooser::DelegateIncubator::reset(QString id) |
||||
{ |
||||
if (!chooser.room_ || id.isEmpty()) |
||||
return; |
||||
|
||||
nhlog::ui()->debug("Reset with id {}, reply {}", id.toStdString(), forReply); |
||||
|
||||
this->currentId = id; |
||||
|
||||
auto role = |
||||
chooser.room_ |
||||
->dataById(id, TimelineModel::Roles::Type, forReply ? chooser.eventId_ : QString()) |
||||
.toInt(); |
||||
|
||||
for (const auto choice : qAsConst(chooser.choices_)) { |
||||
const auto &choiceValue = choice->roleValues(); |
||||
if (choiceValue.contains(role) || choiceValue.empty()) { |
||||
if (auto child = qobject_cast<QQuickItem *>(object())) { |
||||
child->setParentItem(nullptr); |
||||
} |
||||
|
||||
choice->delegate()->create(*this, QQmlEngine::contextForObject(&chooser)); |
||||
return; |
||||
} |
||||
} |
||||
} |
||||
|
||||
void |
||||
EventDelegateChooser::DelegateIncubator::statusChanged(QQmlIncubator::Status status) |
||||
{ |
||||
if (status == QQmlIncubator::Ready) { |
||||
auto child = qobject_cast<QQuickItem *>(object()); |
||||
if (child == nullptr) { |
||||
nhlog::ui()->error("Delegate has to be derived of Item!"); |
||||
return; |
||||
} |
||||
|
||||
child->setParentItem(&chooser); |
||||
QQmlEngine::setObjectOwnership(child, QQmlEngine::ObjectOwnership::JavaScriptOwnership); |
||||
if (forReply) |
||||
emit chooser.replyChanged(); |
||||
else |
||||
emit chooser.mainChanged(); |
||||
|
||||
} else if (status == QQmlIncubator::Error) { |
||||
auto errors_ = errors(); |
||||
for (const auto &e : qAsConst(errors_)) |
||||
nhlog::ui()->error("Error instantiating delegate: {}", e.toString().toStdString()); |
||||
} |
||||
} |
||||
|
@ -0,0 +1,136 @@ |
||||
// SPDX-FileCopyrightText: Nheko Contributors
|
||||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// 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 <QAbstractItemModel> |
||||
#include <QQmlComponent> |
||||
#include <QQmlIncubator> |
||||
#include <QQmlListProperty> |
||||
#include <QQuickItem> |
||||
#include <QtCore/QObject> |
||||
#include <QtCore/QVariant> |
||||
|
||||
#include "TimelineModel.h" |
||||
|
||||
class EventDelegateChoice : public QObject |
||||
{ |
||||
Q_OBJECT |
||||
QML_ELEMENT |
||||
Q_CLASSINFO("DefaultProperty", "delegate") |
||||
|
||||
public: |
||||
Q_PROPERTY(QList<int> roleValues READ roleValues WRITE setRoleValues NOTIFY roleValuesChanged |
||||
REQUIRED FINAL) |
||||
Q_PROPERTY( |
||||
QQmlComponent *delegate READ delegate WRITE setDelegate NOTIFY delegateChanged REQUIRED FINAL) |
||||
|
||||
[[nodiscard]] QQmlComponent *delegate() const; |
||||
void setDelegate(QQmlComponent *delegate); |
||||
|
||||
[[nodiscard]] QList<int> roleValues() const; |
||||
void setRoleValues(const QList<int> &value); |
||||
|
||||
signals: |
||||
void delegateChanged(); |
||||
void roleValuesChanged(); |
||||
void changed(); |
||||
|
||||
private: |
||||
QList<int> roleValues_; |
||||
QQmlComponent *delegate_ = nullptr; |
||||
}; |
||||
|
||||
class EventDelegateChooser : public QQuickItem |
||||
{ |
||||
Q_OBJECT |
||||
QML_ELEMENT |
||||
Q_CLASSINFO("DefaultProperty", "choices") |
||||
|
||||
public: |
||||
Q_PROPERTY(QQmlListProperty<EventDelegateChoice> choices READ choices CONSTANT FINAL) |
||||
Q_PROPERTY(QQuickItem *main READ main NOTIFY mainChanged FINAL) |
||||
Q_PROPERTY(TimelineModel *room READ room WRITE setRoom NOTIFY roomChanged REQUIRED FINAL) |
||||
Q_PROPERTY(QString eventId READ eventId WRITE setEventId NOTIFY eventIdChanged REQUIRED FINAL) |
||||
Q_PROPERTY(QString replyTo READ replyTo WRITE setReplyTo NOTIFY replyToChanged REQUIRED FINAL) |
||||
|
||||
QQmlListProperty<EventDelegateChoice> choices(); |
||||
|
||||
[[nodiscard]] QQuickItem *main() const |
||||
{ |
||||
return qobject_cast<QQuickItem *>(eventIncubator.object()); |
||||
} |
||||
|
||||
void setRoom(TimelineModel *m) |
||||
{ |
||||
if (m != room_) { |
||||
room_ = m; |
||||
eventIncubator.reset(eventId_); |
||||
replyIncubator.reset(replyId); |
||||
emit roomChanged(); |
||||
} |
||||
} |
||||
[[nodiscard]] TimelineModel *room() { return room_; } |
||||
|
||||
void setEventId(QString idx) |
||||
{ |
||||
eventId_ = idx; |
||||
emit eventIdChanged(); |
||||
} |
||||
[[nodiscard]] QString eventId() const { return eventId_; } |
||||
void setReplyTo(QString id) |
||||
{ |
||||
replyId = id; |
||||
emit replyToChanged(); |
||||
} |
||||
[[nodiscard]] QString replyTo() const { return replyId; } |
||||
|
||||
void componentComplete() override; |
||||
|
||||
signals: |
||||
void mainChanged(); |
||||
void replyChanged(); |
||||
void roomChanged(); |
||||
void eventIdChanged(); |
||||
void replyToChanged(); |
||||
|
||||
private: |
||||
struct DelegateIncubator final : public QQmlIncubator |
||||
{ |
||||
DelegateIncubator(EventDelegateChooser &parent, bool forReply) |
||||
: QQmlIncubator(QQmlIncubator::AsynchronousIfNested) |
||||
, chooser(parent) |
||||
, forReply(forReply) |
||||
{ |
||||
} |
||||
void setInitialState(QObject *object) override; |
||||
void statusChanged(QQmlIncubator::Status status) override; |
||||
|
||||
void reset(QString id); |
||||
|
||||
EventDelegateChooser &chooser; |
||||
bool forReply; |
||||
QString currentId; |
||||
|
||||
QString instantiatedId; |
||||
int instantiatedRole = -1; |
||||
QAbstractItemModel *instantiatedModel = nullptr; |
||||
}; |
||||
|
||||
QVariant roleValue_; |
||||
QList<EventDelegateChoice *> choices_; |
||||
DelegateIncubator eventIncubator{*this, false}; |
||||
DelegateIncubator replyIncubator{*this, true}; |
||||
TimelineModel *room_{nullptr}; |
||||
QString eventId_; |
||||
QString replyId; |
||||
|
||||
static void appendChoice(QQmlListProperty<EventDelegateChoice> *, EventDelegateChoice *); |
||||
static qsizetype choiceCount(QQmlListProperty<EventDelegateChoice> *); |
||||
static EventDelegateChoice *choice(QQmlListProperty<EventDelegateChoice> *, qsizetype index); |
||||
static void clearChoices(QQmlListProperty<EventDelegateChoice> *); |
||||
}; |
Loading…
Reference in new issue