From 7dab863738eddc29081dd9ebe63f05a8636046b3 Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Thu, 3 May 2018 17:29:02 +0300 Subject: [PATCH] Remove flickering by updating auto-complete menu items in-place Instead of deleting the current items and creating new ones. --- include/SuggestionsPopup.hpp | 12 +++- src/SuggestionsPopup.cpp | 114 +++++++++++++++++++++++++++++------ 2 files changed, 105 insertions(+), 21 deletions(-) diff --git a/include/SuggestionsPopup.hpp b/include/SuggestionsPopup.hpp index ba3aebe5..4a8dd00c 100644 --- a/include/SuggestionsPopup.hpp +++ b/include/SuggestionsPopup.hpp @@ -53,11 +53,14 @@ class UserItem : public PopupItem public: UserItem(QWidget *parent, const QString &user_id); QString selectedText() const { return userId_; } + void updateItem(const QString &user_id); protected: void mousePressEvent(QMouseEvent *event) override; private: + void resolveAvatar(const QString &user_id); + QLabel *userName_; QString userId_; }; @@ -69,6 +72,7 @@ class RoomItem : public PopupItem public: RoomItem(QWidget *parent, const RoomSearchResult &res); QString selectedText() const { return roomId_; } + void updateItem(const RoomSearchResult &res); protected: void mousePressEvent(QMouseEvent *event) override; @@ -124,12 +128,16 @@ private: void resetSelection() { selectedItem_ = -1; } void selectFirstItem() { selectedItem_ = 0; } void selectLastItem() { selectedItem_ = layout_->count() - 1; } - void removeItems() + void removeLayoutItemsAfter(size_t startingPos) { + size_t posToRemove = layout_->count() - 1; + QLayoutItem *item; - while ((item = layout_->takeAt(0)) != 0) { + while (startingPos <= posToRemove && (item = layout_->takeAt(posToRemove)) != 0) { delete item->widget(); delete item; + + posToRemove = layout_->count() - 1; } } diff --git a/src/SuggestionsPopup.cpp b/src/SuggestionsPopup.cpp index 9ac2ef23..82b7938b 100644 --- a/src/SuggestionsPopup.cpp +++ b/src/SuggestionsPopup.cpp @@ -5,7 +5,6 @@ #include "SuggestionsPopup.hpp" #include "Utils.h" -#include #include #include #include @@ -60,10 +59,39 @@ UserItem::UserItem(QWidget *parent, const QString &user_id) topLayout_->addWidget(avatar_); topLayout_->addWidget(userName_, 1); - AvatarProvider::resolve(ChatPage::instance()->currentRoom(), - userId_, - this, - [this](const QImage &img) { avatar_->setImage(img); }); + resolveAvatar(user_id); +} + +void +UserItem::updateItem(const QString &user_id) +{ + userId_ = user_id; + + auto displayName = Cache::displayName(ChatPage::instance()->currentRoom(), userId_); + + // If it's a matrix id we use the second letter. + if (displayName.size() > 1 && displayName.at(0) == '@') + avatar_->setLetter(QChar(displayName.at(1))); + else + avatar_->setLetter(utils::firstChar(displayName)); + + userName_->setText(displayName); + resolveAvatar(user_id); +} + +void +UserItem::resolveAvatar(const QString &user_id) +{ + AvatarProvider::resolve( + ChatPage::instance()->currentRoom(), userId_, this, [this, user_id](const QImage &img) { + // The user on the widget when the avatar is resolved, + // might be different from the user that made the call. + if (user_id == userId_) + avatar_->setImage(img); + else + // We try to resolve the avatar again. + resolveAvatar(userId_); + }); } void @@ -96,6 +124,24 @@ RoomItem::RoomItem(QWidget *parent, const RoomSearchResult &res) avatar_->setImage(res.img); } +void +RoomItem::updateItem(const RoomSearchResult &result) +{ + roomId_ = QString::fromStdString(std::move(result.room_id)); + + auto name = + QFontMetrics(QFont()).elidedText(QString::fromStdString(std::move(result.info.name)), + Qt::ElideRight, + parentWidget()->width() - 10); + + roomName_->setText(name); + + if (!result.img.isNull()) + avatar_->setImage(result.img); + else + avatar_->setLetter(utils::firstChar(name)); +} + void RoomItem::mousePressEvent(QMouseEvent *event) { @@ -119,17 +165,33 @@ SuggestionsPopup::SuggestionsPopup(QWidget *parent) void SuggestionsPopup::addRooms(const std::vector &rooms) { - removeItems(); - if (rooms.empty()) { hide(); return; } - for (const auto &r : rooms) { - auto room = new RoomItem(this, r); - layout_->addWidget(room); - connect(room, &RoomItem::clicked, this, &SuggestionsPopup::itemSelected); + const size_t layoutCount = layout_->count(); + const size_t roomCount = rooms.size(); + + // Remove the extra widgets from the layout. + if (roomCount < layoutCount) + removeLayoutItemsAfter(roomCount - 1); + + for (size_t i = 0; i < roomCount; ++i) { + auto item = layout_->itemAt(i); + + // Create a new widget if there isn't already one in that + // layout position. + if (!item) { + auto room = new RoomItem(this, rooms.at(i)); + connect(room, &RoomItem::clicked, this, &SuggestionsPopup::itemSelected); + layout_->addWidget(room); + } else { + // Update the current widget with the new data. + auto room = qobject_cast(item->widget()); + if (room) + room->updateItem(rooms.at(i)); + } } resetSelection(); @@ -143,24 +205,38 @@ SuggestionsPopup::addRooms(const std::vector &rooms) void SuggestionsPopup::addUsers(const QVector &users) { - removeItems(); - if (users.isEmpty()) { hide(); return; } - for (const auto &u : users) { - auto user = new UserItem(this, u.user_id); - layout_->addWidget(user); - connect(user, &UserItem::clicked, this, &SuggestionsPopup::itemSelected); + const size_t layoutCount = layout_->count(); + const size_t userCount = users.size(); + + // Remove the extra widgets from the layout. + if (userCount < layoutCount) + removeLayoutItemsAfter(userCount - 1); + + for (size_t i = 0; i < userCount; ++i) { + auto item = layout_->itemAt(i); + + // Create a new widget if there isn't already one in that + // layout position. + if (!item) { + auto user = new UserItem(this, users.at(i).user_id); + connect(user, &UserItem::clicked, this, &SuggestionsPopup::itemSelected); + layout_->addWidget(user); + } else { + // Update the current widget with the new data. + auto userWidget = qobject_cast(item->widget()); + if (userWidget) + userWidget->updateItem(users.at(i).user_id); + } } resetSelection(); adjustSize(); - resize(geometry().width(), 40 * users.size()); - selectNextSuggestion(); }