mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-10-30 09:30:47 +03:00
Basic bubble style
This commit is contained in:
parent
fc7a1bdfba
commit
a86e364d1a
5 changed files with 445 additions and 71 deletions
|
@ -703,6 +703,8 @@ set(QML_SOURCES
|
||||||
resources/qml/RoomList.qml
|
resources/qml/RoomList.qml
|
||||||
resources/qml/TimelineSectionHeader.qml
|
resources/qml/TimelineSectionHeader.qml
|
||||||
resources/qml/TimelineDefaultMessageStyle.qml
|
resources/qml/TimelineDefaultMessageStyle.qml
|
||||||
|
resources/qml/TimelineBubbleMessageStyle.qml
|
||||||
|
resources/qml/TimelineMetadata.qml
|
||||||
resources/qml/TimelineView.qml
|
resources/qml/TimelineView.qml
|
||||||
resources/qml/Avatar.qml
|
resources/qml/Avatar.qml
|
||||||
resources/qml/Completer.qml
|
resources/qml/Completer.qml
|
||||||
|
|
|
@ -68,8 +68,17 @@ Item {
|
||||||
scrolledToThis: eventId === room.scrollTarget && (y + height > chat.y + chat.contentY && y < chat.y + chat.height + chat.contentY)
|
scrolledToThis: eventId === room.scrollTarget && (y + height > chat.y + chat.contentY && y < chat.y + chat.height + chat.contentY)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Component {
|
||||||
|
id: bubbleMessageStyle
|
||||||
|
|
||||||
delegate: defaultMessageStyle
|
TimelineBubbleMessageStyle {
|
||||||
|
messageActions: messageActionsC
|
||||||
|
messageContextMenu: messageContextMenuC
|
||||||
|
scrolledToThis: eventId === room.scrollTarget && (y + height > chat.y + chat.contentY && y < chat.y + chat.height + chat.contentY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: Settings.bubbles ? bubbleMessageStyle : defaultMessageStyle
|
||||||
footer: Item {
|
footer: Item {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
anchors.margins: Nheko.paddingLarge
|
anchors.margins: Nheko.paddingLarge
|
||||||
|
|
323
resources/qml/TimelineBubbleMessageStyle.qml
Normal file
323
resources/qml/TimelineBubbleMessageStyle.qml
Normal file
|
@ -0,0 +1,323 @@
|
||||||
|
// 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 notificationlevel
|
||||||
|
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
|
||||||
|
property bool scrolledToThis: false
|
||||||
|
|
||||||
|
mainInset: (threadId ? (4 + Nheko.paddingSmall) : 0) + 4
|
||||||
|
replyInset: mainInset + 4 + Nheko.paddingSmall
|
||||||
|
|
||||||
|
property int bubbleMargin: 40
|
||||||
|
|
||||||
|
maxWidth: chat.delegateMaxWidth - avatarMargin - bubbleMargin
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Rectangle {
|
||||||
|
id: scrollHighlight
|
||||||
|
anchors.fill: gridContainer
|
||||||
|
|
||||||
|
color: palette.highlight
|
||||||
|
enabled: false
|
||||||
|
opacity: 0
|
||||||
|
visible: true
|
||||||
|
z: 1
|
||||||
|
|
||||||
|
states: State {
|
||||||
|
name: "revealed"
|
||||||
|
when: wrapper.scrolledToThis
|
||||||
|
}
|
||||||
|
transitions: Transition {
|
||||||
|
from: ""
|
||||||
|
to: "revealed"
|
||||||
|
|
||||||
|
SequentialAnimation {
|
||||||
|
PropertyAnimation {
|
||||||
|
duration: 500
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
from: 0
|
||||||
|
properties: "opacity"
|
||||||
|
target: scrollHighlight
|
||||||
|
to: 1
|
||||||
|
}
|
||||||
|
PropertyAnimation {
|
||||||
|
duration: 500
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
from: 1
|
||||||
|
properties: "opacity"
|
||||||
|
target: scrollHighlight
|
||||||
|
to: 0
|
||||||
|
}
|
||||||
|
ScriptAction {
|
||||||
|
script: wrapper.room.eventShown()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Item {
|
||||||
|
id: gridContainer
|
||||||
|
|
||||||
|
width: wrapper.width - wrapper.avatarMargin
|
||||||
|
implicitHeight: messageBubble.implicitHeight
|
||||||
|
x: wrapper.avatarMargin
|
||||||
|
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
|
||||||
|
//messageActions.anchors.rightMargin = metadata.width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
AbstractButton {
|
||||||
|
id: messageBubble
|
||||||
|
|
||||||
|
anchors.left: (wrapper.isStateEvent || wrapper.isSender) ? undefined : parent.left
|
||||||
|
anchors.right: (wrapper.isStateEvent || !wrapper.isSender) ? undefined : parent.right
|
||||||
|
anchors.horizontalCenter: wrapper.isStateEvent ? parent.horizontalCenter : undefined
|
||||||
|
|
||||||
|
property color userColor: TimelineManager.userColor(wrapper.main?.userId ?? '', palette.base)
|
||||||
|
|
||||||
|
contentItem: Item {
|
||||||
|
id: contentPlacementContainer
|
||||||
|
|
||||||
|
property int metadataWidth: 100
|
||||||
|
property int metadataHeight: 20
|
||||||
|
|
||||||
|
property bool fitsMetadata: ((wrapper.main?.width ?? 0) + wrapper.mainInset + metadata.width) < wrapper.maxWidth
|
||||||
|
|
||||||
|
implicitWidth: Math.max((wrapper.reply?.width ?? 0) + wrapper.replyInset, (wrapper.main?.width ?? 0) + wrapper.mainInset + (fitsMetadata ? metadata.width : 0))
|
||||||
|
implicitHeight: contentColumn.implicitHeight + (fitsMetadata ? 0 : metadata.height)
|
||||||
|
|
||||||
|
TimelineMetadata {
|
||||||
|
id: metadata
|
||||||
|
|
||||||
|
scaling: 0.75
|
||||||
|
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
|
||||||
|
visible: !wrapper.isStateEvent
|
||||||
|
|
||||||
|
eventId: wrapper.eventId
|
||||||
|
status: wrapper.status
|
||||||
|
trustlevel: wrapper.trustlevel
|
||||||
|
isEdited: wrapper.isEdited
|
||||||
|
isEncrypted: wrapper.isEncrypted
|
||||||
|
threadId: wrapper.threadId
|
||||||
|
timestamp: wrapper.timestamp
|
||||||
|
room: wrapper.room
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: contentColumn
|
||||||
|
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
AbstractButton {
|
||||||
|
id: replyRow
|
||||||
|
visible: wrapper.reply
|
||||||
|
|
||||||
|
height: replyLine.height
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
property color userColor: TimelineManager.userColor(wrapper.reply?.userId ?? '', palette.base)
|
||||||
|
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
NhekoCursorShape {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
}
|
||||||
|
|
||||||
|
contentItem: Row {
|
||||||
|
id: replyRowLay
|
||||||
|
|
||||||
|
spacing: Nheko.paddingSmall
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: replyLine
|
||||||
|
height: Math.min( wrapper.reply?.height, timelineView.height / 5) + Nheko.paddingSmall + replyUserButton.height
|
||||||
|
color: replyRow.userColor
|
||||||
|
width: 4
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
id: replyCol
|
||||||
|
|
||||||
|
AbstractButton {
|
||||||
|
id: replyUserButton
|
||||||
|
|
||||||
|
contentItem: Label {
|
||||||
|
id: userName_
|
||||||
|
text: wrapper.reply?.userName ?? ''
|
||||||
|
color: replyRow.userColor
|
||||||
|
textFormat: Text.RichText
|
||||||
|
width: wrapper.maxWidth
|
||||||
|
//elideWidth: wrapper.maxWidth
|
||||||
|
}
|
||||||
|
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]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
padding: 4
|
||||||
|
background: Rectangle {
|
||||||
|
color: !wrapper.isStateEvent ? Qt.tint(palette.base, Qt.hsla(messageBubble.userColor.hslHue, 0.5, messageBubble.userColor.hslLightness, 0.2)) : "transparent"
|
||||||
|
radius: 4
|
||||||
|
border.color: Nheko.theme.red
|
||||||
|
border.width: wrapper.notificationlevel == MtxEvent.Highlight ? 1 : 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
|
@ -51,7 +51,7 @@ TimelineEvent {
|
||||||
property alias hovered: messageHover.hovered
|
property alias hovered: messageHover.hovered
|
||||||
property bool scrolledToThis: false
|
property bool scrolledToThis: false
|
||||||
|
|
||||||
mainInset: (threadId ? (4 + Nheko.paddingSmall) : 0) + 4
|
mainInset: (threadId ? (4 + Nheko.paddingSmall) : 0)
|
||||||
replyInset: mainInset + 4 + Nheko.paddingSmall
|
replyInset: mainInset + 4 + Nheko.paddingSmall
|
||||||
|
|
||||||
maxWidth: chat.delegateMaxWidth - avatarMargin - metadata.width
|
maxWidth: chat.delegateMaxWidth - avatarMargin - metadata.width
|
||||||
|
@ -269,82 +269,24 @@ TimelineEvent {
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
RowLayout {
|
TimelineMetadata {
|
||||||
id: metadata
|
id: metadata
|
||||||
|
|
||||||
property int iconSize: Math.floor(fontMetrics.ascent * scaling)
|
scaling: 1
|
||||||
property double scaling: Settings.bubbles ? 0.75 : 1
|
|
||||||
|
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
y: section.visible && section.active ? section.y + section.height : 0
|
y: section.visible && section.active ? section.y + section.height : 0
|
||||||
|
|
||||||
spacing: 2
|
visible: !wrapper.isStateEvent
|
||||||
visible: !isStateEvent
|
|
||||||
|
|
||||||
StatusIndicator {
|
eventId: wrapper.eventId
|
||||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
status: wrapper.status
|
||||||
eventId: wrapper.eventId
|
trustlevel: wrapper.trustlevel
|
||||||
height: parent.iconSize
|
isEdited: wrapper.isEdited
|
||||||
status: wrapper.status
|
isEncrypted: wrapper.isEncrypted
|
||||||
width: parent.iconSize
|
threadId: wrapper.threadId
|
||||||
}
|
timestamp: wrapper.timestamp
|
||||||
Image {
|
room: wrapper.room
|
||||||
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 {
|
Reactions {
|
||||||
id: reactionRow
|
id: reactionRow
|
||||||
|
|
98
resources/qml/TimelineMetadata.qml
Normal file
98
resources/qml/TimelineMetadata.qml
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
// 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
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: metadata
|
||||||
|
|
||||||
|
property int iconSize: Math.floor(fontMetrics.ascent * scaling)
|
||||||
|
required property double scaling
|
||||||
|
|
||||||
|
required property string eventId
|
||||||
|
required property int status
|
||||||
|
required property int trustlevel
|
||||||
|
required property bool isEdited
|
||||||
|
required property bool isEncrypted
|
||||||
|
required property string threadId
|
||||||
|
required property date timestamp
|
||||||
|
required property Room room
|
||||||
|
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
StatusIndicator {
|
||||||
|
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||||
|
eventId: metadata.eventId
|
||||||
|
height: parent.iconSize
|
||||||
|
status: metadata.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?" + ((metadata.eventId == metadata.room.edit) ? palette.highlight : palette.buttonText)
|
||||||
|
sourceSize.height: parent.iconSize * Screen.devicePixelRatio
|
||||||
|
sourceSize.width: parent.iconSize * Screen.devicePixelRatio
|
||||||
|
visible: metadata.isEdited || metadata.eventId == metadata.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(metadata.threadId, palette.base)
|
||||||
|
height: parent.iconSize
|
||||||
|
image: ":/icons/icons/ui/thread.svg"
|
||||||
|
visible: metadata.threadId
|
||||||
|
width: parent.iconSize
|
||||||
|
|
||||||
|
onClicked: metadata.room.thread = threadId
|
||||||
|
}
|
||||||
|
EncryptionIndicator {
|
||||||
|
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||||
|
encrypted: metadata.isEncrypted
|
||||||
|
height: parent.iconSize
|
||||||
|
sourceSize.height: parent.iconSize * Screen.devicePixelRatio
|
||||||
|
sourceSize.width: parent.iconSize * Screen.devicePixelRatio
|
||||||
|
trust: metadata.trustlevel
|
||||||
|
visible: metadata.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(metadata.timestamp, Qt.DefaultLocaleLongDate)
|
||||||
|
ToolTip.visible: ma.hovered
|
||||||
|
color: palette.inactive.text
|
||||||
|
font.pointSize: fontMetrics.font.pointSize * parent.scaling
|
||||||
|
text: metadata.timestamp.toLocaleTimeString(Locale.ShortFormat)
|
||||||
|
|
||||||
|
HoverHandler {
|
||||||
|
id: ma
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue