diff --git a/autotests/texthandlertest.cpp b/autotests/texthandlertest.cpp index d19b5aef5..6a472f0ed 100644 --- a/autotests/texthandlertest.cpp +++ b/autotests/texthandlertest.cpp @@ -34,6 +34,10 @@ private Q_SLOTS: void stripDisallowedTags(); void stripDisallowedAttributes(); void emptyCodeTags(); + void addStyle_data(); + void addStyle(); + void dontAddStyle_data(); + void dontAddStyle(); void sendSimpleStringCase(); void sendSingleParaMarkup(); @@ -71,6 +75,9 @@ private Q_SLOTS: void componentOutput_data(); void componentOutput(); + + void updateSpoiler_data(); + void updateSpoiler(); }; void TextHandlerTest::initTestCase() @@ -89,21 +96,26 @@ void TextHandlerTest::initTestCase() void TextHandlerTest::allowedAttributes() { + auto theme = static_cast(qmlAttachedPropertiesObject(this, true)); const QString testInputString1 = u"Test"_s; - const QString testOutputString1 = u"Test"_s; + const QString testOutputString1S = u"Test"_s; + const QString testOutputString1R = u"Test"_s.arg( + theme->alternateBackgroundColor().name()); // Handle urls where the href has either single (') or double (") quotes. const QString testInputString2 = u"linklink"_s; - const QString testOutputString2 = u"linklink"_s; + const QString testOutputString2S = u"linklink"_s; + const QString testOutputString2R = + u"linklink"_s; TextHandler testTextHandler; testTextHandler.setData(testInputString1); - QCOMPARE(testTextHandler.handleSendText(), testOutputString1); - QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString1); + QCOMPARE(testTextHandler.handleSendText(), testOutputString1S); + QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString1R); testTextHandler.setData(testInputString2); - QCOMPARE(testTextHandler.handleSendText(), testOutputString2); - QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString2); + QCOMPARE(testTextHandler.handleSendText(), testOutputString2S); + QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString2R); } void TextHandlerTest::stripDisallowedTags() @@ -146,6 +158,56 @@ void TextHandlerTest::emptyCodeTags() QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString); } +void TextHandlerTest::addStyle_data() +{ + QTest::addColumn("testInputString"); + QTest::addColumn("testOutputString"); + + QTest::newRow("link") << u"link"_s << u"link"_s; + QTest::newRow("table") + << u"
CompanyContactCountry
Alfreds FutterkisteMaria AndersGermany
Centro comercial MoctezumaFrancisco ChangMexico
"_s + << u"
CompanyContactCountry
Alfreds FutterkisteMaria AndersGermany
Centro comercial MoctezumaFrancisco ChangMexico
"_s; + auto theme = static_cast(qmlAttachedPropertiesObject(this, true)); + QTest::newRow("spoiler") << u"Test"_s + << u"Test"_s.arg( + theme->alternateBackgroundColor().name()); +} + +void TextHandlerTest::addStyle() +{ + QFETCH(QString, testInputString); + QFETCH(QString, testOutputString); + + TextHandler testTextHandler; + testTextHandler.setData(testInputString); + + QCOMPARE(testTextHandler.handleRecieveRichText(), testOutputString); +} + +void TextHandlerTest::dontAddStyle_data() +{ + QTest::addColumn("testInputString"); + QTest::addColumn("testOutputString"); + + QTest::newRow("link") << u"link"_s << u"link"_s; + QTest::newRow("table") + << u"
CompanyContactCountry
Alfreds FutterkisteMaria AndersGermany
Centro comercial MoctezumaFrancisco ChangMexico
"_s + << u"
CompanyContactCountry
Alfreds FutterkisteMaria AndersGermany
Centro comercial MoctezumaFrancisco ChangMexico
"_s; + QTest::newRow("spoiler") << u"Test"_s + << u"Test"_s; +} + +void TextHandlerTest::dontAddStyle() +{ + QFETCH(QString, testInputString); + QFETCH(QString, testOutputString); + + TextHandler testTextHandler; + testTextHandler.setData(testInputString); + + QCOMPARE(testTextHandler.handleSendText(), testOutputString); +} + void TextHandlerTest::sendSimpleStringCase() { const QString testInputString = u"This data should just be left alone."_s; @@ -338,7 +400,8 @@ void TextHandlerTest::receiveRichInPlainOut() void TextHandlerTest::receivePlainTextIn() { const QString testInputString = u"\nTest link https://kde.org."_s; - const QString testOutputStringRich = u"<plain text in tag bracket>
Test link https://kde.org."_s; + const QString testOutputStringRich = + u"<plain text in tag bracket>
Test link https://kde.org."_s; QString testOutputStringPlain = u"\nTest link https://kde.org."_s; // Make sure quotes are maintained in a plain string. @@ -408,7 +471,7 @@ void TextHandlerTest::receivePlainStripMarkup() void TextHandlerTest::receiveRichUserPill() { const QString testInputString = u"

@alice:example.org

"_s; - const QString testOutputString = u"@alice:example.org"_s; + const QString testOutputString = u"@alice:example.org"_s; TextHandler testTextHandler; testTextHandler.setData(testInputString); @@ -460,21 +523,23 @@ void TextHandlerTest::receiveRichPlainUrl_data() // so we can confirm consistent behaviour for complex urls. QTest::addRow("link 1") << u"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&via=matrix.org&via=fedora.im Link already rich"_s - << u"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&via=matrix.org&via=fedora.im Link already rich"_s; + << u"https://matrix.to/#/!RvzunyTWZGfNxJVQqv:matrix.org/$-9TJVTh5PvW6MvIhFDwteiyLBVGriinueO5eeIazQS8?via=libera.chat&via=matrix.org&via=fedora.im Link already rich"_s; // Another real case. The linkification wasn't handling it when a single link // contains what looks like and email. It was broken into 3 but needs to // be just single link. QTest::addRow("link 2") << u"https://lore.kernel.org/lkml/CAHk-=wio46vC4t6xXD-sFqjoPwFm_u515jm3suzmkGxQTeA1_A@mail.gmail.com/"_s - << u"https://lore.kernel.org/lkml/CAHk-=wio46vC4t6xXD-sFqjoPwFm_u515jm3suzmkGxQTeA1_A@mail.gmail.com/"_s; + << u"https://lore.kernel.org/lkml/CAHk-=wio46vC4t6xXD-sFqjoPwFm_u515jm3suzmkGxQTeA1_A@mail.gmail.com/"_s; - QTest::addRow("email") << uR"(email@example.com Link already rich)"_s - << uR"(email@example.com Link already rich)"_s; + QTest::addRow("email") + << uR"(email@example.com Link already rich)"_s + << uR"(email@example.com Link already rich)"_s; QTest::addRow("mxid") << u"@user:kde.org Link already rich"_s - << u"@user:kde.org Link already rich"_s; - QTest::addRow("mxid with prefix") << u"a @user:kde.org b"_s << u"a @user:kde.org b"_s; + << u"@user:kde.org Link already rich"_s; + QTest::addRow("mxid with prefix") << u"a @user:kde.org b"_s + << u"a @user:kde.org b"_s; } /** @@ -596,5 +661,35 @@ void TextHandlerTest::componentOutput() QCOMPARE(testTextHandler.textComponents(testInputString), testOutputComponents); } +void TextHandlerTest::updateSpoiler_data() +{ + QTest::addColumn("testInputString"); + QTest::addColumn("testOutputString"); + QTest::addColumn("spoilerRevealed"); + + auto theme = static_cast(qmlAttachedPropertiesObject(this, true)); + QTest::newRow("same length") << u"Test"_s + << u"Test"_s.arg( + theme->alternateBackgroundColor().name()) + << false; + QTest::newRow("different length") << u"Test"_s + << u"Test"_s.arg( + theme->alternateBackgroundColor().name()) + << false; + QTest::newRow("spoiler revealed") + << u"Test"_s.arg(theme->alternateBackgroundColor().name()) + << u"Test"_s.arg(theme->textColor().name(), theme->alternateBackgroundColor().name()) + << true; +} + +void TextHandlerTest::updateSpoiler() +{ + QFETCH(QString, testInputString); + QFETCH(QString, testOutputString); + QFETCH(bool, spoilerRevealed); + + QCOMPARE(TextHandler::updateSpoilerText(this, testInputString, spoilerRevealed), testOutputString); +} + QTEST_MAIN(TextHandlerTest) #include "texthandlertest.moc" diff --git a/src/libneochat/texthandler.cpp b/src/libneochat/texthandler.cpp index e93ecf848..d2e351acd 100644 --- a/src/libneochat/texthandler.cpp +++ b/src/libneochat/texthandler.cpp @@ -93,8 +93,12 @@ QString TextHandler::handleSendText() return outputString; } -QString -TextHandler::handleRecieveRichText(Qt::TextFormat inputFormat, const NeoChatRoom *room, const Quotient::RoomEvent *event, bool stripNewlines, bool isEdited) +QString TextHandler::handleRecieveRichText(Qt::TextFormat inputFormat, + const NeoChatRoom *room, + const Quotient::RoomEvent *event, + bool stripNewlines, + bool isEdited, + bool spoilerRevealed) { m_pos = 0; m_dataBuffer = m_data; @@ -151,7 +155,7 @@ TextHandler::handleRecieveRichText(Qt::TextFormat inputFormat, const NeoChatRoom } else if ((getTagType(m_nextToken) == u"br"_s && stripNewlines)) { nextTokenBuffer = u' '; } - nextTokenBuffer = cleanAttributes(getTagType(m_nextToken), nextTokenBuffer); + nextTokenBuffer = cleanAttributes(getTagType(m_nextToken), nextTokenBuffer, true, spoilerRevealed); } outputString.append(nextTokenBuffer); @@ -333,7 +337,8 @@ MessageComponent TextHandler::nextBlock(const QString &string, Qt::TextFormat inputFormat, const NeoChatRoom *room, const Quotient::RoomEvent *event, - bool isEdited) + bool isEdited, + bool spoilerRevealed) { if (string.isEmpty()) { return {}; @@ -355,7 +360,11 @@ MessageComponent TextHandler::nextBlock(const QString &string, content = unescapeHtml(content); break; default: - content = handleRecieveRichText(inputFormat, room, event, false, isEdited); + content = handleRecieveRichText(inputFormat, room, event, false, isEdited, spoilerRevealed); + } + + if (content.contains(u"data-mx-spoiler"_s)) { + attributes[u"hasSpoiler"_s] = true; } return MessageComponent{messageComponentType, content, attributes}; } @@ -462,8 +471,11 @@ bool TextHandler::isAllowedLink(const QString &link, bool isImg) } } -QString TextHandler::cleanAttributes(const QString &tag, const QString &tagString) +QString TextHandler::cleanAttributes(const QString &tag, const QString &tagString, bool addStyle, bool spoilerRevealed) { + if (!tagString.contains(u'<') || !tagString.contains(u'>')) { + return tagString; + } int nextAttributeIndex = tagString.indexOf(u' ', 1); if (nextAttributeIndex != -1) { @@ -518,11 +530,33 @@ QString TextHandler::cleanAttributes(const QString &tag, const QString &tagStrin nextAttributeIndex = nextSpaceIndex + 1; } - outputString += u'>'; - return outputString; + return addStyle ? this->addStyle(tag, outputString, spoilerRevealed) : outputString + u'>'; } - return tagString; + return addStyle ? this->addStyle(tag, tagString) : tagString; +} + +QString TextHandler::addStyle(const QString &tag, QString cleanTagString, bool spoilerRevealed) +{ + if (cleanTagString.endsWith(u'>')) { + cleanTagString.removeLast(); + } + + if (!cleanTagString.startsWith(u"(qmlAttachedPropertiesObject(this, true)); + cleanTagString += u" style=\"color: %1; background: %2;\""_s.arg(spoilerRevealed ? theme->highlightedTextColor().name() : u"transparent"_s, + theme->alternateBackgroundColor().name()); + } + } + return cleanTagString + u'>'; } QVariantMap TextHandler::getAttributes(const QString &tag, const QString &tagString) @@ -567,8 +601,12 @@ QVariantMap TextHandler::getAttributes(const QString &tag, const QString &tagStr return attributes; } -QList -TextHandler::textComponents(QString string, Qt::TextFormat inputFormat, const NeoChatRoom *room, const Quotient::RoomEvent *event, bool isEdited) +QList TextHandler::textComponents(QString string, + Qt::TextFormat inputFormat, + const NeoChatRoom *room, + const Quotient::RoomEvent *event, + bool isEdited, + bool spoilerRevealed) { if (string.trimmed().isEmpty()) { return {MessageComponent{MessageComponentType::Text, i18n("This event does not have any content."), {}}}; @@ -580,7 +618,8 @@ TextHandler::textComponents(QString string, Qt::TextFormat inputFormat, const Ne QList components; while (!string.isEmpty()) { const auto nextBlockPos = this->nextBlockPos(string); - const auto nextBlock = this->nextBlock(string, nextBlockPos, inputFormat, room, event, nextBlockPos == string.size() ? isEdited : false); + const auto nextBlock = + this->nextBlock(string, nextBlockPos, inputFormat, room, event, nextBlockPos == string.size() ? isEdited : false, spoilerRevealed); components += nextBlock; string.remove(0, nextBlockPos); @@ -798,4 +837,20 @@ QString TextHandler::convertCodeLanguageString(const QString &languageString) return languageString.right(languageString.length() - equalsPos - 1); } +QString TextHandler::updateSpoilerText(QObject *object, QString string, bool spoilerRevealed) +{ + auto it = QRegularExpression(u"]*data-mx-spoiler[^>]*style=\"color: (.*?); background: (.*?);\">"_s).globalMatch(string); + Kirigami::Platform::PlatformTheme *theme = + static_cast(qmlAttachedPropertiesObject(object, true)); + int offset = 0; + while (it.hasNext()) { + const QRegularExpressionMatch match = it.next(); + const auto newColor = spoilerRevealed ? theme->textColor().name() : u"transparent"_s; + string.replace(match.capturedStart(2) + offset, match.capturedLength(2), theme->alternateBackgroundColor().name()); + string.replace(match.capturedStart(1) + offset, match.capturedLength(1), newColor); + offset = newColor.length() - match.capturedLength(1); + } + return string; +} + #include "moc_texthandler.cpp" diff --git a/src/libneochat/texthandler.h b/src/libneochat/texthandler.h index ac356482d..ba4b58fee 100644 --- a/src/libneochat/texthandler.h +++ b/src/libneochat/texthandler.h @@ -75,7 +75,8 @@ public: const NeoChatRoom *room = nullptr, const Quotient::RoomEvent *event = nullptr, bool stripNewlines = false, - bool isEdited = false); + bool isEdited = false, + bool spoilerRevealed = false); /** * @brief Handle the text as a plain output for a message being received. @@ -104,7 +105,13 @@ public: Qt::TextFormat inputFormat = Qt::RichText, const NeoChatRoom *room = nullptr, const Quotient::RoomEvent *event = nullptr, - bool isEdited = false); + bool isEdited = false, + bool spoilerRevealed = false); + + /** + * @brief Modify the style parameters of the spoilers to reveal or hide the text. + */ + static QString updateSpoilerText(QObject *object, QString string, bool spoilerRevealed); private: QString m_data; @@ -123,7 +130,8 @@ private: Qt::TextFormat inputFormat = Qt::RichText, const NeoChatRoom *room = nullptr, const Quotient::RoomEvent *event = nullptr, - bool isEdited = false); + bool isEdited = false, + bool spoilerRevealed = false); QString stripBlockTags(QString string, const QString &tagType) const; QString getTagType(const QString &tagToken) const; @@ -133,7 +141,8 @@ private: bool isAllowedTag(const QString &type); bool isAllowedAttribute(const QString &tag, const QString &attribute); bool isAllowedLink(const QString &link, bool isImg = false); - QString cleanAttributes(const QString &tag, const QString &tagString); + QString cleanAttributes(const QString &tag, const QString &tagString, bool addStyle = false, bool spoilerRevealed = false); + QString addStyle(const QString &tag, QString cleanTagString, bool spoilerRevealed = false); QVariantMap getAttributes(const QString &tag, const QString &tagString); QString markdownToHTML(const QString &markdown); diff --git a/src/messagecontent/TextComponent.qml b/src/messagecontent/TextComponent.qml index 5b93c00b8..cfe592383 100644 --- a/src/messagecontent/TextComponent.qml +++ b/src/messagecontent/TextComponent.qml @@ -16,6 +16,11 @@ import org.kde.neochat TextEdit { id: root + /** + * @brief The index of the delegate in the model. + */ + required property int index + /** * @brief The matrix ID of the message event. */ @@ -35,90 +40,28 @@ TextEdit { */ required property string display + /** + * @brief The attributes of the component. + */ + required property var componentAttributes + + /** + * @brief Whether the message contains a spoiler + */ + readonly property var hasSpoiler: root.componentAttributes?.hasSpoiler ?? false + /** * @brief Whether this message is replying to another. */ property bool isReply: false - /** - * @brief Regex for detecting a message with a spoiler. - */ - readonly property var hasSpoiler: /data-mx-spoiler/g - - /** - * @brief Whether a spoiler should be revealed. - */ - property bool spoilerRevealed: !hasSpoiler.test(display) - - /** - * @brief The color of spoiler blocks, to be theme-agnostic. - */ - property color spoilerBlockColor: Kirigami.ColorUtils.tintWithAlpha("#232629", Kirigami.Theme.textColor, 0.15) - Layout.fillWidth: true Layout.fillHeight: true Layout.maximumWidth: Message.maxContentWidth - ListView.onReused: Qt.binding(() => !hasSpoiler.test(display)) - persistentSelection: true - text: "" + display + text: display color: Kirigami.Theme.textColor selectedTextColor: Kirigami.Theme.highlightedTextColor @@ -133,7 +76,6 @@ a{ textFormat: Text.RichText onLinkActivated: link => { - spoilerRevealed = true; RoomManager.resolveResource(link, "join"); } onHoveredLinkChanged: if (hoveredLink.length > 0 && hoveredLink !== "1") { @@ -143,11 +85,11 @@ a{ } HoverHandler { - cursorShape: (root.hoveredLink || !spoilerRevealed) ? Qt.PointingHandCursor : Qt.IBeamCursor + cursorShape: root.hoveredLink || (!(root.componentAttributes?.spoilerRevealed ?? false) && root.hasSpoiler) ? Qt.PointingHandCursor : Qt.IBeamCursor } TapHandler { - enabled: !root.hoveredLink && !spoilerRevealed - onTapped: spoilerRevealed = true + enabled: !root.hoveredLink && root.hasSpoiler + onTapped: root.Message.contentModel.toggleSpoiler(root.Message.contentFilterModel.mapToSource(root.Message.contentFilterModel.index(root.index, 0))) } TapHandler { enabled: !root.hoveredLink diff --git a/src/messagecontent/models/messagecontentmodel.cpp b/src/messagecontent/models/messagecontentmodel.cpp index 47462ee87..151940ccd 100644 --- a/src/messagecontent/models/messagecontentmodel.cpp +++ b/src/messagecontent/models/messagecontentmodel.cpp @@ -9,6 +9,7 @@ #include "contentprovider.h" #include "enums/messagecomponenttype.h" #include "neochatconnection.h" +#include "texthandler.h" using namespace Quotient; @@ -17,6 +18,11 @@ MessageContentModel::MessageContentModel(NeoChatRoom *room, MessageContentModel , m_room(room) , m_eventId(eventId) { + m_theme = static_cast(qmlAttachedPropertiesObject(this, true)); + if (m_theme) { + connect(m_theme, &Kirigami::Platform::PlatformTheme::colorsChanged, this, &MessageContentModel::updateSpoilers); + } + initializeModel(); } @@ -277,4 +283,40 @@ void MessageContentModel::closeLinkPreview(int row) } } +void MessageContentModel::updateSpoilers() +{ + for (auto it = m_components.begin(); it != m_components.end(); ++it) { + updateSpoiler(index(it - m_components.begin())); + } +} + +void MessageContentModel::updateSpoiler(const QModelIndex &index) +{ + const auto row = index.row(); + if (row < 0 || row >= rowCount()) { + qWarning() << __FUNCTION__ << "called with row" << row << "which does not exist. m_components.size() =" << m_components.size(); + return; + } + + const auto spoilerRevealed = m_components[row].attributes.value("spoilerRevealed"_L1, false).toBool(); + m_components[row].display = TextHandler::updateSpoilerText(this, m_components[row].display, spoilerRevealed); + Q_EMIT dataChanged(index, index, {DisplayRole}); +} + +void MessageContentModel::toggleSpoiler(QModelIndex index) +{ + const auto row = index.row(); + if (row < 0 || row >= rowCount()) { + qWarning() << __FUNCTION__ << "called with row" << row << "which does not exist. m_components.size() =" << m_components.size(); + return; + } + if (m_components[row].type != MessageComponentType::Text) { + return; + } + const auto spoilerRevealed = !m_components[row].attributes.value("spoilerRevealed"_L1, false).toBool(); + m_components[row].attributes["spoilerRevealed"_L1] = spoilerRevealed; + Q_EMIT dataChanged(index, index, {ComponentAttributesRole}); + updateSpoiler(index); +} + #include "moc_messagecontentmodel.cpp" diff --git a/src/messagecontent/models/messagecontentmodel.h b/src/messagecontent/models/messagecontentmodel.h index e2dc5f7e3..508f3221f 100644 --- a/src/messagecontent/models/messagecontentmodel.h +++ b/src/messagecontent/models/messagecontentmodel.h @@ -7,6 +7,7 @@ #include #include +#include #ifndef Q_OS_ANDROID #include #include @@ -101,6 +102,11 @@ public: */ Q_INVOKABLE void closeLinkPreview(int row); + /** + * @brief Toggle spoiler for the component at the given row. + */ + Q_INVOKABLE void toggleSpoiler(QModelIndex index); + Q_SIGNALS: void authorChanged(); @@ -232,4 +238,8 @@ private: QList m_removedLinkPreviews; MessageComponent linkPreviewComponent(const QUrl &link); + + Kirigami::Platform::PlatformTheme *m_theme = nullptr; + void updateSpoilers(); + void updateSpoiler(const QModelIndex &index); }; diff --git a/src/timeline/Bubble.qml b/src/timeline/Bubble.qml index 8a008052d..fdac863f3 100644 --- a/src/timeline/Bubble.qml +++ b/src/timeline/Bubble.qml @@ -73,6 +73,8 @@ QQC2.Control { */ signal showMessageMenu + Message.contentFilterModel: messageContentFilterModel + contentItem: RowLayout { Kirigami.Icon { source: "content-loading-symbolic" @@ -87,6 +89,7 @@ QQC2.Control { Repeater { id: contentRepeater model: MessageContentFilterModel { + id: messageContentFilterModel showAuthor: root.showAuthor sourceModel: root.contentModel } diff --git a/src/timeline/messageattached.cpp b/src/timeline/messageattached.cpp index 9798dc8bf..c685cbd0d 100644 --- a/src/timeline/messageattached.cpp +++ b/src/timeline/messageattached.cpp @@ -66,6 +66,22 @@ void MessageAttached::setContentModel(MessageContentModel *contentModel) Q_EMIT contentModelChanged(); } +MessageContentFilterModel *MessageAttached::contentFilterModel() const +{ + return m_contentFilterModel; +} + +void MessageAttached::setContentFilterModel(MessageContentFilterModel *contentFilterModel) +{ + m_explicitContentFilterModel = true; + if (m_contentFilterModel == contentFilterModel) { + return; + } + m_contentFilterModel = contentFilterModel; + propagateMessage(this); + Q_EMIT contentFilterModelChanged(); +} + int MessageAttached::index() const { return m_index; @@ -147,6 +163,11 @@ void MessageAttached::propagateMessage(MessageAttached *message) Q_EMIT contentModelChanged(); } + if (!m_explicitContentFilterModel && m_contentFilterModel != message->contentFilterModel()) { + m_contentFilterModel = message->contentFilterModel(); + Q_EMIT contentFilterModelChanged(); + } + if (m_explicitIndex || m_index != message->index()) { m_index = message->index(); Q_EMIT indexChanged(); diff --git a/src/timeline/messageattached.h b/src/timeline/messageattached.h index 89fd61cce..3d7841b2f 100644 --- a/src/timeline/messageattached.h +++ b/src/timeline/messageattached.h @@ -7,6 +7,7 @@ #include #include +#include "models/messagecontentfiltermodel.h" #include "models/messagecontentmodel.h" #include "neochatroom.h" @@ -32,6 +33,11 @@ class MessageAttached : public QQuickAttachedPropertyPropagator */ Q_PROPERTY(MessageContentModel *contentModel READ contentModel WRITE setContentModel NOTIFY contentModelChanged FINAL) + /** + * @brief The content model for the current message. + */ + Q_PROPERTY(MessageContentFilterModel *contentFilterModel READ contentFilterModel WRITE setContentFilterModel NOTIFY contentFilterModelChanged FINAL) + /** * @brief The index of the message in the timeline */ @@ -66,6 +72,9 @@ public: MessageContentModel *contentModel() const; void setContentModel(MessageContentModel *contentModel); + MessageContentFilterModel *contentFilterModel() const; + void setContentFilterModel(MessageContentFilterModel *contentFilterModel); + int index() const; void setIndex(int index); @@ -82,6 +91,7 @@ Q_SIGNALS: void roomChanged(); void timelineChanged(); void contentModelChanged(); + void contentFilterModelChanged(); void indexChanged(); void maxContentWidthChanged(); void selectedTextChanged(); @@ -101,6 +111,9 @@ private: QPointer m_contentModel; bool m_explicitContentModel = false; + QPointer m_contentFilterModel; + bool m_explicitContentFilterModel = false; + int m_index = -1; bool m_explicitIndex = false;