diff --git a/CMakeLists.txt b/CMakeLists.txt
index 7e02bf34..cbe2b20e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -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
diff --git a/resources/qml/TimelineDefaultMessageStyle.qml b/resources/qml/TimelineDefaultMessageStyle.qml
index 0866fab6..8beaa8f0 100644
--- a/resources/qml/TimelineDefaultMessageStyle.qml
+++ b/resources/qml/TimelineDefaultMessageStyle.qml
@@ -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
}
diff --git a/resources/qml/TimelineEvent.qml b/resources/qml/TimelineEvent.qml
index f1b6bd2a..5120fd12 100644
--- a/resources/qml/TimelineEvent.qml
+++ b/resources/qml/TimelineEvent.qml
@@ -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
}
diff --git a/resources/qml/delegates/ImageMessage.qml b/resources/qml/delegates/ImageMessage.qml
index 0369d5a1..06a1a9e7 100644
--- a/resources/qml/delegates/ImageMessage.qml
+++ b/resources/qml/delegates/ImageMessage.qml
@@ -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 {
diff --git a/resources/qml/delegates/MessageDelegate.qml b/resources/qml/delegates/MessageDelegate.qml
deleted file mode 100644
index cd1bdcd4..00000000
--- a/resources/qml/delegates/MessageDelegate.qml
+++ /dev/null
@@ -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
- }
-
- }
-
- }
-
-}
diff --git a/resources/qml/delegates/PlayableMediaMessage.qml b/resources/qml/delegates/PlayableMediaMessage.qml
index fb7bf0cc..ac4a82b0 100644
--- a/resources/qml/delegates/PlayableMediaMessage.qml
+++ b/resources/qml/delegates/PlayableMediaMessage.qml
@@ -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)
diff --git a/resources/qml/delegates/Reply.qml b/resources/qml/delegates/Reply.qml
index 52cc982d..1598e8c0 100644
--- a/resources/qml/delegates/Reply.qml
+++ b/resources/qml/delegates/Reply.qml
@@ -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,
+ ]
+ }
}
}
diff --git a/resources/qml/delegates/TextMessage.qml b/resources/qml/delegates/TextMessage.qml
index 03623924..e9254eed 100644
--- a/resources/qml/delegates/TextMessage.qml
+++ b/resources/qml/delegates/TextMessage.qml
@@ -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
diff --git a/src/timeline/EventDelegateChooser.cpp b/src/timeline/EventDelegateChooser.cpp
index 0060907d..a8629b3e 100644
--- a/src/timeline/EventDelegateChooser.cpp
+++ b/src/timeline/EventDelegateChooser.cpp
@@ -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);
-
- // 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();
-
- if (width > maxWidth_ || attached->fillWidth())
- width = maxWidth_;
-
- nhlog::ui()->debug(
- "Made event delegate width: {}, {}", width, mainChild->metaObject()->className());
- mainChild->setWidth(width);
- mainChild->ensurePolished();
- }
-
- if (replyChild) {
- auto attached = qobject_cast<EventDelegateChooserAttachedType *>(
- qmlAttachedPropertiesObject<EventDelegateChooser>(replyChild));
- 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);
+
+ int maxWidth = maxWidth_ - inset;
+
+ // 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();
+
+ 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);
+ }
- // 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();
+ nhlog::ui()->debug(
+ "Made event delegate width: {}, {}", width, item->metaObject()->className());
+ item->setWidth(width);
+ item->ensurePolished();
+ }
+ };
- if (width > maxWidth_ || attached->fillWidth())
- width = maxWidth_;
+ layoutItem(mainChild, mainInset_);
+ layoutItem(replyChild, replyInset_);
+}
- replyChild->setWidth(width);
- replyChild->ensurePolished();
+void
+EventDelegateChooserAttachedType::polishChooser()
+{
+ auto p = parent();
+ if (p) {
+ auto chooser = qobject_cast<EventDelegateChooser *>(p->parent());
+ if (chooser) {
+ chooser->polish();
+ }
}
}
diff --git a/src/timeline/EventDelegateChooser.h b/src/timeline/EventDelegateChooser.h
index ff67ccd8..ce79444a 100644
--- a/src/timeline/EventDelegateChooser.h
+++ b/src/timeline/EventDelegateChooser.h
@@ -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)
+ {
+ if (fill != keepAspectRatio_) {
+ keepAspectRatio_ = fill;
+ emit keepAspectRatioChanged();
+ polishChooser();
+ }
+ }
+
+ double aspectRatio() const { return aspectRatio_; }
+ void setAspectRatio(double fill)
{
- fillWidth_ = fill;
- emit fillWidthChanged();
+ 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> *);
|