mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-21 18:50:47 +03:00
Switch to manual polishing of event delegates
This commit is contained in:
parent
205a42dade
commit
6c6370c83f
7 changed files with 169 additions and 60 deletions
|
@ -52,8 +52,8 @@ Item {
|
|||
//onModelChanged: if (room) room.sendReset()
|
||||
//reuseItems: true
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
displayMarginBeginning: height / 2
|
||||
displayMarginEnd: height / 2
|
||||
displayMarginBeginning: height / 4
|
||||
displayMarginEnd: height / 4
|
||||
model: (filteredTimeline.filterByThread || filteredTimeline.filterByContent) ? filteredTimeline : room
|
||||
//pixelAligned: true
|
||||
spacing: 2
|
||||
|
|
|
@ -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, 10)
|
||||
height: Math.max((section.item?.height ?? 0) + gridContainer.implicitHeight + reactionRow.implicitHeight + unreadRow.height, 20)
|
||||
anchors.horizontalCenter: ListView.view.contentItem.horizontalCenter
|
||||
//room: chatRoot.roommodel
|
||||
|
||||
|
@ -51,6 +51,8 @@ TimelineEvent {
|
|||
property alias hovered: messageHover.hovered
|
||||
property bool scrolledToThis: false
|
||||
|
||||
maxWidth: chat.delegateMaxWidth - avatarMargin - metadata.width
|
||||
|
||||
data: [
|
||||
Loader {
|
||||
id: section
|
||||
|
@ -131,7 +133,7 @@ TimelineEvent {
|
|||
anchors.top: gridContainer.top
|
||||
anchors.left: gridContainer.left
|
||||
anchors.topMargin: -2
|
||||
anchors.leftMargin: wrapper.avatarMargin + 2
|
||||
anchors.leftMargin: -2
|
||||
color: "transparent"
|
||||
border.color: Nheko.theme.red
|
||||
border.width: wrapper.notificationlevel == MtxEvent.Highlight ? 1 : 0
|
||||
|
@ -139,11 +141,13 @@ TimelineEvent {
|
|||
height: contentColumn.implicitHeight + 4
|
||||
width: contentColumn.implicitWidth + 4
|
||||
},
|
||||
RowLayout {
|
||||
Row {
|
||||
id: gridContainer
|
||||
|
||||
width: wrapper.width
|
||||
width: wrapper.width - wrapper.avatarMargin
|
||||
x: wrapper.avatarMargin
|
||||
y: section.visible && section.active ? section.y + section.height : 0
|
||||
spacing: Nheko.paddingSmall
|
||||
|
||||
HoverHandler {
|
||||
id: messageHover
|
||||
|
@ -154,23 +158,20 @@ TimelineEvent {
|
|||
messageActions.model = wrapper;
|
||||
messageActions.attached = wrapper;
|
||||
messageActions.anchors.bottomMargin = -gridContainer.y
|
||||
messageActions.anchors.rightMargin = metadata.width
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.preferredWidth: wrapper.avatarMargin
|
||||
}
|
||||
|
||||
AbstractButton {
|
||||
ToolTip.delay: Nheko.tooltipDelay
|
||||
ToolTip.text: qsTr("Part of a thread")
|
||||
ToolTip.visible: hovered
|
||||
Layout.fillHeight: true
|
||||
height: contentColumn.height
|
||||
visible: wrapper.threadId
|
||||
Layout.preferredWidth: 4
|
||||
width: 4
|
||||
|
||||
onClicked: wrapper.room.thread = wrapper.threadId
|
||||
|
||||
|
@ -181,17 +182,16 @@ TimelineEvent {
|
|||
color: TimelineManager.userColor(wrapper.threadId, palette.base)
|
||||
}
|
||||
}
|
||||
ColumnLayout {
|
||||
Column {
|
||||
id: contentColumn
|
||||
Layout.fillWidth: true
|
||||
|
||||
AbstractButton {
|
||||
id: replyRow
|
||||
visible: wrapper.reply
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumHeight: timelineView.height / 8
|
||||
Layout.preferredWidth: replyRowLay.implicitWidth
|
||||
Layout.preferredHeight: replyRowLay.implicitHeight
|
||||
//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)
|
||||
|
||||
|
@ -202,32 +202,33 @@ TimelineEvent {
|
|||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
|
||||
contentItem: RowLayout {
|
||||
contentItem: Row {
|
||||
id: replyRowLay
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
spacing: Nheko.paddingSmall
|
||||
|
||||
Rectangle {
|
||||
id: replyLine
|
||||
Layout.fillHeight: true
|
||||
height: replyCol.height
|
||||
color: replyRow.userColor
|
||||
Layout.preferredWidth: 4
|
||||
width: 4
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Column {
|
||||
spacing: 0
|
||||
|
||||
id: replyCol
|
||||
|
||||
AbstractButton {
|
||||
id: replyUserButton
|
||||
Layout.fillWidth: true
|
||||
contentItem: ElidedLabel {
|
||||
|
||||
contentItem: Label {
|
||||
id: userName_
|
||||
fullText: wrapper.reply?.userName ?? ''
|
||||
text: wrapper.reply?.userName ?? ''
|
||||
color: replyRow.userColor
|
||||
textFormat: Text.RichText
|
||||
width: parent.width
|
||||
elideWidth: width
|
||||
width: wrapper.maxWidth
|
||||
//elideWidth: wrapper.maxWidth
|
||||
}
|
||||
onClicked: wrapper.room.openUserProfile(wrapper.reply?.userId)
|
||||
}
|
||||
|
@ -239,7 +240,7 @@ TimelineEvent {
|
|||
}
|
||||
|
||||
background: Rectangle {
|
||||
width: replyRow.implicitContentWidth
|
||||
//width: replyRow.implicitContentWidth
|
||||
color: Qt.tint(palette.base, Qt.hsla(replyRow.userColor.hslHue, 0.5, replyRow.userColor.hslLightness, 0.1))
|
||||
}
|
||||
|
||||
|
@ -259,19 +260,16 @@ TimelineEvent {
|
|||
]
|
||||
}
|
||||
|
||||
Item {
|
||||
// spacer to fill width if needed
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
},
|
||||
RowLayout {
|
||||
id: metadata
|
||||
|
||||
property int iconSize: Math.floor(fontMetrics.ascent * scaling)
|
||||
property double scaling: Settings.bubbles ? 0.75 : 1
|
||||
|
||||
Layout.alignment: Qt.AlignTop | Qt.AlignRight
|
||||
Layout.preferredWidth: implicitWidth
|
||||
anchors.right: parent.right
|
||||
y: section.visible && section.active ? section.y + section.height : 0
|
||||
|
||||
spacing: 2
|
||||
visible: !isStateEvent
|
||||
|
||||
|
@ -339,8 +337,7 @@ TimelineEvent {
|
|||
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
Reactions {
|
||||
id: reactionRow
|
||||
|
||||
|
|
|
@ -620,7 +620,6 @@ Item {
|
|||
roleValue: MtxEvent.Member
|
||||
|
||||
ColumnLayout {
|
||||
width: parent?.width ?? 100
|
||||
|
||||
NoticeMessage {
|
||||
body: formatted
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
import ".."
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
//import QtQuick.Layouts
|
||||
import im.nheko
|
||||
|
||||
MatrixText {
|
||||
|
@ -41,7 +41,10 @@ MatrixText {
|
|||
}" : "") + // 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>")
|
||||
Layout.maximumHeight: !keepFullText ? Math.round(Math.min(timelineView.height / 8, implicitHeight)) : implicitHeight
|
||||
//Layout.maximumHeight: !keepFullText ? Math.round(Math.min(timelineView.height / 8, implicitHeight)) : implicitHeight
|
||||
|
||||
//EventDelegateChooser.fillWidth: true
|
||||
|
||||
clip: !keepFullText
|
||||
selectByMouse: !Settings.mobileMode && !isReply
|
||||
enabled: !Settings.mobileMode && !isReply
|
||||
|
|
|
@ -131,7 +131,7 @@ EventDelegateChooser::DelegateIncubator::setInitialState(QObject *obj)
|
|||
roleToPropIdx.insert(*role, i);
|
||||
roles.emplace_back(*role);
|
||||
|
||||
nhlog::ui()->critical("Found prop {}, idx {}, role {}", prop.name(), i, *role);
|
||||
// nhlog::ui()->critical("Found prop {}, idx {}, role {}", prop.name(), i, *role);
|
||||
} else {
|
||||
nhlog::ui()->critical("Required property {} not found in model!", prop.name());
|
||||
}
|
||||
|
@ -140,14 +140,15 @@ EventDelegateChooser::DelegateIncubator::setInitialState(QObject *obj)
|
|||
nhlog::ui()->debug("Querying data for id {}", currentId.toStdString());
|
||||
chooser.room_->multiData(currentId, forReply ? chooser.eventId_ : QString(), roles);
|
||||
|
||||
Qt::beginPropertyUpdateGroup();
|
||||
for (const auto &role : roles) {
|
||||
const auto &roleName = roleNames[role.role()];
|
||||
nhlog::ui()->critical("Setting role {}, {} to {}",
|
||||
role.role(),
|
||||
roleName.toStdString(),
|
||||
role.data().toString().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());
|
||||
// nhlog::ui()->critical("Setting {}", mo->property(roleToPropIdx[role.role()]).name());
|
||||
mo->property(roleToPropIdx[role.role()]).write(obj, role.data());
|
||||
|
||||
if (const auto &req = requiredProperties.find(roleName); req != requiredProperties.end())
|
||||
|
@ -156,14 +157,15 @@ EventDelegateChooser::DelegateIncubator::setInitialState(QObject *obj)
|
|||
|
||||
if (isReplyNeeded) {
|
||||
const auto roleName = QByteArray("isReply");
|
||||
nhlog::ui()->critical("Setting role {} to {}", roleName.toStdString(), forReply);
|
||||
// nhlog::ui()->critical("Setting role {} to {}", roleName.toStdString(), forReply);
|
||||
|
||||
nhlog::ui()->critical("Setting {}", mo->property(roleToPropIdx[-1]).name());
|
||||
// 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);
|
||||
|
||||
|
@ -188,9 +190,12 @@ EventDelegateChooser::DelegateIncubator::setInitialState(QObject *obj)
|
|||
auto mo = obj->metaObject();
|
||||
chooser.room_->multiData(
|
||||
currentId, forReply ? chooser.eventId_ : QString(), rolesToRequest);
|
||||
|
||||
Qt::beginPropertyUpdateGroup();
|
||||
for (const auto &role : rolesToRequest) {
|
||||
mo->property(roleToPropIdx[role.role()]).write(obj, role.data());
|
||||
}
|
||||
Qt::endPropertyUpdateGroup();
|
||||
};
|
||||
|
||||
if (!forReply) {
|
||||
|
@ -257,11 +262,22 @@ EventDelegateChooser::DelegateIncubator::statusChanged(QQmlIncubator::Status sta
|
|||
|
||||
child->setParentItem(&chooser);
|
||||
QQmlEngine::setObjectOwnership(child, QQmlEngine::ObjectOwnership::JavaScriptOwnership);
|
||||
|
||||
// connect(child, &QQuickItem::parentChanged, child, [child](QQuickItem *) {
|
||||
// // QTBUG-115687
|
||||
// if (child->flags().testFlag(QQuickItem::ItemObservesViewport)) {
|
||||
// nhlog::ui()->critical("SETTING OBSERVES VIEWPORT");
|
||||
// // Re-trigger the parent traversal to get subtreeTransformChangedEnabled turned
|
||||
// on child->setFlag(QQuickItem::ItemObservesViewport);
|
||||
// }
|
||||
// });
|
||||
|
||||
if (forReply)
|
||||
emit chooser.replyChanged();
|
||||
else
|
||||
emit chooser.mainChanged();
|
||||
|
||||
chooser.polish();
|
||||
} else if (status == QQmlIncubator::Error) {
|
||||
auto errors_ = errors();
|
||||
for (const auto &e : qAsConst(errors_))
|
||||
|
@ -269,3 +285,49 @@ EventDelegateChooser::DelegateIncubator::statusChanged(QQmlIncubator::Status sta
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
EventDelegateChooser::updatePolish()
|
||||
{
|
||||
auto mainChild = qobject_cast<QQuickItem *>(eventIncubator.object());
|
||||
auto replyChild = qobject_cast<QQuickItem *>(replyIncubator.object());
|
||||
|
||||
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);
|
||||
|
||||
// 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 (width > maxWidth_ || attached->fillWidth())
|
||||
width = maxWidth_;
|
||||
|
||||
replyChild->setWidth(width);
|
||||
replyChild->ensurePolished();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,6 @@
|
|||
//
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// A DelegateChooser like the one, that was added to Qt5.12 (in labs), but compatible with older Qt
|
||||
// versions see KDE/kquickitemviews see qtdeclarative/qqmldelagatecomponent
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
|
@ -17,6 +14,32 @@
|
|||
|
||||
#include "TimelineModel.h"
|
||||
|
||||
class EventDelegateChooserAttachedType : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool fillWidth READ fillWidth WRITE setFillWidth NOTIFY fillWidthChanged)
|
||||
QML_ANONYMOUS
|
||||
public:
|
||||
EventDelegateChooserAttachedType(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
bool fillWidth() const { return fillWidth_; }
|
||||
void setFillWidth(bool fill)
|
||||
{
|
||||
fillWidth_ = fill;
|
||||
emit fillWidthChanged();
|
||||
}
|
||||
signals:
|
||||
void fillWidthChanged();
|
||||
|
||||
private:
|
||||
bool fillWidth_ = false, keepAspectRatio = false;
|
||||
double aspectRatio = 1.;
|
||||
int maxWidth = -1;
|
||||
int maxHeight = -1;
|
||||
};
|
||||
|
||||
class EventDelegateChoice : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -51,14 +74,18 @@ class EventDelegateChooser : public QQuickItem
|
|||
QML_ELEMENT
|
||||
Q_CLASSINFO("DefaultProperty", "choices")
|
||||
|
||||
public:
|
||||
QML_ATTACHED(EventDelegateChooserAttachedType)
|
||||
|
||||
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(QString eventId READ eventId WRITE setEventId NOTIFY eventIdChanged REQUIRED FINAL)
|
||||
Q_PROPERTY(QString replyTo READ replyTo WRITE setReplyTo NOTIFY replyToChanged REQUIRED FINAL)
|
||||
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)
|
||||
|
||||
public:
|
||||
QQmlListProperty<EventDelegateChoice> choices();
|
||||
|
||||
[[nodiscard]] QQuickItem *main() const
|
||||
|
@ -70,6 +97,20 @@ public:
|
|||
return qobject_cast<QQuickItem *>(replyIncubator.object());
|
||||
}
|
||||
|
||||
bool sameWidth() const { return sameWidth_; }
|
||||
void setSameWidth(bool width)
|
||||
{
|
||||
sameWidth_ = width;
|
||||
emit sameWidthChanged();
|
||||
}
|
||||
bool maxWidth() const { return maxWidth_; }
|
||||
void setMaxWidth(int width)
|
||||
{
|
||||
maxWidth_ = width;
|
||||
emit maxWidthChanged();
|
||||
polish();
|
||||
}
|
||||
|
||||
void setRoom(TimelineModel *m)
|
||||
{
|
||||
if (m != room_) {
|
||||
|
@ -105,12 +146,21 @@ public:
|
|||
|
||||
void componentComplete() override;
|
||||
|
||||
static EventDelegateChooserAttachedType *qmlAttachedProperties(QObject *object)
|
||||
{
|
||||
return new EventDelegateChooserAttachedType(object);
|
||||
}
|
||||
|
||||
void updatePolish() override;
|
||||
|
||||
signals:
|
||||
void mainChanged();
|
||||
void replyChanged();
|
||||
void roomChanged();
|
||||
void eventIdChanged();
|
||||
void replyToChanged();
|
||||
void sameWidthChanged();
|
||||
void maxWidthChanged();
|
||||
|
||||
private:
|
||||
struct DelegateIncubator final : public QQmlIncubator
|
||||
|
@ -142,6 +192,8 @@ private:
|
|||
TimelineModel *room_{nullptr};
|
||||
QString eventId_;
|
||||
QString replyId;
|
||||
bool sameWidth_ = false;
|
||||
int maxWidth_ = 400;
|
||||
|
||||
static void appendChoice(QQmlListProperty<EventDelegateChoice> *, EventDelegateChoice *);
|
||||
static qsizetype choiceCount(QQmlListProperty<EventDelegateChoice> *);
|
||||
|
|
|
@ -601,12 +601,8 @@ TimelineModel::data(const mtx::events::collections::TimelineEvents &event, int r
|
|||
case UserName:
|
||||
return QVariant(displayName(QString::fromStdString(acc::sender(event))));
|
||||
case UserPowerlevel: {
|
||||
return static_cast<qlonglong>(mtx::events::state::PowerLevels{
|
||||
cache::client()
|
||||
->getStateEvent<mtx::events::state::PowerLevels>(room_id_.toStdString())
|
||||
.value_or(mtx::events::StateEvent<mtx::events::state::PowerLevels>{})
|
||||
.content}
|
||||
.user_level(acc::sender(event)));
|
||||
return static_cast<qlonglong>(
|
||||
permissions_.powerlevelEvent().user_level(acc::sender(event)));
|
||||
}
|
||||
|
||||
case Day: {
|
||||
|
|
Loading…
Reference in a new issue