matrixion/resources/qml/TimelineRow.qml

350 lines
14 KiB
QML
Raw Permalink Normal View History

// SPDX-FileCopyrightText: Nheko Contributors
//
2021-03-05 02:35:15 +03:00
// SPDX-License-Identifier: GPL-3.0-or-later
2020-10-08 22:11:21 +03:00
import "./delegates"
import "./emoji"
2022-03-27 22:23:40 +03:00
import QtQuick 2.15
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.2
import QtQuick.Window 2.13
import im.nheko 1.0
2022-03-24 03:35:42 +03:00
AbstractButton {
id: r
required property string blurhash
required property string body
2023-06-02 02:45:24 +03:00
required property string callType
required property int duration
required property int encryptionError
required property string eventId
required property string filename
required property string filesize
2023-06-02 02:45:24 +03:00
required property string formattedBody
required property int index
required property bool isEditable
required property bool isEdited
2023-06-02 02:45:24 +03:00
required property bool isEncrypted
required property bool isOnlyEmoji
required property bool isSender
required property bool isStateEvent
2023-06-02 02:45:24 +03:00
required property int notificationlevel
required property int originalWidth
required property double proportionalHeight
required property var reactions
required property int relatedEventCacheBuster
required property string replyTo
2023-06-02 02:45:24 +03:00
required property string roomName
required property string roomTopic
required property int status
2022-09-30 04:27:05 +03:00
required property string threadId
2023-06-02 02:45:24 +03:00
required property string thumbnailUrl
required property var timestamp
required property int trustlevel
required property int type
required property string typeString
required property string url
required property string userId
required property string userName
2023-06-02 02:45:24 +03:00
height: row.height + (reactionRow.height > 0 ? reactionRow.height - 2 : 0) + unreadRow.height
2022-03-24 03:35:42 +03:00
hoverEnabled: true
2022-03-27 22:23:40 +03:00
states: State {
name: "dragging"
when: draghandler.active
}
transitions: Transition {
from: "dragging"
to: ""
2023-06-02 02:45:24 +03:00
2022-03-27 22:23:40 +03:00
PropertyAnimation {
2023-06-02 02:45:24 +03:00
duration: 100
2022-03-27 22:23:40 +03:00
easing.type: Easing.InOutQuad
2023-06-02 02:45:24 +03:00
properties: "x"
target: r
2022-03-27 22:23:40 +03:00
to: 0
}
}
onClicked: {
2023-06-02 02:45:24 +03:00
let link = contentItem.child.linkAt != undefined && contentItem.child.linkAt(pressX - row.x - msg.x, pressY - row.y - msg.y - contentItem.y);
if (link) {
2023-06-02 02:45:24 +03:00
Nheko.openLink(link);
}
}
2023-06-02 02:45:24 +03:00
onDoubleClicked: room.reply = eventId
onPressAndHold: messageContextMenu.show(eventId, threadId, type, isSender, isEncrypted, isEditable, contentItem.child.hoveredLink, contentItem.child.copyText)
Rectangle {
anchors.fill: parent
color: (Settings.messageHoverHighlight && hovered) ? palette.alternateBase : "transparent"
// this looks better without margins
TapHandler {
acceptedButtons: Qt.RightButton
acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus | PointerDevice.TouchPad
gesturePolicy: TapHandler.ReleaseWithinBounds
onSingleTapped: messageContextMenu.show(eventId, threadId, type, isSender, isEncrypted, isEditable, contentItem.child.hoveredLink, contentItem.child.copyText)
}
}
DragHandler {
id: draghandler
xAxis.maximum: 100
xAxis.minimum: -100
yAxis.enabled: false
2023-06-02 02:45:24 +03:00
onActiveChanged: {
if (!active && (x < -70 || x > 70))
room.reply = eventId;
}
}
2023-04-04 21:25:09 +03:00
AbstractButton {
2023-06-02 02:45:24 +03:00
ToolTip.delay: Nheko.tooltipDelay
ToolTip.text: qsTr("Part of a thread")
ToolTip.visible: hovered
2023-04-04 21:25:09 +03:00
anchors.left: parent.left
2023-06-02 02:45:24 +03:00
anchors.leftMargin: Settings.smallAvatars ? 0 : (Nheko.avatarSize + 8) // align bubble with section header
height: parent.height
2023-04-04 21:25:09 +03:00
visible: threadId
width: 4
2023-06-02 02:45:24 +03:00
onClicked: room.thread = threadId
2023-04-04 21:25:09 +03:00
Rectangle {
id: threadLine
anchors.fill: parent
2023-06-02 02:45:24 +03:00
color: TimelineManager.userColor(threadId, palette.base)
2023-04-04 21:25:09 +03:00
}
}
2022-03-24 03:35:42 +03:00
Rectangle {
2020-10-08 22:11:21 +03:00
id: row
property color bgColor: palette.base
2023-06-02 02:45:24 +03:00
property bool bubbleOnRight: isSender && Settings.bubbles
property int maxWidth: (parent.width - (Settings.smallAvatars || isStateEvent ? 0 : Nheko.avatarSize + 8)) * (Settings.bubbles && !isStateEvent ? 0.9 : 1)
property color userColor: TimelineManager.userColor(userId, palette.base)
anchors.horizontalCenter: isStateEvent ? parent.horizontalCenter : undefined
anchors.left: (isStateEvent || bubbleOnRight) ? undefined : parent.left
anchors.leftMargin: (isStateEvent || Settings.smallAvatars ? 0 : (Nheko.avatarSize + 8)) + (threadId ? 6 : 0) // align bubble with section header
anchors.right: (isStateEvent || !bubbleOnRight) ? undefined : parent.right
border.color: Nheko.theme.red
border.width: r.notificationlevel == MtxEvent.Highlight ? 1 : 0
2022-03-24 03:35:42 +03:00
color: (Settings.bubbles && !isStateEvent) ? Qt.tint(bgColor, Qt.hsla(userColor.hslHue, 0.5, userColor.hslLightness, 0.2)) : "#00000000"
2023-06-02 02:45:24 +03:00
height: msg.height + msg.anchors.margins * 2
2022-03-24 03:35:42 +03:00
radius: 4
2023-06-02 02:45:24 +03:00
width: Settings.bubbles ? Math.min(maxWidth, Math.max(reply.implicitWidth + 8, contentItem.implicitWidth + metadata.width + 20)) : maxWidth
2022-02-05 01:12:30 +03:00
2022-03-24 03:35:42 +03:00
GridLayout {
2023-06-02 02:45:24 +03:00
id: msg
columnSpacing: 2
columns: Settings.bubbles ? 1 : 2
rowSpacing: 0
rows: Settings.bubbles ? 3 : 2
2022-03-24 03:35:42 +03:00
anchors {
left: parent.left
leftMargin: 4
2023-06-02 02:45:24 +03:00
margins: (Settings.bubbles && !isStateEvent) ? 4 : 2
right: parent.right
rightMargin: 4
2023-06-02 02:45:24 +03:00
top: parent.top
2022-03-24 03:35:42 +03:00
}
2020-10-08 22:11:21 +03:00
// fancy reply, if this is a reply
Reply {
id: reply
2021-07-12 23:28:01 +03:00
function fromModel(role) {
return replyTo != "" ? room.dataById(replyTo, role, r.eventId) : null;
2021-07-12 23:28:01 +03:00
}
2023-06-02 02:45:24 +03:00
Layout.bottomMargin: visible ? 2 : 0
Layout.column: 0
Layout.fillWidth: true
Layout.maximumWidth: Settings.bubbles ? Number.MAX_VALUE : implicitWidth
Layout.preferredHeight: height
Layout.row: 0
blurhash: r.relatedEventCacheBuster, fromModel(Room.Blurhash) ?? ""
body: r.relatedEventCacheBuster, fromModel(Room.Body) ?? ""
2023-06-02 02:45:24 +03:00
callType: r.relatedEventCacheBuster, fromModel(Room.CallType) ?? ""
duration: r.relatedEventCacheBuster, fromModel(Room.Duration) ?? 0
encryptionError: r.relatedEventCacheBuster, fromModel(Room.EncryptionError) ?? 0
2021-07-12 23:28:01 +03:00
eventId: fromModel(Room.EventId) ?? ""
filename: r.relatedEventCacheBuster, fromModel(Room.Filename) ?? ""
filesize: r.relatedEventCacheBuster, fromModel(Room.Filesize) ?? ""
2023-06-02 02:45:24 +03:00
formattedBody: r.relatedEventCacheBuster, fromModel(Room.FormattedBody) ?? ""
isOnlyEmoji: r.relatedEventCacheBuster, fromModel(Room.IsOnlyEmoji) ?? false
isStateEvent: r.relatedEventCacheBuster, fromModel(Room.IsStateEvent) ?? false
originalWidth: r.relatedEventCacheBuster, fromModel(Room.OriginalWidth) ?? 0
proportionalHeight: r.relatedEventCacheBuster, fromModel(Room.ProportionalHeight) ?? 1
2023-06-02 02:45:24 +03:00
relatedEventCacheBuster: r.relatedEventCacheBuster, fromModel(Room.RelatedEventCacheBuster) ?? 0
roomName: r.relatedEventCacheBuster, fromModel(Room.RoomName) ?? ""
roomTopic: r.relatedEventCacheBuster, fromModel(Room.RoomTopic) ?? ""
thumbnailUrl: r.relatedEventCacheBuster, fromModel(Room.ThumbnailUrl) ?? ""
type: r.relatedEventCacheBuster, fromModel(Room.Type) ?? MtxEvent.UnknownMessage
typeString: r.relatedEventCacheBuster, fromModel(Room.TypeString) ?? ""
url: r.relatedEventCacheBuster, fromModel(Room.Url) ?? ""
2023-06-02 02:45:24 +03:00
userColor: r.relatedEventCacheBuster, TimelineManager.userColor(userId, palette.base)
userId: r.relatedEventCacheBuster, fromModel(Room.UserId) ?? ""
userName: r.relatedEventCacheBuster, fromModel(Room.UserName) ?? ""
2023-06-02 02:45:24 +03:00
visible: replyTo
2020-10-08 22:11:21 +03:00
}
// actual message content
MessageDelegate {
2023-06-02 02:45:24 +03:00
id: contentItem
Layout.column: 0
Layout.fillWidth: true
Layout.preferredHeight: height
2023-06-02 02:45:24 +03:00
Layout.row: 1
blurhash: r.blurhash
body: r.body
2023-06-02 02:45:24 +03:00
callType: r.callType
duration: r.duration
encryptionError: r.encryptionError
eventId: r.eventId
filename: r.filename
filesize: r.filesize
2023-06-02 02:45:24 +03:00
formattedBody: r.formattedBody
isOnlyEmoji: r.isOnlyEmoji
isReply: false
isStateEvent: r.isStateEvent
metadataWidth: metadata.width
originalWidth: r.originalWidth
proportionalHeight: r.proportionalHeight
2023-06-02 02:45:24 +03:00
relatedEventCacheBuster: r.relatedEventCacheBuster
roomName: r.roomName
roomTopic: r.roomTopic
thumbnailUrl: r.thumbnailUrl
type: r.type
typeString: r.typeString ?? ""
url: r.url
userId: r.userId
userName: r.userName
2020-10-08 22:11:21 +03:00
}
2022-03-01 05:12:57 +03:00
Row {
id: metadata
2023-06-02 02:45:24 +03:00
property int iconSize: Math.floor(fontMetrics.ascent * scaling)
property double scaling: Settings.bubbles ? 0.75 : 1
Layout.alignment: Qt.AlignTop | Qt.AlignRight
2023-06-02 02:45:24 +03:00
Layout.bottomMargin: -2
Layout.column: Settings.bubbles ? 0 : 1
Layout.preferredWidth: implicitWidth
2023-06-02 02:45:24 +03:00
Layout.row: Settings.bubbles ? 2 : 0
Layout.rowSpan: Settings.bubbles ? 1 : 2
Layout.topMargin: (contentItem.fitsMetadata && Settings.bubbles) ? -height - Layout.bottomMargin : 0
2022-03-01 05:12:57 +03:00
spacing: 2
2023-06-02 02:45:24 +03:00
visible: !isStateEvent
2022-03-01 05:12:57 +03:00
StatusIndicator {
Layout.alignment: Qt.AlignRight | Qt.AlignTop
2023-06-02 02:45:24 +03:00
anchors.verticalCenter: ts.verticalCenter
eventId: r.eventId
2022-03-01 05:12:57 +03:00
height: parent.iconSize
status: r.status
2023-06-02 02:45:24 +03:00
width: parent.iconSize
}
Image {
Layout.alignment: Qt.AlignRight | Qt.AlignTop
ToolTip.delay: Nheko.tooltipDelay
ToolTip.text: qsTr("Edited")
2023-06-02 02:45:24 +03:00
ToolTip.visible: editHovered.hovered
2022-03-01 05:12:57 +03:00
anchors.verticalCenter: ts.verticalCenter
2023-06-02 02:45:24 +03:00
height: parent.iconSize
source: "image://colorimage/:/icons/icons/ui/edit.svg?" + ((eventId == room.edit) ? palette.highlight : palette.buttonText)
sourceSize.height: parent.iconSize * Screen.devicePixelRatio
sourceSize.width: parent.iconSize * Screen.devicePixelRatio
visible: isEdited || eventId == room.edit
width: parent.iconSize
HoverHandler {
id: editHovered
2020-10-08 22:11:21 +03:00
2023-06-02 02:45:24 +03:00
}
}
2022-10-04 01:38:59 +03:00
ImageButton {
Layout.alignment: Qt.AlignRight | Qt.AlignTop
ToolTip.delay: Nheko.tooltipDelay
ToolTip.text: qsTr("Part of a thread")
2023-06-02 02:45:24 +03:00
ToolTip.visible: hovered
2022-10-04 01:38:59 +03:00
anchors.verticalCenter: ts.verticalCenter
2023-06-02 02:45:24 +03:00
buttonTextColor: TimelineManager.userColor(threadId, palette.base)
height: parent.iconSize
image: ":/icons/icons/ui/thread.svg"
visible: threadId
width: parent.iconSize
2022-10-04 01:38:59 +03:00
onClicked: room.thread = threadId
}
EncryptionIndicator {
Layout.alignment: Qt.AlignRight | Qt.AlignTop
2023-06-02 02:45:24 +03:00
anchors.verticalCenter: ts.verticalCenter
encrypted: isEncrypted
2022-03-01 05:12:57 +03:00
height: parent.iconSize
sourceSize.height: parent.iconSize * Screen.devicePixelRatio
2023-06-02 02:45:24 +03:00
sourceSize.width: parent.iconSize * Screen.devicePixelRatio
trust: trustlevel
visible: room.isEncrypted
width: parent.iconSize
}
Label {
2022-03-01 05:12:57 +03:00
id: ts
2023-06-02 02:45:24 +03:00
Layout.alignment: Qt.AlignRight | Qt.AlignTop
Layout.preferredWidth: implicitWidth
ToolTip.delay: Nheko.tooltipDelay
ToolTip.text: Qt.formatDateTime(timestamp, Qt.DefaultLocaleLongDate)
2023-06-02 02:45:24 +03:00
ToolTip.visible: ma.hovered
color: palette.inactive.text
font.pointSize: fontMetrics.font.pointSize * parent.scaling
text: timestamp.toLocaleTimeString(Locale.ShortFormat)
HoverHandler {
id: ma
2020-10-08 22:11:21 +03:00
2023-06-02 02:45:24 +03:00
}
}
}
2021-11-11 06:43:37 +03:00
}
2022-02-05 23:53:21 +03:00
}
Reactions {
2023-06-02 02:45:24 +03:00
id: reactionRow
eventId: r.eventId
layoutDirection: row.bubbleOnRight ? Qt.RightToLeft : Qt.LeftToRight
reactions: r.reactions
width: row.maxWidth
2022-02-05 23:53:21 +03:00
anchors {
2023-06-02 02:45:24 +03:00
left: row.bubbleOnRight ? undefined : row.left
right: row.bubbleOnRight ? row.right : undefined
2022-02-05 23:53:21 +03:00
top: row.bottom
topMargin: -4
2022-02-05 23:53:21 +03:00
}
2020-10-08 22:11:21 +03:00
}
Rectangle {
id: unreadRow
2023-06-02 02:45:24 +03:00
color: palette.highlight
height: visible ? 3 : 0
visible: (r.index > 0 && (room.fullyReadEventId == r.eventId))
anchors {
2022-12-18 07:57:40 +03:00
left: parent.left
right: parent.right
2023-06-02 02:45:24 +03:00
top: reactionRow.bottom
topMargin: 5
}
}
}