mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-25 12:38:48 +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.Layouts 1.5
|
||||
|
||||
import com.github.nheko 1.0
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
|
||||
|
@ -26,20 +28,43 @@ Rectangle {
|
|||
}
|
||||
|
||||
model: timelineManager.timeline
|
||||
spacing: 4
|
||||
delegate: RowLayout {
|
||||
anchors.leftMargin: 52
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: scrollbar.width
|
||||
|
||||
Text {
|
||||
Loader {
|
||||
id: loader
|
||||
Layout.fillWidth: true
|
||||
height: contentHeight
|
||||
text: "Event content"
|
||||
height: item.height
|
||||
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 {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||
id: replyButton
|
||||
flat: true
|
||||
height: replyButtonImg.contentHeight
|
||||
|
@ -54,7 +79,7 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
Button {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||
id: optionsButton
|
||||
flat: true
|
||||
height: optionsButtonImg.contentHeight
|
||||
|
@ -90,7 +115,7 @@ Rectangle {
|
|||
}
|
||||
|
||||
Text {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignTop
|
||||
text: model.timestamp.toLocaleTimeString("HH:mm")
|
||||
}
|
||||
}
|
||||
|
@ -98,13 +123,18 @@ Rectangle {
|
|||
section {
|
||||
property: "section"
|
||||
delegate: Column {
|
||||
topPadding: 4
|
||||
bottomPadding: 4
|
||||
spacing: 8
|
||||
|
||||
width: parent.width
|
||||
height: dateBubble.visible ? dateBubble.height + userName.height : userName.height
|
||||
|
||||
Label {
|
||||
id: dateBubble
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
visible: section.includes(" ")
|
||||
text: chat.model.formatDateSeparator(new Date(Number(section.split(" ")[1])))
|
||||
|
||||
height: contentHeight * 1.2
|
||||
width: contentWidth * 1.2
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
|
@ -114,6 +144,7 @@ Rectangle {
|
|||
}
|
||||
}
|
||||
Row {
|
||||
height: userName.height
|
||||
spacing: 4
|
||||
Rectangle {
|
||||
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 prefix="/">
|
||||
<file>qml/TimelineView.qml</file>
|
||||
<file>qml/delegates/TextMessage.qml</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#include "TimelineModel.h"
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include <QRegularExpression>
|
||||
|
||||
#include "Logging.h"
|
||||
|
@ -31,6 +33,119 @@ eventTimestamp(const T &event)
|
|||
{
|
||||
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)
|
||||
|
@ -105,6 +220,14 @@ TimelineModel::data(const QModelIndex &index, int role) const
|
|||
case Timestamp:
|
||||
return QVariant(boost::apply_visitor(
|
||||
[](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:
|
||||
return QVariant();
|
||||
}
|
||||
|
|
|
@ -7,6 +7,65 @@
|
|||
|
||||
#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
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -26,8 +85,8 @@ public:
|
|||
};
|
||||
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const;
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
Q_INVOKABLE QColor userColor(QString id, QColor background);
|
||||
Q_INVOKABLE QString displayName(QString id) const;
|
||||
|
@ -40,6 +99,7 @@ public slots:
|
|||
|
||||
private slots:
|
||||
// Add old events at the top of the timeline.
|
||||
|
||||
void addBackwardsEvents(const mtx::responses::Messages &msgs);
|
||||
|
||||
signals:
|
||||
|
|
|
@ -7,6 +7,13 @@
|
|||
|
||||
TimelineViewManager::TimelineViewManager(QWidget *parent)
|
||||
{
|
||||
qmlRegisterUncreatableMetaObject(qml_mtx_events::staticMetaObject,
|
||||
"com.github.nheko",
|
||||
1,
|
||||
0,
|
||||
"MtxEvent",
|
||||
"Can't instantiate enum!");
|
||||
|
||||
view = new QQuickView();
|
||||
container = QWidget::createWindowContainer(view, parent);
|
||||
container->setMinimumSize(200, 200);
|
||||
|
|
Loading…
Reference in a new issue