Refactor Timeline Container

Update the base item in the timeline container to be a column layout. This means that all the items can be laid out automatically without the need to set lots of manual settings and anchoring. 

The overall height calculation for the delegate is vastly simplified (in fact it is removed) which deals with the fact that there were still instances where the manual calculation didn't work e.g. a delegate with a reaction followed by another message from the same user didn't give the correct bottom margin (see below)

before:
![timelineContainer_height_bug](/uploads/5b14568294698198dee8412f6cd19be0/timelineContainer_height_bug.png)

after:
![timelineContainer_height_bug_fix](/uploads/c5828f1b793817fd0ed523c3580a2ecc/timelineContainer_height_bug_fix.png)

This also improves upon the recently changed hover highlight behaviour. The previous patched moved it to cover the avatar as well as the bubble however it also covered the section and reaction when present which didn't look good. The highlight now only covers the avatar and bubble

before:
![highlight_bug](/uploads/0d08dc769ff737e0fb4981243d02d5f3/highlight_bug.png)

after:
![highlight_bug_fixed](/uploads/536ed672d0f1bb6cbe6c45777fed4b53/highlight_bug_fixed.png)

This also cleans up some of the margins in both bubble and compact to ensure consistency.
This commit is contained in:
James Graham
2022-11-11 17:05:14 +00:00
parent 8addf0f078
commit 460997bca3
8 changed files with 221 additions and 246 deletions

View File

