Message attached property

Create Message attached property to propagate parameters like room, timeline, index and maxContentWidth down to the message content avoiding lots of boilerplate
This commit is contained in:
James Graham
2025-03-10 18:28:42 +00:00
parent ea6ad902a7
commit 2aeed10429
32 changed files with 310 additions and 447 deletions

View File

@@ -296,6 +296,9 @@ ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
qml/HoverLinkIndicator.qml
qml/AvatarNotification.qml
qml/ReasonDialog.qml
SOURCES
messageattached.cpp
messageattached.h
DEPENDENCIES
QtCore
QtQuick

View File

@@ -349,7 +349,7 @@ QQC2.Control {
replyEventId: _private.chatBarCache.replyId
replyAuthor: _private.chatBarCache.relationAuthor
replyContentModel: _private.chatBarCache.relationEventContentModel
maxContentWidth: paneLoader.item.width
Message.maxContentWidth: paneLoader.item.width
}
QQC2.Button {
id: cancelButton

126
src/messageattached.cpp Normal file
View File

@@ -0,0 +1,126 @@
// SPDX-FileCopyrightText: 2025 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include "messageattached.h"
MessageAttached::MessageAttached(QObject *parent)
: QQuickAttachedPropertyPropagator(parent)
{
if (parent == nullptr) {
qWarning() << "Message must be attached to an Item" << parent;
return;
}
initialize();
}
MessageAttached *MessageAttached::qmlAttachedProperties(QObject *object)
{
return new MessageAttached(object);
}
NeoChatRoom *MessageAttached::room() const
{
return m_room;
}
void MessageAttached::setRoom(NeoChatRoom *room)
{
m_explicitRoom = true;
if (m_room == room) {
return;
}
m_room = room;
propagateMessage(this);
Q_EMIT roomChanged();
}
QQuickItem *MessageAttached::timeline() const
{
return m_timeline;
}
void MessageAttached::setTimeline(QQuickItem *timeline)
{
m_explicitTimeline = true;
if (m_timeline == timeline) {
return;
}
m_timeline = timeline;
propagateMessage(this);
Q_EMIT timelineChanged();
}
int MessageAttached::index() const
{
return m_index;
}
void MessageAttached::setIndex(int index)
{
m_explicitIndex = true;
if (m_index == index) {
return;
}
m_index = index;
propagateMessage(this);
Q_EMIT indexChanged();
}
qreal MessageAttached::maxContentWidth() const
{
return m_maxContentWidth;
}
void MessageAttached::setMaxContentWidth(qreal maxContentWidth)
{
m_explicitMaxContentWidth = true;
if (m_maxContentWidth == maxContentWidth) {
return;
}
m_maxContentWidth = maxContentWidth;
propagateMessage(this);
Q_EMIT maxContentWidthChanged();
}
void MessageAttached::propagateMessage(MessageAttached *message)
{
if (m_explicitRoom || m_room != message->room()) {
m_room = message->room();
Q_EMIT roomChanged();
}
if (m_explicitTimeline || m_timeline != message->timeline()) {
m_timeline = message->timeline();
Q_EMIT timelineChanged();
}
if (m_explicitIndex || m_index != message->index()) {
m_index = message->index();
Q_EMIT indexChanged();
}
if (m_explicitMaxContentWidth || m_maxContentWidth != message->maxContentWidth()) {
m_maxContentWidth = message->maxContentWidth();
Q_EMIT maxContentWidthChanged();
}
const auto styles = attachedChildren();
for (auto *child : attachedChildren()) {
MessageAttached *message = qobject_cast<MessageAttached *>(child);
if (message != nullptr) {
message->propagateMessage(this);
}
}
}
void MessageAttached::attachedParentChange(QQuickAttachedPropertyPropagator *newParent, QQuickAttachedPropertyPropagator *oldParent)
{
Q_UNUSED(oldParent);
MessageAttached *attachedParent = qobject_cast<MessageAttached *>(newParent);
if (attachedParent) {
propagateMessage(attachedParent);
}
}
#include "moc_messageattached.cpp"

78
src/messageattached.h Normal file
View File

@@ -0,0 +1,78 @@
// SPDX-FileCopyrightText: 2025 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#pragma once
#include <QQmlEngine>
#include <QQuickAttachedPropertyPropagator>
#include <QQuickItem>
#include "neochatroom.h"
class MessageAttached : public QQuickAttachedPropertyPropagator
{
Q_OBJECT
QML_NAMED_ELEMENT(Message)
QML_ATTACHED(MessageAttached)
QML_UNCREATABLE("")
/**
* @brief The room that the message comes from.
*/
Q_PROPERTY(NeoChatRoom *room READ room WRITE setRoom NOTIFY roomChanged FINAL)
/**
* @brief The timeline for the current message.
*/
Q_PROPERTY(QQuickItem *timeline READ timeline WRITE setTimeline NOTIFY timelineChanged FINAL)
/**
* @brief The index of the message in the timeline
*/
Q_PROPERTY(int index READ index WRITE setIndex NOTIFY indexChanged FINAL)
/**
* @brief The width available to the message content.
*/
Q_PROPERTY(qreal maxContentWidth READ maxContentWidth WRITE setMaxContentWidth NOTIFY maxContentWidthChanged FINAL)
public:
explicit MessageAttached(QObject *parent = nullptr);
static MessageAttached *qmlAttachedProperties(QObject *object);
NeoChatRoom *room() const;
void setRoom(NeoChatRoom *room);
QQuickItem *timeline() const;
void setTimeline(QQuickItem *timeline);
int index() const;
void setIndex(int index);
qreal maxContentWidth() const;
void setMaxContentWidth(qreal maxContentWidth);
Q_SIGNALS:
void roomChanged();
void timelineChanged();
void indexChanged();
void maxContentWidthChanged();
protected:
void propagateMessage(MessageAttached *message);
void attachedParentChange(QQuickAttachedPropertyPropagator *newParent, QQuickAttachedPropertyPropagator *oldParent) override;
private:
QPointer<NeoChatRoom> m_room;
bool m_explicitRoom = false;
QPointer<QQuickItem> m_timeline;
bool m_explicitTimeline = false;
int m_index;
bool m_explicitIndex = false;
qreal m_maxContentWidth = -1;
bool m_explicitMaxContentWidth = false;
};

View File

@@ -18,11 +18,6 @@ import org.kde.neochat
ColumnLayout {
id: root
/**
* @brief The NeoChatRoom the delegate is being displayed in.
*/
required property NeoChatRoom room
/**
* @brief The matrix ID of the message event.
*/
@@ -56,11 +51,6 @@ ColumnLayout {
audio.play();
}
/**
* @brief The maximum width that the bubble's content can be.
*/
property real maxContentWidth: -1
MediaPlayer {
id: audio
onErrorOccurred: (error, errorString) => console.warn("Audio playback error:" + error + errorString)
@@ -75,7 +65,7 @@ ColumnLayout {
PropertyChanges {
target: playButton
icon.name: "media-playback-start"
onClicked: root.room.downloadFile(root.eventId)
onClicked: Message.room.downloadFile(root.eventId)
}
},
State {
@@ -89,7 +79,7 @@ ColumnLayout {
target: playButton
icon.name: "media-playback-stop"
onClicked: {
root.room.cancelFileTransfer(root.eventId);
Message.room.cancelFileTransfer(root.eventId);
}
}
},
@@ -159,7 +149,7 @@ ColumnLayout {
}
QQC2.Label {
visible: root.maxContentWidth > Kirigami.Units.gridUnit * 12
visible: root.Message.maxContentWidth > Kirigami.Units.gridUnit * 12
text: Format.formatDuration(audio.position) + "/" + Format.formatDuration(audio.duration)
}
@@ -167,7 +157,7 @@ ColumnLayout {
QQC2.Label {
Layout.alignment: Qt.AlignRight
Layout.rightMargin: Kirigami.Units.smallSpacing
visible: audio.hasAudio && root.maxContentWidth < Kirigami.Units.gridUnit * 12
visible: audio.hasAudio && root.Message.maxContentWidth < Kirigami.Units.gridUnit * 12
text: Format.formatDuration(audio.position) + "/" + Format.formatDuration(audio.duration)
}

View File

@@ -31,13 +31,8 @@ RowLayout {
*/
required property string timeString
/**
* @brief The maximum width that the bubble's content can be.
*/
property real maxContentWidth: -1
Layout.fillWidth: true
Layout.maximumWidth: root.maxContentWidth
Layout.maximumWidth: Message.maxContentWidth
implicitHeight: Math.max(nameButton.implicitHeight, timeLabel.implicitHeight)

View File

@@ -15,31 +15,6 @@ import org.kde.neochat
DelegateChooser {
id: root
/**
* @brief The NeoChatRoom the delegate is being displayed in.
*/
required property NeoChatRoom room
/**
* @brief The index of the delegate in the model.
*/
required property var index
/**
* @brief The timeline ListView this component is being used in.
*/
required property ListView timeline
/**
* @brief The maximum width that the bubble's content can be.
*/
property real maxContentWidth: -1
/**
* @brief The reply has been clicked.
*/
signal replyClicked(string eventID)
/**
* @brief The user selected text has changed.
*/
@@ -66,15 +41,12 @@ DelegateChooser {
DelegateChoice {
roleValue: MessageComponentType.Author
delegate: AuthorComponent {
maxContentWidth: root.maxContentWidth
}
delegate: AuthorComponent {}
}
DelegateChoice {
roleValue: MessageComponentType.Text
delegate: TextComponent {
maxContentWidth: root.maxContentWidth
onSelectedTextChanged: root.selectedTextChanged(selectedText)
onHoveredLinkChanged: root.hoveredLinkChanged(hoveredLink)
onShowMessageMenu: root.showMessageMenu()
@@ -83,28 +55,17 @@ DelegateChooser {
DelegateChoice {
roleValue: MessageComponentType.Image
delegate: ImageComponent {
room: root.room
index: root.index
timeline: root.timeline
maxContentWidth: root.maxContentWidth
}
delegate: ImageComponent {}
}
DelegateChoice {
roleValue: MessageComponentType.Video
delegate: VideoComponent {
room: root.room
index: root.index
timeline: root.timeline
maxContentWidth: root.maxContentWidth
}
delegate: VideoComponent {}
}
DelegateChoice {
roleValue: MessageComponentType.Code
delegate: CodeComponent {
maxContentWidth: root.maxContentWidth
onSelectedTextChanged: selectedText => {
root.selectedTextChanged(selectedText);
}
@@ -115,7 +76,6 @@ DelegateChooser {
DelegateChoice {
roleValue: MessageComponentType.Quote
delegate: QuoteComponent {
maxContentWidth: root.maxContentWidth
onSelectedTextChanged: selectedText => {
root.selectedTextChanged(selectedText);
}
@@ -125,86 +85,57 @@ DelegateChooser {
DelegateChoice {
roleValue: MessageComponentType.Audio
delegate: AudioComponent {
room: root.room
maxContentWidth: root.maxContentWidth
}
delegate: AudioComponent {}
}
DelegateChoice {
roleValue: MessageComponentType.File
delegate: FileComponent {
room: root.room
maxContentWidth: root.maxContentWidth
}
delegate: FileComponent {}
}
DelegateChoice {
roleValue: MessageComponentType.Itinerary
delegate: ItineraryComponent {
maxContentWidth: root.maxContentWidth
}
delegate: ItineraryComponent {}
}
DelegateChoice {
roleValue: MessageComponentType.Pdf
delegate: PdfPreviewComponent {
maxContentWidth: root.maxContentWidth
}
delegate: PdfPreviewComponent {}
}
DelegateChoice {
roleValue: MessageComponentType.Poll
delegate: PollComponent {
room: root.room
maxContentWidth: root.maxContentWidth
}
delegate: PollComponent {}
}
DelegateChoice {
roleValue: MessageComponentType.Location
delegate: LocationComponent {
maxContentWidth: root.maxContentWidth
}
delegate: LocationComponent {}
}
DelegateChoice {
roleValue: MessageComponentType.LiveLocation
delegate: LiveLocationComponent {
room: root.room
maxContentWidth: root.maxContentWidth
}
delegate: LiveLocationComponent {}
}
DelegateChoice {
roleValue: MessageComponentType.Encrypted
delegate: EncryptedComponent {
maxContentWidth: root.maxContentWidth
}
delegate: EncryptedComponent {}
}
DelegateChoice {
roleValue: MessageComponentType.Reply
delegate: ReplyComponent {
maxContentWidth: root.maxContentWidth
onReplyClicked: eventId => {
root.replyClicked(eventId);
}
}
delegate: ReplyComponent {}
}
DelegateChoice {
roleValue: MessageComponentType.Reaction
delegate: ReactionComponent {
room: root.room
maxContentWidth: root.maxContentWidth
}
delegate: ReactionComponent {}
}
DelegateChoice {
roleValue: MessageComponentType.LinkPreview
delegate: LinkPreviewComponent {
maxContentWidth: root.maxContentWidth
onRemove: index => root.removeLinkPreview(index)
}
}
@@ -213,31 +144,23 @@ DelegateChooser {
roleValue: MessageComponentType.LinkPreviewLoad
delegate: LinkPreviewLoadComponent {
type: LinkPreviewLoadComponent.LinkPreview
maxContentWidth: root.maxContentWidth
onRemove: index => root.removeLinkPreview(index)
}
}
DelegateChoice {
roleValue: MessageComponentType.ChatBar
delegate: ChatBarComponent {
room: root.room
maxContentWidth: root.maxContentWidth
}
delegate: ChatBarComponent {}
}
DelegateChoice {
roleValue: MessageComponentType.ReplyButton
delegate: ReplyButtonComponent {
room: root.room
maxContentWidth: root.maxContentWidth
}
delegate: ReplyButtonComponent {}
}
DelegateChoice {
roleValue: MessageComponentType.FetchButton
delegate: FetchButtonComponent {
maxContentWidth: root.maxContentWidth
onFetchMoreEvents: root.fetchMoreEvents()
}
}
@@ -252,16 +175,14 @@ DelegateChooser {
DelegateChoice {
roleValue: MessageComponentType.Loading
delegate: LoadComponent {
maxContentWidth: root.maxContentWidth
}
delegate: LoadComponent {}
}
DelegateChoice {
roleValue: MessageComponentType.Separator
delegate: Kirigami.Separator {
Layout.fillWidth: true
Layout.maximumWidth: root.maxContentWidth
Layout.maximumWidth: Message.maxContentWidth
}
}

View File

@@ -22,16 +22,6 @@ import org.kde.neochat
QQC2.Control {
id: root
/**
* @brief The NeoChatRoom the delegate is being displayed in.
*/
required property NeoChatRoom room
/**
* @brief The index of the delegate in the model.
*/
required property var index
/**
* @brief The message author.
*
@@ -66,23 +56,8 @@ QQC2.Control {
*/
property alias showBackground: bubbleBackground.visible
/**
* @brief The timeline ListView this component is being used in.
*/
required property ListView timeline
/**
* @brief The maximum width that the bubble's content can be.
*/
property real maxContentWidth: -1
required property bool isPending
/**
* @brief The reply has been clicked.
*/
signal replyClicked(string eventID)
/**
* @brief The user selected text has changed.
*/
@@ -108,6 +83,7 @@ QQC2.Control {
ColumnLayout {
id: contentColumn
spacing: Kirigami.Units.smallSpacing
Repeater {
id: contentRepeater
model: MessageContentFilterModel {
@@ -115,14 +91,6 @@ QQC2.Control {
sourceModel: root.contentModel
}
delegate: MessageComponentChooser {
room: root.room
index: root.index
timeline: root.timeline
maxContentWidth: root.maxContentWidth
onReplyClicked: eventId => {
root.replyClicked(eventId);
}
onSelectedTextChanged: selectedText => {
root.selectedTextChanged(selectedText);
}

View File

@@ -16,25 +16,15 @@ import org.kde.neochat.chatbar
QQC2.TextArea {
id: root
/**
* @brief The NeoChatRoom the delegate is being displayed in.
*/
required property NeoChatRoom room
/**
* @brief The ChatBarCache to use.
*/
required property ChatBarCache chatBarCache
onChatBarCacheChanged: documentHandler.chatBarCache = chatBarCache
/**
* @brief The maximum width that the bubble's content can be.
*/
property real maxContentWidth: -1
Layout.fillWidth: true
Layout.preferredWidth: textMetrics.advanceWidth + rightPadding + Kirigami.Units.smallSpacing + Kirigami.Units.gridUnit
Layout.maximumWidth: root.maxContentWidth
Layout.maximumWidth: Message.maxContentWidth
Layout.minimumHeight: chatButtons.height + topPadding + bottomPadding
Component.onCompleted: _private.updateText()
@@ -124,7 +114,7 @@ QQC2.TextArea {
height: implicitHeight
y: -height - 5
z: 10
connection: root.room.connection
connection: root.Message.room.connection
chatDocumentHandler: documentHandler
Behavior on height {
NumberAnimation {
@@ -144,7 +134,7 @@ QQC2.TextArea {
cursorPosition: root.cursorPosition
selectionStart: root.selectionStart
selectionEnd: root.selectionEnd
room: root.room // We don't care about saving for edits so this is OK.
room: root.Message.room // We don't care about saving for edits so this is OK.
mentionColor: Kirigami.Theme.linkColor
errorColor: Kirigami.Theme.negativeTextColor
}

View File

@@ -37,11 +37,6 @@ QQC2.Control {
*/
required property var componentAttributes
/**
* @brief The maximum width that the bubble's content can be.
*/
property real maxContentWidth: -1
/**
* @brief The user selected text has changed.
*/
@@ -54,7 +49,7 @@ QQC2.Control {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.maximumWidth: root.maxContentWidth
Layout.maximumWidth: Message.maxContentWidth
Layout.maximumHeight: Kirigami.Units.gridUnit * 20
topPadding: 0
@@ -64,7 +59,7 @@ QQC2.Control {
contentItem: QQC2.ScrollView {
id: codeScrollView
contentWidth: root.maxContentWidth
contentWidth: root.Message.maxContentWidth
// HACK: Hide unnecessary horizontal scrollbar (https://bugreports.qt.io/browse/QTBUG-83890)
QQC2.ScrollBar.horizontal.policy: QQC2.ScrollBar.AlwaysOff

View File

@@ -6,17 +6,14 @@ import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.neochat
/**
* @brief A component for an encrypted message that can't be decrypted.
*/
TextEdit {
/**
* @brief The maximum width that the bubble's content can be.
*/
property real maxContentWidth: -1
Layout.fillWidth: true
Layout.maximumWidth: root.maxContentWidth
Layout.maximumWidth: Message.maxContentWidth
text: i18n("This message is encrypted and the sender has not shared the key with this device.")
color: Kirigami.Theme.disabledTextColor

View File

@@ -17,18 +17,13 @@ import org.kde.neochat.chatbar
Delegates.RoundedItemDelegate {
id: root
/**
* @brief The maximum width that the bubble's content can be.
*/
property real maxContentWidth: -1
/**
* @brief Request more events in the thread be loaded.
*/
signal fetchMoreEvents()
Layout.fillWidth: true
Layout.maximumWidth: root.maxContentWidth
Layout.maximumWidth: Message.maxContentWidth
leftInset: 0
rightInset: 0

View File

@@ -20,11 +20,6 @@ import org.kde.neochat
ColumnLayout {
id: root
/**
* @brief The NeoChatRoom the delegate is being displayed in.
*/
required property NeoChatRoom room
/**
* @brief The matrix ID of the message event.
*/
@@ -65,14 +60,9 @@ ColumnLayout {
*/
property bool autoOpenFile: false
/**
* @brief The maximum width that the bubble's content can be.
*/
property real maxContentWidth: -1
function saveFileAs() {
const dialog = fileDialog.createObject(QQC2.Overlay.overlay);
dialog.selectedFile = root.room.fileNameToDownload(root.eventId);
dialog.selectedFile = Message.room.fileNameToDownload(root.eventId);
dialog.open();
}
@@ -81,7 +71,7 @@ ColumnLayout {
}
Layout.fillWidth: true
Layout.maximumWidth: root.maxContentWidth
Layout.maximumWidth: Message.maxContentWidth
spacing: Kirigami.Units.largeSpacing
RowLayout {
@@ -138,7 +128,7 @@ ColumnLayout {
target: downloadButton
icon.name: "media-playback-stop"
QQC2.ToolTip.text: i18nc("tooltip for a button on a message; stops downloading the message's file", "Stop Download")
onClicked: root.room.cancelFileTransfer(root.eventId)
onClicked: Message.room.cancelFileTransfer(root.eventId)
}
}
]
@@ -171,7 +161,7 @@ ColumnLayout {
icon.name: "document-open"
onClicked: {
autoOpenFile = true;
root.room.downloadTempFile(root.eventId);
root.Message.room.downloadTempFile(root.eventId);
}
QQC2.ToolTip.text: i18nc("tooltip for a button on a message; offers ability to open its downloaded file with an appropriate application", "Open File")
@@ -201,7 +191,7 @@ ColumnLayout {
if (autoOpenFile) {
UrlHelper.copyTo(root.fileTransferInfo.localPath, selectedFile);
} else {
root.room.download(root.eventId, selectedFile);
root.Message.room.download(root.eventId, selectedFile);
}
}
}

View File

@@ -16,16 +16,6 @@ import org.kde.neochat
Item {
id: root
/**
* @brief The NeoChatRoom the delegate is being displayed in.
*/
required property NeoChatRoom room
/**
* @brief The index of the delegate in the model.
*/
required property var index
/**
* @brief The matrix ID of the message event.
*/
@@ -56,16 +46,6 @@ Item {
*/
required property var fileTransferInfo
/**
* @brief The timeline ListView this component is being used in.
*/
required property ListView timeline
/**
* @brief The maximum width that the bubble's content can be.
*/
property real maxContentWidth: -1
implicitWidth: mediaSizeHelper.currentSize.width
implicitHeight: mediaSizeHelper.currentSize.height
@@ -153,13 +133,13 @@ Item {
if (root.mediaInfo.animated) {
_private.imageItem.paused = true;
}
root.timeline.interactive = false;
root.Message.timeline.interactive = false;
if (!root.mediaInfo.isSticker) {
// We need to make sure the index is that of the MediaMessageFilterModel.
if (root.timeline.model instanceof MessageFilterModel) {
RoomManager.maximizeMedia(RoomManager.mediaMessageFilterModel.getRowForSourceItem(root.index));
if (root.Message.timeline.model instanceof MessageFilterModel) {
RoomManager.maximizeMedia(RoomManager.mediaMessageFilterModel.getRowForSourceItem(root.Message.index));
} else {
RoomManager.maximizeMedia(root.index);
RoomManager.maximizeMedia(root.Message.index);
}
}
}
@@ -170,7 +150,7 @@ Item {
openSavedFile();
} else {
openOnFinished = true;
root.room.downloadFile(root.eventId, StandardPaths.writableLocation(StandardPaths.CacheLocation) + "/" + root.eventId.replace(":", "_").replace("/", "_").replace("+", "_") + root.room.fileNameToDownload(root.eventId));
Message.room.downloadFile(root.eventId, StandardPaths.writableLocation(StandardPaths.CacheLocation) + "/" + root.eventId.replace(":", "_").replace("/", "_").replace("+", "_") + Message.room.fileNameToDownload(root.eventId));
}
}
@@ -183,7 +163,7 @@ Item {
MediaSizeHelper {
id: mediaSizeHelper
contentMaxWidth: root.maxContentWidth
contentMaxWidth: root.Message.maxContentWidth
mediaWidth: root?.mediaInfo.isSticker ? 256 : (root?.mediaInfo.width ?? 0)
mediaHeight: root?.mediaInfo.isSticker ? 256 : (root?.mediaInfo.height ?? 0)
}

View File

@@ -9,6 +9,8 @@ import Qt.labs.qmlmodels
import org.kde.kirigami as Kirigami
import org.kde.neochat
/**
* @brief A component to show a preview of a file that can integrate with KDE itinerary.
*/
@@ -20,13 +22,8 @@ ColumnLayout {
*/
required property var itineraryModel
/**
* @brief The maximum width that the bubble's content can be.
*/
property real maxContentWidth: -1
Layout.fillWidth: true
Layout.maximumWidth: root.maxContentWidth
Layout.maximumWidth: Message.maxContentWidth
spacing: Kirigami.Units.largeSpacing
Repeater {

View File

@@ -43,18 +43,13 @@ QQC2.Control {
property bool truncated: linkPreviewDescription.truncated || !linkPreviewDescription.visible
/**
* @brief The maximum width that the bubble's content can be.
*/
property real maxContentWidth: -1
/**
* @brief Request for this delegate to be removed.
*/
signal remove(int index)
Layout.fillWidth: true
Layout.maximumWidth: root.maxContentWidth
Layout.maximumWidth: Message.maxContentWidth
Layout.minimumHeight: root.defaultHeight
leftPadding: 0

View File

@@ -7,6 +7,8 @@ import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.neochat
/**
* @brief A component to show a link preview loading from a message.
*/
@@ -28,11 +30,6 @@ QQC2.Control {
*/
property var defaultHeight: Kirigami.Units.gridUnit * 3 + Kirigami.Units.smallSpacing * 2
/**
* @brief The maximum width that the bubble's content can be.
*/
property real maxContentWidth: -1
/**
* @brief Request for this delegate to be removed.
*/
@@ -44,7 +41,7 @@ QQC2.Control {
}
Layout.fillWidth: true
Layout.maximumWidth: root.maxContentWidth
Layout.maximumWidth: Message.maxContentWidth
contentItem : RowLayout {
spacing: Kirigami.Units.smallSpacing

View File

@@ -17,11 +17,6 @@ import org.kde.neochat
ColumnLayout {
id: root
/**
* @brief The NeoChatRoom the delegate is being displayed in.
*/
required property NeoChatRoom room
/**
* @brief The matrix ID of the message event.
*/
@@ -32,24 +27,19 @@ ColumnLayout {
*/
required property string display
/**
* @brief The maximum width that the bubble's content can be.
*/
property real maxContentWidth: -1
Layout.fillWidth: true
Layout.maximumWidth: root.maxContentWidth
Layout.maximumWidth: Message.maxContentWidth
LiveLocationsModel {
id: liveLocationModel
eventId: root.eventId
room: root.room
room: Message.room
}
MapView {
id: mapView
Layout.fillWidth: true
Layout.preferredWidth: root.maxContentWidth
Layout.preferredHeight: root.maxContentWidth / 16 * 9
Layout.preferredWidth: root.Message.maxContentWidth
Layout.preferredHeight: root.Message.maxContentWidth / 16 * 9
map.center: QtPositioning.coordinate(liveLocationModel.boundingBox.y, liveLocationModel.boundingBox.x)
map.zoomLevel: 15

View File

@@ -7,6 +7,8 @@ import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.neochat
/**
* @brief A component to show that part of a message is loading.
*/
@@ -15,13 +17,8 @@ RowLayout {
required property string display
/**
* @brief The maximum width that the bubble's content can be.
*/
property real maxContentWidth: -1
Layout.fillWidth: true
Layout.maximumWidth: root.maxContentWidth
Layout.maximumWidth: Message.maxContentWidth
spacing: Kirigami.Units.smallSpacing
QQC2.BusyIndicator {}

View File

@@ -49,19 +49,14 @@ ColumnLayout {
*/
required property string asset
/**
* @brief The maximum width that the bubble's content can be.
*/
property real maxContentWidth: -1
Layout.fillWidth: true
Layout.maximumWidth: root.maxContentWidth
Layout.maximumWidth: Message.maxContentWidth
MapView {
id: mapView
Layout.fillWidth: true
Layout.preferredWidth: root.maxContentWidth
Layout.preferredHeight: root.maxContentWidth / 16 * 9
Layout.preferredWidth: root.Message.maxContentWidth
Layout.preferredHeight: root.Message.maxContentWidth / 16 * 9
map.center: QtPositioning.coordinate(root.latitude, root.longitude)
map.zoomLevel: 15

View File

@@ -14,11 +14,6 @@ BaseMessageComponentChooser {
DelegateChoice {
roleValue: MessageComponentType.ThreadBody
delegate: ThreadBodyComponent {
room: root.room
index: root.index
timeline: root.timeline
maxContentWidth: root.maxContentWidth
}
delegate: ThreadBodyComponent {}
}
}

View File

@@ -143,12 +143,6 @@ TimelineDelegate {
*/
signal openExternally
/**
* @brief The reply has been clicked.
*/
signal replyClicked(string eventID)
onReplyClicked: eventID => ListView.view.goToEvent(eventID)
/**
* @brief The main delegate content item to show in the bubble.
*/
@@ -198,6 +192,11 @@ TimelineDelegate {
*/
property real contentMaxWidth: (root.isThreaded ? bubbleSizeHelper.parentWidth : bubbleSizeHelper.currentWidth) - bubble.leftPadding - bubble.rightPadding
Message.room: root.room
Message.timeline: root.ListView.view
Message.index: root.index
Message.maxContentWidth: contentMaxWidth
width: parent?.width
rightPadding: NeoChatConfig.compactLayout && root.ListView.view.width >= Kirigami.Units.gridUnit * 20 ? Kirigami.Units.gridUnit * 2 + Kirigami.Units.largeSpacing : Kirigami.Units.largeSpacing
@@ -255,7 +254,6 @@ TimelineDelegate {
anchors.left: avatar.right
anchors.leftMargin: Kirigami.Units.largeSpacing
anchors.rightMargin: rightPadding
maxContentWidth: root.contentMaxWidth
topPadding: NeoChatConfig.compactLayout ? Kirigami.Units.smallSpacing / 2 : Kirigami.Units.largeSpacing
bottomPadding: NeoChatConfig.compactLayout ? Kirigami.Units.mediumSpacing / 2 : Kirigami.Units.largeSpacing
@@ -284,22 +282,16 @@ TimelineDelegate {
}
]
room: root.room
index: root.index
author: root.author
showAuthor: root.showAuthor
isThreaded: root.isThreaded
contentModel: root.contentModel
timeline: root.ListView.view
showHighlight: root.showHighlight
isPending: root.isPending
onReplyClicked: eventId => {
root.replyClicked(eventId);
}
onSelectedTextChanged: selectedText => {
root.selectedText = selectedText;
}

View File

@@ -20,11 +20,6 @@ Rectangle {
*/
required property var componentAttributes
/**
* @brief The maximum width that the bubble's content can be.
*/
property real maxContentWidth: -1
Layout.preferredWidth: mediaSizeHelper.currentSize.width
Layout.preferredHeight: mediaSizeHelper.currentSize.height
@@ -36,7 +31,7 @@ Rectangle {
MediaSizeHelper {
id: mediaSizeHelper
contentMaxWidth: root.maxContentWidth
contentMaxWidth: root.Message.maxContentWidth
mediaWidth: root.componentAttributes.size.width
mediaHeight: root.componentAttributes.size.height
}

View File

@@ -16,11 +16,6 @@ import org.kde.neochat
ColumnLayout {
id: root
/**
* @brief The NeoChatRoom the delegate is being displayed in.
*/
required property NeoChatRoom room
/**
* @brief The matrix ID of the message event.
*/
@@ -34,13 +29,8 @@ ColumnLayout {
*/
required property var pollHandler
/**
* @brief The maximum width that the bubble's content can be.
*/
property real maxContentWidth: -1
Layout.fillWidth: true
Layout.maximumWidth: root.maxContentWidth
Layout.maximumWidth: Message.maxContentWidth
spacing: 0
@@ -59,7 +49,7 @@ ColumnLayout {
Layout.leftMargin: -Kirigami.Units.largeSpacing - Kirigami.Units.smallSpacing
Layout.rightMargin: -Kirigami.Units.largeSpacing - Kirigami.Units.smallSpacing
checked: root.pollHandler.answers[root.room.localMember.id] ? root.pollHandler.answers[root.room.localMember.id].includes(modelData["id"]) : false
checked: root.pollHandler.answers[root.Message.room.localMember.id] ? root.pollHandler.answers[root.Message.room.localMember.id].includes(modelData["id"]) : false
onClicked: root.pollHandler.sendPollAnswer(root.eventId, modelData["id"])
enabled: !root.pollHandler.hasEnded
text: modelData["org.matrix.msc1767.text"]

View File

@@ -7,6 +7,8 @@ import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.neochat
QQC2.Control {
id: root
@@ -15,11 +17,6 @@ QQC2.Control {
*/
required property string display
/**
* @brief The maximum width that the bubble's content can be.
*/
property real maxContentWidth: -1
/**
* @brief The user selected text has changed.
*/
@@ -32,7 +29,7 @@ QQC2.Control {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.maximumWidth: root.maxContentWidth
Layout.maximumWidth: Message.maxContentWidth
topPadding: 0
bottomPadding: 0

View File

@@ -13,11 +13,6 @@ import org.kde.neochat
Flow {
id: root
/**
* @brief The NeoChatRoom the delegate is being displayed in.
*/
required property NeoChatRoom room
/**
* @brief The matrix ID of the message event.
*/
@@ -28,14 +23,9 @@ Flow {
*/
required property ReactionModel reactionModel
/**
* @brief The maximum width that the bubble's content can be.
*/
property real maxContentWidth: -1
Layout.fillWidth: true
Layout.fillHeight: true
Layout.maximumWidth: root.maxContentWidth
Layout.maximumWidth: Message.maxContentWidth
spacing: Kirigami.Units.smallSpacing
@@ -79,7 +69,9 @@ Flow {
}
}
onClicked: root.room.toggleReaction(root.eventId, reactionDelegate.reaction)
onClicked: {
root.Message.room.toggleReaction(root.eventId, reactionDelegate.reaction)
}
hoverEnabled: true
@@ -114,7 +106,7 @@ Flow {
onClicked: {
var dialog = emojiDialog.createObject(reactButton);
dialog.chosen.connect(emoji => {
root.room.toggleReaction(root.eventId, emoji);
root.Message.room.toggleReaction(root.eventId, emoji);
if (!Kirigami.Settings.isMobile) {
root.focusChatBar();
}
@@ -133,7 +125,7 @@ Flow {
id: emojiDialog
EmojiDialog {
currentRoom: root.room
currentRoom: root.Message.room
showQuickReaction: true
}
}

View File

@@ -17,23 +17,13 @@ import org.kde.neochat.chatbar
Delegates.RoundedItemDelegate {
id: root
/**
* @brief The NeoChatRoom the delegate is being displayed in.
*/
required property NeoChatRoom room
/**
* @brief The thread root ID.
*/
required property string threadRoot
/**
* @brief The maximum width that the bubble's content can be.
*/
property real maxContentWidth: -1
Layout.fillWidth: true
Layout.maximumWidth: root.maxContentWidth
Layout.maximumWidth: Message.maxContentWidth
leftInset: 0
rightInset: 0
@@ -44,9 +34,9 @@ Delegates.RoundedItemDelegate {
text: i18nc("@action:button", "Reply")
onClicked: {
root.room.threadCache.replyId = "";
root.room.threadCache.threadId = root.threadRoot;
root.room.mainCache.clearRelations();
root.room.editCache.clearRelations();
Message.room.threadCache.replyId = "";
Message.room.threadCache.threadId = root.threadRoot;
Message.room.mainCache.clearRelations();
Message.room.editCache.clearRelations();
}
}

View File

@@ -43,16 +43,6 @@ RowLayout {
*/
required property var replyContentModel
/**
* @brief The maximum width that the bubble's content can be.
*/
property real maxContentWidth: -1
/**
* @brief The reply has been clicked.
*/
signal replyClicked(string eventID)
Layout.fillWidth: true
spacing: Kirigami.Units.largeSpacing
@@ -69,13 +59,13 @@ RowLayout {
id: contentColumn
spacing: Kirigami.Units.smallSpacing
Message.maxContentWidth: _private.availableContentWidth
Repeater {
id: contentRepeater
model: root.replyContentModel
delegate: ReplyMessageComponentChooser {
maxContentWidth: _private.availableContentWidth
onReplyClicked: root.replyClicked(root.replyEventId)
onReplyClicked: root.Message.timeline.goToEvent(root.replyEventId)
}
}
}
@@ -84,11 +74,11 @@ RowLayout {
}
TapHandler {
acceptedButtons: Qt.LeftButton
onTapped: root.replyClicked(root.replyEventId)
onTapped: root.Message.timeline.goToEvent(root.replyEventId)
}
QtObject {
id: _private
// The space available for the component after taking away the border
readonly property real availableContentWidth: root.maxContentWidth - verticalBorder.implicitWidth - root.spacing
readonly property real availableContentWidth: root.Message.maxContentWidth - verticalBorder.implicitWidth - root.spacing
}
}

View File

@@ -15,11 +15,6 @@ import org.kde.neochat
DelegateChooser {
id: root
/**
* @brief The maximum width that the bubble's content can be.
*/
property real maxContentWidth: -1
/**
* @brief The reply has been clicked.
*/
@@ -29,16 +24,12 @@ DelegateChooser {
DelegateChoice {
roleValue: MessageComponentType.Author
delegate: ReplyAuthorComponent {
maxContentWidth: root.maxContentWidth
}
delegate: ReplyAuthorComponent {}
}
DelegateChoice {
roleValue: MessageComponentType.Text
delegate: TextComponent {
maxContentWidth: root.maxContentWidth
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton
@@ -61,7 +52,7 @@ DelegateChooser {
MediaSizeHelper {
id: mediaSizeHelper
contentMaxWidth: root.maxContentWidth
contentMaxWidth: Message.maxContentWidth
contentMaxHeight: Kirigami.Units.gridUnit * 5
mediaWidth: image.mediaInfo.width ?? 0
mediaHeight: image.mediaInfo.height ?? 0
@@ -85,7 +76,6 @@ DelegateChooser {
roleValue: MessageComponentType.Code
delegate: CodeComponent {
Layout.maximumHeight: Kirigami.Units.gridUnit * 5
maxContentWidth: root.maxContentWidth
MouseArea {
anchors.fill: parent
@@ -99,8 +89,6 @@ DelegateChooser {
DelegateChoice {
roleValue: MessageComponentType.Quote
delegate: QuoteComponent {
maxContentWidth: root.maxContentWidth
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton
@@ -137,10 +125,7 @@ DelegateChooser {
DelegateChoice {
roleValue: MessageComponentType.Poll
delegate: PollComponent {
room: root.room
maxContentWidth: root.maxContentWidth
}
delegate: PollComponent {}
}
DelegateChoice {
@@ -161,16 +146,12 @@ DelegateChooser {
DelegateChoice {
roleValue: MessageComponentType.Encrypted
delegate: EncryptedComponent {
maxContentWidth: root.maxContentWidth
}
delegate: EncryptedComponent {}
}
DelegateChoice {
roleValue: MessageComponentType.Loading
delegate: LoadComponent {
maxContentWidth: root.maxContentWidth
}
delegate: LoadComponent {}
}
DelegateChoice {

View File

@@ -35,11 +35,6 @@ TextEdit {
*/
property bool spoilerRevealed: !hasSpoiler.test(display)
/**
* @brief The maximum width that the bubble's content can be.
*/
property real maxContentWidth: -1
/**
* @brief Request a context menu be show for the message.
*/
@@ -47,7 +42,7 @@ TextEdit {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.maximumWidth: root.maxContentWidth
Layout.maximumWidth: Message.maxContentWidth
ListView.onReused: Qt.binding(() => !hasSpoiler.test(display))

View File

@@ -16,36 +16,11 @@ import org.kde.neochat
ColumnLayout {
id: root
/**
* @brief The NeoChatRoom the delegate is being displayed in.
*/
required property NeoChatRoom room
/**
* @brief The index of the delegate in the model.
*/
required property var index
/**
* @brief The Matrix ID of the root message in the thread, if any.
*/
required property string threadRoot
/**
* @brief The timeline ListView this component is being used in.
*/
required property ListView timeline
/**
* @brief The maximum width that the bubble's content can be.
*/
property real maxContentWidth: -1
/**
* @brief The reply has been clicked.
*/
signal replyClicked(string eventID)
/**
* @brief The user selected text has changed.
*/
@@ -63,22 +38,14 @@ ColumnLayout {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.maximumWidth: root.maxContentWidth
Layout.maximumWidth: Message.maxContentWidth
spacing: Kirigami.Units.smallSpacing
Repeater {
id: threadRepeater
model: root.room.modelForThread(root.threadRoot);
model: root.Message.room.modelForThread(root.threadRoot);
delegate: BaseMessageComponentChooser {
room: root.room
index: root.index
timeline: root.timeline
maxContentWidth: root.maxContentWidth
onReplyClicked: eventId => {
root.replyClicked(eventId);
}
onSelectedTextChanged: selectedText => {
root.selectedTextChanged(selectedText);
}

View File

@@ -19,16 +19,6 @@ import org.kde.neochat
Video {
id: root
/**
* @brief The NeoChatRoom the delegate is being displayed in.
*/
required property NeoChatRoom room
/**
* @brief The index of the delegate in the model.
*/
required property var index
/**
* @brief The matrix ID of the message event.
*/
@@ -77,16 +67,6 @@ Video {
*/
property bool playOnFinished: false
/**
* @brief The timeline ListView this component is being used in.
*/
required property ListView timeline
/**
* @brief The maximum width that the bubble's content can be.
*/
property real maxContentWidth: -1
Layout.preferredWidth: mediaSizeHelper.currentSize.width
Layout.preferredHeight: mediaSizeHelper.currentSize.height
@@ -382,13 +362,13 @@ Video {
text: i18n("Maximize")
icon.name: "view-fullscreen"
onTriggered: {
root.timeline.interactive = false;
root.Message.timeline.interactive = false;
root.pause();
// We need to make sure the index is that of the MediaMessageFilterModel.
if (root.timeline.model instanceof MessageFilterModel) {
RoomManager.maximizeMedia(RoomManager.mediaMessageFilterModel.getRowForSourceItem(root.index));
if (root.Message.timeline.model instanceof MessageFilterModel) {
RoomManager.maximizeMedia(RoomManager.mediaMessageFilterModel.getRowForSourceItem(root.Message.index));
} else {
RoomManager.maximizeMedia(root.index);
RoomManager.maximizeMedia(root.Message.index);
}
}
}
@@ -440,7 +420,7 @@ Video {
MediaSizeHelper {
id: mediaSizeHelper
contentMaxWidth: root.maxContentWidth
contentMaxWidth: root.Message.maxContentWidth
mediaWidth: root.mediaInfo.width
mediaHeight: root.mediaInfo.height
}
@@ -450,7 +430,7 @@ Video {
playSavedFile();
} else {
playOnFinished = true;
root.room.downloadFile(root.eventId, Core.StandardPaths.writableLocation(Core.StandardPaths.CacheLocation) + "/" + root.eventId.replace(":", "_").replace("/", "_").replace("+", "_") + root.room.fileNameToDownload(root.eventId));
Message.room.downloadFile(root.eventId, Core.StandardPaths.writableLocation(Core.StandardPaths.CacheLocation) + "/" + root.eventId.replace(":", "_").replace("/", "_").replace("+", "_") + Message.room.fileNameToDownload(root.eventId));
}
}