Working text messages in delegate rework

delegate-rework
Nicolas Werner 1 year ago
parent 4d8b8c3b81
commit 76b40f452b
No known key found for this signature in database
GPG Key ID: C8D75E610773F2D9
  1. 32
      resources/qml/MatrixText.qml
  2. 148
      resources/qml/MessageView.qml
  3. 15
      resources/qml/delegates/TextMessage.qml
  4. 26
      src/timeline/EventDelegateChooser.cpp
  5. 5
      src/timeline/EventDelegateChooser.h

@ -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

@ -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
}
}
}

@ -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

@ -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);

@ -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)
{

Loading…
Cancel
Save