Implement TextMessage delegate

Text selection over multiple items doesn't work yet
This commit is contained in:
Nicolas Werner 2019-09-02 23:28:05 +02:00
parent 56e27ced25
commit 34f5400e99
6 changed files with 243 additions and 11 deletions

View file

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

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

View file

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

View file

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

View file

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

View file

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