// SPDX-FileCopyrightText: Nheko Contributors // // SPDX-License-Identifier: GPL-3.0-or-later #pragma once #include #include #include #include #include #include #include class TimelineModel; class EventDelegateChooserAttachedType : public QObject { Q_OBJECT Q_PROPERTY(bool keepAspectRatio READ keepAspectRatio WRITE setKeepAspectRatio NOTIFY keepAspectRatioChanged) Q_PROPERTY(double aspectRatio READ aspectRatio WRITE setAspectRatio NOTIFY aspectRatioChanged) Q_PROPERTY(int maxWidth READ maxWidth WRITE setMaxWidth NOTIFY maxWidthChanged) Q_PROPERTY(int maxHeight READ maxHeight WRITE setMaxHeight NOTIFY maxHeightChanged) Q_PROPERTY(bool isReply READ isReply WRITE setIsReply NOTIFY isReplyChanged) QML_ANONYMOUS public: EventDelegateChooserAttachedType(QObject *parent) : QObject(parent) { } bool keepAspectRatio() const { return keepAspectRatio_; } void setKeepAspectRatio(bool fill) { if (fill != keepAspectRatio_) { keepAspectRatio_ = fill; emit keepAspectRatioChanged(); polishChooser(); } } double aspectRatio() const { return aspectRatio_; } void setAspectRatio(double fill) { aspectRatio_ = fill; emit aspectRatioChanged(); polishChooser(); } int maxWidth() const { return maxWidth_; } void setMaxWidth(int fill) { maxWidth_ = fill; emit maxWidthChanged(); polishChooser(); } int maxHeight() const { return maxHeight_; } void setMaxHeight(int fill) { maxHeight_ = fill; emit maxHeightChanged(); } bool isReply() const { return isReply_; } void setIsReply(bool fill) { if (fill != isReply_) { isReply_ = fill; emit isReplyChanged(); polishChooser(); } } signals: void keepAspectRatioChanged(); void aspectRatioChanged(); void maxWidthChanged(); void maxHeightChanged(); void isReplyChanged(); private: void polishChooser(); double aspectRatio_ = 1.; int maxWidth_ = -1; int maxHeight_ = -1; bool keepAspectRatio_ = false; bool isReply_ = false; }; class EventDelegateChoice : public QObject { Q_OBJECT QML_ELEMENT Q_CLASSINFO("DefaultProperty", "delegate") public: Q_PROPERTY(QList 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 roleValues() const; void setRoleValues(const QList &value); signals: void delegateChanged(); void roleValuesChanged(); void changed(); private: QList roleValues_; QQmlComponent *delegate_ = nullptr; }; class EventDelegateChooser : public QQuickItem { Q_OBJECT QML_ELEMENT Q_CLASSINFO("DefaultProperty", "choices") QML_ATTACHED(EventDelegateChooserAttachedType) Q_PROPERTY(QQmlListProperty choices READ choices CONSTANT FINAL) Q_PROPERTY(QQuickItem *main READ main NOTIFY mainChanged FINAL) Q_PROPERTY(QQuickItem *reply READ reply NOTIFY replyChanged 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) Q_PROPERTY(TimelineModel *room READ room WRITE setRoom NOTIFY roomChanged REQUIRED FINAL) Q_PROPERTY(bool sameWidth READ sameWidth WRITE setSameWidth NOTIFY sameWidthChanged) Q_PROPERTY(int maxWidth READ maxWidth WRITE setMaxWidth NOTIFY maxWidthChanged) Q_PROPERTY(int replyInset READ replyInset WRITE setReplyInset NOTIFY replyInsetChanged) Q_PROPERTY(int mainInset READ mainInset WRITE setMainInset NOTIFY mainInsetChanged) Q_PROPERTY(bool limitAsReply READ limitAsReply WRITE setLimitAsReply NOTIFY limitAsReplyChanged) public: QQmlListProperty choices(); [[nodiscard]] QQuickItem *main() const { return qobject_cast(eventIncubator.object()); } [[nodiscard]] QQuickItem *reply() const { return qobject_cast(replyIncubator.object()); } bool sameWidth() const { return sameWidth_; } void setSameWidth(bool width) { sameWidth_ = width; emit sameWidthChanged(); } bool limitAsReply() const { return limitAsReply_; } void setLimitAsReply(bool width) { limitAsReply_ = width; emit limitAsReplyChanged(); } int maxWidth() const { return maxWidth_; } void setMaxWidth(int width) { maxWidth_ = width; emit maxWidthChanged(); polish(); } int replyInset() const { return replyInset_; } void setReplyInset(int width) { replyInset_ = width; emit replyInsetChanged(); polish(); } int mainInset() const { return mainInset_; } void setMainInset(int width) { mainInset_ = width; emit mainInsetChanged(); polish(); } void setRoom(TimelineModel *m) { if (m != room_) { room_ = m; emit roomChanged(); if (isComponentComplete()) { eventIncubator.reset(eventId_); replyIncubator.reset(replyId); } } } [[nodiscard]] TimelineModel *room() { return room_; } void setEventId(QString idx) { eventId_ = idx; emit eventIdChanged(); if (isComponentComplete()) eventIncubator.reset(eventId_); } [[nodiscard]] QString eventId() const { return eventId_; } void setReplyTo(QString id) { replyId = id; emit replyToChanged(); if (isComponentComplete()) replyIncubator.reset(replyId); } [[nodiscard]] QString replyTo() const { return replyId; } void componentComplete() override; static EventDelegateChooserAttachedType *qmlAttachedProperties(QObject *object) { return new EventDelegateChooserAttachedType(object); } void updatePolish() override; signals: void mainChanged(); void replyChanged(); void roomChanged(); void eventIdChanged(); void replyToChanged(); void sameWidthChanged(); void limitAsReplyChanged(); void maxWidthChanged(); void replyInsetChanged(); void mainInsetChanged(); 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; int oldType = -1; }; QVariant roleValue_; QList choices_; DelegateIncubator eventIncubator{*this, false}; DelegateIncubator replyIncubator{*this, true}; TimelineModel *room_{nullptr}; QString eventId_; QString replyId; bool sameWidth_ = false; bool limitAsReply_ = false; int maxWidth_ = 400; int replyInset_ = 0; int mainInset_ = 0; static void appendChoice(QQmlListProperty *, EventDelegateChoice *); static qsizetype choiceCount(QQmlListProperty *); static EventDelegateChoice *choice(QQmlListProperty *, qsizetype index); static void clearChoices(QQmlListProperty *); };