From 8727831de7308ee5cf202c9b20a7bbf916405b2a Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Sat, 7 Sep 2019 02:01:44 +0200 Subject: Fix QML emojis --- src/Utils.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'src/Utils.cpp') diff --git a/src/Utils.cpp b/src/Utils.cpp index 8c02b1c2..d458dbcc 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -40,9 +40,8 @@ utils::replaceEmoji(const QString &body) for (auto &code : utf32_string) { // TODO: Be more precise here. if (code > 9000) - fmtBody += - QString("") + - QString::fromUcs4(&code, 1) + ""; + fmtBody += QString("") + + QString::fromUcs4(&code, 1) + ""; else fmtBody += QString::fromUcs4(&code, 1); } -- cgit 1.5.1 From cff46d97a8636f41dd5ac2ace9dc00ecb5f4c51c Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Thu, 17 Oct 2019 09:36:16 +0200 Subject: Add native themeing to QML (where possible) --- resources/qml/TimelineView.qml | 5 ++-- resources/qml/delegates/TimelineRow.qml | 28 +++++++++++++++------ src/Utils.cpp | 36 +++++++++++++++++---------- src/timeline2/TimelineViewManager.cpp | 44 +++++++++++++++++++++++++++++++++ src/timeline2/TimelineViewManager.h | 1 + 5 files changed, 91 insertions(+), 23 deletions(-) (limited to 'src/Utils.cpp') diff --git a/resources/qml/TimelineView.qml b/resources/qml/TimelineView.qml index b0a8853e..d1ada3ea 100644 --- a/resources/qml/TimelineView.qml +++ b/resources/qml/TimelineView.qml @@ -12,8 +12,9 @@ import "./delegates" Rectangle { anchors.fill: parent - SystemPalette { id: colors; colorGroup: SystemPalette.Active } - SystemPalette { id: inactiveColors; colorGroup: SystemPalette.Disabled } + property var colors: currentActivePalette + property var systemInactive: SystemPalette { colorGroup: SystemPalette.Disabled } + property var inactiveColors: currentInactivePalette ? currentInactivePalette : systemInactive property int avatarSize: 32 color: colors.window diff --git a/resources/qml/delegates/TimelineRow.qml b/resources/qml/delegates/TimelineRow.qml index 28a2ec8c..3019deb1 100644 --- a/resources/qml/delegates/TimelineRow.qml +++ b/resources/qml/delegates/TimelineRow.qml @@ -1,5 +1,5 @@ import QtQuick 2.6 -import QtQuick.Controls 2.1 +import QtQuick.Controls 2.3 import QtQuick.Layouts 1.2 import QtGraphicalEffects 1.0 import QtQuick.Window 2.2 @@ -48,8 +48,12 @@ RowLayout { id: replyButton flat: true Layout.preferredHeight: 16 - ToolTip.visible: hovered - ToolTip.text: qsTr("Reply") + + ToolTip { + visible: replyButton.hovered + text: qsTr("Reply") + palette: colors + } // disable background, because we don't want a border on hover background: Item { @@ -74,8 +78,12 @@ RowLayout { id: optionsButton flat: true Layout.preferredHeight: 16 - ToolTip.visible: hovered - ToolTip.text: qsTr("Options") + + ToolTip { + visible: optionsButton.hovered + text: qsTr("Options") + palette: colors + } // disable background, because we don't want a border on hover background: Item { @@ -98,6 +106,7 @@ RowLayout { Menu { y: optionsButton.height id: contextMenu + palette: colors MenuItem { text: qsTr("Read receipts") @@ -127,13 +136,16 @@ RowLayout { text: model.timestamp.toLocaleTimeString("HH:mm") color: inactiveColors.text - ToolTip.visible: ma.containsMouse - ToolTip.text: Qt.formatDateTime(model.timestamp, Qt.DefaultLocaleLongDate) - MouseArea{ id: ma anchors.fill: parent hoverEnabled: true } + + ToolTip { + visible: ma.containsMouse + text: Qt.formatDateTime(model.timestamp, Qt.DefaultLocaleLongDate) + palette: colors + } } } diff --git a/src/Utils.cpp b/src/Utils.cpp index d458dbcc..5a1447ac 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -323,19 +323,29 @@ utils::linkifyMessage(const QString &body) return doc; } -QByteArray escapeRawHtml(const QByteArray &data) { - QByteArray buffer; - const size_t length = data.size(); - buffer.reserve(length); - for(size_t pos = 0; pos != length; ++pos) { - switch(data.at(pos)) { - case '&': buffer.append("&"); break; - case '<': buffer.append("<"); break; - case '>': buffer.append(">"); break; - default: buffer.append(data.at(pos)); break; - } - } - return buffer; +QByteArray +escapeRawHtml(const QByteArray &data) +{ + QByteArray buffer; + const size_t length = data.size(); + buffer.reserve(length); + for (size_t pos = 0; pos != length; ++pos) { + switch (data.at(pos)) { + case '&': + buffer.append("&"); + break; + case '<': + buffer.append("<"); + break; + case '>': + buffer.append(">"); + break; + default: + buffer.append(data.at(pos)); + break; + } + } + return buffer; } QString diff --git a/src/timeline2/TimelineViewManager.cpp b/src/timeline2/TimelineViewManager.cpp index 13025864..057f03de 100644 --- a/src/timeline2/TimelineViewManager.cpp +++ b/src/timeline2/TimelineViewManager.cpp @@ -3,13 +3,51 @@ #include #include #include +#include #include #include +#include "ChatPage.h" #include "Logging.h" #include "MxcImageProvider.h" +#include "UserSettingsPage.h" #include "dialogs/ImageOverlay.h" +void +TimelineViewManager::updateColorPalette() +{ + UserSettings settings; + if (settings.theme() == "light") { + QPalette lightActive(/*windowText*/ QColor("#333"), + /*button*/ QColor("#333"), + /*light*/ QColor(), + /*dark*/ QColor(220, 220, 220, 120), + /*mid*/ QColor(), + /*text*/ QColor("#333"), + /*bright_text*/ QColor(), + /*base*/ QColor("white"), + /*window*/ QColor("white")); + view->rootContext()->setContextProperty("currentActivePalette", lightActive); + view->rootContext()->setContextProperty("currentInactivePalette", lightActive); + } else if (settings.theme() == "dark") { + QPalette darkActive(/*windowText*/ QColor("#caccd1"), + /*button*/ QColor("#caccd1"), + /*light*/ QColor(), + /*dark*/ QColor(45, 49, 57, 120), + /*mid*/ QColor(), + /*text*/ QColor("#caccd1"), + /*bright_text*/ QColor(), + /*base*/ QColor("#202228"), + /*window*/ QColor("#202228")); + darkActive.setColor(QPalette::Highlight, QColor("#e7e7e9")); + view->rootContext()->setContextProperty("currentActivePalette", darkActive); + view->rootContext()->setContextProperty("currentInactivePalette", darkActive); + } else { + view->rootContext()->setContextProperty("currentActivePalette", QPalette()); + view->rootContext()->setContextProperty("currentInactivePalette", nullptr); + } +} + TimelineViewManager::TimelineViewManager(QWidget *parent) : imgProvider(new MxcImageProvider()) { @@ -23,8 +61,14 @@ TimelineViewManager::TimelineViewManager(QWidget *parent) container = QWidget::createWindowContainer(view, parent); container->setMinimumSize(200, 200); view->rootContext()->setContextProperty("timelineManager", this); + updateColorPalette(); view->engine()->addImageProvider("MxcImage", imgProvider); view->setSource(QUrl("qrc:///qml/TimelineView.qml")); + + connect(dynamic_cast(parent), + &ChatPage::themeChanged, + this, + &TimelineViewManager::updateColorPalette); } void diff --git a/src/timeline2/TimelineViewManager.h b/src/timeline2/TimelineViewManager.h index 6a6d3c6b..b14e78ff 100644 --- a/src/timeline2/TimelineViewManager.h +++ b/src/timeline2/TimelineViewManager.h @@ -71,6 +71,7 @@ public slots: void initWithMessages(const std::map &msgs); void setHistoryView(const QString &room_id); + void updateColorPalette(); void queueTextMessage(const QString &msg); void queueReplyMessage(const QString &reply, const RelatedInfo &related); -- cgit 1.5.1 From 2c37beba8dc5b32e843010ba117abb84951c820b Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Sat, 2 Nov 2019 18:17:06 +0100 Subject: Fix translation of roomlist message preview This also makes long messages unreadable, because we don't shorten long usernames anymore. We may eventually want to do that again, but it is hard with translations and we probably want to shorten the displayname more, as before this change the message was only ever as long as the timestamp, which is usually just 5 characters... --- src/RoomInfoListItem.cpp | 30 ++------------- src/Utils.cpp | 5 --- src/Utils.h | 99 ++++++++++++++++++++++++++++++------------------ 3 files changed, 67 insertions(+), 67 deletions(-) (limited to 'src/Utils.cpp') diff --git a/src/RoomInfoListItem.cpp b/src/RoomInfoListItem.cpp index f135451c..8bebb0f5 100644 --- a/src/RoomInfoListItem.cpp +++ b/src/RoomInfoListItem.cpp @@ -118,7 +118,7 @@ RoomInfoListItem::RoomInfoListItem(QString room_id, RoomInfo info, QWidget *pare // so we can't use them for sorting. if (roomType_ == RoomType::Invited) lastMsgInfo_ = { - emptyEventId, "-", "-", "-", "-", QDateTime::currentDateTime().addYears(10)}; + emptyEventId, "-", "-", "-", QDateTime::currentDateTime().addYears(10)}; } void @@ -210,33 +210,11 @@ RoomInfoListItem::paintEvent(QPaintEvent *event) p.setFont(QFont{}); p.setPen(subtitlePen); - // The limit is the space between the end of the avatar and the start of the - // timestamp. - int usernameLimit = - std::max(0, width() - 3 * wm.padding - msgStampWidth - wm.iconSize - 20); - auto userName = - metrics.elidedText(lastMsgInfo_.username, Qt::ElideRight, usernameLimit); - - p.setFont(QFont{}); - p.drawText(QPoint(2 * wm.padding + wm.iconSize, bottom_y), userName); - -#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0) - int nameWidth = QFontMetrics(QFont{}).width(userName); -#else - int nameWidth = QFontMetrics(QFont{}).horizontalAdvance(userName); -#endif - p.setFont(QFont{}); - - // The limit is the space between the end of the username and the start of - // the timestamp. - int descriptionLimit = - std::max(0, - width() - 3 * wm.padding - bottomLineWidthLimit - wm.iconSize - - nameWidth - 5); + int descriptionLimit = std::max( + 0, width() - 3 * wm.padding - bottomLineWidthLimit - wm.iconSize); auto description = metrics.elidedText(lastMsgInfo_.body, Qt::ElideRight, descriptionLimit); - p.drawText(QPoint(2 * wm.padding + wm.iconSize + nameWidth, bottom_y), - description); + p.drawText(QPoint(2 * wm.padding + wm.iconSize, bottom_y), description); // We show the last message timestamp. p.save(); diff --git a/src/Utils.cpp b/src/Utils.cpp index 5a1447ac..e27bc995 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -146,11 +146,6 @@ utils::getMessageDescription(const TimelineEvent &event, const auto ts = QDateTime::fromMSecsSinceEpoch(msg.origin_server_ts); DescInfo info; - if (sender == localUser) - info.username = QCoreApplication::translate("utils", "You"); - else - info.username = username; - info.userid = sender; info.body = QString(" %1").arg(messageDescription()); info.timestamp = utils::descriptiveTime(ts); diff --git a/src/Utils.h b/src/Utils.h index 225754be..8cb891cc 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -94,38 +94,72 @@ messageDescription(const QString &username = "", using Video = mtx::events::RoomEvent; using Encrypted = mtx::events::EncryptedEvent; - // Sometimes the verb form of sent changes in some languages depending on the actor. - auto remoteSent = QCoreApplication::translate( - "message-description: ", "sent", "For when you are the sender"); - auto localSent = QCoreApplication::translate( - "message-description:", "sent", "For when someone else is the sender"); - QString sentVerb = isLocal ? localSent : remoteSent; if (std::is_same::value || std::is_same::value) { - return QCoreApplication::translate("message-description sent:", "%1 an audio clip") - .arg(sentVerb); + if (isLocal) + return QCoreApplication::translate("message-description sent:", + "You sent an audio clip"); + else + return QCoreApplication::translate("message-description sent:", + "%1 sent an audio clip") + .arg(username); } else if (std::is_same::value || std::is_same::value) { - return QCoreApplication::translate("message-description sent:", "%1 an image") - .arg(sentVerb); + if (isLocal) + return QCoreApplication::translate("message-description sent:", + "You sent an image"); + else + return QCoreApplication::translate("message-description sent:", + "%1 sent an image") + .arg(username); } else if (std::is_same::value || std::is_same::value) { - return QCoreApplication::translate("message-description sent:", "%1 a file") - .arg(sentVerb); + if (isLocal) + return QCoreApplication::translate("message-description sent:", + "You sent a file"); + else + return QCoreApplication::translate("message-description sent:", + "%1 sent a file") + .arg(username); } else if (std::is_same::value || std::is_same::value) { - return QCoreApplication::translate("message-description sent:", "%1 a video clip") - .arg(sentVerb); + if (isLocal) + return QCoreApplication::translate("message-description sent:", + "You sent a video"); + else + return QCoreApplication::translate("message-description sent:", + "%1 sent a video") + .arg(username); } else if (std::is_same::value || std::is_same::value) { - return QCoreApplication::translate("message-description sent:", "%1 a sticker") - .arg(sentVerb); + if (isLocal) + return QCoreApplication::translate("message-description sent:", + "You sent a sticker"); + else + return QCoreApplication::translate("message-description sent:", + "%1 sent a sticker") + .arg(username); } else if (std::is_same::value) { - return QCoreApplication::translate("message-description sent:", "%1 a notification") - .arg(sentVerb); + if (isLocal) + return QCoreApplication::translate("message-description sent:", + "You sent a notification"); + else + return QCoreApplication::translate("message-description sent:", + "%1 sent a notification") + .arg(username); } else if (std::is_same::value) { - return QString(": %1").arg(body); + if (isLocal) + return QCoreApplication::translate("message-description sent:", "You: %1") + .arg(body); + else + return QCoreApplication::translate("message-description sent:", "%1: %2") + .arg(username) + .arg(body); } else if (std::is_same::value) { return QString("* %1 %2").arg(username).arg(body); } else if (std::is_same::value) { - return QCoreApplication::translate("message-description sent:", - "%1 an encrypted message") - .arg(sentVerb); + if (isLocal) + return QCoreApplication::translate("message-description sent:", + "You sent an encrypted message"); + else + return QCoreApplication::translate("message-description sent:", + "%1 sent an encrypted message") + .arg(username); } else { return QCoreApplication::translate("utils", "Unknown Message Type"); } @@ -144,20 +178,13 @@ createDescriptionInfo(const Event &event, const QString &localUser, const QStrin const auto username = Cache::displayName(room_id, sender); const auto ts = QDateTime::fromMSecsSinceEpoch(msg.origin_server_ts); - bool isText = std::is_same::value; - bool isEmote = std::is_same::value; - - return DescInfo{ - QString::fromStdString(msg.event_id), - isEmote ? "" - : (sender == localUser ? QCoreApplication::translate("utils", "You") : username), - sender, - (isText || isEmote) - ? messageDescription( - username, QString::fromStdString(msg.content.body).trimmed(), sender == localUser) - : QString(" %1").arg(messageDescription()), - utils::descriptiveTime(ts), - ts}; + return DescInfo{QString::fromStdString(msg.event_id), + sender, + messageDescription(username, + QString::fromStdString(msg.content.body).trimmed(), + sender == localUser), + utils::descriptiveTime(ts), + ts}; } //! Scale down an image to fit to the given width & height limitations. -- cgit 1.5.1 From 1268e9f11c22e8cd22302342e80daef94b15001d Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Tue, 5 Nov 2019 17:16:04 +0100 Subject: Make replies format nicer Also lays a bit of groundwork for better reply rendering --- resources/qml/TimelineRow.qml | 15 ++++++++++++-- src/Utils.cpp | 5 +---- src/timeline2/TimelineModel.cpp | 44 +++++++++++++++++++++++++++++++---------- src/timeline2/TimelineModel.h | 1 + 4 files changed, 49 insertions(+), 16 deletions(-) (limited to 'src/Utils.cpp') diff --git a/resources/qml/TimelineRow.qml b/resources/qml/TimelineRow.qml index c5c3fde0..8f9090e3 100644 --- a/resources/qml/TimelineRow.qml +++ b/resources/qml/TimelineRow.qml @@ -14,12 +14,23 @@ RowLayout { anchors.left: parent.left anchors.right: parent.right - implicitHeight: contentItem.childrenRect.height + implicitHeight: contentItem.height - MessageDelegate { + Column { Layout.fillWidth: true Layout.alignment: Qt.AlignTop id: contentItem + + //property var replyTo: model.replyTo + + //Text { + // property int idx: timelineManager.timeline.idToIndex(replyTo) + // text: "" + (idx != -1 ? timelineManager.timeline.data(timelineManager.timeline.index(idx, 0), 2) : "nothing") + //} + MessageDelegate { + width: parent.width + height: childrenRect.height + } } StatusIndicator { diff --git a/src/Utils.cpp b/src/Utils.cpp index e27bc995..8f9e0643 100644 --- a/src/Utils.cpp +++ b/src/Utils.cpp @@ -366,7 +366,7 @@ utils::getFormattedQuoteBody(const RelatedInfo &related, const QString &html) { return QString("
In reply " - "to* %4
%4%5
") .arg(related.room, QString::fromStdString(related.related_event), @@ -382,9 +382,6 @@ utils::getQuoteBody(const RelatedInfo &related) using MsgType = mtx::events::MessageType; switch (related.type) { - case MsgType::Text: { - return markdownToHtml(related.quoted_body); - } case MsgType::File: { return QString(QCoreApplication::translate("utils", "sent a file.")); } diff --git a/src/timeline2/TimelineModel.cpp b/src/timeline2/TimelineModel.cpp index bdb3ea6f..b2b6f803 100644 --- a/src/timeline2/TimelineModel.cpp +++ b/src/timeline2/TimelineModel.cpp @@ -13,6 +13,8 @@ #include "Utils.h" #include "dialogs/RawMessage.h" +Q_DECLARE_METATYPE(QModelIndex) + namespace { template QString @@ -80,12 +82,6 @@ eventFormattedBody(const mtx::events::RoomEvent &e) { auto temp = e.content.formatted_body; if (!temp.empty()) { - auto pos = temp.find(""); - if (pos != std::string::npos) - temp.erase(pos, std::string("").size()); - pos = temp.find(""); - if (pos != std::string::npos) - temp.erase(pos, std::string("").size()); return QString::fromStdString(temp); } else { return QString::fromStdString(e.content.body).toHtmlEscaped().replace("\n", "
"); @@ -182,6 +178,21 @@ eventMimeType(const mtx::events::RoomEvent &e) return QString::fromStdString(e.content.info.mimetype); } +template +QString +eventRelatesTo(const mtx::events::Event &) +{ + return QString(); +} +template +auto +eventRelatesTo(const mtx::events::RoomEvent &e) -> std::enable_if_t< + std::is_same::value, + QString> +{ + return QString::fromStdString(e.content.relates_to.in_reply_to.event_id); +} + template qml_mtx_events::EventType toRoomEventType(const mtx::events::Event &e) @@ -383,6 +394,7 @@ TimelineModel::roleNames() const {Id, "id"}, {State, "state"}, {IsEncrypted, "isEncrypted"}, + {ReplyTo, "replyTo"}, }; } int @@ -450,8 +462,12 @@ TimelineModel::data(const QModelIndex &index, int role) const return QVariant(utils::replaceEmoji(boost::apply_visitor( [](const auto &e) -> QString { return eventBody(e); }, event))); case FormattedBody: - return QVariant(utils::replaceEmoji(boost::apply_visitor( - [](const auto &e) -> QString { return eventFormattedBody(e); }, event))); + return QVariant( + utils::replaceEmoji( + boost::apply_visitor( + [](const auto &e) -> QString { return eventFormattedBody(e); }, event)) + .remove("") + .remove("")); case Url: return QVariant(boost::apply_visitor( [](const auto &e) -> QString { return eventUrl(e); }, event)); @@ -501,6 +517,11 @@ TimelineModel::data(const QModelIndex &index, int role) const return boost::get>( &tempEvent) != nullptr; } + case ReplyTo: { + QString evId = boost::apply_visitor( + [](const auto &e) -> QString { return eventRelatesTo(e); }, event); + return QVariant(evId); + } default: return QVariant(); } @@ -825,8 +846,11 @@ TimelineModel::replyAction(QString id) event); related.type = mtx::events::getMessageType(boost::apply_visitor( [](const auto &e) -> std::string { return eventMsgType(e); }, event)); - related.quoted_body = - boost::apply_visitor([](const auto &e) -> QString { return eventBody(e); }, event); + related.quoted_body = boost::apply_visitor( + [](const auto &e) -> QString { return eventFormattedBody(e); }, event); + related.quoted_body.remove(QRegularExpression( + ".*", QRegularExpression::DotMatchesEverythingOption)); + nhlog::ui()->debug("after replacement: {}", related.quoted_body.toStdString()); related.room = room_id_; if (related.quoted_body.isEmpty()) diff --git a/src/timeline2/TimelineModel.h b/src/timeline2/TimelineModel.h index 1ed6e72c..31e41315 100644 --- a/src/timeline2/TimelineModel.h +++ b/src/timeline2/TimelineModel.h @@ -139,6 +139,7 @@ public: Id, State, IsEncrypted, + ReplyTo, }; QHash roleNames() const override; -- cgit 1.5.1