summary refs log tree commit diff
path: root/resources/qml
diff options
context:
space:
mode:
authorNicolas Werner <nicolas.werner@hotmail.de>2022-02-19 02:49:58 +0100
committerNicolas Werner <nicolas.werner@hotmail.de>2022-02-19 21:45:43 +0100
commit46fbb0e74990e1d5909fdef12d8e28da484db7e0 (patch)
treef0ca8ba06cc015a115b41e95bfa4169cfbea0297 /resources/qml
parentMerge pull request #947 from maltee1/search_button (diff)
downloadnheko-46fbb0e74990e1d5909fdef12d8e28da484db7e0.tar.xz
Use ListView without scrollview for messages
That way we can autohide the scollbar if needed, it should fix some
jumping issues, it makes it possible to flick on mobile, etc.

Some related bugs:

https://bugreports.qt.io/browse/QTBUG-75223
https://bugreports.qt.io/browse/QTBUG-44902
Diffstat (limited to 'resources/qml')
-rw-r--r--resources/qml/Avatar.qml1
-rw-r--r--resources/qml/MessageView.qml897
2 files changed, 459 insertions, 439 deletions
diff --git a/resources/qml/Avatar.qml b/resources/qml/Avatar.qml

index aca4a1a7..5875e309 100644 --- a/resources/qml/Avatar.qml +++ b/resources/qml/Avatar.qml
@@ -105,6 +105,7 @@ Rectangle { id: mouseArea onSingleTapped: avatar.clicked(eventPoint) + dragThreshold: 0 } Ripple { diff --git a/resources/qml/MessageView.qml b/resources/qml/MessageView.qml
index 2700cda6..e8cc9ed8 100644 --- a/resources/qml/MessageView.qml +++ b/resources/qml/MessageView.qml
@@ -14,544 +14,564 @@ import QtQuick.Layouts 1.2 import QtQuick.Window 2.13 import im.nheko 1.0 -ScrollView { - clip: false - palette: Nheko.colors - padding: 8 - ScrollBar.horizontal.visible: false - - ListView { - id: chat - - property int delegateMaxWidth: ((Settings.timelineMaxWidth > 100 && Settings.timelineMaxWidth < parent.availableWidth) ? Settings.timelineMaxWidth : parent.availableWidth) - parent.padding * 2 - - displayMarginBeginning: height / 2 - displayMarginEnd: height / 2 - model: room - // reuseItems still has a few bugs, see https://bugreports.qt.io/browse/QTBUG-95105 https://bugreports.qt.io/browse/QTBUG-95107 - //onModelChanged: if (room) room.sendReset() - //reuseItems: true - boundsBehavior: Flickable.StopAtBounds - pixelAligned: true - spacing: 2 - verticalLayoutDirection: ListView.BottomToTop - onCountChanged: { - // Mark timeline as read - if (atYEnd && room) - model.currentIndex = 0; - } +Item { + id: chatRoot + property int padding: Nheko.paddingMedium + + property int availableWidth: width - Rectangle { - //closePolicy: Popup.NoAutoClose + ScrollBar { + id: scrollbar + interactive: !touchObserver.wasTouched + parent: chat.parent + anchors.top: parent.top + anchors.right: parent.right + anchors.bottom: parent.bottom + } - id: messageActions + EventObserver { + id: touchObserver + anchors.fill: parent - property Item attached: null - property alias model: row.model - // use comma to update on scroll - property var attachedPos: chat.contentY, attached ? chat.mapFromItem(attached, attached ? attached.width - width : 0, -height) : null - readonly property int padding: Nheko.paddingSmall + ListView { + id: chat - visible: Settings.buttonsInTimeline && !!attached && (attached.hovered || messageActionHover.hovered) - x: attached ? attachedPos.x : 0 - y: attached ? attachedPos.y : 0 - z: 10 - height: row.implicitHeight + padding * 2 - width: row.implicitWidth + padding * 2 - color: Nheko.colors.window - border.color: Nheko.colors.buttonText - border.width: 1 - radius: padding + anchors.fill: parent - HoverHandler { - id: messageActionHover + property int delegateMaxWidth: ((Settings.timelineMaxWidth > 100 && Settings.timelineMaxWidth < chatRoot.availableWidth) ? Settings.timelineMaxWidth : chatRoot.availableWidth) - chatRoot.padding * 2 - scrollbar.width + + displayMarginBeginning: height / 2 + displayMarginEnd: height / 2 + model: room + // reuseItems still has a few bugs, see https://bugreports.qt.io/browse/QTBUG-95105 https://bugreports.qt.io/browse/QTBUG-95107 + //onModelChanged: if (room) room.sendReset() + //reuseItems: true + boundsBehavior: Flickable.StopAtBounds + //pixelAligned: true + spacing: 2 + verticalLayoutDirection: ListView.BottomToTop + onCountChanged: { + // Mark timeline as read + if (atYEnd && room) model.currentIndex = 0; - grabPermissions: PointerHandler.CanTakeOverFromAnything } - Row { - id: row + ScrollBar.vertical: scrollbar - property var model + anchors.rightMargin: scrollbar.interactive ? scrollbar.width : 0 - anchors.centerIn: parent - spacing: messageActions.padding + Rectangle { + //closePolicy: Popup.NoAutoClose - Repeater { - model: Settings.recentReactions + id: messageActions - delegate: TextButton { - required property string modelData + property Item attached: null + property alias model: row.model + // use comma to update on scroll + property var attachedPos: chat.contentY, attached ? chat.mapFromItem(attached, attached ? attached.width - width : 0, -height) : null + readonly property int padding: Nheko.paddingSmall - visible: chat.model ? chat.model.permissions.canSend(MtxEvent.Reaction) : false + visible: Settings.buttonsInTimeline && !!attached && (attached.hovered || messageActionHover.hovered) + x: attached ? attachedPos.x : 0 + y: attached ? attachedPos.y : 0 + z: 10 + height: row.implicitHeight + padding * 2 + width: row.implicitWidth + padding * 2 + color: Nheko.colors.window + border.color: Nheko.colors.buttonText + border.width: 1 + radius: padding - height: fontMetrics.height - font.family: Settings.emojiFont + HoverHandler { + id: messageActionHover - text: modelData - onClicked: { - room.input.reaction(row.model.eventId, modelData); - TimelineManager.focusMessageInput(); + grabPermissions: PointerHandler.CanTakeOverFromAnything + } + + Row { + id: row + + property var model + + anchors.centerIn: parent + spacing: messageActions.padding + + Repeater { + model: Settings.recentReactions + + delegate: TextButton { + required property string modelData + + visible: chat.model ? chat.model.permissions.canSend(MtxEvent.Reaction) : false + + height: fontMetrics.height + font.family: Settings.emojiFont + + text: modelData + onClicked: { + room.input.reaction(row.model.eventId, modelData); + TimelineManager.focusMessageInput(); + } } } - } - ImageButton { - id: editButton - - visible: !!row.model && row.model.isEditable - buttonTextColor: Nheko.colors.buttonText - width: 16 - hoverEnabled: true - image: ":/icons/icons/ui/edit.svg" - ToolTip.visible: hovered - ToolTip.delay: Nheko.tooltipDelay - ToolTip.text: qsTr("Edit") - onClicked: { - if (row.model.isEditable) + ImageButton { + id: editButton + + visible: !!row.model && row.model.isEditable + buttonTextColor: Nheko.colors.buttonText + width: 16 + hoverEnabled: true + image: ":/icons/icons/ui/edit.svg" + ToolTip.visible: hovered + ToolTip.delay: Nheko.tooltipDelay + ToolTip.text: qsTr("Edit") + onClicked: { + if (row.model.isEditable) chat.model.editAction(row.model.eventId); + } } - } - ImageButton { - id: reactButton - - visible: chat.model ? chat.model.permissions.canSend(MtxEvent.Reaction) : false - width: 16 - hoverEnabled: true - image: ":/icons/icons/ui/smile.svg" - ToolTip.visible: hovered - ToolTip.delay: Nheko.tooltipDelay - ToolTip.text: qsTr("React") - onClicked: emojiPopup.visible ? emojiPopup.close() : emojiPopup.show(reactButton, function(emoji) { - var event_id = row.model ? row.model.eventId : ""; - room.input.reaction(event_id, emoji); - TimelineManager.focusMessageInput(); - }) - } + ImageButton { + id: reactButton - ImageButton { - id: replyButton - - visible: chat.model ? chat.model.permissions.canSend(MtxEvent.TextMessage) : false - width: 16 - hoverEnabled: true - image: ":/icons/icons/ui/reply.svg" - ToolTip.visible: hovered - ToolTip.delay: Nheko.tooltipDelay - ToolTip.text: qsTr("Reply") - onClicked: chat.model.replyAction(row.model.eventId) - } + visible: chat.model ? chat.model.permissions.canSend(MtxEvent.Reaction) : false + width: 16 + hoverEnabled: true + image: ":/icons/icons/ui/smile.svg" + ToolTip.visible: hovered + ToolTip.delay: Nheko.tooltipDelay + ToolTip.text: qsTr("React") + onClicked: emojiPopup.visible ? emojiPopup.close() : emojiPopup.show(reactButton, function(emoji) { + var event_id = row.model ? row.model.eventId : ""; + room.input.reaction(event_id, emoji); + TimelineManager.focusMessageInput(); + }) + } - ImageButton { - id: optionsButton + ImageButton { + id: replyButton + + visible: chat.model ? chat.model.permissions.canSend(MtxEvent.TextMessage) : false + width: 16 + hoverEnabled: true + image: ":/icons/icons/ui/reply.svg" + ToolTip.visible: hovered + ToolTip.delay: Nheko.tooltipDelay + ToolTip.text: qsTr("Reply") + onClicked: chat.model.replyAction(row.model.eventId) + } + + ImageButton { + id: optionsButton + + width: 16 + hoverEnabled: true + image: ":/icons/icons/ui/options.svg" + ToolTip.visible: hovered + ToolTip.delay: Nheko.tooltipDelay + ToolTip.text: qsTr("Options") + onClicked: messageContextMenu.show(row.model.eventId, row.model.type, row.model.isSender, row.model.isEncrypted, row.model.isEditable, "", row.model.body, optionsButton) + } - width: 16 - hoverEnabled: true - image: ":/icons/icons/ui/options.svg" - ToolTip.visible: hovered - ToolTip.delay: Nheko.tooltipDelay - ToolTip.text: qsTr("Options") - onClicked: messageContextMenu.show(row.model.eventId, row.model.type, row.model.isSender, row.model.isEncrypted, row.model.isEditable, "", row.model.body, optionsButton) } } - } - - ScrollHelper { - flickable: parent - anchors.fill: parent - enabled: !Settings.mobileMode - } + ScrollHelper { + flickable: parent + anchors.fill: parent + } - Shortcut { - sequence: StandardKey.MoveToPreviousPage - onActivated: { - chat.contentY = chat.contentY - chat.height / 2; - chat.returnToBounds(); + Shortcut { + sequence: StandardKey.MoveToPreviousPage + onActivated: { + chat.contentY = chat.contentY - chat.height / 2; + chat.returnToBounds(); + } } - } - Shortcut { - sequence: StandardKey.MoveToNextPage - onActivated: { - chat.contentY = chat.contentY + chat.height / 2; - chat.returnToBounds(); + Shortcut { + sequence: StandardKey.MoveToNextPage + onActivated: { + chat.contentY = chat.contentY + chat.height / 2; + chat.returnToBounds(); + } } - } - Shortcut { - sequence: StandardKey.Cancel - onActivated: { - if (chat.model.reply) + Shortcut { + sequence: StandardKey.Cancel + onActivated: { + if (chat.model.reply) chat.model.reply = undefined; - else + else chat.model.edit = undefined; + } } - } - - Shortcut { - sequence: "Alt+Up" - onActivated: chat.model.reply = chat.model.indexToId(chat.model.reply ? chat.model.idToIndex(chat.model.reply) + 1 : 0) - } - Shortcut { - sequence: "Alt+Down" - onActivated: { - var idx = chat.model.reply ? chat.model.idToIndex(chat.model.reply) - 1 : -1; - chat.model.reply = idx >= 0 ? chat.model.indexToId(idx) : null; + Shortcut { + sequence: "Alt+Up" + onActivated: chat.model.reply = chat.model.indexToId(chat.model.reply ? chat.model.idToIndex(chat.model.reply) + 1 : 0) } - } - Shortcut { - sequence: "Alt+F" - onActivated: { - if (chat.model.reply) { - var forwardMess = forwardCompleterComponent.createObject(timelineRoot); - forwardMess.setMessageEventId(chat.model.reply); - forwardMess.open(); - chat.model.reply = null; + Shortcut { + sequence: "Alt+Down" + onActivated: { + var idx = chat.model.reply ? chat.model.idToIndex(chat.model.reply) - 1 : -1; + chat.model.reply = idx >= 0 ? chat.model.indexToId(idx) : null; } } - } - Shortcut { - sequence: "Ctrl+E" - onActivated: { - chat.model.edit = chat.model.reply; + Shortcut { + sequence: "Alt+F" + onActivated: { + if (chat.model.reply) { + var forwardMess = forwardCompleterComponent.createObject(timelineRoot); + forwardMess.setMessageEventId(chat.model.reply); + forwardMess.open(); + chat.model.reply = null; + } + } } - } - Connections { - function onFocusChanged() { - readTimer.running = TimelineManager.isWindowFocused; + Shortcut { + sequence: "Ctrl+E" + onActivated: { + chat.model.edit = chat.model.reply; + } } - target: TimelineManager - } + Connections { + function onFocusChanged() { + readTimer.running = TimelineManager.isWindowFocused; + } + + target: TimelineManager + } - Timer { - id: readTimer + Timer { + id: readTimer - // force current read index to update - onTriggered: { - if (chat.model) + // force current read index to update + onTriggered: { + if (chat.model) chat.model.setCurrentIndex(chat.model.currentIndex); + } + interval: 1000 } - interval: 1000 - } - Component { - id: sectionHeader - - Column { - topPadding: userName_.visible? 4: 0 - bottomPadding: Settings.bubbles? (isSender? 0 : 2) : 3 - spacing: 8 - visible: (previousMessageUserId !== userId || previousMessageDay !== day || isStateEvent !== previousMessageIsStateEvent) - width: parentWidth - height: ((previousMessageDay !== day) ? dateBubble.height : 0) + (isStateEvent? 0 : userName.height +8 ) - - Label { - id: dateBubble - - anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined - visible: room && previousMessageDay !== day - text: room ? room.formatDateSeparator(timestamp) : "" - color: Nheko.colors.text - height: Math.round(fontMetrics.height * 1.4) - width: contentWidth * 1.2 - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - - background: Rectangle { - radius: parent.height / 2 - color: Nheko.colors.window - } - - } + Component { + id: sectionHeader - Row { - height: userName_.height + Column { + topPadding: userName_.visible? 4: 0 + bottomPadding: Settings.bubbles? (isSender? 0 : 2) : 3 spacing: 8 - visible: !isStateEvent && (!isSender || !Settings.bubbles) - - Avatar { - id: messageUserAvatar - - width: Nheko.avatarSize * (Settings.smallAvatars? 0.5 : 1) - height: Nheko.avatarSize * (Settings.smallAvatars? 0.5 : 1) - url: !room ? "" : room.avatarUrl(userId).replace("mxc://", "image://MxcImage/") - displayName: userName - userid: userId - onClicked: room.openUserProfile(userId) - ToolTip.visible: avatarHover.hovered - ToolTip.delay: Nheko.tooltipDelay - ToolTip.text: userid + visible: (previousMessageUserId !== userId || previousMessageDay !== day || isStateEvent !== previousMessageIsStateEvent) + width: parentWidth + height: ((previousMessageDay !== day) ? dateBubble.height : 0) + (isStateEvent? 0 : userName.height +8 ) - HoverHandler { - id: avatarHover + Label { + id: dateBubble + + anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined + visible: room && previousMessageDay !== day + text: room ? room.formatDateSeparator(timestamp) : "" + color: Nheko.colors.text + height: Math.round(fontMetrics.height * 1.4) + width: contentWidth * 1.2 + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + + background: Rectangle { + radius: parent.height / 2 + color: Nheko.colors.window } } - Connections { - function onRoomAvatarUrlChanged() { - messageUserAvatar.url = chat.model.avatarUrl(userId).replace("mxc://", "image://MxcImage/"); - } + Row { + height: userName_.height + spacing: 8 + visible: !isStateEvent && (!isSender || !Settings.bubbles) + + Avatar { + id: messageUserAvatar + + width: Nheko.avatarSize * (Settings.smallAvatars? 0.5 : 1) + height: Nheko.avatarSize * (Settings.smallAvatars? 0.5 : 1) + url: !room ? "" : room.avatarUrl(userId).replace("mxc://", "image://MxcImage/") + displayName: userName + userid: userId + onClicked: room.openUserProfile(userId) + ToolTip.visible: avatarHover.hovered + ToolTip.delay: Nheko.tooltipDelay + ToolTip.text: userid + + HoverHandler { + id: avatarHover + } - function onScrollToIndex(index) { - chat.positionViewAtIndex(index, ListView.Center); } - target: chat.model - } - - Label { - id: userName_ + Connections { + function onRoomAvatarUrlChanged() { + messageUserAvatar.url = chat.model.avatarUrl(userId).replace("mxc://", "image://MxcImage/"); + } - text: TimelineManager.escapeEmoji(userName) - color: TimelineManager.userColor(userId, Nheko.colors.base) - textFormat: Text.RichText - ToolTip.visible: displayNameHover.hovered - ToolTip.delay: Nheko.tooltipDelay - ToolTip.text: userId + function onScrollToIndex(index) { + chat.positionViewAtIndex(index, ListView.Center); + } - TapHandler { - onSingleTapped: chat.model.openUserProfile(userId) + target: chat.model } - CursorShape { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - } + Label { + id: userName_ - HoverHandler { - id: displayNameHover - } + text: TimelineManager.escapeEmoji(userName) + color: TimelineManager.userColor(userId, Nheko.colors.base) + textFormat: Text.RichText + ToolTip.visible: displayNameHover.hovered + ToolTip.delay: Nheko.tooltipDelay + ToolTip.text: userId - } + TapHandler { + onSingleTapped: chat.model.openUserProfile(userId) + dragThreshold: 0 + } - Label { - id: statusMsg - color: Nheko.colors.buttonText - text: Presence.userStatus(userId) - textFormat: Text.PlainText - elide: Text.ElideRight - width: chat.delegateMaxWidth - parent.spacing * 2 - userName.implicitWidth - Nheko.avatarSize - font.italic: true + CursorShape { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + } - Connections { - target: Presence + HoverHandler { + id: displayNameHover + } - function onPresenceChanged(id) { - if (id == userId) statusMsg.text = Presence.userStatus(userId); + } + + Label { + id: statusMsg + color: Nheko.colors.buttonText + text: Presence.userStatus(userId) + textFormat: Text.PlainText + elide: Text.ElideRight + width: chat.delegateMaxWidth - parent.spacing * 2 - userName.implicitWidth - Nheko.avatarSize + font.italic: true + + Connections { + target: Presence + + function onPresenceChanged(id) { + if (id == userId) statusMsg.text = Presence.userStatus(userId); + } } } + } } } - } + delegate: ItemDelegate { + id: wrapper + + required property double proportionalHeight + required property int type + required property string typeString + required property int originalWidth + required property string blurhash + required property string body + required property string formattedBody + required property string eventId + required property string filename + required property string filesize + required property string url + required property string thumbnailUrl + required property bool isOnlyEmoji + required property bool isSender + required property bool isEncrypted + required property bool isEditable + required property bool isEdited + required property bool isStateEvent + required property bool previousMessageIsStateEvent + required property string replyTo + required property string userId + required property string roomTopic + required property string roomName + required property string callType + required property var reactions + required property int trustlevel + required property int encryptionError + required property var timestamp + required property int status + required property int index + required property int relatedEventCacheBuster + required property string previousMessageUserId + required property string day + required property string previousMessageDay + required property string userName + property bool scrolledToThis: eventId === chat.model.scrollTarget && (y + height > chat.y + chat.contentY && y < chat.y + chat.height + chat.contentY) + + anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined + width: chat.delegateMaxWidth + height: section.active ? section.height + timelinerow.height : timelinerow.height + + hoverEnabled: true + + background: Rectangle { + id: scrollHighlight + + opacity: 0 + visible: true + z: 1 + enabled: false + color: Nheko.colors.highlight + + states: State { + name: "revealed" + when: wrapper.scrolledToThis + } - delegate: ItemDelegate { - id: wrapper - - required property double proportionalHeight - required property int type - required property string typeString - required property int originalWidth - required property string blurhash - required property string body - required property string formattedBody - required property string eventId - required property string filename - required property string filesize - required property string url - required property string thumbnailUrl - required property bool isOnlyEmoji - required property bool isSender - required property bool isEncrypted - required property bool isEditable - required property bool isEdited - required property bool isStateEvent - required property bool previousMessageIsStateEvent - required property string replyTo - required property string userId - required property string roomTopic - required property string roomName - required property string callType - required property var reactions - required property int trustlevel - required property int encryptionError - required property var timestamp - required property int status - required property int index - required property int relatedEventCacheBuster - required property string previousMessageUserId - required property string day - required property string previousMessageDay - required property string userName - property bool scrolledToThis: eventId === chat.model.scrollTarget && (y + height > chat.y + chat.contentY && y < chat.y + chat.height + chat.contentY) - - anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined - width: chat.delegateMaxWidth - height: section.active ? section.height + timelinerow.height : timelinerow.height - - hoverEnabled: true - - background: Rectangle { - id: scrollHighlight - - opacity: 0 - visible: true - z: 1 - enabled: false - color: Nheko.colors.highlight - - states: State { - name: "revealed" - when: wrapper.scrolledToThis - } + transitions: Transition { + from: "" + to: "revealed" + + SequentialAnimation { + PropertyAnimation { + target: scrollHighlight + properties: "opacity" + easing.type: Easing.InOutQuad + from: 0 + to: 1 + duration: 500 + } - transitions: Transition { - from: "" - to: "revealed" - - SequentialAnimation { - PropertyAnimation { - target: scrollHighlight - properties: "opacity" - easing.type: Easing.InOutQuad - from: 0 - to: 1 - duration: 500 - } + PropertyAnimation { + target: scrollHighlight + properties: "opacity" + easing.type: Easing.InOutQuad + from: 1 + to: 0 + duration: 500 + } - PropertyAnimation { - target: scrollHighlight - properties: "opacity" - easing.type: Easing.InOutQuad - from: 1 - to: 0 - duration: 500 - } + ScriptAction { + script: chat.model.eventShown() + } - ScriptAction { - script: chat.model.eventShown() } } } - } - - Loader { - id: section - - property int parentWidth: parent.width - property string userId: wrapper.userId - property string previousMessageUserId: wrapper.previousMessageUserId - property string day: wrapper.day - property string previousMessageDay: wrapper.previousMessageDay - property bool previousMessageIsStateEvent: wrapper.previousMessageIsStateEvent - property bool isStateEvent: wrapper.isStateEvent - property bool isSender: wrapper.isSender - property string userName: wrapper.userName - property date timestamp: wrapper.timestamp - - z: 4 - active: previousMessageUserId !== undefined && previousMessageUserId !== userId || previousMessageDay !== day || previousMessageIsStateEvent !== isStateEvent - //asynchronous: true - sourceComponent: sectionHeader - visible: status == Loader.Ready - } + Loader { + id: section + + property int parentWidth: parent.width + property string userId: wrapper.userId + property string previousMessageUserId: wrapper.previousMessageUserId + property string day: wrapper.day + property string previousMessageDay: wrapper.previousMessageDay + property bool previousMessageIsStateEvent: wrapper.previousMessageIsStateEvent + property bool isStateEvent: wrapper.isStateEvent + property bool isSender: wrapper.isSender + property string userName: wrapper.userName + property date timestamp: wrapper.timestamp + + z: 4 + active: previousMessageUserId !== undefined && previousMessageUserId !== userId || previousMessageDay !== day || previousMessageIsStateEvent !== isStateEvent + //asynchronous: true + sourceComponent: sectionHeader + visible: status == Loader.Ready + } - TimelineRow { - id: timelinerow - - hovered: (wrapper.hovered && !messageActionHover.hovered) || (messageActions.model != undefined && messageActions.model.eventId == timelinerow.eventId) - - proportionalHeight: wrapper.proportionalHeight - type: chat.model, wrapper.type - typeString: wrapper.typeString - originalWidth: wrapper.originalWidth - blurhash: wrapper.blurhash - body: wrapper.body - formattedBody: wrapper.formattedBody - eventId: chat.model, wrapper.eventId - filename: wrapper.filename - filesize: wrapper.filesize - url: wrapper.url - thumbnailUrl: wrapper.thumbnailUrl - isOnlyEmoji: wrapper.isOnlyEmoji - isSender: wrapper.isSender - isEncrypted: wrapper.isEncrypted - isEditable: wrapper.isEditable - isEdited: wrapper.isEdited - isStateEvent: wrapper.isStateEvent - replyTo: wrapper.replyTo - userId: wrapper.userId - userName: wrapper.userName - roomTopic: wrapper.roomTopic - roomName: wrapper.roomName - callType: wrapper.callType - reactions: wrapper.reactions - trustlevel: wrapper.trustlevel - encryptionError: wrapper.encryptionError - timestamp: wrapper.timestamp - status: wrapper.status - relatedEventCacheBuster: wrapper.relatedEventCacheBuster - y: section.visible && section.active ? section.y + section.height : 0 - } + TimelineRow { + id: timelinerow + + hovered: (wrapper.hovered && !messageActionHover.hovered) || (messageActions.model != undefined && messageActions.model.eventId == timelinerow.eventId) + + proportionalHeight: wrapper.proportionalHeight + type: chat.model, wrapper.type + typeString: wrapper.typeString + originalWidth: wrapper.originalWidth + blurhash: wrapper.blurhash + body: wrapper.body + formattedBody: wrapper.formattedBody + eventId: chat.model, wrapper.eventId + filename: wrapper.filename + filesize: wrapper.filesize + url: wrapper.url + thumbnailUrl: wrapper.thumbnailUrl + isOnlyEmoji: wrapper.isOnlyEmoji + isSender: wrapper.isSender + isEncrypted: wrapper.isEncrypted + isEditable: wrapper.isEditable + isEdited: wrapper.isEdited + isStateEvent: wrapper.isStateEvent + replyTo: wrapper.replyTo + userId: wrapper.userId + userName: wrapper.userName + roomTopic: wrapper.roomTopic + roomName: wrapper.roomName + callType: wrapper.callType + reactions: wrapper.reactions + trustlevel: wrapper.trustlevel + encryptionError: wrapper.encryptionError + timestamp: wrapper.timestamp + status: wrapper.status + relatedEventCacheBuster: wrapper.relatedEventCacheBuster + y: section.visible && section.active ? section.y + section.height : 0 + } - onHoveredChanged: { - if (!Settings.mobileMode && hovered) { - if (!messageActionHover.hovered) { - messageActions.attached = timelinerow; - messageActions.model = timelinerow; + onHoveredChanged: { + if (!Settings.mobileMode && hovered) { + if (!messageActionHover.hovered) { + messageActions.attached = timelinerow; + messageActions.model = timelinerow; + } } } - } - Connections { - function onMovementEnded() { - if (y + height + 2 * chat.spacing > chat.contentY + chat.height && y < chat.contentY + chat.height) + Connections { + function onMovementEnded() { + if (y + height + 2 * chat.spacing > chat.contentY + chat.height && y < chat.contentY + chat.height) chat.model.currentIndex = index; + } + + target: chat } - target: chat } - } - - footer: Item { - anchors.horizontalCenter: parent.horizontalCenter - anchors.margins: Nheko.paddingLarge - visible: chat.model && chat.model.paginationInProgress - // hacky, but works - height: loadingSpinner.height + 2 * Nheko.paddingLarge - - Spinner { - id: loadingSpinner - - anchors.centerIn: parent + footer: Item { + anchors.horizontalCenter: parent.horizontalCenter anchors.margins: Nheko.paddingLarge - running: chat.model && chat.model.paginationInProgress - foreground: Nheko.colors.mid - z: 3 + visible: chat.model && chat.model.paginationInProgress + // hacky, but works + height: loadingSpinner.height + 2 * Nheko.paddingLarge + + Spinner { + id: loadingSpinner + + anchors.centerIn: parent + anchors.margins: Nheko.paddingLarge + running: chat.model && chat.model.paginationInProgress + foreground: Nheko.colors.mid + z: 3 + } + } } - } Platform.Menu { @@ -572,17 +592,17 @@ ScrollView { isEditable = isEditable_; isSender = isSender_; if (text_) - text = text_; + text = text_; else - text = ""; + text = ""; if (link_) - link = link_; + link = link_; else - link = ""; + link = ""; if (showAt_) - open(showAt_); + open(showAt_); else - open(); + open(); } Platform.MenuItem { @@ -732,5 +752,4 @@ ScrollView { } } - }