diff options
Diffstat (limited to 'resources/qml/TimelineRow.qml')
-rw-r--r-- | resources/qml/TimelineRow.qml | 358 |
1 files changed, 179 insertions, 179 deletions
diff --git a/resources/qml/TimelineRow.qml b/resources/qml/TimelineRow.qml index 07cb5ce2..a064bd15 100644 --- a/resources/qml/TimelineRow.qml +++ b/resources/qml/TimelineRow.qml @@ -13,72 +13,45 @@ import im.nheko 1.0 AbstractButton { id: r - 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 callType + required property int duration + required property int encryptionError 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 string formattedBody + required property int index required property bool isEditable required property bool isEdited + required property bool isEncrypted + required property bool isOnlyEmoji + required property bool isSender required property bool isStateEvent + required property int notificationlevel + required property int originalWidth + required property double proportionalHeight + required property var reactions + required property int relatedEventCacheBuster required property string replyTo + required property string roomName + required property string roomTopic + required property int status required property string threadId + 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 - required property string roomTopic - required property string roomName - required property string callType - required property var reactions - required property int trustlevel - required property int notificationlevel - required property int encryptionError - required property int duration - required property var timestamp - required property int status - required property int index - required property int relatedEventCacheBuster + height: row.height + (reactionRow.height > 0 ? reactionRow.height - 2 : 0) + unreadRow.height hoverEnabled: true - width: parent.width - height: row.height+(reactionRow.height > 0 ? reactionRow.height-2 : 0 )+unreadRow.height - - Rectangle { - color: (Settings.messageHoverHighlight && hovered) ? palette.alternateBase : "transparent" - anchors.fill: parent - // this looks better without margins - TapHandler { - acceptedButtons: Qt.RightButton - onSingleTapped: messageContextMenu.show(eventId, threadId, type, isSender, isEncrypted, isEditable, contentItem.child.hoveredLink, contentItem.child.copyText) - gesturePolicy: TapHandler.ReleaseWithinBounds - acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus | PointerDevice.TouchPad - } - } - - onPressAndHold: messageContextMenu.show(eventId, threadId, type, isSender, isEncrypted, isEditable, contentItem.child.hoveredLink, contentItem.child.copyText) - onDoubleClicked: room.reply = eventId - - DragHandler { - id: draghandler - yAxis.enabled: false - xAxis.maximum: 100 - xAxis.minimum: -100 - onActiveChanged: { - if(!active && (x < -70 || x > 70)) - room.reply = eventId - } - } states: State { name: "dragging" when: draghandler.active @@ -86,265 +59,292 @@ AbstractButton { transitions: Transition { from: "dragging" to: "" + PropertyAnimation { - target: r - properties: "x" + duration: 100 easing.type: Easing.InOutQuad + properties: "x" + target: r to: 0 - duration: 100 } } onClicked: { - let link = contentItem.child.linkAt != undefined && contentItem.child.linkAt(pressX-row.x-msg.x, pressY-row.y-msg.y-contentItem.y); + let link = contentItem.child.linkAt != undefined && contentItem.child.linkAt(pressX - row.x - msg.x, pressY - row.y - msg.y - contentItem.y); if (link) { - Nheko.openLink(link) + Nheko.openLink(link); } } + 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 + onActiveChanged: { + if (!active && (x < -70 || x > 70)) + room.reply = eventId; + } + } AbstractButton { - anchors.leftMargin: Settings.smallAvatars? 0 : (Nheko.avatarSize + 8) // align bubble with section header + ToolTip.delay: Nheko.tooltipDelay + ToolTip.text: qsTr("Part of a thread") + ToolTip.visible: hovered anchors.left: parent.left + anchors.leftMargin: Settings.smallAvatars ? 0 : (Nheko.avatarSize + 8) // align bubble with section header + height: parent.height visible: threadId width: 4 - height: parent.height + + onClicked: room.thread = threadId Rectangle { id: threadLine - color: TimelineManager.userColor(threadId, palette.base) anchors.fill: parent + color: TimelineManager.userColor(threadId, palette.base) } - - ToolTip.visible: hovered - ToolTip.delay: Nheko.tooltipDelay - ToolTip.text: qsTr("Part of a thread") - onClicked: room.thread = threadId } - Rectangle { id: row - property bool bubbleOnRight : isSender && Settings.bubbles - anchors.leftMargin: (isStateEvent || Settings.smallAvatars? 0 : (Nheko.avatarSize + 8)) + (threadId ? 6 : 0) // align bubble with section header - anchors.left: (isStateEvent || bubbleOnRight) ? undefined : parent.left - anchors.right: (isStateEvent || !bubbleOnRight) ? undefined : parent.right - anchors.horizontalCenter: isStateEvent? parent.horizontalCenter : undefined - property int maxWidth: (parent.width-(Settings.smallAvatars || isStateEvent? 0 : Nheko.avatarSize+8))*(Settings.bubbles && !isStateEvent? 0.9 : 1) - width: Settings.bubbles? Math.min(maxWidth,Math.max(reply.implicitWidth+8,contentItem.implicitWidth+metadata.width+20)) : maxWidth - height: msg.height+msg.anchors.margins*2 - property color userColor: TimelineManager.userColor(userId, palette.base) property color bgColor: palette.base + 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 color: (Settings.bubbles && !isStateEvent) ? Qt.tint(bgColor, Qt.hsla(userColor.hslHue, 0.5, userColor.hslLightness, 0.2)) : "#00000000" + height: msg.height + msg.anchors.margins * 2 radius: 4 - border.width: r.notificationlevel == MtxEvent.Highlight ? 1 : 0 - border.color: Nheko.theme.red + width: Settings.bubbles ? Math.min(maxWidth, Math.max(reply.implicitWidth + 8, contentItem.implicitWidth + metadata.width + 20)) : maxWidth GridLayout { + id: msg + + columnSpacing: 2 + columns: Settings.bubbles ? 1 : 2 + rowSpacing: 0 + rows: Settings.bubbles ? 3 : 2 + anchors { left: parent.left - top: parent.top - right: parent.right - margins: (Settings.bubbles && ! isStateEvent)? 4 : 2 leftMargin: 4 + margins: (Settings.bubbles && !isStateEvent) ? 4 : 2 + right: parent.right rightMargin: 4 + top: parent.top } - id: msg - rowSpacing: 0 - columnSpacing: 2 - columns: Settings.bubbles? 1 : 2 - rows: Settings.bubbles? 3 : 2 // fancy reply, if this is a reply Reply { - Layout.row: 0 - Layout.column: 0 - Layout.fillWidth: true - Layout.maximumWidth: Settings.bubbles? Number.MAX_VALUE : implicitWidth - Layout.bottomMargin: visible? 2 : 0 - Layout.preferredHeight: height id: reply function fromModel(role) { return replyTo != "" ? room.dataById(replyTo, role, r.eventId) : null; } - visible: replyTo - userColor: r.relatedEventCacheBuster, TimelineManager.userColor(userId, palette.base) + + 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) ?? "" - formattedBody: r.relatedEventCacheBuster, fromModel(Room.FormattedBody) ?? "" + callType: r.relatedEventCacheBuster, fromModel(Room.CallType) ?? "" + duration: r.relatedEventCacheBuster, fromModel(Room.Duration) ?? 0 + encryptionError: r.relatedEventCacheBuster, fromModel(Room.EncryptionError) ?? 0 eventId: fromModel(Room.EventId) ?? "" filename: r.relatedEventCacheBuster, fromModel(Room.Filename) ?? "" filesize: r.relatedEventCacheBuster, fromModel(Room.Filesize) ?? "" + 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 + 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) ?? "" - originalWidth: r.relatedEventCacheBuster, fromModel(Room.OriginalWidth) ?? 0 - isOnlyEmoji: r.relatedEventCacheBuster, fromModel(Room.IsOnlyEmoji) ?? false - isStateEvent: r.relatedEventCacheBuster, fromModel(Room.IsStateEvent) ?? false + userColor: r.relatedEventCacheBuster, TimelineManager.userColor(userId, palette.base) userId: r.relatedEventCacheBuster, fromModel(Room.UserId) ?? "" userName: r.relatedEventCacheBuster, fromModel(Room.UserName) ?? "" - thumbnailUrl: r.relatedEventCacheBuster, fromModel(Room.ThumbnailUrl) ?? "" - duration: r.relatedEventCacheBuster, fromModel(Room.Duration) ?? 0 - roomTopic: r.relatedEventCacheBuster, fromModel(Room.RoomTopic) ?? "" - roomName: r.relatedEventCacheBuster, fromModel(Room.RoomName) ?? "" - callType: r.relatedEventCacheBuster, fromModel(Room.CallType) ?? "" - encryptionError: r.relatedEventCacheBuster, fromModel(Room.EncryptionError) ?? 0 - relatedEventCacheBuster: r.relatedEventCacheBuster, fromModel(Room.RelatedEventCacheBuster) ?? 0 + visible: replyTo } // actual message content MessageDelegate { - Layout.row: 1 + id: contentItem + Layout.column: 0 Layout.fillWidth: true Layout.preferredHeight: height - id: contentItem - + Layout.row: 1 blurhash: r.blurhash body: r.body - formattedBody: r.formattedBody + callType: r.callType + duration: r.duration + encryptionError: r.encryptionError eventId: r.eventId filename: r.filename filesize: r.filesize + formattedBody: r.formattedBody + isOnlyEmoji: r.isOnlyEmoji + isReply: false + isStateEvent: r.isStateEvent + metadataWidth: metadata.width + originalWidth: r.originalWidth proportionalHeight: r.proportionalHeight + relatedEventCacheBuster: r.relatedEventCacheBuster + roomName: r.roomName + roomTopic: r.roomTopic + thumbnailUrl: r.thumbnailUrl type: r.type typeString: r.typeString ?? "" url: r.url - thumbnailUrl: r.thumbnailUrl - duration: r.duration - originalWidth: r.originalWidth - isOnlyEmoji: r.isOnlyEmoji - isStateEvent: r.isStateEvent userId: r.userId userName: r.userName - roomTopic: r.roomTopic - roomName: r.roomName - callType: r.callType - encryptionError: r.encryptionError - relatedEventCacheBuster: r.relatedEventCacheBuster - isReply: false - metadataWidth: metadata.width } - Row { id: metadata - Layout.column: Settings.bubbles? 0 : 1 - Layout.row: Settings.bubbles? 2 : 0 - Layout.rowSpan: Settings.bubbles? 1 : 2 - Layout.bottomMargin: -2 - Layout.topMargin: (contentItem.fitsMetadata && Settings.bubbles)? -height-Layout.bottomMargin : 0 + + property int iconSize: Math.floor(fontMetrics.ascent * scaling) + property double scaling: Settings.bubbles ? 0.75 : 1 + Layout.alignment: Qt.AlignTop | Qt.AlignRight + Layout.bottomMargin: -2 + Layout.column: Settings.bubbles ? 0 : 1 Layout.preferredWidth: implicitWidth - visible: !isStateEvent + Layout.row: Settings.bubbles ? 2 : 0 + Layout.rowSpan: Settings.bubbles ? 1 : 2 + Layout.topMargin: (contentItem.fitsMetadata && Settings.bubbles) ? -height - Layout.bottomMargin : 0 spacing: 2 - - property double scaling: Settings.bubbles? 0.75 : 1 - - property int iconSize: Math.floor(fontMetrics.ascent*scaling) + visible: !isStateEvent StatusIndicator { Layout.alignment: Qt.AlignRight | Qt.AlignTop + anchors.verticalCenter: ts.verticalCenter + eventId: r.eventId height: parent.iconSize - width: parent.iconSize status: r.status - eventId: r.eventId - anchors.verticalCenter: ts.verticalCenter + width: parent.iconSize } - Image { - visible: isEdited || eventId == room.edit Layout.alignment: Qt.AlignRight | Qt.AlignTop - height: parent.iconSize - width: parent.iconSize - sourceSize.width: parent.iconSize * Screen.devicePixelRatio - sourceSize.height: parent.iconSize * Screen.devicePixelRatio - source: "image://colorimage/:/icons/icons/ui/edit.svg?" + ((eventId == room.edit) ? palette.highlight : palette.buttonText) - ToolTip.visible: editHovered.hovered ToolTip.delay: Nheko.tooltipDelay ToolTip.text: qsTr("Edited") + ToolTip.visible: editHovered.hovered anchors.verticalCenter: ts.verticalCenter + 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 - } + } } - ImageButton { - visible: threadId Layout.alignment: Qt.AlignRight | Qt.AlignTop - height: parent.iconSize - width: parent.iconSize - image: ":/icons/icons/ui/thread.svg" - buttonTextColor: TimelineManager.userColor(threadId, palette.base) - ToolTip.visible: hovered ToolTip.delay: Nheko.tooltipDelay ToolTip.text: qsTr("Part of a thread") + ToolTip.visible: hovered anchors.verticalCenter: ts.verticalCenter + buttonTextColor: TimelineManager.userColor(threadId, palette.base) + height: parent.iconSize + image: ":/icons/icons/ui/thread.svg" + visible: threadId + width: parent.iconSize + onClicked: room.thread = threadId } - EncryptionIndicator { - visible: room.isEncrypted - encrypted: isEncrypted - trust: trustlevel Layout.alignment: Qt.AlignRight | Qt.AlignTop + anchors.verticalCenter: ts.verticalCenter + encrypted: isEncrypted height: parent.iconSize - width: parent.iconSize - sourceSize.width: parent.iconSize * Screen.devicePixelRatio sourceSize.height: parent.iconSize * Screen.devicePixelRatio - anchors.verticalCenter: ts.verticalCenter + sourceSize.width: parent.iconSize * Screen.devicePixelRatio + trust: trustlevel + visible: room.isEncrypted + width: parent.iconSize } - Label { id: ts + Layout.alignment: Qt.AlignRight | Qt.AlignTop Layout.preferredWidth: implicitWidth - text: timestamp.toLocaleTimeString(Locale.ShortFormat) - color: palette.inactive.text - ToolTip.visible: ma.hovered ToolTip.delay: Nheko.tooltipDelay ToolTip.text: Qt.formatDateTime(timestamp, Qt.DefaultLocaleLongDate) - font.pointSize: fontMetrics.font.pointSize*parent.scaling + ToolTip.visible: ma.hovered + color: palette.inactive.text + font.pointSize: fontMetrics.font.pointSize * parent.scaling + text: timestamp.toLocaleTimeString(Locale.ShortFormat) + HoverHandler { id: ma - } + } } } } } - Reactions { + id: reactionRow + + eventId: r.eventId + layoutDirection: row.bubbleOnRight ? Qt.RightToLeft : Qt.LeftToRight + reactions: r.reactions + width: row.maxWidth + anchors { + left: row.bubbleOnRight ? undefined : row.left + right: row.bubbleOnRight ? row.right : undefined top: row.bottom topMargin: -4 - left: row.bubbleOnRight? undefined : row.left - right: row.bubbleOnRight? row.right : undefined } - width: row.maxWidth - layoutDirection: row.bubbleOnRight? Qt.RightToLeft : Qt.LeftToRight - - id: reactionRow - - reactions: r.reactions - eventId: r.eventId } - Rectangle { id: unreadRow + + color: palette.highlight + height: visible ? 3 : 0 + visible: (r.index > 0 && (room.fullyReadEventId == r.eventId)) + anchors { - top: reactionRow.bottom - topMargin: 5 left: parent.left right: parent.right + top: reactionRow.bottom + topMargin: 5 } - color: palette.highlight - - visible: (r.index > 0 && (room.fullyReadEventId == r.eventId)) - height: visible ? 3 : 0 - } } |