mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-22 19:08:58 +03:00
Implement TextMessage delegate
Text selection over multiple items doesn't work yet
This commit is contained in:
parent
56e27ced25
commit
34f5400e99
6 changed files with 243 additions and 11 deletions
|
@ -1,7 +1,9 @@
|
||||||
import QtQuick 2.5
|
import QtQuick 2.6
|
||||||
import QtQuick.Controls 2.5
|
import QtQuick.Controls 2.5
|
||||||
import QtQuick.Layouts 1.5
|
import QtQuick.Layouts 1.5
|
||||||
|
|
||||||
|
import com.github.nheko 1.0
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
|
@ -26,20 +28,43 @@ Rectangle {
|
||||||
}
|
}
|
||||||
|
|
||||||
model: timelineManager.timeline
|
model: timelineManager.timeline
|
||||||
|
spacing: 4
|
||||||
delegate: RowLayout {
|
delegate: RowLayout {
|
||||||
anchors.leftMargin: 52
|
anchors.leftMargin: 52
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.rightMargin: scrollbar.width
|
anchors.rightMargin: scrollbar.width
|
||||||
|
|
||||||
Text {
|
Loader {
|
||||||
|
id: loader
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
height: contentHeight
|
height: item.height
|
||||||
text: "Event content"
|
Layout.alignment: Qt.AlignTop
|
||||||
|
|
||||||
|
source: switch(model.type) {
|
||||||
|
case MtxEvent.Aliases: return "delegates/Aliases.qml"
|
||||||
|
case MtxEvent.Avatar: return "delegates/Avatar.qml"
|
||||||
|
case MtxEvent.CanonicalAlias: return "delegates/CanonicalAlias.qml"
|
||||||
|
case MtxEvent.Create: return "delegates/Create.qml"
|
||||||
|
case MtxEvent.GuestAccess: return "delegates/GuestAccess.qml"
|
||||||
|
case MtxEvent.HistoryVisibility: return "delegates/HistoryVisibility.qml"
|
||||||
|
case MtxEvent.JoinRules: return "delegates/JoinRules.qml"
|
||||||
|
case MtxEvent.Member: return "delegates/Member.qml"
|
||||||
|
case MtxEvent.Name: return "delegates/Name.qml"
|
||||||
|
case MtxEvent.PowerLevels: return "delegates/PowerLevels.qml"
|
||||||
|
case MtxEvent.Topic: return "delegates/Topic.qml"
|
||||||
|
case MtxEvent.NoticeMessage: return "delegates/NoticeMessage.qml"
|
||||||
|
case MtxEvent.TextMessage: return "delegates/TextMessage.qml"
|
||||||
|
case MtxEvent.ImageMessage: return "delegates/ImageMessage.qml"
|
||||||
|
case MtxEvent.VideoMessage: return "delegates/VideoMessage.qml"
|
||||||
|
default: return "delegates/placeholder.qml"
|
||||||
|
}
|
||||||
|
property variant eventData: model
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Button {
|
Button {
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||||
id: replyButton
|
id: replyButton
|
||||||
flat: true
|
flat: true
|
||||||
height: replyButtonImg.contentHeight
|
height: replyButtonImg.contentHeight
|
||||||
|
@ -54,7 +79,7 @@ Rectangle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Button {
|
Button {
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||||
id: optionsButton
|
id: optionsButton
|
||||||
flat: true
|
flat: true
|
||||||
height: optionsButtonImg.contentHeight
|
height: optionsButtonImg.contentHeight
|
||||||
|
@ -90,7 +115,7 @@ Rectangle {
|
||||||
}
|
}
|
||||||
|
|
||||||
Text {
|
Text {
|
||||||
Layout.alignment: Qt.AlignRight
|
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||||
text: model.timestamp.toLocaleTimeString("HH:mm")
|
text: model.timestamp.toLocaleTimeString("HH:mm")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -98,13 +123,18 @@ Rectangle {
|
||||||
section {
|
section {
|
||||||
property: "section"
|
property: "section"
|
||||||
delegate: Column {
|
delegate: Column {
|
||||||
|
topPadding: 4
|
||||||
|
bottomPadding: 4
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: dateBubble.visible ? dateBubble.height + userName.height : userName.height
|
|
||||||
Label {
|
Label {
|
||||||
id: dateBubble
|
id: dateBubble
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
visible: section.includes(" ")
|
visible: section.includes(" ")
|
||||||
text: chat.model.formatDateSeparator(new Date(Number(section.split(" ")[1])))
|
text: chat.model.formatDateSeparator(new Date(Number(section.split(" ")[1])))
|
||||||
|
|
||||||
height: contentHeight * 1.2
|
height: contentHeight * 1.2
|
||||||
width: contentWidth * 1.2
|
width: contentWidth * 1.2
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
@ -114,6 +144,7 @@ Rectangle {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Row {
|
Row {
|
||||||
|
height: userName.height
|
||||||
spacing: 4
|
spacing: 4
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: 48
|
width: 48
|
||||||
|
|
10
resources/qml/delegates/TextMessage.qml
Normal file
10
resources/qml/delegates/TextMessage.qml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import QtQuick 2.5
|
||||||
|
|
||||||
|
TextEdit {
|
||||||
|
text: eventData.formattedBody
|
||||||
|
textFormat: TextEdit.RichText
|
||||||
|
readOnly: true
|
||||||
|
wrapMode: Text.Wrap
|
||||||
|
width: parent.width
|
||||||
|
selectByMouse: true
|
||||||
|
}
|
|
@ -116,5 +116,6 @@
|
||||||
</qresource>
|
</qresource>
|
||||||
<qresource prefix="/">
|
<qresource prefix="/">
|
||||||
<file>qml/TimelineView.qml</file>
|
<file>qml/TimelineView.qml</file>
|
||||||
|
<file>qml/delegates/TextMessage.qml</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
#include "TimelineModel.h"
|
#include "TimelineModel.h"
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
|
|
||||||
#include "Logging.h"
|
#include "Logging.h"
|
||||||
|
@ -31,6 +33,119 @@ eventTimestamp(const T &event)
|
||||||
{
|
{
|
||||||
return QDateTime::fromMSecsSinceEpoch(event.origin_server_ts);
|
return QDateTime::fromMSecsSinceEpoch(event.origin_server_ts);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
QString
|
||||||
|
eventFormattedBody(const mtx::events::Event<T> &)
|
||||||
|
{
|
||||||
|
return QString("");
|
||||||
|
}
|
||||||
|
template<class T>
|
||||||
|
auto
|
||||||
|
eventFormattedBody(const mtx::events::RoomEvent<T> &e)
|
||||||
|
-> std::enable_if_t<std::is_same<decltype(e.content.formatted_body), std::string>::value, QString>
|
||||||
|
{
|
||||||
|
auto temp = e.content.formatted_body;
|
||||||
|
if (!temp.empty()) {
|
||||||
|
auto pos = temp.find("<mx-reply>");
|
||||||
|
if (pos != std::string::npos)
|
||||||
|
temp.erase(pos, std::string("<mx-reply>").size());
|
||||||
|
pos = temp.find("</mx-reply>");
|
||||||
|
if (pos != std::string::npos)
|
||||||
|
temp.erase(pos, std::string("</mx-reply>").size());
|
||||||
|
return QString::fromStdString(temp);
|
||||||
|
} else
|
||||||
|
return QString::fromStdString(e.content.body);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
qml_mtx_events::EventType
|
||||||
|
toRoomEventType(const mtx::events::Event<T> &e)
|
||||||
|
{
|
||||||
|
using mtx::events::EventType;
|
||||||
|
switch (e.type) {
|
||||||
|
case EventType::RoomKeyRequest:
|
||||||
|
return qml_mtx_events::EventType::KeyRequest;
|
||||||
|
case EventType::RoomAliases:
|
||||||
|
return qml_mtx_events::EventType::Aliases;
|
||||||
|
case EventType::RoomAvatar:
|
||||||
|
return qml_mtx_events::EventType::Avatar;
|
||||||
|
case EventType::RoomCanonicalAlias:
|
||||||
|
return qml_mtx_events::EventType::CanonicalAlias;
|
||||||
|
case EventType::RoomCreate:
|
||||||
|
return qml_mtx_events::EventType::Create;
|
||||||
|
case EventType::RoomEncrypted:
|
||||||
|
return qml_mtx_events::EventType::Encrypted;
|
||||||
|
case EventType::RoomEncryption:
|
||||||
|
return qml_mtx_events::EventType::Encryption;
|
||||||
|
case EventType::RoomGuestAccess:
|
||||||
|
return qml_mtx_events::EventType::GuestAccess;
|
||||||
|
case EventType::RoomHistoryVisibility:
|
||||||
|
return qml_mtx_events::EventType::HistoryVisibility;
|
||||||
|
case EventType::RoomJoinRules:
|
||||||
|
return qml_mtx_events::EventType::JoinRules;
|
||||||
|
case EventType::RoomMember:
|
||||||
|
return qml_mtx_events::EventType::Member;
|
||||||
|
case EventType::RoomMessage:
|
||||||
|
return qml_mtx_events::EventType::UnknownMessage;
|
||||||
|
case EventType::RoomName:
|
||||||
|
return qml_mtx_events::EventType::Name;
|
||||||
|
case EventType::RoomPowerLevels:
|
||||||
|
return qml_mtx_events::EventType::PowerLevels;
|
||||||
|
case EventType::RoomTopic:
|
||||||
|
return qml_mtx_events::EventType::Topic;
|
||||||
|
case EventType::RoomTombstone:
|
||||||
|
return qml_mtx_events::EventType::Tombstone;
|
||||||
|
case EventType::RoomRedaction:
|
||||||
|
return qml_mtx_events::EventType::Redaction;
|
||||||
|
case EventType::RoomPinnedEvents:
|
||||||
|
return qml_mtx_events::EventType::PinnedEvents;
|
||||||
|
case EventType::Sticker:
|
||||||
|
return qml_mtx_events::EventType::Sticker;
|
||||||
|
case EventType::Tag:
|
||||||
|
return qml_mtx_events::EventType::Tag;
|
||||||
|
case EventType::Unsupported:
|
||||||
|
default:
|
||||||
|
return qml_mtx_events::EventType::Unsupported;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
qml_mtx_events::EventType
|
||||||
|
toRoomEventType(const mtx::events::Event<mtx::events::msg::Audio> &)
|
||||||
|
{
|
||||||
|
return qml_mtx_events::EventType::AudioMessage;
|
||||||
|
}
|
||||||
|
qml_mtx_events::EventType
|
||||||
|
toRoomEventType(const mtx::events::Event<mtx::events::msg::Emote> &)
|
||||||
|
{
|
||||||
|
return qml_mtx_events::EventType::EmoteMessage;
|
||||||
|
}
|
||||||
|
qml_mtx_events::EventType
|
||||||
|
toRoomEventType(const mtx::events::Event<mtx::events::msg::File> &)
|
||||||
|
{
|
||||||
|
return qml_mtx_events::EventType::FileMessage;
|
||||||
|
}
|
||||||
|
qml_mtx_events::EventType
|
||||||
|
toRoomEventType(const mtx::events::Event<mtx::events::msg::Image> &)
|
||||||
|
{
|
||||||
|
return qml_mtx_events::EventType::ImageMessage;
|
||||||
|
}
|
||||||
|
qml_mtx_events::EventType
|
||||||
|
toRoomEventType(const mtx::events::Event<mtx::events::msg::Notice> &)
|
||||||
|
{
|
||||||
|
return qml_mtx_events::EventType::NoticeMessage;
|
||||||
|
}
|
||||||
|
qml_mtx_events::EventType
|
||||||
|
toRoomEventType(const mtx::events::Event<mtx::events::msg::Text> &)
|
||||||
|
{
|
||||||
|
return qml_mtx_events::EventType::TextMessage;
|
||||||
|
}
|
||||||
|
qml_mtx_events::EventType
|
||||||
|
toRoomEventType(const mtx::events::Event<mtx::events::msg::Video> &)
|
||||||
|
{
|
||||||
|
return qml_mtx_events::EventType::VideoMessage;
|
||||||
|
}
|
||||||
|
// ::EventType::Type toRoomEventType(const Event<mtx::events::msg::Location> &e) { return
|
||||||
|
// ::EventType::LocationMessage; }
|
||||||
}
|
}
|
||||||
|
|
||||||
TimelineModel::TimelineModel(QString room_id, QObject *parent)
|
TimelineModel::TimelineModel(QString room_id, QObject *parent)
|
||||||
|
@ -105,6 +220,14 @@ TimelineModel::data(const QModelIndex &index, int role) const
|
||||||
case Timestamp:
|
case Timestamp:
|
||||||
return QVariant(boost::apply_visitor(
|
return QVariant(boost::apply_visitor(
|
||||||
[](const auto &e) -> QDateTime { return eventTimestamp(e); }, events.value(id)));
|
[](const auto &e) -> QDateTime { return eventTimestamp(e); }, events.value(id)));
|
||||||
|
case Type:
|
||||||
|
return QVariant(boost::apply_visitor(
|
||||||
|
[](const auto &e) -> qml_mtx_events::EventType { return toRoomEventType(e); },
|
||||||
|
events.value(id)));
|
||||||
|
case FormattedBody:
|
||||||
|
return QVariant(utils::replaceEmoji(boost::apply_visitor(
|
||||||
|
[](const auto &e) -> QString { return eventFormattedBody(e); },
|
||||||
|
events.value(id))));
|
||||||
default:
|
default:
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,65 @@
|
||||||
|
|
||||||
#include <mtx/responses.hpp>
|
#include <mtx/responses.hpp>
|
||||||
|
|
||||||
|
namespace qml_mtx_events {
|
||||||
|
Q_NAMESPACE
|
||||||
|
|
||||||
|
enum EventType
|
||||||
|
{
|
||||||
|
// Unsupported event
|
||||||
|
Unsupported,
|
||||||
|
/// m.room_key_request
|
||||||
|
KeyRequest,
|
||||||
|
/// m.room.aliases
|
||||||
|
Aliases,
|
||||||
|
/// m.room.avatar
|
||||||
|
Avatar,
|
||||||
|
/// m.room.canonical_alias
|
||||||
|
CanonicalAlias,
|
||||||
|
/// m.room.create
|
||||||
|
Create,
|
||||||
|
/// m.room.encrypted.
|
||||||
|
Encrypted,
|
||||||
|
/// m.room.encryption.
|
||||||
|
Encryption,
|
||||||
|
/// m.room.guest_access
|
||||||
|
GuestAccess,
|
||||||
|
/// m.room.history_visibility
|
||||||
|
HistoryVisibility,
|
||||||
|
/// m.room.join_rules
|
||||||
|
JoinRules,
|
||||||
|
/// m.room.member
|
||||||
|
Member,
|
||||||
|
/// m.room.name
|
||||||
|
Name,
|
||||||
|
/// m.room.power_levels
|
||||||
|
PowerLevels,
|
||||||
|
/// m.room.tombstone
|
||||||
|
Tombstone,
|
||||||
|
/// m.room.topic
|
||||||
|
Topic,
|
||||||
|
/// m.room.redaction
|
||||||
|
Redaction,
|
||||||
|
/// m.room.pinned_events
|
||||||
|
PinnedEvents,
|
||||||
|
// m.sticker
|
||||||
|
Sticker,
|
||||||
|
// m.tag
|
||||||
|
Tag,
|
||||||
|
/// m.room.message
|
||||||
|
AudioMessage,
|
||||||
|
EmoteMessage,
|
||||||
|
FileMessage,
|
||||||
|
ImageMessage,
|
||||||
|
LocationMessage,
|
||||||
|
NoticeMessage,
|
||||||
|
TextMessage,
|
||||||
|
VideoMessage,
|
||||||
|
UnknownMessage,
|
||||||
|
};
|
||||||
|
Q_ENUM_NS(EventType)
|
||||||
|
}
|
||||||
|
|
||||||
class TimelineModel : public QAbstractListModel
|
class TimelineModel : public QAbstractListModel
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -26,8 +85,8 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
QHash<int, QByteArray> roleNames() const override;
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
int rowCount(const QModelIndex &parent = QModelIndex()) const;
|
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||||
|
|
||||||
Q_INVOKABLE QColor userColor(QString id, QColor background);
|
Q_INVOKABLE QColor userColor(QString id, QColor background);
|
||||||
Q_INVOKABLE QString displayName(QString id) const;
|
Q_INVOKABLE QString displayName(QString id) const;
|
||||||
|
@ -40,6 +99,7 @@ public slots:
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
// Add old events at the top of the timeline.
|
// Add old events at the top of the timeline.
|
||||||
|
|
||||||
void addBackwardsEvents(const mtx::responses::Messages &msgs);
|
void addBackwardsEvents(const mtx::responses::Messages &msgs);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
|
|
@ -7,6 +7,13 @@
|
||||||
|
|
||||||
TimelineViewManager::TimelineViewManager(QWidget *parent)
|
TimelineViewManager::TimelineViewManager(QWidget *parent)
|
||||||
{
|
{
|
||||||
|
qmlRegisterUncreatableMetaObject(qml_mtx_events::staticMetaObject,
|
||||||
|
"com.github.nheko",
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
"MtxEvent",
|
||||||
|
"Can't instantiate enum!");
|
||||||
|
|
||||||
view = new QQuickView();
|
view = new QQuickView();
|
||||||
container = QWidget::createWindowContainer(view, parent);
|
container = QWidget::createWindowContainer(view, parent);
|
||||||
container->setMinimumSize(200, 200);
|
container->setMinimumSize(200, 200);
|
||||||
|
|
Loading…
Reference in a new issue