mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-21 18:50:47 +03:00
Working text messages in delegate rework
This commit is contained in:
parent
4d8b8c3b81
commit
76b40f452b
5 changed files with 191 additions and 35 deletions
|
@ -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…
Reference in a new issue