diff --git a/resources/qml/MatrixText.qml b/resources/qml/MatrixText.qml
index 94b8bb98..de15e078 100644
--- a/resources/qml/MatrixText.qml
+++ b/resources/qml/MatrixText.qml
@@ -2,24 +2,24 @@
//
// SPDX-License-Identifier: GPL-3.0-or-later
-import QtQuick 2.5
-import QtQuick.Controls 2.3
-import im.nheko 1.0
+import QtQuick
+import QtQuick.Controls
+import im.nheko
-TextEdit {
+TextArea {
id: r
property alias cursorShape: cs.cursorShape
- //leftInset: 0
- //bottomInset: 0
- //rightInset: 0
- //topInset: 0
- //leftPadding: 0
- //bottomPadding: 0
- //rightPadding: 0
- //topPadding: 0
- //background: null
+ leftInset: 0
+ bottomInset: 0
+ rightInset: 0
+ topInset: 0
+ leftPadding: 0
+ bottomPadding: 0
+ rightPadding: 0
+ topPadding: 0
+ background: null
ToolTip.text: hoveredLink
ToolTip.visible: hoveredLink || false
@@ -39,9 +39,9 @@ TextEdit {
}
onLinkActivated: Nheko.openLink(link)
- //// propagate events up
- //onPressAndHold: (event) => event.accepted = false
- //onPressed: (event) => event.accepted = (event.button == Qt.LeftButton)
+ // propagate events up
+ onPressAndHold: (event) => event.accepted = false
+ onPressed: (event) => event.accepted = (event.button == Qt.LeftButton)
NhekoCursorShape {
id: cs
diff --git a/resources/qml/MessageView.qml b/resources/qml/MessageView.qml
index fde7ee57..2852e5f7 100644
--- a/resources/qml/MessageView.qml
+++ b/resources/qml/MessageView.qml
@@ -63,28 +63,160 @@ Item {
id: wrapper
ListView.delayRemove: true
width: chat.delegateMaxWidth
- height: main?.height ?? 10
+ height: Math.max((section.item?.height ?? 0) + gridContainer.implicitHeight, 10)
+ anchors.horizontalCenter: ListView.view.contentItem.horizontalCenter
room: chatRoot.roommodel
+ required property var day
+ required property bool isSender
+ required property bool isStateEvent
+ //required property var previousMessageDay
+ //required property bool previousMessageIsStateEvent
+ //required property string previousMessageUserId
+ required property int index
+ property var previousMessageDay: (index + 1) >= chat.count ? 0 : chat.model.dataByIndex(index + 1, Room.Day)
+ property bool previousMessageIsStateEvent: (index + 1) >= chat.count ? true : chat.model.dataByIndex(index + 1, Room.IsStateEvent)
+ property string previousMessageUserId: (index + 1) >= chat.count ? "" : chat.model.dataByIndex(index + 1, Room.UserId)
+ required property date timestamp
+ required property string userId
+ required property string userName
+ required property string threadId
+
+ data: [
+ Loader {
+ id: section
+
+ property var day: wrapper.day
+ property bool isSender: wrapper.isSender
+ property bool isStateEvent: wrapper.isStateEvent
+ property int parentWidth: wrapper.width
+ property var previousMessageDay: wrapper.previousMessageDay
+ property bool previousMessageIsStateEvent: wrapper.previousMessageIsStateEvent
+ property string previousMessageUserId: wrapper.previousMessageUserId
+ property date timestamp: wrapper.timestamp
+ property string userId: wrapper.userId
+ property string userName: wrapper.userName
+
+ active: previousMessageUserId !== userId || previousMessageDay !== day || previousMessageIsStateEvent !== isStateEvent
+ //asynchronous: true
+ sourceComponent: sectionHeader
+ visible: status == Loader.Ready
+ z: 4
+ },
+ GridLayout {
+ id: gridContainer
+
+ width: wrapper.width
+ y: section.visible && section.active ? section.y + section.height : 0
+
+ ColumnLayout {
+ id: contentColumn
+ Layout.fillWidth: true
+ Layout.leftMargin: (wrapper.isStateEvent || Settings.smallAvatars ? 0 : (Nheko.avatarSize + 8)) + (wrapper.threadId ? 6 : 0) // align bubble with section header
+
+ AbstractButton {
+ id: replyRow
+ visible: wrapper.reply
+ Layout.fillWidth: true
+ Layout.maximumHeight: timelineView.height / 8
+ Layout.preferredWidth: replyRowLay.implicitWidth
+ Layout.preferredHeight: replyRowLay.implicitHeight
+
+ property color userColor: TimelineManager.userColor(wrapper.reply?.userId ?? '', palette.base)
+
+ clip: true
+
+ contentItem: RowLayout {
+ id: replyRowLay
+
+ anchors.fill: parent
+
+
+ Rectangle {
+ id: replyLine
+ Layout.fillHeight: true
+ color: replyRow.userColor
+ Layout.preferredWidth: 4
+ }
+
+ ColumnLayout {
+ AbstractButton {
+ id: replyUserButton
+ Layout.fillWidth: true
+ contentItem: ElidedLabel {
+ id: userName_
+ fullText: wrapper.reply?.userName ?? ''
+ color: replyRow.userColor
+ textFormat: Text.RichText
+ width: parent.width
+ elideWidth: width
+ }
+ onClicked: room.openUserProfile(wrapper.reply?.userId)
+ }
+ data: [
+ replyUserButton,
+ wrapper.reply,
+ ]
+ }
+ }
+
+ background: Rectangle {
+ width: replyRow.implicitContentWidth
+ color: Qt.tint(palette.base, Qt.hsla(replyRow.userColor.hslHue, 0.5, replyRow.userColor.hslLightness, 0.1))
+ }
+ }
+
+ data: [
+ replyRow, wrapper.main,
+ ]
+ }
+
+ Rectangle {
+ color: 'yellow'
+ Layout.preferredWidth: 100
+ Layout.preferredHeight: 20
+ Layout.alignment: Qt.AlignRight | Qt.AlignTop
+ }
+ },
+
+ Rectangle {
+ width: Math.min(contentColumn.implicitWidth, contentColumn.width)
+ height: contentColumn.implicitHeight
+ color: "blue"
+ opacity: 0.2
+ }
+ ]
+
EventDelegateChoice {
roleValues: [
MtxEvent.TextMessage,
MtxEvent.NoticeMessage,
]
- TextArea {
- required property string body
+ TextMessage {
+ id: textMes
+
+ keepFullText: true
+ required property string userId
+ required property string userName
+
+ Layout.fillWidth: true
+ //Layout.maximumWidth: implicitWidth
- width: parent.width
- text: body
}
}
EventDelegateChoice {
roleValues: [
]
- TextArea {
- width: parent.width
- text: "Unsupported"
+ MatrixText {
+ Layout.fillWidth: true
+
+ required property string typeString
+
+ text: "Unsupported: " + typeString
+
+ required property string userId
+ required property string userName
}
}
}
diff --git a/resources/qml/delegates/TextMessage.qml b/resources/qml/delegates/TextMessage.qml
index 1eb5e2c0..d4b72965 100644
--- a/resources/qml/delegates/TextMessage.qml
+++ b/resources/qml/delegates/TextMessage.qml
@@ -3,17 +3,19 @@
// SPDX-License-Identifier: GPL-3.0-or-later
import ".."
-import QtQuick.Controls 2.3
-import im.nheko 1.0
+import QtQuick.Controls
+import QtQuick.Layouts
+import im.nheko
MatrixText {
required property string body
required property bool isOnlyEmoji
required property bool isReply
required property bool keepFullText
- required property string formatted
+ required property string formattedBody
+
property string copyText: selectedText ? getText(selectionStart, selectionEnd) : body
- property int metadataWidth
+ property int metadataWidth: 100
property bool fitsMetadata: false //positionAt(width,height-4) == positionAt(width-metadataWidth-10, height-4)
// table border-collapse doesn't seem to work
@@ -38,9 +40,8 @@ MatrixText {
background-color: " + palette.text + ";
}" : "") + // TODO(Nico): Figure out how to support mobile
"</style>
- " + formatted.replace(/<del>/g, "<s>").replace(/<\/del>/g, "</s>").replace(/<strike>/g, "<s>").replace(/<\/strike>/g, "</s>")
- width: parent?.width ?? 0
- height: !keepFullText ? Math.round(Math.min(timelineView.height / 8, implicitHeight)) : implicitHeight
+ " + formattedBody.replace(/<del>/g, "<s>").replace(/<\/del>/g, "</s>").replace(/<strike>/g, "<s>").replace(/<\/strike>/g, "</s>")
+ Layout.maximumHeight: !keepFullText ? Math.round(Math.min(timelineView.height / 8, implicitHeight)) : implicitHeight
clip: !keepFullText
selectByMouse: !Settings.mobileMode && !isReply
enabled: !Settings.mobileMode
diff --git a/src/timeline/EventDelegateChooser.cpp b/src/timeline/EventDelegateChooser.cpp
index 7618e20b..5e6ee37e 100644
--- a/src/timeline/EventDelegateChooser.cpp
+++ b/src/timeline/EventDelegateChooser.cpp
@@ -85,6 +85,7 @@ EventDelegateChooser::componentComplete()
{
QQuickItem::componentComplete();
// eventIncubator.reset(eventIndex);
+ // eventIncubator.forceCompletion();
}
void
@@ -104,6 +105,7 @@ 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;
@@ -121,7 +123,10 @@ EventDelegateChooser::DelegateIncubator::setInitialState(QObject *obj)
if (!prop.isRequired() && !requiredProperties.contains(prop.name()))
continue;
- if (auto role = nameToRole.find(prop.name()); role != nameToRole.end()) {
+ if (prop.name() == std::string_view("isReply")) {
+ isReplyNeeded = true;
+ roleToPropIdx.insert(-1, i);
+ } else if (auto role = nameToRole.find(prop.name()); role != nameToRole.end()) {
roleToPropIdx.insert(*role, i);
roles.emplace_back(*role);
@@ -134,13 +139,26 @@ EventDelegateChooser::DelegateIncubator::setInitialState(QObject *obj)
nhlog::ui()->debug("Querying data for id {}", currentId.toStdString());
chooser.room_->multiData(currentId, forReply ? chooser.eventId_ : QString(), roles);
- QVariantMap rolesToSet;
for (const auto &role : roles) {
const auto &roleName = roleNames[role.role()];
- nhlog::ui()->critical("Setting role {}, {}", role.role(), roleName.toStdString());
+ nhlog::ui()->critical("Setting role {}, {} to {}",
+ role.role(),
+ roleName.toStdString(),
+ role.data().toString().toStdString());
+ nhlog::ui()->critical("Setting {}", mo->property(roleToPropIdx[role.role()]).name());
mo->property(roleToPropIdx[role.role()]).write(obj, role.data());
- rolesToSet.insert(roleName, role.data());
+
+ if (const auto &req = requiredProperties.find(roleName); req != requiredProperties.end())
+ 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);
diff --git a/src/timeline/EventDelegateChooser.h b/src/timeline/EventDelegateChooser.h
index ce22ca3a..b627b383 100644
--- a/src/timeline/EventDelegateChooser.h
+++ b/src/timeline/EventDelegateChooser.h
@@ -54,6 +54,7 @@ class EventDelegateChooser : public QQuickItem
public:
Q_PROPERTY(QQmlListProperty<EventDelegateChoice> choices READ choices CONSTANT FINAL)
Q_PROPERTY(QQuickItem *main READ main NOTIFY mainChanged FINAL)
+ Q_PROPERTY(QQuickItem *reply READ reply NOTIFY replyChanged FINAL)
Q_PROPERTY(TimelineModel *room READ room WRITE setRoom NOTIFY roomChanged REQUIRED FINAL)
Q_PROPERTY(QString eventId READ eventId WRITE setEventId NOTIFY eventIdChanged REQUIRED FINAL)
Q_PROPERTY(QString replyTo READ replyTo WRITE setReplyTo NOTIFY replyToChanged REQUIRED FINAL)
@@ -64,6 +65,10 @@ public:
{
return qobject_cast<QQuickItem *>(eventIncubator.object());
}
+ [[nodiscard]] QQuickItem *reply() const
+ {
+ return qobject_cast<QQuickItem *>(replyIncubator.object());
+ }
void setRoom(TimelineModel *m)
{
|