diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp
index 777709be..125e229a 100644
--- a/src/ChatPage.cpp
+++ b/src/ChatPage.cpp
@@ -34,7 +34,6 @@
#include "Splitter.h"
#include "TextInputWidget.h"
#include "TopRoomBar.h"
-#include "TypingDisplay.h"
#include "UserInfoWidget.h"
#include "UserSettingsPage.h"
#include "Utils.h"
@@ -130,11 +129,6 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
text_input_ = new TextInputWidget(this);
contentLayout_->addWidget(text_input_);
- typingDisplay_ = new TypingDisplay(content_);
- typingDisplay_->hide();
- connect(
- text_input_, &TextInputWidget::heightChanged, typingDisplay_, &TypingDisplay::setOffset);
-
typingRefresher_ = new QTimer(this);
typingRefresher_->setInterval(TYPING_REFRESH_TIMEOUT);
@@ -225,19 +219,6 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
}
});
- connect(room_list_, &RoomList::roomChanged, this, [this](const QString &roomid) {
- QStringList users;
-
- if (!userSettings_->isTypingNotificationsEnabled()) {
- typingDisplay_->setUsers(users);
- return;
- }
-
- if (typingUsers_.find(roomid) != typingUsers_.end())
- users = typingUsers_[roomid];
-
- typingDisplay_->setUsers(users);
- });
connect(room_list_, &RoomList::roomChanged, text_input_, &TextInputWidget::stopTyping);
connect(room_list_, &RoomList::roomChanged, this, &ChatPage::changeTopRoomInfo);
connect(room_list_, &RoomList::roomChanged, splitter, &Splitter::showChatView);
@@ -472,8 +453,6 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
bool hasNotifications = false;
for (const auto &room : rooms.join) {
auto room_id = QString::fromStdString(room.first);
-
- updateTypingUsers(room_id, room.second.ephemeral.typing);
updateRoomNotificationCount(
room_id,
room.second.unread_notifications.notification_count,
@@ -787,38 +766,6 @@ ChatPage::removeRoom(const QString &room_id)
}
void
-ChatPage::updateTypingUsers(const QString &roomid, const std::vector<std::string> &user_ids)
-{
- if (!userSettings_->isTypingNotificationsEnabled())
- return;
-
- typingUsers_[roomid] = generateTypingUsers(roomid, user_ids);
-
- if (current_room_ == roomid)
- typingDisplay_->setUsers(typingUsers_[roomid]);
-}
-
-QStringList
-ChatPage::generateTypingUsers(const QString &room_id, const std::vector<std::string> &typing_users)
-{
- QStringList users;
- auto local_user = utils::localUser();
-
- for (const auto &uid : typing_users) {
- const auto remote_user = QString::fromStdString(uid);
-
- if (remote_user == local_user)
- continue;
-
- users.append(cache::displayName(room_id, remote_user));
- }
-
- users.sort();
-
- return users;
-}
-
-void
ChatPage::removeLeftRooms(const std::map<std::string, mtx::responses::LeftRoom> &rooms)
{
for (auto it = rooms.cbegin(); it != rooms.cend(); ++it) {
diff --git a/src/ChatPage.h b/src/ChatPage.h
index e4c0ef16..354a21f3 100644
--- a/src/ChatPage.h
+++ b/src/ChatPage.h
@@ -48,7 +48,6 @@ class Splitter;
class TextInputWidget;
class TimelineViewManager;
class TopRoomBar;
-class TypingDisplay;
class UserInfoWidget;
class UserSettings;
class NotificationsManager;
@@ -187,8 +186,6 @@ private:
using LeftRooms = std::map<std::string, mtx::responses::LeftRoom>;
void removeLeftRooms(const LeftRooms &rooms);
- void updateTypingUsers(const QString &roomid, const std::vector<std::string> &user_ids);
-
void loadStateFromCache();
void resetUI();
//! Decides whether or not to hide the group's sidebar.
@@ -206,9 +203,6 @@ private:
void showNotificationsDialog(const QPoint &point);
- QStringList generateTypingUsers(const QString &room_id,
- const std::vector<std::string> &typing_users);
-
QHBoxLayout *topLayout_;
Splitter *splitter;
@@ -228,7 +222,6 @@ private:
TopRoomBar *top_bar_;
TextInputWidget *text_input_;
- TypingDisplay *typingDisplay_;
QTimer connectivityTimer_;
std::atomic_bool isConnected_;
@@ -240,8 +233,6 @@ private:
popups::UserMentions *user_mentions_popup_;
- // Keeps track of the users currently typing on each room.
- std::map<QString, QList<QString>> typingUsers_;
QTimer *typingRefresher_;
// Global user settings.
diff --git a/src/TypingDisplay.cpp b/src/TypingDisplay.cpp
deleted file mode 100644
index 43fabcd8..00000000
--- a/src/TypingDisplay.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-#include <QDebug>
-#include <QPainter>
-#include <QPoint>
-#include <QShowEvent>
-#include <QtGlobal>
-
-#include "Config.h"
-#include "TypingDisplay.h"
-#include "ui/Painter.h"
-
-constexpr int LEFT_PADDING = 24;
-constexpr int RECT_PADDING = 2;
-
-TypingDisplay::TypingDisplay(QWidget *parent)
- : OverlayWidget(parent)
- , offset_{conf::textInput::height}
-{
- setFixedHeight(QFontMetrics(font()).height() + RECT_PADDING);
- setAttribute(Qt::WA_TransparentForMouseEvents);
-}
-
-void
-TypingDisplay::setOffset(int margin)
-{
- offset_ = margin;
- move(0, parentWidget()->height() - offset_ - height());
-}
-
-void
-TypingDisplay::setUsers(const QStringList &uid)
-{
- move(0, parentWidget()->height() - offset_ - height());
-
- text_.clear();
-
- QString temp = text_ +=
- tr("%1 and %2 are typing",
- "Multiple users are typing. First argument is a comma separated list of potentially "
- "multiple users. Second argument is the last user of that list. (If only one user is "
- "typing, %1 is empty. You should still use it in your string though to silence Qt "
- "warnings.)",
- uid.size());
-
- if (uid.isEmpty()) {
- hide();
- update();
-
- return;
- }
-
- QStringList uidWithoutLast = uid;
- uidWithoutLast.pop_back();
- text_ = temp.arg(uidWithoutLast.join(", ")).arg(uid.back());
-
- show();
- update();
-}
-
-void
-TypingDisplay::paintEvent(QPaintEvent *)
-{
- Painter p(this);
- PainterHighQualityEnabler hq(p);
-
- QFont f;
- f.setPointSizeF(f.pointSizeF() * 0.9);
-
- p.setFont(f);
- p.setPen(QPen(textColor()));
-
- QRect region = rect();
- region.translate(LEFT_PADDING, 0);
-
- QFontMetrics fm(f);
- text_ = fm.elidedText(text_, Qt::ElideRight, (double)(width() * 0.75));
-
- QPainterPath path;
-#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
- path.addRoundedRect(QRectF(0, 0, fm.width(text_) + 2 * LEFT_PADDING, height()), 3, 3);
-#else
- path.addRoundedRect(
- QRectF(0, 0, fm.horizontalAdvance(text_) + 2 * LEFT_PADDING, height()), 3, 3);
-#endif
- p.fillPath(path, backgroundColor());
- p.drawText(region, Qt::AlignVCenter, text_);
-}
diff --git a/src/TypingDisplay.h b/src/TypingDisplay.h
deleted file mode 100644
index 332d9c66..00000000
--- a/src/TypingDisplay.h
+++ /dev/null
@@ -1,36 +0,0 @@
-#pragma once
-
-#include "ui/OverlayWidget.h"
-
-class QPaintEvent;
-
-class TypingDisplay : public OverlayWidget
-{
- Q_OBJECT
-
- Q_PROPERTY(QColor textColor WRITE setTextColor READ textColor)
- Q_PROPERTY(QColor backgroundColor WRITE setBackgroundColor READ backgroundColor)
-
-public:
- TypingDisplay(QWidget *parent = nullptr);
-
- void setUsers(const QStringList &user_ids);
-
- void setTextColor(const QColor &color) { textColor_ = color; };
- QColor textColor() const { return textColor_; };
-
- void setBackgroundColor(const QColor &color) { bgColor_ = color; };
- QColor backgroundColor() const { return bgColor_; };
-
-public slots:
- void setOffset(int margin);
-
-protected:
- void paintEvent(QPaintEvent *event) override;
-
-private:
- int offset_;
- QColor textColor_;
- QColor bgColor_;
- QString text_;
-};
diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp
index 08c29927..2fd4b6d4 100644
--- a/src/timeline/TimelineModel.cpp
+++ b/src/timeline/TimelineModel.cpp
@@ -1388,3 +1388,33 @@ TimelineModel::cacheMedia(QString eventId)
emit mediaCached(mxcUrl, filename.filePath());
});
}
+
+QString
+TimelineModel::formatTypingUsers(const std::vector<QString> &users, QColor bg)
+{
+ QString temp =
+ tr("%1 and %2 are typing",
+ "Multiple users are typing. First argument is a comma separated list of potentially "
+ "multiple users. Second argument is the last user of that list. (If only one user is "
+ "typing, %1 is empty. You should still use it in your string though to silence Qt "
+ "warnings.)",
+ users.size());
+
+ if (users.empty()) {
+ return "";
+ }
+
+ QStringList uidWithoutLast;
+
+ auto formatUser = [this, bg](const QString &user_id) -> QString {
+ return QString("<font color=\"%1\">%2</font>")
+ .arg(userColor(user_id, bg).name())
+ .arg(escapeEmoji(displayName(user_id).toHtmlEscaped()));
+ };
+
+ for (size_t i = 0; i + 1 < users.size(); i++) {
+ uidWithoutLast.append(formatUser(users[i]));
+ }
+
+ return temp.arg(uidWithoutLast.join(", ")).arg(formatUser(users.back()));
+}
diff --git a/src/timeline/TimelineModel.h b/src/timeline/TimelineModel.h
index ae505c17..6d351359 100644
--- a/src/timeline/TimelineModel.h
+++ b/src/timeline/TimelineModel.h
@@ -120,6 +120,8 @@ class TimelineModel : public QAbstractListModel
Q_OBJECT
Q_PROPERTY(
int currentIndex READ currentIndex WRITE setCurrentIndex NOTIFY currentIndexChanged)
+ Q_PROPERTY(std::vector<QString> typingUsers READ typingUsers WRITE updateTypingUsers NOTIFY
+ typingUsersChanged)
public:
explicit TimelineModel(TimelineViewManager *manager, QString room_id, QObject *parent = 0);
@@ -162,6 +164,7 @@ public:
Q_INVOKABLE QString displayName(QString id) const;
Q_INVOKABLE QString avatarUrl(QString id) const;
Q_INVOKABLE QString formatDateSeparator(QDate date) const;
+ Q_INVOKABLE QString formatTypingUsers(const std::vector<QString> &users, QColor bg);
Q_INVOKABLE QString escapeEmoji(QString str) const;
Q_INVOKABLE void viewRawMessage(QString id) const;
@@ -183,6 +186,14 @@ public slots:
int currentIndex() const { return idToIndex(currentId); }
void markEventsAsRead(const std::vector<QString> &event_ids);
QVariantMap getDump(QString eventId) const;
+ void updateTypingUsers(const std::vector<QString> &users)
+ {
+ if (this->typingUsers_ != users) {
+ this->typingUsers_ = users;
+ emit typingUsersChanged(typingUsers_);
+ }
+ }
+ std::vector<QString> typingUsers() const { return typingUsers_; }
private slots:
// Add old events at the top of the timeline.
@@ -202,6 +213,7 @@ signals:
void mediaCached(QString mxcUrl, QString cacheUrl);
void newEncryptedImage(mtx::crypto::EncryptedFile encryptionInfo);
void replyFetched(QString requestingEvent, mtx::events::collections::TimelineEvents event);
+ void typingUsersChanged(std::vector<QString> users);
private:
DecryptionResult decryptEvent(
@@ -232,6 +244,7 @@ private:
QHash<QString, QColor> userColors;
QString currentId;
+ std::vector<QString> typingUsers_;
TimelineViewManager *manager_;
diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp
index ddbc6af8..c7a4e50e 100644
--- a/src/timeline/TimelineViewManager.cpp
+++ b/src/timeline/TimelineViewManager.cpp
@@ -8,6 +8,7 @@
#include "ColorImageProvider.h"
#include "DelegateChooser.h"
#include "Logging.h"
+#include "MatrixClient.h"
#include "MxcImageProvider.h"
#include "UserSettingsPage.h"
#include "dialogs/ImageOverlay.h"
@@ -92,10 +93,21 @@ TimelineViewManager::TimelineViewManager(QWidget *parent)
void
TimelineViewManager::sync(const mtx::responses::Rooms &rooms)
{
- for (auto it = rooms.join.cbegin(); it != rooms.join.cend(); ++it) {
+ for (const auto &[room_id, room] : rooms.join) {
// addRoom will only add the room, if it doesn't exist
- addRoom(QString::fromStdString(it->first));
- models.value(QString::fromStdString(it->first))->addEvents(it->second.timeline);
+ addRoom(QString::fromStdString(room_id));
+ const auto &room_model = models.value(QString::fromStdString(room_id));
+ room_model->addEvents(room.timeline);
+
+ if (ChatPage::instance()->userSettings()->isTypingNotificationsEnabled()) {
+ std::vector<QString> typing;
+ typing.reserve(room.ephemeral.typing.size());
+ for (const auto &user : room.ephemeral.typing) {
+ if (user != http::client()->user_id().to_string())
+ typing.push_back(QString::fromStdString(user));
+ }
+ room_model->updateTypingUsers(typing);
+ }
}
this->isInitialSync_ = false;
|