@@ -13,15 +13,13 @@ import org.kde.neochat 1.0
TimelineContainer { TimelineContainer {
id: audioDelegate id: audioDelegate
onReplyClicked: ListView.view.goToEvent(eventID)
onOpenContextMenu: openFileContext(model, audioDelegate) onOpenContextMenu: openFileContext(model, audioDelegate)
readonly property bool downloaded: model.progressInfo && model.progressInfo.completed readonly property bool downloaded: model.progressInfo && model.progressInfo.completed
onDownloadedChanged: audio.play() onDownloadedChanged: audio.play()
hoverComponent: hoverActions
innerObject: ColumnLayout { innerObject: ColumnLayout {
Layout.fillWidth: true
Layout.maximumWidth: audioDelegate.contentMaxWidth Layout.maximumWidth: audioDelegate.contentMaxWidth
Audio { Audio {

View File

@@ -13,9 +13,6 @@ import org.kde.neochat 1.0
TimelineContainer { TimelineContainer {
id: fileDelegate id: fileDelegate
onReplyClicked: ListView.view.goToEvent(eventID)
hoverComponent: hoverActions
onOpenContextMenu: openFileContext(model, fileDelegate) onOpenContextMenu: openFileContext(model, fileDelegate)
readonly property bool downloaded: progressInfo && progressInfo.completed readonly property bool downloaded: progressInfo && progressInfo.completed

View File

@@ -13,9 +13,6 @@ import org.kde.neochat 1.0
TimelineContainer { TimelineContainer {
id: imageDelegate id: imageDelegate
onReplyClicked: ListView.view.goToEvent(eventID)
hoverComponent: hoverActions
onOpenContextMenu: openFileContext(model, imageDelegate) onOpenContextMenu: openFileContext(model, imageDelegate)
property var content: model.content property var content: model.content

View File

@@ -16,9 +16,6 @@ TimelineContainer {
property bool isEmote: false property bool isEmote: false
onOpenContextMenu: openMessageContext(model, label.selectedText, Controller.plainText(label.textDocument)) onOpenContextMenu: openMessageContext(model, label.selectedText, Controller.plainText(label.textDocument))
onReplyClicked: ListView.view.goToEvent(eventID)
hoverComponent: hoverActions
innerObject: ColumnLayout { innerObject: ColumnLayout {
Layout.maximumWidth: messageDelegate.contentMaxWidth Layout.maximumWidth: messageDelegate.contentMaxWidth
RichLabel { RichLabel {
@@ -29,7 +26,6 @@ TimelineContainer {
Loader { Loader {
id: linkPreviewLoader id: linkPreviewLoader
Layout.fillWidth: true Layout.fillWidth: true
height: active ? item.implicitHeight : 0
active: !currentRoom.usesEncryption && model.display && model.display.includes("http") active: !currentRoom.usesEncryption && model.display && model.display.includes("http")
visible: Config.showLinkPreview && active visible: Config.showLinkPreview && active
sourceComponent: LinkPreviewDelegate { sourceComponent: LinkPreviewDelegate {

View File

@@ -9,7 +9,8 @@ import QtQuick.Layouts 1.15
import org.kde.kirigami 2.15 as Kirigami import org.kde.kirigami 2.15 as Kirigami
Flow { Flow {
spacing: Kirigami.Units.largeSpacing spacing: Kirigami.Units.smallSpacing
Repeater { Repeater {
model: reaction ?? null model: reaction ?? null
@@ -32,7 +33,6 @@ Flow {
border.width: 1 border.width: 1
} }
checkable: true checkable: true
checked: modelData.hasLocalUser checked: modelData.hasLocalUser

View File

@@ -9,15 +9,21 @@ import org.kde.kirigami 2.15 as Kirigami
import org.kde.neochat 1.0 import org.kde.neochat 1.0
QQC2.ItemDelegate { ColumnLayout {
id: timelineContainer id: root
signal openContextMenu
signal openExternally()
signal replyClicked(string eventID)
onReplyClicked: ListView.view.goToEvent(eventID)
default property alias innerObject : column.children default property alias innerObject : column.children
// readonly property bool failed: marks == EventStatus.SendingFailed
readonly property bool sectionVisible: model.showSection
property Item hoverComponent: hoverActions
property bool isEmote: false property bool isEmote: false
property bool cardBackground: true property bool cardBackground: true
property bool showUserMessageOnRight: Config.showLocalMessagesOnRight && model.author.isLocalUser && !Config.compactLayout
property bool isHighlighted: model.isHighlighted || isTemporaryHighlighted property bool isHighlighted: model.isHighlighted || isTemporaryHighlighted
property bool isTemporaryHighlighted: false property bool isTemporaryHighlighted: false
@@ -30,58 +36,15 @@ QQC2.ItemDelegate {
onTriggered: isTemporaryHighlighted = false onTriggered: isTemporaryHighlighted = false
} }
signal openContextMenu
// The bubble and delegate widths are allowed to grow once the ListView gets beyond a certain size // The bubble and delegate widths are allowed to grow once the ListView gets beyond a certain size
// extraWidth defines this as the excess after a certain ListView width, capped to a max value // extraWidth defines this as the excess after a certain ListView width, capped to a max value
readonly property int extraWidth: messageListView.width >= Kirigami.Units.gridUnit * 46 ? Math.min((messageListView.width - Kirigami.Units.gridUnit * 46), Kirigami.Units.gridUnit * 20) : 0 readonly property int extraWidth: messageListView.width >= Kirigami.Units.gridUnit * 46 ? Math.min((messageListView.width - Kirigami.Units.gridUnit * 46), Kirigami.Units.gridUnit * 20) : 0
readonly property int bubbleMaxWidth: Kirigami.Units.gridUnit * 20 + extraWidth * 0.5 readonly property int bubbleMaxWidth: Kirigami.Units.gridUnit * 20 + extraWidth * 0.5
readonly property int delegateMaxWidth: Config.compactLayout ? messageListView.width : Math.min(messageListView.width, Kirigami.Units.gridUnit * 40 + extraWidth) readonly property int delegateWidth: Config.compactLayout ? messageListView.width : Math.min(messageListView.width, Kirigami.Units.gridUnit * 40 + extraWidth)
readonly property int contentMaxWidth: Config.compactLayout ? width - (Config.showAvatarInTimeline ? Kirigami.Units.gridUnit * 2 : 0) - Kirigami.Units.largeSpacing * 4 : Math.min(width - Kirigami.Units.gridUnit * 2 - Kirigami.Units.largeSpacing * 6, bubbleMaxWidth) readonly property int contentMaxWidth: Config.compactLayout ? width - (Config.showAvatarInTimeline ? Kirigami.Units.gridUnit * 2 : 0) - Kirigami.Units.largeSpacing * 4 : Math.min(width - Kirigami.Units.gridUnit * 2 - Kirigami.Units.largeSpacing * 6, bubbleMaxWidth)
property bool showUserMessageOnRight: Config.showLocalMessagesOnRight && width: delegateWidth
model.author.isLocalUser && !Config.compactLayout spacing: Kirigami.Units.smallSpacing
signal openExternally()
signal replyClicked(string eventID)
Component.onCompleted: {
if (model.isReply && model.reply === undefined) {
messageEventModel.loadReply(sortedMessageEventModel.mapToSource(sortedMessageEventModel.index(model.index, 0)))
}
}
topPadding: 0
bottomPadding: 0
topInset: showAuthor ? Kirigami.Units.largeSpacing : (Config.compactLayout ? 1 : Kirigami.Units.smallSpacing)
leftInset: Kirigami.Units.smallSpacing
rightInset: Kirigami.Units.smallSpacing
width: delegateMaxWidth
height: sectionDelegate.height + Math.max(model.showAuthor ? avatar.height : 0, bubble.implicitHeight) + loader.height + loader.anchors.topMargin + avatar.anchors.topMargin
background: Rectangle {
visible: timelineContainer.hovered
color: Kirigami.ColorUtils.tintWithAlpha(Kirigami.Theme.backgroundColor, Kirigami.Theme.highlightColor, 0.15)
radius: Kirigami.Units.smallSpacing
}
property Item hoverComponent
// show hover actions
onHoveredChanged: {
if (hovered && !Kirigami.Settings.isMobile) {
updateHoverComponent();
}
}
// updates the global hover component to point to this delegate, and update its position
function updateHoverComponent() {
if (hoverComponent) {
hoverComponent.delegate = timelineContainer
hoverComponent.bubble = bubble
hoverComponent.updateFunction = updateHoverComponent;
hoverComponent.event = model
}
}
state: Config.compactLayout ? "alignLeft" : "alignCenter" state: Config.compactLayout ? "alignLeft" : "alignCenter"
// Align left when in compact mode and center when using bubbles // Align left when in compact mode and center when using bubbles
@@ -89,7 +52,7 @@ QQC2.ItemDelegate {
State { State {
name: "alignLeft" name: "alignLeft"
AnchorChanges { AnchorChanges {
target: timelineContainer target: root
anchors.horizontalCenter: undefined anchors.horizontalCenter: undefined
anchors.left: parent ? parent.left : undefined anchors.left: parent ? parent.left : undefined
} }
@@ -97,7 +60,7 @@ QQC2.ItemDelegate {
State { State {
name: "alignCenter" name: "alignCenter"
AnchorChanges { AnchorChanges {
target: timelineContainer target: root
anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined anchors.horizontalCenter: parent ? parent.horizontalCenter : undefined
anchors.left: undefined anchors.left: undefined
} }
@@ -112,216 +75,244 @@ QQC2.ItemDelegate {
SectionDelegate { SectionDelegate {
id: sectionDelegate id: sectionDelegate
anchors.left: timelineContainer.left
anchors.right: timelineContainer.right Layout.fillWidth: true
visible: sectionVisible visible: model.showSection
height: visible ? implicitHeight : 0
labelText: model.showSection ? section : "" labelText: model.showSection ? section : ""
} }
Kirigami.Avatar { QQC2.ItemDelegate {
id: avatar id: mainContainer
width: visible || Config.showAvatarInTimeline ? Kirigami.Units.gridUnit * 2 + Kirigami.Units.smallSpacing * 2 : 0
height: width
padding: Kirigami.Units.smallSpacing
topInset: Kirigami.Units.smallSpacing
bottomInset: Kirigami.Units.smallSpacing
leftInset: Kirigami.Units.smallSpacing
rightInset: Kirigami.Units.smallSpacing
sourceSize.width: width
sourceSize.height: width
anchors {
top: sectionDelegate.bottom
topMargin: model.showAuthor ? Kirigami.Units.largeSpacing : (Config.compactLayout ? 1 : Kirigami.Units.smallSpacing)
left: parent.left
leftMargin: Kirigami.Units.smallSpacing
}
visible: model.showAuthor && Layout.fillWidth: true
Config.showAvatarInTimeline && Layout.topMargin: showAuthor ? Kirigami.Units.largeSpacing : (Config.compactLayout ? 1 : Kirigami.Units.smallSpacing)
(Config.compactLayout || !showUserMessageOnRight) Layout.leftMargin: Kirigami.Units.smallSpacing
name: model.displayNameForInitials
source: visible && model.author.avatarMediaId ? ("image://mxc/" + model.author.avatarMediaId) : ""
color: model.author.color
MouseArea { implicitHeight: Math.max(model.showAuthor ? avatar.implicitHeight : 0, bubble.height)
anchors.fill: parent
onClicked: { Component.onCompleted: {
userDetailDialog.createObject(QQC2.ApplicationWindow.overlay, { if (model.isReply && model.reply === undefined) {
room: currentRoom, messageEventModel.loadReply(sortedMessageEventModel.mapToSource(sortedMessageEventModel.index(model.index, 0)))
user: author.object,
displayName: author.displayName,
avatarMediaId: author.avatarMediaId,
avatarUrl: author.avatarUrl
}).open();
} }
cursorShape: Qt.PointingHandCursor
} }
}
QQC2.Control { // show hover actions
id: bubble onHoveredChanged: {
topPadding: Config.compactLayout ? Kirigami.Units.smallSpacing / 2 : Kirigami.Units.largeSpacing if (hovered && !Kirigami.Settings.isMobile) {
bottomPadding: Config.compactLayout ? Kirigami.Units.mediumSpacing / 2 : Kirigami.Units.largeSpacing updateHoverComponent();
leftPadding: Kirigami.Units.largeSpacing + Kirigami.Units.smallSpacing }
rightPadding: Kirigami.Units.largeSpacing + Kirigami.Units.smallSpacing
hoverEnabled: true
anchors {
top: avatar.top
leftMargin: Kirigami.Units.smallSpacing
rightMargin: showUserMessageOnRight ? Kirigami.Units.smallSpacing : Kirigami.Units.largeSpacing
} }
// HACK: anchoring didn't reset anchors.right when switching from parent.right to undefined reliably
width: Config.compactLayout ? timelineContainer.width - (Config.showAvatarInTimeline ? Kirigami.Units.gridUnit * 2 : 0) + Kirigami.Units.largeSpacing * 2 : implicitWidth
state: showUserMessageOnRight ? "userMessageOnRight" : "userMessageOnLeft"
// states for anchor animations on window resize // Show hover actions by updating the global hover component to this delegate
// as setting anchors to undefined did not work reliably function updateHoverComponent() {
states: [ if (hovered && !Kirigami.Settings.isMobile) {
State { hoverComponent.delegate = root
name: "userMessageOnRight" hoverComponent.bubble = bubble
AnchorChanges { hoverComponent.event = model
target: bubble hoverComponent.updateFunction = updateHoverComponent;
anchors.left: undefined }
anchors.right: parent.right }
Kirigami.Avatar {
id: avatar
width: visible || Config.showAvatarInTimeline ? Kirigami.Units.gridUnit * 2 + Kirigami.Units.smallSpacing * 2 : 0
height: width
padding: Kirigami.Units.smallSpacing
topInset: Kirigami.Units.smallSpacing
bottomInset: Kirigami.Units.smallSpacing
leftInset: Kirigami.Units.smallSpacing
rightInset: Kirigami.Units.smallSpacing
anchors {
left: parent.left
leftMargin: Kirigami.Units.smallSpacing
}
visible: model.showAuthor &&
Config.showAvatarInTimeline &&
(Config.compactLayout || !showUserMessageOnRight)
name: model.author.name ?? model.author.displayName
source: visible && model.author.avatarMediaId ? ("image://mxc/" + model.author.avatarMediaId) : ""
color: model.author.color
MouseArea {
anchors.fill: parent
onClicked: {
userDetailDialog.createObject(QQC2.ApplicationWindow.overlay, {
room: currentRoom,
user: author.object,
displayName: author.displayName,
avatarMediaId: author.avatarMediaId,
avatarUrl: author.avatarUrl
}).open();
} }
}, cursorShape: Qt.PointingHandCursor
State { }
name: "userMessageOnLeft" }
AnchorChanges {
target: bubble QQC2.Control {
anchors.left: avatar.right id: bubble
anchors.right: undefined topPadding: Config.compactLayout ? Kirigami.Units.smallSpacing / 2 : Kirigami.Units.largeSpacing
bottomPadding: Config.compactLayout ? Kirigami.Units.mediumSpacing / 2 : Kirigami.Units.largeSpacing
leftPadding: Config.compactLayout ? 0 : Kirigami.Units.largeSpacing + Kirigami.Units.smallSpacing
rightPadding: Kirigami.Units.largeSpacing + Kirigami.Units.smallSpacing
hoverEnabled: true
anchors {
leftMargin: Kirigami.Units.smallSpacing
rightMargin: Kirigami.Units.largeSpacing
}
// HACK: anchoring didn't reset anchors.right when switching from parent.right to undefined reliably
width: Config.compactLayout ? mainContainer.width - (Config.showAvatarInTimeline ? Kirigami.Units.gridUnit * 2 : 0) + Kirigami.Units.largeSpacing * 2 : implicitWidth
state: showUserMessageOnRight ? "userMessageOnRight" : "userMessageOnLeft"
// states for anchor animations on window resize
// as setting anchors to undefined did not work reliably
states: [
State {
name: "userMessageOnRight"
AnchorChanges {
target: bubble
anchors.left: undefined
anchors.right: parent.right
}
},
State {
name: "userMessageOnLeft"
AnchorChanges {
target: bubble
anchors.left: avatar.right
anchors.right: undefined
}
} }
} ]
]
transitions: [ transitions: [
Transition { Transition {
AnchorAnimation{duration: Kirigami.Units.longDuration; easing.type: Easing.OutCubic} AnchorAnimation{duration: Kirigami.Units.longDuration; easing.type: Easing.OutCubic}
} }
] ]
contentItem: ColumnLayout {
id: column
spacing: Kirigami.Units.smallSpacing
RowLayout {
id: rowLayout
contentItem: ColumnLayout {
id: column
spacing: Kirigami.Units.smallSpacing spacing: Kirigami.Units.smallSpacing
visible: model.showAuthor && !isEmote RowLayout {
id: rowLayout
QQC2.Label { spacing: Kirigami.Units.smallSpacing
id: nameLabel visible: model.showAuthor && !isEmote
Layout.maximumWidth: contentMaxWidth - timeLabel.implicitWidth - rowLayout.spacing QQC2.Label {
id: nameLabel
text: visible ? author.displayName : "" Layout.maximumWidth: contentMaxWidth - timeLabel.implicitWidth - rowLayout.spacing
textFormat: Text.PlainText
font.weight: Font.Bold text: visible ? author.displayName : ""
color: author.color textFormat: Text.PlainText
elide: Text.ElideRight font.weight: Font.Bold
MouseArea { color: author.color
anchors.fill: parent elide: Text.ElideRight
cursorShape: Qt.PointingHandCursor MouseArea {
onClicked: { anchors.fill: parent
userDetailDialog.createObject(QQC2.ApplicationWindow.overlay, { cursorShape: Qt.PointingHandCursor
room: currentRoom, onClicked: {
user: author.object, userDetailDialog.createObject(QQC2.ApplicationWindow.overlay, {
displayName: author.displayName, room: currentRoom,
avatarMediaId: author.avatarMediaId, user: author.object,
avatarUrl: author.avatarUrl displayName: author.displayName,
}).open(); avatarMediaId: author.avatarMediaId,
avatarUrl: author.avatarUrl
}).open();
}
}
}
QQC2.Label {
id: timeLabel
text: visible ? time.toLocaleTimeString(Qt.locale(), Locale.ShortFormat) : ""
color: Kirigami.Theme.disabledTextColor
QQC2.ToolTip.visible: hoverHandler.hovered
QQC2.ToolTip.text: time.toLocaleString(Qt.locale(), Locale.LongFormat)
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
HoverHandler {
id: hoverHandler
} }
} }
} }
QQC2.Label { Loader {
id: timeLabel id: replyLoader
text: visible ? time.toLocaleTimeString(Qt.locale(), Locale.ShortFormat) : "" Layout.maximumWidth: contentMaxWidth
color: Kirigami.Theme.disabledTextColor
QQC2.ToolTip.visible: hoverHandler.hovered
QQC2.ToolTip.text: time.toLocaleString(Qt.locale(), Locale.LongFormat)
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
HoverHandler { active: model.reply !== undefined
id: hoverHandler visible: active
sourceComponent: ReplyComponent {
name: currentRoom.htmlSafeMemberName(reply.author.id)
avatar: reply.author.avatarMediaId ? ("image://mxc/" + reply.author.avatarMediaId) : ""
color: reply.author.color
}
Connections {
target: replyLoader.item
function onReplyClicked() {
replyClicked(reply.eventId)
}
} }
} }
} }
Loader {
id: replyLoader
Layout.maximumWidth: contentMaxWidth background: Item {
Kirigami.ShadowedRectangle {
id: bubbleBackground
visible: cardBackground && !Config.compactLayout
anchors.fill: parent
color: {
if (model.author.isLocalUser) {
return Kirigami.ColorUtils.tintWithAlpha(Kirigami.Theme.backgroundColor, Kirigami.Theme.highlightColor, 0.15)
} else if (root.isHighlighted) {
return Kirigami.Theme.positiveBackgroundColor
} else {
return Kirigami.Theme.backgroundColor
}
}
radius: Kirigami.Units.smallSpacing
shadow.size: Kirigami.Units.smallSpacing
shadow.color: root.isHighlighted ? Qt.rgba(0.0, 0.0, 0.0, 0.10) : Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.10)
border.color: Kirigami.ColorUtils.tintWithAlpha(color, Kirigami.Theme.textColor, 0.15)
border.width: 1
active: model.reply !== undefined Behavior on color {
visible: active ColorAnimation {target: bubbleBackground; duration: Kirigami.Units.veryLongDuration; easing.type: Easing.InOutCubic}
sourceComponent: ReplyComponent {
name: currentRoom.htmlSafeMemberName(reply.author.id)
avatar: reply.author.avatarMediaId ? ("image://mxc/" + reply.author.avatarMediaId) : ""
color: reply.author.color
}
Connections {
target: replyLoader.item
function onReplyClicked() {
replyClicked(reply.eventId)
} }
} }
} }
} }
background: Item { background: Rectangle {
Kirigami.ShadowedRectangle { visible: mainContainer.hovered
id: bubbleBackground color: Kirigami.ColorUtils.tintWithAlpha(Kirigami.Theme.backgroundColor, Kirigami.Theme.highlightColor, 0.15)
visible: cardBackground && !Config.compactLayout radius: Kirigami.Units.smallSpacing
anchors.fill: parent }
color: {
if (model.author.isLocalUser) {
return Kirigami.ColorUtils.tintWithAlpha(Kirigami.Theme.backgroundColor, Kirigami.Theme.highlightColor, 0.15)
} else if (timelineContainer.isHighlighted) {
return Kirigami.Theme.positiveBackgroundColor
} else {
return Kirigami.Theme.backgroundColor
}
}
radius: Kirigami.Units.smallSpacing
shadow.size: Kirigami.Units.smallSpacing
shadow.color: timelineContainer.isHighlighted ? Qt.rgba(0.0, 0.0, 0.0, 0.10) : Qt.rgba(Kirigami.Theme.textColor.r, Kirigami.Theme.textColor.g, Kirigami.Theme.textColor.b, 0.10)
border.color: Kirigami.ColorUtils.tintWithAlpha(color, Kirigami.Theme.textColor, 0.15)
border.width: 1
Behavior on color { TapHandler {
ColorAnimation {target: bubbleBackground; duration: Kirigami.Units.veryLongDuration; easing.type: Easing.InOutCubic} acceptedButtons: Qt.RightButton
} onTapped: root.openContextMenu()
} }
TapHandler {
acceptedButtons: Qt.LeftButton
onLongPressed: root.openContextMenu()
} }
} }
Loader { ReactionDelegate {
id: loader Layout.maximumWidth: delegateWidth - Kirigami.Units.largeSpacing * 2
anchors { Layout.alignment: showUserMessageOnRight ? Qt.AlignRight : Qt.AlignLeft
left: bubble.left Layout.leftMargin: showUserMessageOnRight ? 0 : bubble.x + bubble.anchors.leftMargin
right: parent.right Layout.rightMargin: showUserMessageOnRight ? Kirigami.Units.largeSpacing : 0
top: bubble.bottom
topMargin: active ? Kirigami.Units.smallSpacing : 0
}
height: active ? item.implicitHeight : 0
active: eventType !== MessageEventModel.State && eventType !== MessageEventModel.Notice && reaction != undefined && reaction.length > 0
visible: active
sourceComponent: ReactionDelegate { }
}
TapHandler { visible: eventType !== MessageEventModel.State && eventType !== MessageEventModel.Notice && reaction != undefined && reaction.length > 0
acceptedButtons: Qt.RightButton
acceptedDevices: PointerDevice.Mouse
onTapped: timelineContainer.openContextMenu()
}
TapHandler {
acceptedButtons: Qt.LeftButton
onLongPressed: timelineContainer.openContextMenu()
} }
} }

View File

@@ -14,9 +14,6 @@ import org.kde.neochat 1.0
TimelineContainer { TimelineContainer {
id: videoDelegate id: videoDelegate
onReplyClicked: ListView.view.goToEvent(eventID)
hoverComponent: hoverActions
property bool playOnFinished: false property bool playOnFinished: false
readonly property bool downloaded: progressInfo && progressInfo.completed readonly property bool downloaded: progressInfo && progressInfo.completed

View File

@@ -463,6 +463,7 @@ Kirigami.ScrollablePage {
property var bubble: null property var bubble: null
property var hovered: bubble && bubble.hovered property var hovered: bubble && bubble.hovered
property var visibleDelayed: (hovered || hoverHandler.hovered) && !Kirigami.Settings.isMobile property var visibleDelayed: (hovered || hoverHandler.hovered) && !Kirigami.Settings.isMobile
property var updateFunction
onVisibleDelayedChanged: if (visibleDelayed) { onVisibleDelayedChanged: if (visibleDelayed) {
visible = true; visible = true;
} else { } else {
@@ -482,8 +483,6 @@ Kirigami.ScrollablePage {
visible: false visible: false
property var updateFunction
property alias childWidth: hoverActionsRow.width property alias childWidth: hoverActionsRow.width
property alias childHeight: hoverActionsRow.height property alias childHeight: hoverActionsRow.height