Reimplement reply delegate by moving out the timeline event without layout

This commit is contained in:
Nicolas Werner 2023-08-25 20:43:04 +02:00
parent aef0cb9884
commit b187440e68
No known key found for this signature in database
GPG key ID: C8D75E610773F2D9
13 changed files with 352 additions and 460 deletions

View file

@ -713,6 +713,7 @@ set(QML_SOURCES
resources/qml/UploadBox.qml resources/qml/UploadBox.qml
resources/qml/MessageInput.qml resources/qml/MessageInput.qml
resources/qml/MessageView.qml resources/qml/MessageView.qml
resources/qml/TimelineEvent.qml
resources/qml/PrivacyScreen.qml resources/qml/PrivacyScreen.qml
resources/qml/Reactions.qml resources/qml/Reactions.qml
resources/qml/ReplyPopup.qml resources/qml/ReplyPopup.qml

View file

@ -145,7 +145,6 @@ Control {
roleValue: "user" roleValue: "user"
RowLayout { RowLayout {
anchors.centerIn: centerRowContent ? parent : undefined anchors.centerIn: centerRowContent ? parent : undefined
spacing: rowSpacing spacing: rowSpacing
@ -171,7 +170,6 @@ Control {
roleValue: "emoji" roleValue: "emoji"
RowLayout { RowLayout {
anchors.centerIn: parent anchors.centerIn: parent
spacing: rowSpacing spacing: rowSpacing
@ -207,7 +205,6 @@ Control {
roleValue: "command" roleValue: "command"
RowLayout { RowLayout {
anchors.centerIn: parent anchors.centerIn: parent
spacing: rowSpacing spacing: rowSpacing
@ -226,7 +223,6 @@ Control {
roleValue: "room" roleValue: "room"
RowLayout { RowLayout {
anchors.centerIn: centerRowContent ? parent : undefined anchors.centerIn: centerRowContent ? parent : undefined
spacing: rowSpacing spacing: rowSpacing
@ -251,7 +247,6 @@ Control {
roleValue: "roomAliases" roleValue: "roomAliases"
RowLayout { RowLayout {
anchors.centerIn: parent anchors.centerIn: parent
spacing: rowSpacing spacing: rowSpacing

View file

@ -54,24 +54,8 @@ Popup {
Reply { Reply {
id: replyPreview id: replyPreview
property var modelData: room ? room.getDump(mid, "") : {} eventId: mid
blurhash: modelData.blurhash ?? ""
body: modelData.body ?? ""
encryptionError: modelData.encryptionError ?? ""
eventId: modelData.eventId ?? ""
filename: modelData.filename ?? ""
filesize: modelData.filesize ?? ""
formattedBody: modelData.formattedBody ?? ""
isOnlyEmoji: modelData.isOnlyEmoji ?? false
originalWidth: modelData.originalWidth ?? 0
proportionalHeight: modelData.proportionalHeight ?? 1
type: modelData.type ?? MtxEvent.UnknownMessage
typeString: modelData.typeString ?? ""
url: modelData.url ?? ""
userColor: TimelineManager.userColor(modelData.userId, palette.window) userColor: TimelineManager.userColor(modelData.userId, palette.window)
userId: modelData.userId ?? ""
userName: modelData.userName ?? ""
width: parent.width width: parent.width
} }
MatrixTextField { MatrixTextField {

View file

@ -11,25 +11,24 @@ TextArea {
property alias cursorShape: cs.cursorShape property alias cursorShape: cs.cursorShape
leftInset: 0
bottomInset: 0
rightInset: 0
topInset: 0
leftPadding: 0
bottomPadding: 0
rightPadding: 0
topPadding: 0
background: null
ToolTip.text: hoveredLink ToolTip.text: hoveredLink
ToolTip.visible: hoveredLink || false ToolTip.visible: hoveredLink || false
background: null
bottomInset: 0
bottomPadding: 0
// this always has to be enabled, otherwise you can't click links anymore! // this always has to be enabled, otherwise you can't click links anymore!
//enabled: selectByMouse //enabled: selectByMouse
color: palette.text color: palette.text
focus: false focus: false
leftInset: 0
leftPadding: 0
readOnly: true readOnly: true
rightInset: 0
rightPadding: 0
selectByMouse: !Settings.mobileMode selectByMouse: !Settings.mobileMode
textFormat: TextEdit.RichText textFormat: TextEdit.RichText
topInset: 0
topPadding: 0
wrapMode: Text.Wrap wrapMode: Text.Wrap
// Setting a tooltip delay makes the hover text empty .-. // Setting a tooltip delay makes the hover text empty .-.
@ -40,8 +39,8 @@ TextArea {
onLinkActivated: Nheko.openLink(link) onLinkActivated: Nheko.openLink(link)
// propagate events up // propagate events up
onPressAndHold: (event) => event.accepted = false onPressAndHold: event => event.accepted = false
onPressed: (event) => event.accepted = (event.button == Qt.LeftButton) onPressed: event => event.accepted = (event.button == Qt.LeftButton)
NhekoCursorShape { NhekoCursorShape {
id: cs id: cs

View file

@ -59,7 +59,7 @@ Item {
spacing: 2 spacing: 2
verticalLayoutDirection: ListView.BottomToTop verticalLayoutDirection: ListView.BottomToTop
delegate: EventDelegateChooser { delegate: TimelineEvent {
id: wrapper id: wrapper
ListView.delayRemove: true ListView.delayRemove: true
width: chat.delegateMaxWidth width: chat.delegateMaxWidth
@ -69,7 +69,6 @@ Item {
required property var day required property var day
required property bool isSender required property bool isSender
required property bool isStateEvent
//required property var previousMessageDay //required property var previousMessageDay
//required property bool previousMessageIsStateEvent //required property bool previousMessageIsStateEvent
//required property string previousMessageUserId //required property string previousMessageUserId
@ -145,6 +144,8 @@ Item {
} }
ColumnLayout { ColumnLayout {
spacing: 0
AbstractButton { AbstractButton {
id: replyUserButton id: replyUserButton
Layout.fillWidth: true Layout.fillWidth: true
@ -222,314 +223,6 @@ Item {
opacity: 0.2 opacity: 0.2
} }
] ]
EventDelegateChoice {
roleValues: [
MtxEvent.TextMessage,
MtxEvent.NoticeMessage,
MtxEvent.ElementEffectMessage,
MtxEvent.UnknownMessage,
]
TextMessage {
keepFullText: true
required property string userId
required property string userName
required property string formattedBody
required property int type
color: type == MtxEvent.NoticeMessage ? palette.buttonText : palette.text
font.italic: type == MtxEvent.NoticeMessage
formatted: formattedBody
Layout.fillWidth: true
//Layout.maximumWidth: implicitWidth
}
}
EventDelegateChoice {
roleValues: [
MtxEvent.EmoteMessage,
]
TextMessage {
keepFullText: true
required property string userId
required property string userName
required property string formattedBody
formatted: TimelineManager.escapeEmoji(userName) + " " + formattedBody
color: TimelineManager.userColor(userId, palette.base)
font.italic: true
Layout.fillWidth: true
//Layout.maximumWidth: implicitWidth
}
}
EventDelegateChoice {
roleValues: [
MtxEvent.CanonicalAlias,
MtxEvent.ServerAcl,
MtxEvent.Name,
MtxEvent.Topic,
MtxEvent.Avatar,
MtxEvent.PinnedEvents,
MtxEvent.ImagePackInRoom,
MtxEvent.SpaceParent,
MtxEvent.RoomCreate,
MtxEvent.PowerLevels,
MtxEvent.PolicyRuleUser,
MtxEvent.PolicyRuleRoom,
MtxEvent.PolicyRuleServer,
MtxEvent.RoomJoinRules,
MtxEvent.RoomHistoryVisibility,
MtxEvent.RoomGuestAccess,
]
TextMessage {
keepFullText: true
required property string userId
required property string userName
required property string formattedStateEvent
isOnlyEmoji: false
text: formattedStateEvent
formatted: ''
body: ''
horizontalAlignment: Text.AlignHCenter
color: palette.buttonText
font.italic: true
Layout.fillWidth: true
//Layout.maximumWidth: implicitWidth
}
}
EventDelegateChoice {
roleValues: [
MtxEvent.CallInvite,
]
TextMessage {
keepFullText: true
required property string userId
required property string userName
required property string callType
isOnlyEmoji: false
body: formatted
formatted: {
switch (callType) {
case "voice":
return qsTr("%1 placed a voice call.").arg(TimelineManager.escapeEmoji(userName));
case "video":
return qsTr("%1 placed a video call.").arg(TimelineManager.escapeEmoji(userName));
default:
return qsTr("%1 placed a call.").arg(TimelineManager.escapeEmoji(userName));
}
}
color: palette.buttonText
font.italic: true
Layout.fillWidth: true
}
}
EventDelegateChoice {
roleValues: [
MtxEvent.CallAnswer,
MtxEvent.CallReject,
MtxEvent.CallSelectAnswer,
MtxEvent.CallHangUp,
MtxEvent.CallCandidates,
MtxEvent.CallNegotiate,
]
TextMessage {
keepFullText: true
required property string userId
required property string userName
required property int type
isOnlyEmoji: false
body: formatted
formatted: {
switch (type) {
case MtxEvent.CallAnswer:
return qsTr("%1 answered the call.").arg(TimelineManager.escapeEmoji(userName));
case MtxEvent.CallReject:
return qsTr("%1 rejected the call.").arg(TimelineManager.escapeEmoji(userName));
case MtxEvent.CallSelectAnswer:
return qsTr("%1 selected answer.").arg(TimelineManager.escapeEmoji(userName));
case MtxEvent.CallHangUp:
return qsTr("%1 ended the call.").arg(TimelineManager.escapeEmoji(userName));
case MtxEvent.CallCandidates:
return qsTr("%1 is negotiating the call...").arg(TimelineManager.escapeEmoji(userName));
case MtxEvent.CallNegotiate:
return qsTr("%1 is negotiating the call...").arg(TimelineManager.escapeEmoji(userName));
}
}
color: palette.buttonText
font.italic: true
Layout.fillWidth: true
}
}
EventDelegateChoice {
roleValues: [
MtxEvent.ImageMessage,
MtxEvent.Sticker,
]
ImageMessage {
Layout.fillWidth: true
containerHeight: timelineView.height
Layout.maximumWidth: tempWidth
}
}
EventDelegateChoice {
roleValues: [
MtxEvent.FileMessage,
]
FileMessage {
Layout.fillWidth: true
}
}
EventDelegateChoice {
roleValues: [
MtxEvent.VideoMessage,
MtxEvent.AudioMessage,
]
PlayableMediaMessage {
Layout.fillWidth: true
}
}
EventDelegateChoice {
roleValues: [
MtxEvent.Encrypted,
]
Encrypted {
Layout.fillWidth: true
}
}
EventDelegateChoice {
roleValues: [
MtxEvent.Encryption,
]
EncryptionEnabled {
Layout.fillWidth: true
}
}
EventDelegateChoice {
roleValues: [
MtxEvent.Redacted
]
Redacted {
Layout.fillWidth: true
required property string userId
required property string userName
}
}
EventDelegateChoice {
roleValues: [
MtxEvent.Member
]
ColumnLayout {
id: member
required property string userId
required property string userName
required property bool isReply
required property Room room
required property string formattedStateEvent
NoticeMessage {
body: formatted
isOnlyEmoji: false
isReply: tombstone.isReply
keepFullText: true
isStateEvent: true
Layout.fillWidth: true
formatted: member.formattedStateEvent
}
Button {
visible: room.showAcceptKnockButton(eventId)
Layout.alignment: Qt.AlignHCenter
text: qsTr("Allow them in")
onClicked: room.acceptKnock(member.eventId)
}
}
}
EventDelegateChoice {
roleValues: [
MtxEvent.Tombstone
]
ColumnLayout {
id: tombstone
required property string userId
required property string userName
required property string body
required property bool isReply
required property Room room
required property string eventId
NoticeMessage {
body: formatted
isOnlyEmoji: false
isReply: tombstone.isReply
keepFullText: true
isStateEvent: true
Layout.fillWidth: true
formatted: qsTr("This room was replaced for the following reason: %1").arg(tombstone.body)
}
Button {
Layout.alignment: Qt.AlignHCenter
text: qsTr("Go to replacement room")
onClicked: tombstone.room.joinReplacementRoom(tombstone.eventId)
}
}
}
EventDelegateChoice {
roleValues: [
]
MatrixText {
Layout.fillWidth: true
required property string typeString
text: "Unsupported: " + typeString
required property string userId
required property string userName
}
}
} }
footer: Item { footer: Item {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter

View file

@ -74,10 +74,10 @@ Flow {
anchors.verticalCenter: divider.verticalCenter anchors.verticalCenter: divider.verticalCenter
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
height: textMetrics.height height: textMetrics.height
mipmap: true
source: modelData.key.startsWith("mxc://") ? (modelData.key.replace("mxc://", "image://MxcImage/") + "?scale") : "" source: modelData.key.startsWith("mxc://") ? (modelData.key.replace("mxc://", "image://MxcImage/") + "?scale") : ""
visible: modelData.key.startsWith("mxc://") visible: modelData.key.startsWith("mxc://")
width: textMetrics.height width: textMetrics.height
mipmap: true
} }
Rectangle { Rectangle {
id: divider id: divider

View file

@ -29,22 +29,8 @@ Rectangle {
anchors.rightMargin: replyPopup.width < 450 ? 2 * (22 + 16) : 3 * (22 + 16) anchors.rightMargin: replyPopup.width < 450 ? 2 * (22 + 16) : 3 * (22 + 16)
anchors.top: parent.top anchors.top: parent.top
anchors.topMargin: Nheko.paddingSmall anchors.topMargin: Nheko.paddingSmall
blurhash: modelData.blurhash ?? "" eventId: room.reply ?? ""
body: modelData.body ?? ""
encryptionError: modelData.encryptionError ?? 0
eventId: modelData.eventId ?? ""
filename: modelData.filename ?? ""
filesize: modelData.filesize ?? ""
formattedBody: modelData.formattedBody ?? ""
isOnlyEmoji: modelData.isOnlyEmoji ?? false
originalWidth: modelData.originalWidth ?? 0
proportionalHeight: modelData.proportionalHeight ?? 1
type: modelData.type ?? MtxEvent.UnknownMessage
typeString: modelData.typeString ?? ""
url: modelData.url ?? ""
userColor: TimelineManager.userColor(modelData.userId, palette.window) userColor: TimelineManager.userColor(modelData.userId, palette.window)
userId: modelData.userId ?? ""
userName: modelData.userName ?? ""
visible: room && room.reply visible: room && room.reply
width: parent.width width: parent.width
} }

View file

@ -728,9 +728,9 @@ Page {
} }
Platform.MenuItem { Platform.MenuItem {
text: qsTr("Mark as read") text: qsTr("Mark as read")
onTriggered: Rooms.getRoomById(roomContextMenu.roomid).markRoomAsRead() onTriggered: Rooms.getRoomById(roomContextMenu.roomid).markRoomAsRead()
} }
Platform.MenuItem { Platform.MenuItem {
text: qsTr("Room settings") text: qsTr("Room settings")

View file

@ -355,7 +355,6 @@ Pane {
onAccepted: UIA.continue3pidReceived() onAccepted: UIA.continue3pidReceived()
} }
Connections { Connections {
function onConfirm3pidToken() { function onConfirm3pidToken() {
uiaConfirmationLinkDialog.open(); uiaConfirmationLinkDialog.open();
@ -363,6 +362,18 @@ Pane {
function onEmail() { function onEmail() {
uiaEmailPrompt.show(); uiaEmailPrompt.show();
} }
function onFallbackAuth(fallback) {
var component = Qt.createComponent("qrc:/resources/qml/dialogs/FallbackAuthDialog.qml");
if (component.status == Component.Ready) {
var dialog = component.createObject(timelineRoot, {
"fallback": fallback
});
dialog.show();
destroyOnClose(dialog);
} else {
console.error("Failed to create component: " + component.errorString());
}
}
function onPassword() { function onPassword() {
console.log("UIA: password needed"); console.log("UIA: password needed");
uiaPassPrompt.show(); uiaPassPrompt.show();
@ -385,18 +396,6 @@ Pane {
console.error("Failed to create component: " + component.errorString()); console.error("Failed to create component: " + component.errorString());
} }
} }
function onFallbackAuth(fallback) {
var component = Qt.createComponent("qrc:/resources/qml/dialogs/FallbackAuthDialog.qml");
if (component.status == Component.Ready) {
var dialog = component.createObject(timelineRoot, {
"fallback": fallback
});
dialog.show();
destroyOnClose(dialog);
} else {
console.error("Failed to create component: " + component.errorString());
}
}
target: UIA target: UIA
} }

View file

@ -0,0 +1,255 @@
// 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 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.2
import QtQuick.Window 2.13
import im.nheko 1.0
EventDelegateChooser {
id: wrapper
required property bool isStateEvent
EventDelegateChoice {
roleValues: [MtxEvent.TextMessage, MtxEvent.NoticeMessage, MtxEvent.ElementEffectMessage, MtxEvent.UnknownMessage,]
TextMessage {
required property string formattedBody
required property int type
required property string userId
required property string userName
Layout.fillWidth: true
//Layout.maximumWidth: implicitWidth
color: type == MtxEvent.NoticeMessage ? palette.buttonText : palette.text
font.italic: type == MtxEvent.NoticeMessage
formatted: formattedBody
keepFullText: true
}
}
EventDelegateChoice {
roleValues: [MtxEvent.EmoteMessage,]
TextMessage {
required property string formattedBody
required property string userId
required property string userName
Layout.fillWidth: true
//Layout.maximumWidth: implicitWidth
color: TimelineManager.userColor(userId, palette.base)
font.italic: true
formatted: TimelineManager.escapeEmoji(userName) + " " + formattedBody
keepFullText: true
}
}
EventDelegateChoice {
roleValues: [MtxEvent.CanonicalAlias, MtxEvent.ServerAcl, MtxEvent.Name, MtxEvent.Topic, MtxEvent.Avatar, MtxEvent.PinnedEvents, MtxEvent.ImagePackInRoom, MtxEvent.SpaceParent, MtxEvent.RoomCreate, MtxEvent.PowerLevels, MtxEvent.PolicyRuleUser, MtxEvent.PolicyRuleRoom, MtxEvent.PolicyRuleServer, MtxEvent.RoomJoinRules, MtxEvent.RoomHistoryVisibility, MtxEvent.RoomGuestAccess,]
TextMessage {
required property string formattedStateEvent
required property string userId
required property string userName
Layout.fillWidth: true
//Layout.maximumWidth: implicitWidth
body: ''
color: palette.buttonText
font.italic: true
formatted: ''
horizontalAlignment: Text.AlignHCenter
isOnlyEmoji: false
keepFullText: true
text: formattedStateEvent
}
}
EventDelegateChoice {
roleValues: [MtxEvent.CallInvite,]
TextMessage {
required property string callType
required property string userId
required property string userName
Layout.fillWidth: true
body: formatted
color: palette.buttonText
font.italic: true
formatted: {
switch (callType) {
case "voice":
return qsTr("%1 placed a voice call.").arg(TimelineManager.escapeEmoji(userName));
case "video":
return qsTr("%1 placed a video call.").arg(TimelineManager.escapeEmoji(userName));
default:
return qsTr("%1 placed a call.").arg(TimelineManager.escapeEmoji(userName));
}
}
isOnlyEmoji: false
keepFullText: true
}
}
EventDelegateChoice {
roleValues: [MtxEvent.CallAnswer, MtxEvent.CallReject, MtxEvent.CallSelectAnswer, MtxEvent.CallHangUp, MtxEvent.CallCandidates, MtxEvent.CallNegotiate,]
TextMessage {
required property int type
required property string userId
required property string userName
Layout.fillWidth: true
body: formatted
color: palette.buttonText
font.italic: true
formatted: {
switch (type) {
case MtxEvent.CallAnswer:
return qsTr("%1 answered the call.").arg(TimelineManager.escapeEmoji(userName));
case MtxEvent.CallReject:
return qsTr("%1 rejected the call.").arg(TimelineManager.escapeEmoji(userName));
case MtxEvent.CallSelectAnswer:
return qsTr("%1 selected answer.").arg(TimelineManager.escapeEmoji(userName));
case MtxEvent.CallHangUp:
return qsTr("%1 ended the call.").arg(TimelineManager.escapeEmoji(userName));
case MtxEvent.CallCandidates:
return qsTr("%1 is negotiating the call...").arg(TimelineManager.escapeEmoji(userName));
case MtxEvent.CallNegotiate:
return qsTr("%1 is negotiating the call...").arg(TimelineManager.escapeEmoji(userName));
}
}
isOnlyEmoji: false
keepFullText: true
}
}
EventDelegateChoice {
roleValues: [MtxEvent.ImageMessage, MtxEvent.Sticker,]
ImageMessage {
Layout.fillWidth: true
Layout.maximumWidth: tempWidth
containerHeight: timelineView.height
}
}
EventDelegateChoice {
roleValues: [MtxEvent.FileMessage,]
FileMessage {
Layout.fillWidth: true
}
}
EventDelegateChoice {
roleValues: [MtxEvent.VideoMessage, MtxEvent.AudioMessage,]
PlayableMediaMessage {
Layout.fillWidth: true
}
}
EventDelegateChoice {
roleValues: [MtxEvent.Encrypted,]
Encrypted {
Layout.fillWidth: true
}
}
EventDelegateChoice {
roleValues: [MtxEvent.Encryption,]
EncryptionEnabled {
Layout.fillWidth: true
}
}
EventDelegateChoice {
roleValues: [MtxEvent.Redacted]
Redacted {
required property string userId
required property string userName
Layout.fillWidth: true
}
}
EventDelegateChoice {
roleValues: [MtxEvent.Member]
ColumnLayout {
id: member
required property string formattedStateEvent
required property bool isReply
required property Room room
required property string userId
required property string userName
NoticeMessage {
Layout.fillWidth: true
body: formatted
formatted: member.formattedStateEvent
isOnlyEmoji: false
isReply: member.isReply
isStateEvent: true
keepFullText: true
}
Button {
Layout.alignment: Qt.AlignHCenter
text: qsTr("Allow them in")
visible: room.showAcceptKnockButton(eventId)
onClicked: room.acceptKnock(member.eventId)
}
}
}
EventDelegateChoice {
roleValues: [MtxEvent.Tombstone]
ColumnLayout {
id: tombstone
required property string body
required property string eventId
required property bool isReply
required property Room room
required property string userId
required property string userName
NoticeMessage {
Layout.fillWidth: true
body: formatted
formatted: qsTr("This room was replaced for the following reason: %1").arg(tombstone.body)
isOnlyEmoji: false
isReply: tombstone.isReply
isStateEvent: true
keepFullText: true
}
Button {
Layout.alignment: Qt.AlignHCenter
text: qsTr("Go to replacement room")
onClicked: tombstone.room.joinReplacementRoom(tombstone.eventId)
}
}
}
EventDelegateChoice {
roleValues: []
MatrixText {
required property string typeString
required property string userId
required property string userName
Layout.fillWidth: true
text: "Unsupported: " + typeString
}
}
}

View file

@ -147,6 +147,7 @@ AbstractButton {
columns: Settings.bubbles ? 1 : 2 columns: Settings.bubbles ? 1 : 2
rowSpacing: 0 rowSpacing: 0
rows: Settings.bubbles ? 3 : 2 rows: Settings.bubbles ? 3 : 2
/* /*
anchors { anchors {
left: parent.left left: parent.left

View file

@ -286,24 +286,9 @@ Pane {
property var e: room ? room.getDump(modelData, "pins") : {} property var e: room ? room.getDump(modelData, "pins") : {}
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: height //Layout.preferredHeight: height
blurhash: e.blurhash ?? ""
body: e.body ?? ""
encryptionError: e.encryptionError ?? 0
eventId: e.eventId ?? "" eventId: e.eventId ?? ""
filename: e.filename ?? ""
filesize: e.filesize ?? ""
formattedBody: e.formattedBody ?? ""
isOnlyEmoji: e.isOnlyEmoji ?? false
keepFullText: true
originalWidth: e.originalWidth ?? 0
proportionalHeight: e.proportionalHeight ?? 1
type: e.type ?? MtxEvent.UnknownMessage
typeString: e.typeString ?? ""
url: e.url ?? ""
userColor: TimelineManager.userColor(e.userId, palette.window) userColor: TimelineManager.userColor(e.userId, palette.window)
userId: e.userId ?? ""
userName: e.userName ?? ""
Connections { Connections {
function onPinnedMessagesChanged() { function onPinnedMessagesChanged() {

View file

@ -14,102 +14,96 @@ AbstractButton {
id: r id: r
property color userColor: "red" property color userColor: "red"
property double proportionalHeight
property int type
property string typeString
property int originalWidth
property string blurhash
property string body
property string formattedBody
property string eventId
property string filename
property string filesize
property string url
property bool isOnlyEmoji
property bool isStateEvent
property string userId
property string userName
property string thumbnailUrl
property string roomTopic
property string roomName
property string callType
property int duration
property int encryptionError
property int relatedEventCacheBuster
property int maxWidth
property bool keepFullText: false property bool keepFullText: false
height: replyContainer.height required property string eventId
implicitHeight: replyContainer.height
implicitWidth: visible? colorLine.width+Math.max(replyContainer.implicitWidth,userName_.fullTextWidth) : 0 // visible? seems to be causing issues property var room_: room
property string userId: eventId ? room.dataById(eventId, Room.UserId, "") : ""
property string userName: eventId ? room.dataById(eventId, Room.UserName, "") : ""
implicitHeight: replyContainer.implicitHeight
implicitWidth: replyContainer.implicitWidth
NhekoCursorShape { NhekoCursorShape {
anchors.fill: parent anchors.fill: parent
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
} }
Rectangle {
id: colorLine
anchors.top: replyContainer.top
anchors.bottom: replyContainer.bottom
width: 4
color: TimelineManager.userColor(userId, palette.base)
}
onClicked: { onClicked: {
let link = reply.child.linkAt != undefined && reply.child.linkAt(pressX-colorLine.width, pressY - userName_.implicitHeight); let link = reply.child.linkAt != undefined && reply.child.linkAt(pressX-colorline.width, pressY - userName_.implicitHeight);
if (link) { if (link) {
Nheko.openLink(link) Nheko.openLink(link)
} else { } else {
room.showEvent(r.eventId) room.showEvent(r.eventId)
} }
} }
onPressAndHold: replyContextMenu.show(reply.child.copyText, reply.child.linkAt(pressX-colorLine.width, pressY - userName_.implicitHeight), r.eventId) onPressAndHold: replyContextMenu.show(reply.child.copyText, reply.child.linkAt(pressX-colorline.width, pressY - userName_.implicitHeight), r.eventId)
ColumnLayout { contentItem: TimelineEvent {
id: timelineEvent
isStateEvent: false
room: room_
eventId: r.eventId
replyTo: ""
width: parent.width
height: replyContainer.implicitHeight
//height: replyContainer.implicitHeight
data: GridLayout {
id: replyContainer id: replyContainer
anchors.left: colorLine.right width: parent.width
width: parent.width - 4 columns: 2
spacing: 0 rows: 2
columnSpacing: Nheko.paddingMedium
rowSpacing: Nheko.paddingSmall
TapHandler { Rectangle {
acceptedButtons: Qt.RightButton id: colorline
onSingleTapped: replyContextMenu.show(reply.child.copyText, reply.child.linkAt(eventPoint.position.x, eventPoint.position.y - userName_.implicitHeight), r.eventId)
gesturePolicy: TapHandler.ReleaseWithinBounds Layout.preferredWidth: 4
acceptedDevices: PointerDevice.Mouse | PointerDevice.Stylus | PointerDevice.TouchPad Layout.rowSpan: 2
Layout.fillHeight: true
Layout.row: 0
Layout.column: 0
color: TimelineManager.userColor(r.userId, palette.base)
} }
AbstractButton { AbstractButton {
Layout.leftMargin: 4 id: usernameBtn
Layout.fillWidth: true Layout.fillWidth: true
Layout.row: 0
Layout.column: 1
contentItem: ElidedLabel { contentItem: ElidedLabel {
id: userName_ id: userName_
fullText: userName fullText: r.userName
color: r.userColor color: r.userColor
textFormat: Text.RichText textFormat: Text.RichText
width: parent.width width: parent.width
elideWidth: width elideWidth: width
} }
onClicked: room.openUserProfile(userId) onClicked: room.openUserProfile(r.userId)
} }
Rectangle { data: [
Layout.leftMargin: 4 colorline, usernameBtn, timelineEvent.main,
Layout.preferredHeight: 20 ]
Layout.fillWidth: true
color: "green"
}
} }
}
Rectangle { background: Rectangle {
id: backgroundItem id: backgroundItem
z: -1 z: -1
anchors.fill: replyContainer property color userColor: TimelineManager.userColor(r.userId, palette.base)
property color userColor: TimelineManager.userColor(userId, palette.base)
property color bgColor: palette.base property color bgColor: palette.base
color: Qt.tint(bgColor, Qt.hsla(userColor.hslHue, 0.5, userColor.hslLightness, 0.1)) color: Qt.tint(bgColor, Qt.hsla(userColor.hslHue, 0.5, userColor.hslLightness, 0.1))
} }