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/FileMessage.qml
resources/qml/delegates/ImageMessage.qml
resources/qml/delegates/MessageDelegate.qml
resources/qml/delegates/NoticeMessage.qml
resources/qml/delegates/Pill.qml
resources/qml/delegates/Placeholder.qml

View file

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

View file

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

View file

@ -5,7 +5,6 @@
import QtQuick
import QtQuick.Window
import QtQuick.Controls
import QtQuick.Layouts
import im.nheko
AbstractButton {
@ -17,16 +16,17 @@ AbstractButton {
required property string blurhash
required property string body
required property string filename
required property bool isReply
required property string eventId
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
enabled: !EventDelegateChooser.isReply
state: (img.status != Image.Ready || timeline.privacyScreen.active) ? "BlurhashVisible" : "ImageVisible"
states: [
@ -133,8 +133,8 @@ AbstractButton {
roomm: room
play: !Settings.animateImagesOnHover || parent.hovered
eventId: parent.eventId
width: parent.implicitWidth
height: parent.implicitHeight
anchors.fill: parent
}
Image {
@ -143,10 +143,10 @@ AbstractButton {
source: blurhash ? ("image://blurhash/" + blurhash) : ("image://colorimage/:/icons/icons/ui/image-failed.svg?" + palette.buttonText)
asynchronous: true
fillMode: Image.PreserveAspectFit
sourceSize.width: parent.implicitWidth * Screen.devicePixelRatio
sourceSize.height: parent.implicitHeight * Screen.devicePixelRatio
width: parent.implicitWidth
height: parent.implicitHeight
sourceSize.width: parent.width * Screen.devicePixelRatio
sourceSize.height: parent.height * Screen.devicePixelRatio
anchors.fill: parent
}
onClicked: Settings.openImageExternal ? room.openMedia(eventId) : TimelineManager.openImageOverlay(room, url, eventId, originalWidth, proportionalHeight);
@ -154,8 +154,8 @@ AbstractButton {
Item {
id: overlay
width: parent.implicitWidth
height: parent.implicitHeight
anchors.fill: parent
visible: parent.hovered
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 body
required property string filesize
property double divisor: isReply ? 4 : 2
property double divisor: EventDelegateChooser.isReply ? 5 : 3
property int tempWidth: originalWidth < 1? 400: originalWidth
implicitWidth: type == MtxEvent.VideoMessage ? Math.round(tempWidth*Math.min((timelineView.height/divisor)/(tempWidth*proportionalHeight), 1)) : 500
width: Math.min(parent?.width ?? implicitWidth, implicitWidth)

View file

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

View file

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

View file

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

View file

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