summary refs log tree commit diff
path: root/resources/qml/TimelineDefaultMessageStyle.qml
diff options
context:
space:
mode:
authorNicolas Werner <nicolas.werner@hotmail.de>2023-09-20 02:17:20 +0200
committerNicolas Werner <nicolas.werner@hotmail.de>2023-09-20 02:17:20 +0200
commit184806bf71faa9f4bcb1745a654dcb8dccc2e864 (patch)
treee115e9c7bd5ad83fdafa2ae50a7a4be0b51d06a9 /resources/qml/TimelineDefaultMessageStyle.qml
parentFix alignment of file messages and redactions (diff)
downloadnheko-184806bf71faa9f4bcb1745a654dcb8dccc2e864.tar.xz
Move message styles to their own files and work around hover not propagating to siblings
Diffstat (limited to 'resources/qml/TimelineDefaultMessageStyle.qml')
-rw-r--r--resources/qml/TimelineDefaultMessageStyle.qml320
1 files changed, 320 insertions, 0 deletions
diff --git a/resources/qml/TimelineDefaultMessageStyle.qml b/resources/qml/TimelineDefaultMessageStyle.qml
new file mode 100644

index 00000000..2dcf153c --- /dev/null +++ b/resources/qml/TimelineDefaultMessageStyle.qml
@@ -0,0 +1,320 @@ +// SPDX-FileCopyrightText: Nheko Contributors +// +// SPDX-License-Identifier: GPL-3.0-or-later + +import "./components" +import "./delegates" +import "./emoji" +import "./ui" +import "./dialogs" +import Qt.labs.platform 1.1 as Platform +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Window +import im.nheko + +TimelineEvent { + id: wrapper + ListView.delayRemove: true + width: chat.delegateMaxWidth + height: Math.max((section.item?.height ?? 0) + gridContainer.implicitHeight + reactionRow.implicitHeight + unreadRow.height, 10) + anchors.horizontalCenter: ListView.view.contentItem.horizontalCenter + //room: chatRoot.roommodel + + required property var day + required property bool isSender + required property int index + property var previousMessageDay: (index + 1) >= chat.count ? 0 : chat.model.dataByIndex(index + 1, Room.Day) + property bool previousMessageIsStateEvent: (index + 1) >= chat.count ? true : chat.model.dataByIndex(index + 1, Room.IsStateEvent) + property string previousMessageUserId: (index + 1) >= chat.count ? "" : chat.model.dataByIndex(index + 1, Room.UserId) + + required property date timestamp + required property string userId + required property string userName + required property string threadId + required property int userPowerlevel + required property bool isEdited + required property bool isEncrypted + required property var reactions + required property int status + required property int trustlevel + required property int type + required property bool isEditable + + required property QtObject messageContextMenu + required property Item messageActions + + property int avatarMargin: (wrapper.isStateEvent || Settings.smallAvatars ? 0 : (Nheko.avatarSize + 8)) // align bubble with section header + + property alias hovered: messageHover.hovered + + data: [ + Loader { + id: section + + active: wrapper.previousMessageUserId !== wrapper.userId || wrapper.previousMessageDay !== wrapper.day || wrapper.previousMessageIsStateEvent !== wrapper.isStateEvent + //asynchronous: true + sourceComponent: TimelineSectionHeader { + day: wrapper.day + isSender: wrapper.isSender + isStateEvent: wrapper.isStateEvent + parentWidth: wrapper.width + previousMessageDay: wrapper.previousMessageDay + previousMessageIsStateEvent: wrapper.previousMessageIsStateEvent + previousMessageUserId: wrapper.previousMessageUserId + timestamp: wrapper.timestamp + userId: wrapper.userId + userName: wrapper.userName + userPowerlevel: wrapper.userPowerlevel + } + visible: status == Loader.Ready + z: 4 + }, + Rectangle { + anchors.fill: gridContainer + color: (Settings.messageHoverHighlight && messageHover.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(wrapper.eventId, wrapper.threadId, wrapper.type, wrapper.isSender, wrapper.isEncrypted, wrapper.isEditable, wrapper.main.hoveredLink, wrapper.main.copyText) + } + }, + RowLayout { + id: gridContainer + + width: wrapper.width + y: section.visible && section.active ? section.y + section.height : 0 + + HoverHandler { + id: messageHover + blocking: false + onHoveredChanged: () => { + if (!Settings.mobileMode && hovered) { + if (!messageActions.hovered) { + messageActions.model = wrapper; + messageActions.attached = wrapper; + messageActions.anchors.bottomMargin = -gridContainer.y + } + } + } + + } + + Item { + Layout.preferredWidth: wrapper.avatarMargin + } + + AbstractButton { + ToolTip.delay: Nheko.tooltipDelay + ToolTip.text: qsTr("Part of a thread") + ToolTip.visible: hovered + Layout.fillHeight: true + visible: wrapper.threadId + Layout.preferredWidth: 4 + + onClicked: wrapper.room.thread = wrapper.threadId + + Rectangle { + id: threadLine + + anchors.fill: parent + color: TimelineManager.userColor(wrapper.threadId, palette.base) + } + } + ColumnLayout { + id: contentColumn + Layout.fillWidth: true + + AbstractButton { + id: replyRow + visible: wrapper.reply + Layout.fillWidth: true + Layout.maximumHeight: timelineView.height / 8 + Layout.preferredWidth: replyRowLay.implicitWidth + Layout.preferredHeight: replyRowLay.implicitHeight + + property color userColor: TimelineManager.userColor(wrapper.reply?.userId ?? '', palette.base) + + clip: true + + NhekoCursorShape { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + } + + contentItem: RowLayout { + id: replyRowLay + + anchors.fill: parent + + + Rectangle { + id: replyLine + Layout.fillHeight: true + color: replyRow.userColor + Layout.preferredWidth: 4 + } + + ColumnLayout { + spacing: 0 + + AbstractButton { + id: replyUserButton + Layout.fillWidth: true + contentItem: ElidedLabel { + id: userName_ + fullText: wrapper.reply?.userName ?? '' + color: replyRow.userColor + textFormat: Text.RichText + width: parent.width + elideWidth: width + } + onClicked: wrapper.room.openUserProfile(wrapper.reply?.userId) + } + data: [ + replyUserButton, + wrapper.reply, + ] + } + } + + background: Rectangle { + width: replyRow.implicitContentWidth + color: Qt.tint(palette.base, Qt.hsla(replyRow.userColor.hslHue, 0.5, replyRow.userColor.hslLightness, 0.1)) + } + + onClicked: { + let link = wrapper.reply.hoveredLink + if (link) { + Nheko.openLink(link) + } else { + console.log("Scrolling to "+wrapper.replyTo); + wrapper.room.showEvent(wrapper.replyTo) + } + } + } + + data: [ + replyRow, wrapper.main, + ] + } + + Item { + // spacer to fill width if needed + Layout.fillWidth: true + } + + RowLayout { + id: metadata + + property int iconSize: Math.floor(fontMetrics.ascent * scaling) + property double scaling: Settings.bubbles ? 0.75 : 1 + + Layout.alignment: Qt.AlignTop | Qt.AlignRight + Layout.preferredWidth: implicitWidth + spacing: 2 + visible: !isStateEvent + + StatusIndicator { + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + eventId: wrapper.eventId + height: parent.iconSize + status: wrapper.status + width: parent.iconSize + } + Image { + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + ToolTip.delay: Nheko.tooltipDelay + ToolTip.text: qsTr("Edited") + ToolTip.visible: editHovered.hovered + height: parent.iconSize + source: "image://colorimage/:/icons/icons/ui/edit.svg?" + ((wrapper.eventId == wrapper.room.edit) ? palette.highlight : palette.buttonText) + sourceSize.height: parent.iconSize * Screen.devicePixelRatio + sourceSize.width: parent.iconSize * Screen.devicePixelRatio + visible: wrapper.isEdited || wrapper.eventId == wrapper.room.edit + width: parent.iconSize + + HoverHandler { + id: editHovered + + } + } + ImageButton { + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + ToolTip.delay: Nheko.tooltipDelay + ToolTip.text: qsTr("Part of a thread") + ToolTip.visible: hovered + buttonTextColor: TimelineManager.userColor(wrapper.threadId, palette.base) + height: parent.iconSize + image: ":/icons/icons/ui/thread.svg" + visible: wrapper.threadId + width: parent.iconSize + + onClicked: wrapper.room.thread = threadId + } + EncryptionIndicator { + Layout.alignment: Qt.AlignRight | Qt.AlignVCenter + encrypted: wrapper.isEncrypted + height: parent.iconSize + sourceSize.height: parent.iconSize * Screen.devicePixelRatio + sourceSize.width: parent.iconSize * Screen.devicePixelRatio + trust: wrapper.trustlevel + visible: wrapper.room.isEncrypted + width: parent.iconSize + } + Label { + id: ts + + Layout.alignment: Qt.AlignRight | Qt.AlignTop + Layout.preferredWidth: implicitWidth + ToolTip.delay: Nheko.tooltipDelay + ToolTip.text: Qt.formatDateTime(wrapper.timestamp, Qt.DefaultLocaleLongDate) + ToolTip.visible: ma.hovered + color: palette.inactive.text + font.pointSize: fontMetrics.font.pointSize * parent.scaling + text: wrapper.timestamp.toLocaleTimeString(Locale.ShortFormat) + + HoverHandler { + id: ma + + } + } + } + }, + Reactions { + id: reactionRow + + eventId: wrapper.eventId + layoutDirection: row.bubbleOnRight ? Qt.RightToLeft : Qt.LeftToRight + reactions: wrapper.reactions + width: wrapper.width - wrapper.avatarMargin + x: wrapper.avatarMargin + + anchors { + //left: row.bubbleOnRight ? undefined : row.left + //right: row.bubbleOnRight ? row.right : undefined + top: gridContainer.bottom + topMargin: -4 + } + }, + Rectangle { + id: unreadRow + + color: palette.highlight + height: visible ? 3 : 0 + visible: (wrapper.index > 0 && (wrapper.room.fullyReadEventId == wrapper.eventId)) + + anchors { + left: parent.left + right: parent.right + top: reactionRow.bottom + topMargin: 5 + } + } + ] +}