Fixup reply and state event rendering

This commit is contained in:
Nicolas Werner 2023-10-08 23:52:23 +02:00
parent 6c6370c83f
commit c4d2ec875d
No known key found for this signature in database
GPG key ID: C8D75E610773F2D9
10 changed files with 207 additions and 897 deletions

View file

@ -742,7 +742,6 @@ set(QML_SOURCES
resources/qml/delegates/Encrypted.qml resources/qml/delegates/Encrypted.qml
resources/qml/delegates/FileMessage.qml resources/qml/delegates/FileMessage.qml
resources/qml/delegates/ImageMessage.qml resources/qml/delegates/ImageMessage.qml
resources/qml/delegates/MessageDelegate.qml
resources/qml/delegates/NoticeMessage.qml resources/qml/delegates/NoticeMessage.qml
resources/qml/delegates/Pill.qml resources/qml/delegates/Pill.qml
resources/qml/delegates/Placeholder.qml resources/qml/delegates/Placeholder.qml

View file

@ -18,7 +18,7 @@ TimelineEvent {
id: wrapper id: wrapper
ListView.delayRemove: true ListView.delayRemove: true
width: chat.delegateMaxWidth width: chat.delegateMaxWidth
height: Math.max((section.item?.height ?? 0) + gridContainer.implicitHeight + reactionRow.implicitHeight + unreadRow.height, 20) height: Math.max((section.item?.height ?? 0) + gridContainer.implicitHeight + reactionRow.implicitHeight + unreadRow.height, 10)
anchors.horizontalCenter: ListView.view.contentItem.horizontalCenter anchors.horizontalCenter: ListView.view.contentItem.horizontalCenter
//room: chatRoot.roommodel //room: chatRoot.roommodel
@ -51,6 +51,9 @@ 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
replyInset: mainInset + 4 + Nheko.paddingSmall
maxWidth: chat.delegateMaxWidth - avatarMargin - metadata.width maxWidth: chat.delegateMaxWidth - avatarMargin - metadata.width
data: [ data: [
@ -182,16 +185,21 @@ TimelineEvent {
color: TimelineManager.userColor(wrapper.threadId, palette.base) color: TimelineManager.userColor(wrapper.threadId, palette.base)
} }
} }
Item {
visible: wrapper.isStateEvent
width: (wrapper.maxWidth - (wrapper.main?.width ?? 0)) / 2
height: 1
}
Column { Column {
id: contentColumn id: contentColumn
AbstractButton { AbstractButton {
id: replyRow id: replyRow
visible: wrapper.reply visible: wrapper.reply
//Layout.fillWidth: true
//Layout.maximumHeight: timelineView.height / 8 height: replyLine.height
//Layout.preferredWidth: replyRowLay.implicitWidth
//Layout.preferredHeight: replyRowLay.implicitHeight
property color userColor: TimelineManager.userColor(wrapper.reply?.userId ?? '', palette.base) property color userColor: TimelineManager.userColor(wrapper.reply?.userId ?? '', palette.base)
@ -209,7 +217,7 @@ TimelineEvent {
Rectangle { Rectangle {
id: replyLine id: replyLine
height: replyCol.height height: Math.min( wrapper.reply?.height, timelineView.height / 5) + Nheko.paddingSmall + replyUserButton.height
color: replyRow.userColor color: replyRow.userColor
width: 4 width: 4
} }

View file

@ -62,12 +62,10 @@ EventDelegateChooser {
required property string userId required property string userId
required property string userName required property string userName
Layout.fillWidth: true
//Layout.maximumWidth: implicitWidth
body: '' body: ''
color: palette.buttonText color: palette.buttonText
font.italic: true font.italic: true
font.pointSize: Settings.fontSize * 0.8
formatted: '' formatted: ''
horizontalAlignment: Text.AlignHCenter horizontalAlignment: Text.AlignHCenter
isOnlyEmoji: false isOnlyEmoji: false
@ -202,7 +200,6 @@ EventDelegateChooser {
id: member id: member
required property string formattedStateEvent required property string formattedStateEvent
required property bool isReply
required property Room room required property Room room
required property string userId required property string userId
required property string userName required property string userName
@ -212,7 +209,7 @@ EventDelegateChooser {
body: formatted body: formatted
formatted: member.formattedStateEvent formatted: member.formattedStateEvent
isOnlyEmoji: false isOnlyEmoji: false
isReply: member.isReply isReply: EventDelegateChooser.isReply
isStateEvent: true isStateEvent: true
keepFullText: true keepFullText: true
} }
@ -233,7 +230,6 @@ EventDelegateChooser {
required property string body required property string body
required property string eventId required property string eventId
required property bool isReply
required property Room room required property Room room
required property string userId required property string userId
required property string userName required property string userName
@ -243,7 +239,7 @@ EventDelegateChooser {
body: formatted body: formatted
formatted: qsTr("This room was replaced for the following reason: %1").arg(tombstone.body) formatted: qsTr("This room was replaced for the following reason: %1").arg(tombstone.body)
isOnlyEmoji: false isOnlyEmoji: false
isReply: tombstone.isReply isReply: EventDelegateChooser.isReply
isStateEvent: true isStateEvent: true
keepFullText: true keepFullText: true
} }

View file

@ -5,7 +5,6 @@
import QtQuick import QtQuick
import QtQuick.Window import QtQuick.Window
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts
import im.nheko import im.nheko
AbstractButton { AbstractButton {
@ -17,16 +16,17 @@ AbstractButton {
required property string blurhash required property string blurhash
required property string body required property string body
required property string filename required property string filename
required property bool isReply
required property string eventId required property string eventId
required property int containerHeight required property int containerHeight
property double divisor: isReply ? 5 : 3 property double divisor: EventDelegateChooser.isReply ? 5 : 3
EventDelegateChooser.keepAspectRatio: true
EventDelegateChooser.maxWidth: originalWidth
EventDelegateChooser.maxHeight: containerHeight / divisor
EventDelegateChooser.aspectRatio: proportionalHeight
//Layout.maximumWidth: originalWidth
Layout.maximumHeight: Math.min(originalHeight, containerHeight / divisor)
implicitWidth: height/proportionalHeight
implicitHeight: Math.min(Layout.maximumHeight, width*proportionalHeight)
hoverEnabled: true hoverEnabled: true
enabled: !EventDelegateChooser.isReply
state: (img.status != Image.Ready || timeline.privacyScreen.active) ? "BlurhashVisible" : "ImageVisible" state: (img.status != Image.Ready || timeline.privacyScreen.active) ? "BlurhashVisible" : "ImageVisible"
states: [ states: [
@ -133,8 +133,8 @@ AbstractButton {
roomm: room roomm: room
play: !Settings.animateImagesOnHover || parent.hovered play: !Settings.animateImagesOnHover || parent.hovered
eventId: parent.eventId eventId: parent.eventId
width: parent.implicitWidth
height: parent.implicitHeight anchors.fill: parent
} }
Image { Image {
@ -143,10 +143,10 @@ AbstractButton {
source: blurhash ? ("image://blurhash/" + blurhash) : ("image://colorimage/:/icons/icons/ui/image-failed.svg?" + palette.buttonText) source: blurhash ? ("image://blurhash/" + blurhash) : ("image://colorimage/:/icons/icons/ui/image-failed.svg?" + palette.buttonText)
asynchronous: true asynchronous: true
fillMode: Image.PreserveAspectFit fillMode: Image.PreserveAspectFit
sourceSize.width: parent.implicitWidth * Screen.devicePixelRatio sourceSize.width: parent.width * Screen.devicePixelRatio
sourceSize.height: parent.implicitHeight * Screen.devicePixelRatio sourceSize.height: parent.height * Screen.devicePixelRatio
width: parent.implicitWidth
height: parent.implicitHeight anchors.fill: parent
} }
onClicked: Settings.openImageExternal ? room.openMedia(eventId) : TimelineManager.openImageOverlay(room, url, eventId, originalWidth, proportionalHeight); onClicked: Settings.openImageExternal ? room.openMedia(eventId) : TimelineManager.openImageOverlay(room, url, eventId, originalWidth, proportionalHeight);
@ -154,8 +154,8 @@ AbstractButton {
Item { Item {
id: overlay id: overlay
width: parent.implicitWidth anchors.fill: parent
height: parent.implicitHeight
visible: parent.hovered visible: parent.hovered
Rectangle { Rectangle {

View file

@ -1,780 +0,0 @@
// SPDX-FileCopyrightText: Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
import QtQuick 2.6
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.2
import im.nheko 1.0
Item {
id: d
required property bool isReply
property bool keepFullText: !isReply
property alias child: chooser.child
//implicitWidth: chooser.child?.implicitWidth ?? 0
required property double proportionalHeight
required property int type
required property string typeString
required property int originalWidth
required property int duration
required property string blurhash
required property string body
required property string formattedBody
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 isStateEvent
required property string userId
required property string userName
required property string roomTopic
required property string roomName
required property string callType
required property int encryptionError
required property int relatedEventCacheBuster
property bool fitsMetadata: (chooser.child && chooser.child.fitsMetadata) ? chooser.child.fitsMetadata : false
property int metadataWidth
implicitWidth: chooser.child?.implicitWidth
height: chooser.child ? chooser.child.height : Nheko.paddingLarge
DelegateChooser {
id: chooser
//role: "type" //< not supported in our custom implementation, have to use roleValue
roleValue: type
//anchors.fill: parent
width: parent?.width ?? 0 // this should get rid of "cannot read property 'width' of null"
DelegateChoice {
roleValue: MtxEvent.UnknownEvent
Placeholder {
typeString: d.typeString
text: "Unretrieved event"
}
}
DelegateChoice {
roleValue: MtxEvent.Tombstone
ColumnLayout {
width: parent.width
NoticeMessage {
body: formatted
isOnlyEmoji: false
isReply: d.isReply
keepFullText: d.keepFullText
isStateEvent: d.isStateEvent
Layout.fillWidth: true
formatted: qsTr("This room was replaced for the following reason: %1").arg(d.body)
}
Button {
Layout.alignment: Qt.AlignHCenter
text: qsTr("Go to replacement room")
onClicked: room.joinReplacementRoom(eventId)
}
}
}
DelegateChoice {
roleValue: MtxEvent.TextMessage
TextMessage {
formatted: d.formattedBody
body: d.body
isOnlyEmoji: d.isOnlyEmoji
isReply: d.isReply
keepFullText: d.keepFullText
metadataWidth: d.metadataWidth
}
}
DelegateChoice {
roleValue: MtxEvent.UnknownMessage
TextMessage {
formatted: d.formattedBody
body: d.body
isOnlyEmoji: d.isOnlyEmoji
isReply: d.isReply
keepFullText: d.keepFullText
metadataWidth: d.metadataWidth
}
}
DelegateChoice {
roleValue: MtxEvent.ElementEffectMessage
TextMessage {
formatted: d.formattedBody
body: d.body
isOnlyEmoji: d.isOnlyEmoji
isReply: d.isReply
keepFullText: d.keepFullText
metadataWidth: d.metadataWidth
}
}
DelegateChoice {
roleValue: MtxEvent.NoticeMessage
NoticeMessage {
formatted: d.formattedBody
body: d.body
isOnlyEmoji: d.isOnlyEmoji
isReply: d.isReply
keepFullText: d.keepFullText
isStateEvent: d.isStateEvent
metadataWidth: d.metadataWidth
}
}
DelegateChoice {
roleValue: MtxEvent.EmoteMessage
NoticeMessage {
formatted: TimelineManager.escapeEmoji(d.userName) + " " + d.formattedBody
color: TimelineManager.userColor(d.userId, palette.base)
body: d.body
isOnlyEmoji: d.isOnlyEmoji
isReply: d.isReply
keepFullText: d.keepFullText
isStateEvent: d.isStateEvent
metadataWidth: d.metadataWidth
}
}
DelegateChoice {
roleValue: MtxEvent.ImageMessage
ImageMessage {
type: d.type
originalWidth: d.originalWidth
proportionalHeight: d.proportionalHeight
url: d.url
blurhash: d.blurhash
body: d.body
filename: d.filename
isReply: d.isReply
eventId: d.eventId
metadataWidth: d.metadataWidth
containerHeight: timelineView.height
}
}
DelegateChoice {
roleValue: MtxEvent.Sticker
ImageMessage {
type: d.type
originalWidth: d.originalWidth
proportionalHeight: d.proportionalHeight
url: d.url
blurhash: d.blurhash
body: d.body
filename: d.filename
isReply: d.isReply
eventId: d.eventId
metadataWidth: d.metadataWidth
containerHeight: timelineView.height
}
}
DelegateChoice {
roleValue: MtxEvent.FileMessage
FileMessage {
eventId: d.eventId
filename: d.filename
filesize: d.filesize
metadataWidth: d.metadataWidth
}
}
DelegateChoice {
roleValue: MtxEvent.VideoMessage
PlayableMediaMessage {
proportionalHeight: d.proportionalHeight
type: d.type
originalWidth: d.originalWidth
thumbnailUrl: d.thumbnailUrl
eventId: d.eventId
url: d.url
body: d.body
filesize: d.filesize
duration: d.duration
metadataWidth: d.metadataWidth
}
}
DelegateChoice {
roleValue: MtxEvent.AudioMessage
PlayableMediaMessage {
proportionalHeight: d.proportionalHeight
type: d.type
originalWidth: d.originalWidth
thumbnailUrl: d.thumbnailUrl
eventId: d.eventId
url: d.url
body: d.body
filesize: d.filesize
duration: d.duration
metadataWidth: d.metadataWidth
}
}
DelegateChoice {
roleValue: MtxEvent.Redacted
Redacted {
metadataWidth: d.metadataWidth
}
}
DelegateChoice {
roleValue: MtxEvent.Redaction
Pill {
text: qsTr("%1 removed a message").arg(d.userName)
isStateEvent: d.isStateEvent
}
}
DelegateChoice {
roleValue: MtxEvent.Encryption
EncryptionEnabled {
username: d.userName
}
}
DelegateChoice {
roleValue: MtxEvent.Encrypted
Encrypted {
encryptionError: d.encryptionError
eventId: d.eventId
}
}
DelegateChoice {
roleValue: MtxEvent.ServerAcl
NoticeMessage {
body: formatted
isOnlyEmoji: false
isReply: d.isReply
keepFullText: d.keepFullText
isStateEvent: d.isStateEvent
formatted: qsTr("%1 changed which servers are allowed in this room.").arg(d.userName)
}
}
DelegateChoice {
roleValue: MtxEvent.Name
NoticeMessage {
body: formatted
isOnlyEmoji: false
isReply: d.isReply
keepFullText: d.keepFullText
isStateEvent: d.isStateEvent
formatted: d.roomName ? qsTr("%2 changed the room name to: %1").arg(d.roomName).arg(d.userName) : qsTr("%1 removed the room name").arg(d.userName)
}
}
DelegateChoice {
roleValue: MtxEvent.Topic
NoticeMessage {
body: formatted
isOnlyEmoji: false
isReply: d.isReply
keepFullText: d.keepFullText
isStateEvent: d.isStateEvent
formatted: d.roomTopic ? qsTr("%2 changed the topic to: %1").arg(d.roomTopic).arg(d.userName): qsTr("%1 removed the topic").arg(d.userName)
}
}
DelegateChoice {
roleValue: MtxEvent.Avatar
NoticeMessage {
body: formatted
isOnlyEmoji: false
isReply: d.isReply
keepFullText: d.keepFullText
isStateEvent: d.isStateEvent
formatted: qsTr("%1 changed the room avatar").arg(d.userName)
}
}
DelegateChoice {
roleValue: MtxEvent.PinnedEvents
NoticeMessage {
body: formatted
isOnlyEmoji: false
isReply: d.isReply
keepFullText: d.keepFullText
isStateEvent: d.isStateEvent
formatted: qsTr("%1 changed the pinned messages.").arg(d.userName)
}
}
DelegateChoice {
roleValue: MtxEvent.ImagePackInRoom
NoticeMessage {
body: formatted
isOnlyEmoji: false
isReply: d.isReply
keepFullText: d.keepFullText
isStateEvent: d.isStateEvent
formatted: d.relatedEventCacheBuster, room.formatImagePackEvent(d.eventId)
}
}
DelegateChoice {
roleValue: MtxEvent.CanonicalAlias
NoticeMessage {
body: formatted
isOnlyEmoji: false
isReply: d.isReply
keepFullText: d.keepFullText
isStateEvent: d.isStateEvent
formatted: qsTr("%1 changed the addresses for this room.").arg(d.userName)
}
}
DelegateChoice {
roleValue: MtxEvent.SpaceParent
NoticeMessage {
body: formatted
isOnlyEmoji: false
isReply: d.isReply
keepFullText: d.keepFullText
isStateEvent: d.isStateEvent
formatted: qsTr("%1 changed the parent communities for this room.").arg(d.userName)
}
}
DelegateChoice {
roleValue: MtxEvent.RoomCreate
NoticeMessage {
body: formatted
isOnlyEmoji: false
isReply: d.isReply
keepFullText: d.keepFullText
isStateEvent: d.isStateEvent
formatted: qsTr("%1 created and configured room: %2").arg(d.userName).arg(room.roomId)
}
}
DelegateChoice {
roleValue: MtxEvent.CallInvite
NoticeMessage {
body: formatted
isOnlyEmoji: false
isReply: d.isReply
keepFullText: d.keepFullText
isStateEvent: d.isStateEvent
formatted: {
switch (d.callType) {
case "voice":
return qsTr("%1 placed a voice call.").arg(d.userName);
case "video":
return qsTr("%1 placed a video call.").arg(d.userName);
default:
return qsTr("%1 placed a call.").arg(d.userName);
}
}
}
}
DelegateChoice {
roleValue: MtxEvent.CallAnswer
NoticeMessage {
body: formatted
isOnlyEmoji: false
isReply: d.isReply
keepFullText: d.keepFullText
isStateEvent: d.isStateEvent
formatted: qsTr("%1 answered the call.").arg(d.userName)
}
}
DelegateChoice {
roleValue: MtxEvent.CallReject
NoticeMessage {
body: formatted
isOnlyEmoji: false
isReply: d.isReply
keepFullText: d.keepFullText
isStateEvent: d.isStateEvent
formatted: qsTr("%1 rejected the call.").arg(d.userName)
}
}
DelegateChoice {
roleValue: MtxEvent.CallSelectAnswer
NoticeMessage {
body: formatted
isOnlyEmoji: false
isReply: d.isReply
keepFullText: d.keepFullText
isStateEvent: d.isStateEvent
formatted: qsTr("%1 select answer").arg(d.userName)
// formatted: qsTr("Call answered elsewhere")
}
}
DelegateChoice {
roleValue: MtxEvent.CallHangUp
NoticeMessage {
body: formatted
isOnlyEmoji: false
isReply: d.isReply
keepFullText: d.keepFullText
isStateEvent: d.isStateEvent
formatted: qsTr("%1 ended the call.").arg(d.userName)
}
}
DelegateChoice {
roleValue: MtxEvent.CallCandidates
NoticeMessage {
body: formatted
isOnlyEmoji: false
isReply: d.isReply
keepFullText: d.keepFullText
isStateEvent: d.isStateEvent
formatted: qsTr("%1 is negotiating the call...").arg(d.userName)
}
}
DelegateChoice {
roleValue: MtxEvent.CallNegotiate
NoticeMessage {
body: formatted
isOnlyEmoji: false
isReply: d.isReply
keepFullText: d.keepFullText
isStateEvent: d.isStateEvent
formatted: qsTr("%1 is negotiating the call...").arg(d.userName)
}
}
DelegateChoice {
roleValue: MtxEvent.PowerLevels
NoticeMessage {
body: formatted
isOnlyEmoji: false
isReply: d.isReply
keepFullText: d.keepFullText
isStateEvent: d.isStateEvent
formatted: d.relatedEventCacheBuster, room.formatPowerLevelEvent(d.eventId)
}
}
DelegateChoice {
roleValue: MtxEvent.PolicyRuleUser
NoticeMessage {
body: formatted
isOnlyEmoji: false
isReply: d.isReply
keepFullText: d.keepFullText
isStateEvent: d.isStateEvent
formatted: d.relatedEventCacheBuster, room.formatPolicyRule(d.eventId)
}
}
DelegateChoice {
roleValue: MtxEvent.PolicyRuleRoom
NoticeMessage {
body: formatted
isOnlyEmoji: false
isReply: d.isReply
keepFullText: d.keepFullText
isStateEvent: d.isStateEvent
formatted: d.relatedEventCacheBuster, room.formatPolicyRule(d.eventId)
}
}
DelegateChoice {
roleValue: MtxEvent.PolicyRuleServer
NoticeMessage {
body: formatted
isOnlyEmoji: false
isReply: d.isReply
keepFullText: d.keepFullText
isStateEvent: d.isStateEvent
formatted: d.relatedEventCacheBuster, room.formatPolicyRule(d.eventId)
}
}
DelegateChoice {
roleValue: MtxEvent.RoomJoinRules
NoticeMessage {
body: formatted
isOnlyEmoji: false
isReply: d.isReply
keepFullText: d.keepFullText
isStateEvent: d.isStateEvent
formatted: d.relatedEventCacheBuster, room.formatJoinRuleEvent(d.eventId)
}
}
DelegateChoice {
roleValue: MtxEvent.RoomHistoryVisibility
NoticeMessage {
body: formatted
isOnlyEmoji: false
isReply: d.isReply
keepFullText: d.keepFullText
isStateEvent: d.isStateEvent
formatted: d.relatedEventCacheBuster, room.formatHistoryVisibilityEvent(d.eventId)
}
}
DelegateChoice {
roleValue: MtxEvent.RoomGuestAccess
NoticeMessage {
body: formatted
isOnlyEmoji: false
isReply: d.isReply
keepFullText: d.keepFullText
isStateEvent: d.isStateEvent
formatted: d.relatedEventCacheBuster, room.formatGuestAccessEvent(d.eventId)
}
}
DelegateChoice {
roleValue: MtxEvent.Member
ColumnLayout {
NoticeMessage {
body: formatted
isOnlyEmoji: false
isReply: d.isReply
keepFullText: d.keepFullText
isStateEvent: d.isStateEvent
Layout.fillWidth: true
formatted: d.relatedEventCacheBuster, room.formatMemberEvent(d.eventId)
}
Button {
visible: d.relatedEventCacheBuster, room.showAcceptKnockButton(d.eventId)
Layout.alignment: Qt.AlignHCenter
text: qsTr("Allow them in")
onClicked: room.acceptKnock(eventId)
}
}
}
DelegateChoice {
roleValue: MtxEvent.KeyVerificationRequest
NoticeMessage {
body: formatted
isOnlyEmoji: false
isReply: d.isReply
keepFullText: d.keepFullText
isStateEvent: d.isStateEvent
formatted: "KeyVerificationRequest"
}
}
DelegateChoice {
roleValue: MtxEvent.KeyVerificationStart
NoticeMessage {
body: formatted
isOnlyEmoji: false
isReply: d.isReply
keepFullText: d.keepFullText
isStateEvent: d.isStateEvent
formatted: "KeyVerificationStart"
}
}
DelegateChoice {
roleValue: MtxEvent.KeyVerificationReady
NoticeMessage {
body: formatted
isOnlyEmoji: false
isReply: d.isReply
keepFullText: d.keepFullText
isStateEvent: d.isStateEvent
formatted: "KeyVerificationReady"
}
}
DelegateChoice {
roleValue: MtxEvent.KeyVerificationCancel
NoticeMessage {
body: formatted
isOnlyEmoji: false
isReply: d.isReply
keepFullText: d.keepFullText
isStateEvent: d.isStateEvent
formatted: "KeyVerificationCancel"
}
}
DelegateChoice {
roleValue: MtxEvent.KeyVerificationKey
NoticeMessage {
body: formatted
isOnlyEmoji: false
isReply: d.isReply
keepFullText: d.keepFullText
isStateEvent: d.isStateEvent
formatted: "KeyVerificationKey"
}
}
DelegateChoice {
roleValue: MtxEvent.KeyVerificationMac
NoticeMessage {
body: formatted
isOnlyEmoji: false
isReply: d.isReply
keepFullText: d.keepFullText
isStateEvent: d.isStateEvent
formatted: "KeyVerificationMac"
}
}
DelegateChoice {
roleValue: MtxEvent.KeyVerificationDone
NoticeMessage {
body: formatted
isOnlyEmoji: false
isReply: d.isReply
keepFullText: d.keepFullText
isStateEvent: d.isStateEvent
formatted: "KeyVerificationDone"
}
}
DelegateChoice {
roleValue: MtxEvent.KeyVerificationDone
NoticeMessage {
body: formatted
isOnlyEmoji: false
isReply: d.isReply
keepFullText: d.keepFullText
isStateEvent: d.isStateEvent
formatted: "KeyVerificationDone"
}
}
DelegateChoice {
roleValue: MtxEvent.KeyVerificationAccept
NoticeMessage {
body: formatted
isOnlyEmoji: false
isReply: d.isReply
keepFullText: d.keepFullText
isStateEvent: d.isStateEvent
formatted: "KeyVerificationAccept"
}
}
DelegateChoice {
Placeholder {
typeString: d.typeString
}
}
}
}

View file

@ -22,7 +22,7 @@ Item {
required property string url required property string url
required property string body required property string body
required property string filesize required property string filesize
property double divisor: isReply ? 4 : 2 property double divisor: EventDelegateChooser.isReply ? 5 : 3
property int tempWidth: originalWidth < 1? 400: originalWidth property int tempWidth: originalWidth < 1? 400: originalWidth
implicitWidth: type == MtxEvent.VideoMessage ? Math.round(tempWidth*Math.min((timelineView.height/divisor)/(tempWidth*proportionalHeight), 1)) : 500 implicitWidth: type == MtxEvent.VideoMessage ? Math.round(tempWidth*Math.min((timelineView.height/divisor)/(tempWidth*proportionalHeight), 1)) : 500
width: Math.min(parent?.width ?? implicitWidth, implicitWidth) width: Math.min(parent?.width ?? implicitWidth, implicitWidth)

View file

@ -48,51 +48,43 @@ AbstractButton {
room: room_ room: room_
eventId: r.eventId eventId: r.eventId
replyTo: "" replyTo: ""
mainInset: 4 + Nheko.paddingMedium
maxWidth: r.maxWidth
//height: replyContainer.implicitHeight //height: replyContainer.implicitHeight
data: GridLayout { data: Row {
id: replyContainer id: replyContainer
width: r.maxWidth spacing: Nheko.paddingSmall
columns: 2
rows: 2
columnSpacing: Nheko.paddingMedium
rowSpacing: Nheko.paddingSmall
Rectangle { Rectangle {
id: colorline id: colorline
Layout.preferredWidth: 4 width: 4
Layout.rowSpan: 2
Layout.fillHeight: true
Layout.row: 0
Layout.column: 0
color: TimelineManager.userColor(r.userId, palette.base) color: TimelineManager.userColor(r.userId, palette.base)
} }
Column {
spacing: 0
AbstractButton { AbstractButton {
id: usernameBtn id: usernameBtn
Layout.fillWidth: true
Layout.row: 0 contentItem: Label {
Layout.column: 1
contentItem: ElidedLabel {
id: userName_ id: userName_
fullText: r.userName text: r.userName
color: r.userColor color: r.userColor
textFormat: Text.RichText textFormat: Text.RichText
width: parent.width width: timelineEvent.main?.width
elideWidth: width
} }
onClicked: room.openUserProfile(r.userId) onClicked: room.openUserProfile(r.userId)
} }
data: [ data: [
colorline, usernameBtn, timelineEvent.main, usernameBtn, timelineEvent.main,
] ]
}
} }
} }

View file

@ -10,7 +10,7 @@ import im.nheko
MatrixText { MatrixText {
required property string body required property string body
required property bool isOnlyEmoji required property bool isOnlyEmoji
required property bool isReply property bool isReply: EventDelegateChooser.isReply
required property bool keepFullText required property bool keepFullText
required property string formatted required property string formatted
@ -45,7 +45,6 @@ MatrixText {
//EventDelegateChooser.fillWidth: true //EventDelegateChooser.fillWidth: true
clip: !keepFullText
selectByMouse: !Settings.mobileMode && !isReply selectByMouse: !Settings.mobileMode && !isReply
enabled: !Settings.mobileMode && !isReply enabled: !Settings.mobileMode && !isReply
hoverEnabled: !Settings.mobileMode hoverEnabled: !Settings.mobileMode

View file

@ -97,6 +97,7 @@ EventDelegateChooser::DelegateIncubator::setInitialState(QObject *obj)
return; return;
item->setParentItem(&chooser); item->setParentItem(&chooser);
item->setParent(&chooser);
auto roleNames = chooser.room_->roleNames(); auto roleNames = chooser.room_->roleNames();
QHash<QByteArray, int> nameToRole; QHash<QByteArray, int> nameToRole;
@ -106,8 +107,6 @@ EventDelegateChooser::DelegateIncubator::setInitialState(QObject *obj)
QHash<int, int> roleToPropIdx; QHash<int, int> roleToPropIdx;
std::vector<QModelRoleData> roles; std::vector<QModelRoleData> roles;
bool isReplyNeeded = false;
// Workaround for https://bugreports.qt.io/browse/QTBUG-98846 // Workaround for https://bugreports.qt.io/browse/QTBUG-98846
QHash<QString, RequiredPropertyKey> requiredProperties; QHash<QString, RequiredPropertyKey> requiredProperties;
for (const auto &[propKey, prop] : for (const auto &[propKey, prop] :
@ -124,10 +123,7 @@ EventDelegateChooser::DelegateIncubator::setInitialState(QObject *obj)
if (!prop.isRequired() && !requiredProperties.contains(prop.name())) if (!prop.isRequired() && !requiredProperties.contains(prop.name()))
continue; continue;
if (prop.name() == std::string_view("isReply")) { if (auto role = nameToRole.find(prop.name()); role != nameToRole.end()) {
isReplyNeeded = true;
roleToPropIdx.insert(-1, i);
} else if (auto role = nameToRole.find(prop.name()); role != nameToRole.end()) {
roleToPropIdx.insert(*role, i); roleToPropIdx.insert(*role, i);
roles.emplace_back(*role); roles.emplace_back(*role);
@ -141,6 +137,11 @@ EventDelegateChooser::DelegateIncubator::setInitialState(QObject *obj)
chooser.room_->multiData(currentId, forReply ? chooser.eventId_ : QString(), roles); chooser.room_->multiData(currentId, forReply ? chooser.eventId_ : QString(), roles);
Qt::beginPropertyUpdateGroup(); Qt::beginPropertyUpdateGroup();
auto attached = qobject_cast<EventDelegateChooserAttachedType *>(
qmlAttachedPropertiesObject<EventDelegateChooser>(obj));
Q_ASSERT(attached != nullptr);
attached->setIsReply(this->forReply);
for (const auto &role : roles) { for (const auto &role : roles) {
const auto &roleName = roleNames[role.role()]; const auto &roleName = roleNames[role.role()];
// nhlog::ui()->critical("Setting role {}, {} to {}", // nhlog::ui()->critical("Setting role {}, {} to {}",
@ -155,22 +156,25 @@ EventDelegateChooser::DelegateIncubator::setInitialState(QObject *obj)
QQmlIncubatorPrivate::get(this)->requiredProperties()->remove(*req); QQmlIncubatorPrivate::get(this)->requiredProperties()->remove(*req);
} }
if (isReplyNeeded) {
const auto roleName = QByteArray("isReply");
// nhlog::ui()->critical("Setting role {} to {}", roleName.toStdString(), forReply);
// nhlog::ui()->critical("Setting {}", mo->property(roleToPropIdx[-1]).name());
mo->property(roleToPropIdx[-1]).write(obj, forReply);
if (const auto &req = requiredProperties.find(roleName); req != requiredProperties.end())
QQmlIncubatorPrivate::get(this)->requiredProperties()->remove(*req);
}
Qt::endPropertyUpdateGroup(); Qt::endPropertyUpdateGroup();
// setInitialProperties(rolesToSet); // setInitialProperties(rolesToSet);
auto update = auto update =
[this, obj, roleToPropIdx = std::move(roleToPropIdx)](const QList<int> &changedRoles) { [this, obj, roleToPropIdx = std::move(roleToPropIdx)](const QList<int> &changedRoles) {
if (changedRoles.empty() || changedRoles.contains(TimelineModel::Roles::Type)) {
int type = chooser.room_
->dataById(currentId,
TimelineModel::Roles::Type,
forReply ? chooser.eventId_ : QString())
.toInt();
if (type != oldType) {
nhlog::ui()->debug("Type changed!");
reset(currentId);
return;
}
}
std::vector<QModelRoleData> rolesToRequest; std::vector<QModelRoleData> rolesToRequest;
if (changedRoles.empty()) { if (changedRoles.empty()) {
@ -233,6 +237,7 @@ EventDelegateChooser::DelegateIncubator::reset(QString id)
chooser.room_ chooser.room_
->dataById(id, TimelineModel::Roles::Type, forReply ? chooser.eventId_ : QString()) ->dataById(id, TimelineModel::Roles::Type, forReply ? chooser.eventId_ : QString())
.toInt(); .toInt();
this->oldType = role;
for (const auto choice : qAsConst(chooser.choices_)) { for (const auto choice : qAsConst(chooser.choices_)) {
const auto &choiceValue = choice->roleValues(); const auto &choiceValue = choice->roleValues();
@ -293,41 +298,58 @@ EventDelegateChooser::updatePolish()
nhlog::ui()->critical("POLISHING {}", (void *)this); nhlog::ui()->critical("POLISHING {}", (void *)this);
if (mainChild) { auto layoutItem = [this](QQuickItem *item, int inset) {
if (item) {
auto attached = qobject_cast<EventDelegateChooserAttachedType *>( auto attached = qobject_cast<EventDelegateChooserAttachedType *>(
qmlAttachedPropertiesObject<EventDelegateChooser>(mainChild)); qmlAttachedPropertiesObject<EventDelegateChooser>(item));
Q_ASSERT(attached != nullptr); Q_ASSERT(attached != nullptr);
// in theory we could also reset the width, but that doesn't seem to work nicely for text int maxWidth = maxWidth_ - inset;
// areas because of how they cache it.
mainChild->setWidth(maxWidth_);
mainChild->ensurePolished();
auto width = mainChild->implicitWidth();
if (width > maxWidth_ || attached->fillWidth()) // in theory we could also reset the width, but that doesn't seem to work nicely for
width = maxWidth_; // text areas because of how they cache it.
if (attached->maxWidth() > 0)
item->setWidth(attached->maxWidth());
else
item->setWidth(maxWidth);
item->ensurePolished();
auto width = item->implicitWidth();
if (width < 1 || width > maxWidth)
width = maxWidth;
if (attached->maxWidth() > 0 && width > attached->maxWidth())
width = attached->maxWidth();
if (attached->keepAspectRatio()) {
auto height = width * attached->aspectRatio();
if (attached->maxHeight() && height > attached->maxHeight()) {
height = attached->maxHeight();
width = height / attached->aspectRatio();
}
item->setHeight(height);
}
nhlog::ui()->debug( nhlog::ui()->debug(
"Made event delegate width: {}, {}", width, mainChild->metaObject()->className()); "Made event delegate width: {}, {}", width, item->metaObject()->className());
mainChild->setWidth(width); item->setWidth(width);
mainChild->ensurePolished(); item->ensurePolished();
}
};
layoutItem(mainChild, mainInset_);
layoutItem(replyChild, replyInset_);
} }
if (replyChild) { void
auto attached = qobject_cast<EventDelegateChooserAttachedType *>( EventDelegateChooserAttachedType::polishChooser()
qmlAttachedPropertiesObject<EventDelegateChooser>(replyChild)); {
Q_ASSERT(attached != nullptr); auto p = parent();
if (p) {
// in theory we could also reset the width, but that doesn't seem to work nicely for text auto chooser = qobject_cast<EventDelegateChooser *>(p->parent());
// areas because of how they cache it. if (chooser) {
replyChild->setWidth(maxWidth_); chooser->polish();
replyChild->ensurePolished(); }
auto width = replyChild->implicitWidth();
if (width > maxWidth_ || attached->fillWidth())
width = maxWidth_;
replyChild->setWidth(width);
replyChild->ensurePolished();
} }
} }

View file

@ -17,27 +17,78 @@
class EventDelegateChooserAttachedType : public QObject class EventDelegateChooserAttachedType : public QObject
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(bool fillWidth READ fillWidth WRITE setFillWidth NOTIFY fillWidthChanged) Q_PROPERTY(bool keepAspectRatio READ keepAspectRatio WRITE setKeepAspectRatio NOTIFY
keepAspectRatioChanged)
Q_PROPERTY(double aspectRatio READ aspectRatio WRITE setAspectRatio NOTIFY aspectRatioChanged)
Q_PROPERTY(int maxWidth READ maxWidth WRITE setMaxWidth NOTIFY maxWidthChanged)
Q_PROPERTY(int maxHeight READ maxHeight WRITE setMaxHeight NOTIFY maxHeightChanged)
Q_PROPERTY(bool isReply READ isReply WRITE setIsReply NOTIFY isReplyChanged)
QML_ANONYMOUS QML_ANONYMOUS
public: public:
EventDelegateChooserAttachedType(QObject *parent) EventDelegateChooserAttachedType(QObject *parent)
: QObject(parent) : QObject(parent)
{ {
} }
bool fillWidth() const { return fillWidth_; }
void setFillWidth(bool fill) bool keepAspectRatio() const { return keepAspectRatio_; }
void setKeepAspectRatio(bool fill)
{ {
fillWidth_ = fill; if (fill != keepAspectRatio_) {
emit fillWidthChanged(); keepAspectRatio_ = fill;
emit keepAspectRatioChanged();
polishChooser();
} }
}
double aspectRatio() const { return aspectRatio_; }
void setAspectRatio(double fill)
{
aspectRatio_ = fill;
emit aspectRatioChanged();
polishChooser();
}
int maxWidth() const { return maxWidth_; }
void setMaxWidth(int fill)
{
maxWidth_ = fill;
emit maxWidthChanged();
polishChooser();
}
int maxHeight() const { return maxHeight_; }
void setMaxHeight(int fill)
{
maxHeight_ = fill;
emit maxHeightChanged();
}
bool isReply() const { return isReply_; }
void setIsReply(bool fill)
{
if (fill != isReply_) {
isReply_ = fill;
emit isReplyChanged();
polishChooser();
}
}
signals: signals:
void fillWidthChanged(); void keepAspectRatioChanged();
void aspectRatioChanged();
void maxWidthChanged();
void maxHeightChanged();
void isReplyChanged();
private: private:
bool fillWidth_ = false, keepAspectRatio = false; void polishChooser();
double aspectRatio = 1.;
int maxWidth = -1; double aspectRatio_ = 1.;
int maxHeight = -1; int maxWidth_ = -1;
int maxHeight_ = -1;
bool keepAspectRatio_ = false;
bool isReply_ = false;
}; };
class EventDelegateChoice : public QObject class EventDelegateChoice : public QObject
@ -84,6 +135,8 @@ class EventDelegateChooser : public QQuickItem
Q_PROPERTY(TimelineModel *room READ room WRITE setRoom NOTIFY roomChanged REQUIRED FINAL) Q_PROPERTY(TimelineModel *room READ room WRITE setRoom NOTIFY roomChanged REQUIRED FINAL)
Q_PROPERTY(bool sameWidth READ sameWidth WRITE setSameWidth NOTIFY sameWidthChanged) Q_PROPERTY(bool sameWidth READ sameWidth WRITE setSameWidth NOTIFY sameWidthChanged)
Q_PROPERTY(int maxWidth READ maxWidth WRITE setMaxWidth NOTIFY maxWidthChanged) Q_PROPERTY(int maxWidth READ maxWidth WRITE setMaxWidth NOTIFY maxWidthChanged)
Q_PROPERTY(int replyInset READ replyInset WRITE setReplyInset NOTIFY replyInsetChanged)
Q_PROPERTY(int mainInset READ mainInset WRITE setMainInset NOTIFY mainInsetChanged)
public: public:
QQmlListProperty<EventDelegateChoice> choices(); QQmlListProperty<EventDelegateChoice> choices();
@ -103,7 +156,7 @@ public:
sameWidth_ = width; sameWidth_ = width;
emit sameWidthChanged(); emit sameWidthChanged();
} }
bool maxWidth() const { return maxWidth_; } int maxWidth() const { return maxWidth_; }
void setMaxWidth(int width) void setMaxWidth(int width)
{ {
maxWidth_ = width; maxWidth_ = width;
@ -111,6 +164,22 @@ public:
polish(); polish();
} }
int replyInset() const { return replyInset_; }
void setReplyInset(int width)
{
replyInset_ = width;
emit replyInsetChanged();
polish();
}
int mainInset() const { return mainInset_; }
void setMainInset(int width)
{
mainInset_ = width;
emit mainInsetChanged();
polish();
}
void setRoom(TimelineModel *m) void setRoom(TimelineModel *m)
{ {
if (m != room_) { if (m != room_) {
@ -161,6 +230,8 @@ signals:
void replyToChanged(); void replyToChanged();
void sameWidthChanged(); void sameWidthChanged();
void maxWidthChanged(); void maxWidthChanged();
void replyInsetChanged();
void mainInsetChanged();
private: private:
struct DelegateIncubator final : public QQmlIncubator struct DelegateIncubator final : public QQmlIncubator
@ -183,6 +254,7 @@ private:
QString instantiatedId; QString instantiatedId;
int instantiatedRole = -1; int instantiatedRole = -1;
QAbstractItemModel *instantiatedModel = nullptr; QAbstractItemModel *instantiatedModel = nullptr;
int oldType = -1;
}; };
QVariant roleValue_; QVariant roleValue_;
@ -194,6 +266,8 @@ private:
QString replyId; QString replyId;
bool sameWidth_ = false; bool sameWidth_ = false;
int maxWidth_ = 400; int maxWidth_ = 400;
int replyInset_ = 0;
int mainInset_ = 0;
static void appendChoice(QQmlListProperty<EventDelegateChoice> *, EventDelegateChoice *); static void appendChoice(QQmlListProperty<EventDelegateChoice> *, EventDelegateChoice *);
static qsizetype choiceCount(QQmlListProperty<EventDelegateChoice> *); static qsizetype choiceCount(QQmlListProperty<EventDelegateChoice> *);