From 73222aa900cbcc081444394ecfd9db3dc114a9a8 Mon Sep 17 00:00:00 2001 From: Victor Berger Date: Mon, 2 Oct 2017 19:52:21 +0200 Subject: [PATCH 01/64] Fix unicode handling of replaceEmoji (#84) --- src/TimelineItem.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/TimelineItem.cc b/src/TimelineItem.cc index 97269584..28cabc12 100644 --- a/src/TimelineItem.cc +++ b/src/TimelineItem.cc @@ -371,17 +371,17 @@ TimelineItem::replaceEmoji(const QString &body) { QString fmtBody = ""; - for (auto &c : body) { - int code = c.unicode(); + QVector utf32_string = body.toUcs4(); + for (auto &code : utf32_string) { // TODO: Be more precise here. if (code > 9000) fmtBody += QString("") .arg(conf::emojiSize) + - QString(c) + ""; + QString::fromUcs4(&code, 1) + ""; else - fmtBody += c; + fmtBody += QString::fromUcs4(&code, 1); } return fmtBody; From 88349eae90cc842427392ababb7903a242f17a94 Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Tue, 3 Oct 2017 21:16:31 +0300 Subject: [PATCH 02/64] Recover from corrupted cache data Make Cache constructor exception free fixes #74 --- include/Cache.h | 10 +++------- src/Cache.cc | 21 ++++++++++++++++----- src/ChatPage.cc | 34 +++++++++++++++++++--------------- 3 files changed, 38 insertions(+), 27 deletions(-) diff --git a/include/Cache.h b/include/Cache.h index 0f6e5cd2..3a98fddc 100644 --- a/include/Cache.h +++ b/include/Cache.h @@ -35,9 +35,9 @@ public: inline void deleteData(); inline void unmount(); - inline QString memberDbName(const QString &roomid); void removeRoom(const QString &roomid); + void setup(); private: void setNextBatchToken(lmdb::txn &txn, const QString &token); @@ -59,15 +59,11 @@ Cache::unmount() isMounted_ = false; } -inline QString -Cache::memberDbName(const QString &roomid) -{ - return QString("m.%1").arg(roomid); -} - inline void Cache::deleteData() { + qInfo() << "Deleting cache data"; + if (!cacheDirectory_.isEmpty()) QDir(cacheDirectory_).removeRecursively(); } diff --git a/src/Cache.cc b/src/Cache.cc index 5ed77086..c96ec37d 100644 --- a/src/Cache.cc +++ b/src/Cache.cc @@ -37,10 +37,21 @@ Cache::Cache(const QString &userId) , isMounted_{ false } , userId_{ userId } { +} + +void +Cache::setup() +{ + qDebug() << "Setting up cache"; + auto statePath = QString("%1/%2/state") .arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)) .arg(QString::fromUtf8(userId_.toUtf8().toHex())); + cacheDirectory_ = QString("%1/%2") + .arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)) + .arg(QString::fromUtf8(userId_.toUtf8().toHex())); + bool isInitial = !QFile::exists(statePath); env_ = lmdb::env::create(); @@ -48,7 +59,7 @@ Cache::Cache(const QString &userId) env_.set_max_dbs(1024UL); if (isInitial) { - qDebug() << "[cache] First time initializing LMDB"; + qDebug() << "First time initializing LMDB"; if (!QDir().mkpath(statePath)) { throw std::runtime_error( @@ -83,10 +94,7 @@ Cache::Cache(const QString &userId) txn.commit(); - isMounted_ = true; - cacheDirectory_ = QString("%1/%2") - .arg(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)) - .arg(QString::fromUtf8(userId_.toUtf8().toHex())); + isMounted_ = true; } void @@ -156,6 +164,9 @@ Cache::insertRoomState(lmdb::txn &txn, const QString &roomid, const RoomState &s void Cache::removeRoom(const QString &roomid) { + if (!isMounted_) + return; + auto txn = lmdb::txn::begin(env_, nullptr, 0); lmdb::dbi_del(txn, roomDb_, lmdb::val(roomid.toUtf8(), roomid.toUtf8().size()), nullptr); diff --git a/src/ChatPage.cc b/src/ChatPage.cc index a6a80e9d..9f983b9f 100644 --- a/src/ChatPage.cc +++ b/src/ChatPage.cc @@ -248,16 +248,23 @@ ChatPage::bootstrap(QString userid, QString homeserver, QString token) client_->setAccessToken(token); client_->getOwnProfile(); + cache_ = QSharedPointer(new Cache(userid)); + try { - cache_ = QSharedPointer(new Cache(userid)); - } catch (const std::exception &e) { - qCritical() << e.what(); + cache_->setup(); + + if (cache_->isInitialized()) { + loadStateFromCache(); + return; + } + } catch (const lmdb::error &e) { + qCritical() << "Cache failure" << e.what(); + cache_->unmount(); + cache_->deleteData(); + qInfo() << "Falling back to initial sync ..."; } - if (cache_->isInitialized()) - loadStateFromCache(); - else - client_->initialSync(); + client_->initialSync(); } void @@ -367,6 +374,7 @@ ChatPage::syncCompleted(const SyncResponse &response) qCritical() << "The cache couldn't be updated: " << e.what(); // TODO: Notify the user. cache_->unmount(); + cache_->deleteData(); } client_->setNextBatchToken(response.nextBatch()); @@ -416,6 +424,7 @@ ChatPage::initialSyncCompleted(const SyncResponse &response) } catch (const lmdb::error &e) { qCritical() << "The cache couldn't be initialized: " << e.what(); cache_->unmount(); + cache_->deleteData(); } client_->setNextBatchToken(response.nextBatch()); @@ -492,14 +501,8 @@ ChatPage::loadStateFromCache() { qDebug() << "Restoring state from cache"; - try { - qDebug() << "Restored nextBatchToken" << cache_->nextBatchToken(); - client_->setNextBatchToken(cache_->nextBatchToken()); - } catch (const lmdb::error &e) { - qCritical() << "Failed to load next_batch_token from cache" << e.what(); - // TODO: Clean the environment - return; - } + qDebug() << "Restored nextBatchToken" << cache_->nextBatchToken(); + client_->setNextBatchToken(cache_->nextBatchToken()); // Fetch all the joined room's state. auto rooms = cache_->states(); @@ -612,6 +615,7 @@ ChatPage::removeRoom(const QString &room_id) qCritical() << "The cache couldn't be updated: " << e.what(); // TODO: Notify the user. cache_->unmount(); + cache_->deleteData(); } room_list_->removeRoom(room_id, room_id == current_room_); } From d60c2b76e30dcbdb1eae2a69b2d3ddff128d00c5 Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Wed, 4 Oct 2017 11:33:34 +0300 Subject: [PATCH 03/64] Receive typing notifications (#88) --- CMakeLists.txt | 34 +++++++++++++------------ include/ChatPage.h | 6 +++++ include/Config.h | 7 +++--- include/Sync.h | 11 ++++++-- include/TypingDisplay.h | 21 ++++++++++++++++ src/ChatPage.cc | 31 ++++++++++++++++++++++- src/MatrixClient.cc | 5 ++-- src/Sync.cc | 16 +++++++++++- src/TextInputWidget.cc | 16 ++++++------ src/TypingDisplay.cc | 56 +++++++++++++++++++++++++++++++++++++++++ 10 files changed, 171 insertions(+), 32 deletions(-) create mode 100644 include/TypingDisplay.h create mode 100644 src/TypingDisplay.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index a40daebc..99ae1d62 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -125,8 +125,8 @@ endif() # set(SRC_FILES src/AvatarProvider.cc - src/ChatPage.cc src/Cache.cc + src/ChatPage.cc src/Deserializable.cc src/EmojiCategory.cc src/EmojiItemDelegate.cc @@ -135,9 +135,6 @@ set(SRC_FILES src/EmojiProvider.cc src/ImageItem.cc src/ImageOverlayDialog.cc - src/TimelineItem.cc - src/TimelineView.cc - src/TimelineViewManager.cc src/InputValidator.cc src/JoinRoomDialog.cc src/LeaveRoomDialog.cc @@ -147,21 +144,25 @@ set(SRC_FILES src/MainWindow.cc src/MatrixClient.cc src/Profile.cc - src/RoomInfoListItem.cc - src/RoomMessages.cc - src/RoomList.cc - src/RoomState.cc + src/QuickSwitcher.cc src/Register.cc src/RegisterPage.cc + src/RoomInfoListItem.cc + src/RoomList.cc + src/RoomMessages.cc + src/RoomState.cc src/Splitter.cc src/Sync.cc src/TextInputWidget.cc - src/TrayIcon.cc + src/TimelineItem.cc + src/TimelineView.cc + src/TimelineViewManager.cc src/TopRoomBar.cc + src/TrayIcon.cc + src/TypingDisplay.cc src/UserInfoWidget.cc src/Versions.cc src/WelcomePage.cc - src/QuickSwitcher.cc src/main.cc src/ui/Avatar.cc @@ -222,23 +223,24 @@ qt5_wrap_cpp(MOC_HEADERS include/ImageItem.h include/ImageOverlayDialog.h include/JoinRoomDialog.h - include/TimelineItem.h - include/TimelineView.h - include/TimelineViewManager.h include/LeaveRoomDialog.h include/LoginPage.h include/LogoutDialog.h include/MainWindow.h include/MatrixClient.h + include/QuickSwitcher.h include/RegisterPage.h include/RoomInfoListItem.h include/RoomList.h include/Splitter.h - include/UserInfoWidget.h + include/TextInputWidget.h + include/TimelineItem.h + include/TimelineView.h + include/TimelineViewManager.h include/TopRoomBar.h include/TrayIcon.h - include/TextInputWidget.h - include/QuickSwitcher.h + include/TypingDisplay.h + include/UserInfoWidget.h include/WelcomePage.h include/ui/Avatar.h diff --git a/include/ChatPage.h b/include/ChatPage.h index 8becc17f..8332225b 100644 --- a/include/ChatPage.h +++ b/include/ChatPage.h @@ -31,6 +31,7 @@ #include "TextInputWidget.h" #include "TimelineViewManager.h" #include "TopRoomBar.h" +#include "TypingDisplay.h" #include "UserInfoWidget.h" class ChatPage : public QWidget @@ -68,6 +69,7 @@ protected: void keyPressEvent(QKeyEvent *event) override; private: + void updateTypingUsers(const QString &roomid, const QList &user_ids); void updateDisplayNames(const RoomState &state); void loadStateFromCache(); void showQuickSwitcher(); @@ -92,6 +94,7 @@ private: TopRoomBar *top_bar_; TextInputWidget *text_input_; + TypingDisplay *typingDisplay_; QTimer *sync_timer_; int sync_interval_; @@ -104,6 +107,9 @@ private: QMap state_manager_; QMap> settingsManager_; + // Keeps track of the users currently typing on each room. + QMap> typingUsers_; + QuickSwitcher *quickSwitcher_ = nullptr; OverlayModal *quickSwitcherModal_ = nullptr; diff --git a/include/Config.h b/include/Config.h index 654eadb6..50f9eb85 100644 --- a/include/Config.h +++ b/include/Config.h @@ -7,9 +7,10 @@ namespace conf { // Global settings. -static const int fontSize = 12; -static const int emojiSize = 14; -static const int headerFontSize = 21; +static const int fontSize = 12; +static const int emojiSize = 14; +static const int headerFontSize = 21; +static const int typingNotificationFontSize = 11; // Window geometry. namespace window diff --git a/include/Sync.h b/include/Sync.h index a9caf473..c49d7101 100644 --- a/include/Sync.h +++ b/include/Sync.h @@ -142,23 +142,30 @@ Timeline::limited() const return limited_; } -// TODO: Add support for ehpmeral, account_data, undread_notifications +// TODO: Add support for account_data, undread_notifications class JoinedRoom : public Deserializable { public: inline State state() const; inline Timeline timeline() const; + inline QList typingUserIDs() const; void deserialize(const QJsonValue &data) override; private: State state_; Timeline timeline_; - /* Ephemeral ephemeral_; */ + QList typingUserIDs_; /* AccountData account_data_; */ /* UnreadNotifications unread_notifications_; */ }; +inline QList +JoinedRoom::typingUserIDs() const +{ + return typingUserIDs_; +} + inline State JoinedRoom::state() const { diff --git a/include/TypingDisplay.h b/include/TypingDisplay.h new file mode 100644 index 00000000..db8a9519 --- /dev/null +++ b/include/TypingDisplay.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +class TypingDisplay : public QWidget +{ + Q_OBJECT + +public: + TypingDisplay(QWidget *parent = nullptr); + + void setUsers(const QStringList &user_ids); + +protected: + void paintEvent(QPaintEvent *event) override; + +private: + QString text_; + int leftPadding_; +}; diff --git a/src/ChatPage.cc b/src/ChatPage.cc index 9f983b9f..52468f64 100644 --- a/src/ChatPage.cc +++ b/src/ChatPage.cc @@ -101,8 +101,10 @@ ChatPage::ChatPage(QSharedPointer client, QWidget *parent) view_manager_ = new TimelineViewManager(client, this); mainContentLayout_->addWidget(view_manager_); - text_input_ = new TextInputWidget(this); + text_input_ = new TextInputWidget(this); + typingDisplay_ = new TypingDisplay(this); contentLayout_->addWidget(text_input_); + contentLayout_->addWidget(typingDisplay_); user_info_widget_ = new UserInfoWidget(sideBarTopWidget_); sideBarTopWidgetLayout_->addWidget(user_info_widget_); @@ -117,6 +119,15 @@ ChatPage::ChatPage(QSharedPointer client, QWidget *parent) connect( top_bar_, &TopRoomBar::leaveRoom, this, [=]() { client_->leaveRoom(current_room_); }); + connect(room_list_, &RoomList::roomChanged, this, [=](const QString &roomid) { + QStringList users; + + if (typingUsers_.contains(roomid)) + users = typingUsers_[roomid]; + + typingDisplay_->setUsers(users); + }); + connect(room_list_, &RoomList::roomChanged, this, &ChatPage::changeTopRoomInfo); connect(room_list_, &RoomList::roomChanged, text_input_, &TextInputWidget::focusLineEdit); connect( @@ -308,6 +319,8 @@ ChatPage::syncCompleted(const SyncResponse &response) auto joined = response.rooms().join(); for (auto it = joined.constBegin(); it != joined.constEnd(); it++) { + updateTypingUsers(it.key(), it.value().typingUserIDs()); + RoomState room_state; // Merge the new updates for rooms that we are tracking. @@ -620,6 +633,22 @@ ChatPage::removeRoom(const QString &room_id) room_list_->removeRoom(room_id, room_id == current_room_); } +void +ChatPage::updateTypingUsers(const QString &roomid, const QList &user_ids) +{ + QStringList users; + + for (const auto uid : user_ids) + users.append(TimelineViewManager::displayName(uid)); + + users.sort(); + + if (current_room_ == roomid) + typingDisplay_->setUsers(users); + + typingUsers_.insert(roomid, users); +} + ChatPage::~ChatPage() { sync_timer_->stop(); diff --git a/src/MatrixClient.cc b/src/MatrixClient.cc index bd43efd8..265b51ce 100644 --- a/src/MatrixClient.cc +++ b/src/MatrixClient.cc @@ -611,8 +611,9 @@ void MatrixClient::sync() noexcept { QJsonObject filter{ { "room", - QJsonObject{ { "include_leave", true }, - { "ephemeral", QJsonObject{ { "limit", 0 } } } } }, + QJsonObject{ + { "include_leave", true }, + } }, { "presence", QJsonObject{ { "limit", 0 } } } }; QUrlQuery query; diff --git a/src/Sync.cc b/src/Sync.cc index 90314352..39d84acb 100644 --- a/src/Sync.cc +++ b/src/Sync.cc @@ -168,7 +168,21 @@ JoinedRoom::deserialize(const QJsonValue &data) if (!ephemeral.value("events").isArray()) qWarning() << "join/ephemeral/events should be an array"; - // TODO: Implement ephemeral handling + auto ephemeralEvents = ephemeral.value("events").toArray(); + + for (const auto e : ephemeralEvents) { + auto obj = e.toObject(); + + if (obj.contains("type") && obj.value("type") == "m.typing") { + auto ids = obj.value("content") + .toObject() + .value("user_ids") + .toArray(); + + for (const auto uid : ids) + typingUserIDs_.push_back(uid.toString()); + } + } } } diff --git a/src/TextInputWidget.cc b/src/TextInputWidget.cc index 5f06d992..4d5f4d5f 100644 --- a/src/TextInputWidget.cc +++ b/src/TextInputWidget.cc @@ -45,13 +45,14 @@ TextInputWidget::TextInputWidget(QWidget *parent) { setFont(QFont("Emoji One")); + setFixedHeight(45); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); setCursor(Qt::ArrowCursor); - setStyleSheet("background-color: #fff; height: 45px;"); + setStyleSheet("background-color: #fff;"); topLayout_ = new QHBoxLayout(); - topLayout_->setSpacing(2); - topLayout_->setMargin(4); + topLayout_->setSpacing(0); + topLayout_->setContentsMargins(5, 15, 0, 5); QIcon send_file_icon; send_file_icon.addFile(":/icons/icons/clip-dark.png", QSize(), QIcon::Normal, QIcon::Off); @@ -63,18 +64,19 @@ TextInputWidget::TextInputWidget(QWidget *parent) spinner_ = new LoadingIndicator(this); spinner_->setColor("#acc7dc"); - spinner_->setFixedHeight(40); - spinner_->setFixedWidth(40); + spinner_->setFixedHeight(32); + spinner_->setFixedWidth(32); spinner_->hide(); QFont font; font.setPixelSize(conf::fontSize); input_ = new FilteredTextEdit(this); - input_->setFixedHeight(45); + input_->setFixedHeight(32); input_->setFont(font); + input_->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); input_->setPlaceholderText(tr("Write a message...")); - input_->setStyleSheet("color: #333333; border-radius: 0; padding-top: 10px;"); + input_->setStyleSheet("color: #333333; border: none; margin: 0 5px"); sendMessageBtn_ = new FlatButton(this); sendMessageBtn_->setForegroundColor(QColor("#acc7dc")); diff --git a/src/TypingDisplay.cc b/src/TypingDisplay.cc new file mode 100644 index 00000000..619b70cb --- /dev/null +++ b/src/TypingDisplay.cc @@ -0,0 +1,56 @@ +#include +#include +#include + +#include "Config.h" +#include "TypingDisplay.h" + +TypingDisplay::TypingDisplay(QWidget *parent) + : QWidget(parent) + , leftPadding_{ 57 } +{ + QFont font; + font.setPixelSize(conf::typingNotificationFontSize); + + setFixedHeight(QFontMetrics(font).height() + 2); +} + +void +TypingDisplay::setUsers(const QStringList &uid) +{ + if (uid.isEmpty()) + text_.clear(); + else + text_ = uid.join(", "); + + if (uid.size() == 1) + text_ += tr(" is typing ..."); + else if (uid.size() > 1) + text_ += tr(" are typing ..."); + + update(); +} + +void +TypingDisplay::paintEvent(QPaintEvent *) +{ + QPen pen(QColor("#333")); + + QFont font; + font.setPixelSize(conf::typingNotificationFontSize); + font.setWeight(40); + font.setItalic(true); + + QPainter p(this); + p.setRenderHint(QPainter::Antialiasing); + p.setFont(font); + p.setPen(pen); + + QRect region = rect(); + region.translate(leftPadding_, 0); + + QFontMetrics fm(font); + text_ = fm.elidedText(text_, Qt::ElideRight, width() - 3 * leftPadding_); + + p.drawText(region, Qt::AlignTop, text_); +} From 28b3a3fde02e5fc422bfc44cfbedb940815b823b Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Wed, 4 Oct 2017 22:00:26 +0300 Subject: [PATCH 04/64] Put typing notifications above the text input --- include/Config.h | 1 + src/ChatPage.cc | 4 ++-- src/TextInputWidget.cc | 8 ++++---- src/TypingDisplay.cc | 13 ++++++------- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/include/Config.h b/include/Config.h index 50f9eb85..2412a4d0 100644 --- a/include/Config.h +++ b/include/Config.h @@ -8,6 +8,7 @@ namespace conf { // Global settings. static const int fontSize = 12; +static const int textInputFontSize = 14; static const int emojiSize = 14; static const int headerFontSize = 21; static const int typingNotificationFontSize = 11; diff --git a/src/ChatPage.cc b/src/ChatPage.cc index 52468f64..0800fe32 100644 --- a/src/ChatPage.cc +++ b/src/ChatPage.cc @@ -103,8 +103,8 @@ ChatPage::ChatPage(QSharedPointer client, QWidget *parent) text_input_ = new TextInputWidget(this); typingDisplay_ = new TypingDisplay(this); - contentLayout_->addWidget(text_input_); contentLayout_->addWidget(typingDisplay_); + contentLayout_->addWidget(text_input_); user_info_widget_ = new UserInfoWidget(sideBarTopWidget_); sideBarTopWidgetLayout_->addWidget(user_info_widget_); @@ -298,7 +298,7 @@ ChatPage::syncFailed(const QString &msg) return; qWarning() << "Sync error:" << msg; - sync_timer_->start(sync_interval_ * 5); + sync_timer_->start(sync_interval_); } // TODO: Should be moved in another class that manages this global list. diff --git a/src/TextInputWidget.cc b/src/TextInputWidget.cc index 4d5f4d5f..f894a247 100644 --- a/src/TextInputWidget.cc +++ b/src/TextInputWidget.cc @@ -45,14 +45,14 @@ TextInputWidget::TextInputWidget(QWidget *parent) { setFont(QFont("Emoji One")); - setFixedHeight(45); + setFixedHeight(50); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); setCursor(Qt::ArrowCursor); setStyleSheet("background-color: #fff;"); topLayout_ = new QHBoxLayout(); topLayout_->setSpacing(0); - topLayout_->setContentsMargins(5, 15, 0, 5); + topLayout_->setContentsMargins(15, 0, 15, 5); QIcon send_file_icon; send_file_icon.addFile(":/icons/icons/clip-dark.png", QSize(), QIcon::Normal, QIcon::Off); @@ -69,14 +69,14 @@ TextInputWidget::TextInputWidget(QWidget *parent) spinner_->hide(); QFont font; - font.setPixelSize(conf::fontSize); + font.setPixelSize(conf::textInputFontSize); input_ = new FilteredTextEdit(this); input_->setFixedHeight(32); input_->setFont(font); input_->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); input_->setPlaceholderText(tr("Write a message...")); - input_->setStyleSheet("color: #333333; border: none; margin: 0 5px"); + input_->setStyleSheet("color: #333333; border: none; padding-top: 5px; margin: 0 5px"); sendMessageBtn_ = new FlatButton(this); sendMessageBtn_->setForegroundColor(QColor("#acc7dc")); diff --git a/src/TypingDisplay.cc b/src/TypingDisplay.cc index 619b70cb..7792d3e4 100644 --- a/src/TypingDisplay.cc +++ b/src/TypingDisplay.cc @@ -7,7 +7,7 @@ TypingDisplay::TypingDisplay(QWidget *parent) : QWidget(parent) - , leftPadding_{ 57 } + , leftPadding_{ 24 } { QFont font; font.setPixelSize(conf::typingNotificationFontSize); @@ -24,9 +24,9 @@ TypingDisplay::setUsers(const QStringList &uid) text_ = uid.join(", "); if (uid.size() == 1) - text_ += tr(" is typing ..."); + text_ += tr(" is typing"); else if (uid.size() > 1) - text_ += tr(" are typing ..."); + text_ += tr(" are typing"); update(); } @@ -34,11 +34,10 @@ TypingDisplay::setUsers(const QStringList &uid) void TypingDisplay::paintEvent(QPaintEvent *) { - QPen pen(QColor("#333")); + QPen pen(QColor("#898989")); - QFont font; + QFont font("Open Sans Bold"); font.setPixelSize(conf::typingNotificationFontSize); - font.setWeight(40); font.setItalic(true); QPainter p(this); @@ -52,5 +51,5 @@ TypingDisplay::paintEvent(QPaintEvent *) QFontMetrics fm(font); text_ = fm.elidedText(text_, Qt::ElideRight, width() - 3 * leftPadding_); - p.drawText(region, Qt::AlignTop, text_); + p.drawText(region, Qt::AlignVCenter, text_); } From 11a32821365ccd6390f1630b03f16fbf32129d74 Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Wed, 4 Oct 2017 22:11:55 +0300 Subject: [PATCH 05/64] Remove full_state from initial sync --- src/MatrixClient.cc | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/src/MatrixClient.cc b/src/MatrixClient.cc index 265b51ce..70fa4205 100644 --- a/src/MatrixClient.cc +++ b/src/MatrixClient.cc @@ -610,11 +610,12 @@ MatrixClient::registerUser(const QString &user, const QString &pass, const QStri void MatrixClient::sync() noexcept { - QJsonObject filter{ { "room", - QJsonObject{ - { "include_leave", true }, - } }, - { "presence", QJsonObject{ { "limit", 0 } } } }; + QJsonObject filter{ + { "room", + QJsonObject{ + { "include_leave", true }, + } }, + }; QUrlQuery query; query.addQueryItem("set_presence", "online"); @@ -686,19 +687,8 @@ MatrixClient::sendRoomMessage(matrix::events::MessageEventType ty, void MatrixClient::initialSync() noexcept { - QJsonArray excluded_presence = { - QString("m.presence"), - }; - - QJsonObject filter{ { "room", - QJsonObject{ { "timeline", QJsonObject{ { "limit", 20 } } }, - { "ephemeral", QJsonObject{ { "limit", 0 } } } } }, - { "presence", QJsonObject{ { "not_types", excluded_presence } } } }; - QUrlQuery query; - query.addQueryItem("full_state", "true"); - query.addQueryItem("set_presence", "online"); - query.addQueryItem("filter", QJsonDocument(filter).toJson(QJsonDocument::Compact)); + query.addQueryItem("timeout", 0); query.addQueryItem("access_token", token_); QUrl endpoint(server_); From 1fc68c5b7f9e77959c5bfaefb8427e91470b40b3 Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Thu, 5 Oct 2017 08:47:29 +0300 Subject: [PATCH 06/64] Use string timeout parameter --- src/MatrixClient.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/MatrixClient.cc b/src/MatrixClient.cc index 70fa4205..82643797 100644 --- a/src/MatrixClient.cc +++ b/src/MatrixClient.cc @@ -636,6 +636,7 @@ MatrixClient::sync() noexcept endpoint.setQuery(query); QNetworkRequest request(QString(endpoint.toEncoded())); + request.setRawHeader("Connection", "keep-alive"); QNetworkReply *reply = get(request); reply->setProperty("endpoint", static_cast(Endpoint::Sync)); @@ -688,7 +689,7 @@ void MatrixClient::initialSync() noexcept { QUrlQuery query; - query.addQueryItem("timeout", 0); + query.addQueryItem("timeout", "0"); query.addQueryItem("access_token", token_); QUrl endpoint(server_); @@ -696,6 +697,7 @@ MatrixClient::initialSync() noexcept endpoint.setQuery(query); QNetworkRequest request(QString(endpoint.toEncoded())); + request.setRawHeader("Connection", "keep-alive"); QNetworkReply *reply = get(request); reply->setProperty("endpoint", static_cast(Endpoint::InitialSync)); From 985530e99b62b253790ed808cf182afe0689af5e Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Thu, 5 Oct 2017 18:13:11 +0300 Subject: [PATCH 07/64] Resume sync when connectivity is established --- include/MatrixClient.h | 2 +- src/MatrixClient.cc | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/include/MatrixClient.h b/include/MatrixClient.h index cd023650..c87f0668 100644 --- a/include/MatrixClient.h +++ b/include/MatrixClient.h @@ -50,7 +50,7 @@ public: void fetchUserAvatar(const QString &userId, const QUrl &avatarUrl); void fetchOwnAvatar(const QUrl &avatar_url); void downloadImage(const QString &event_id, const QUrl &url); - void messages(const QString &room_id, const QString &from_token, int limit = 20) noexcept; + void messages(const QString &room_id, const QString &from_token, int limit = 30) noexcept; void uploadImage(const QString &roomid, const QString &filename); void joinRoom(const QString &roomIdOrAlias); void leaveRoom(const QString &roomId); diff --git a/src/MatrixClient.cc b/src/MatrixClient.cc index 82643797..8c10a0a2 100644 --- a/src/MatrixClient.cc +++ b/src/MatrixClient.cc @@ -44,6 +44,13 @@ MatrixClient::MatrixClient(QString server, QObject *parent) txn_id_ = settings.value("client/transaction_id", 1).toInt(); connect(this, SIGNAL(finished(QNetworkReply *)), this, SLOT(onResponse(QNetworkReply *))); + connect(this, + &QNetworkAccessManager::networkAccessibleChanged, + this, + [=](NetworkAccessibility status) { + if (status != NetworkAccessibility::Accessible) + setNetworkAccessible(NetworkAccessibility::Accessible); + }); } void From 800d9ecff35804968d9c2870adce8b1c67482275 Mon Sep 17 00:00:00 2001 From: RiotTranslate Date: Thu, 5 Oct 2017 18:49:15 +0200 Subject: [PATCH 08/64] Update from Weblate. (#38) Minor translation fixes --- resources/langs/nheko_el.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/resources/langs/nheko_el.ts b/resources/langs/nheko_el.ts index 01861011..a57f545e 100644 --- a/resources/langs/nheko_el.ts +++ b/resources/langs/nheko_el.ts @@ -139,7 +139,7 @@ - Passwords don't match + Passwords don't match Οι κωδικοί δεν ταιριάζουν @@ -174,7 +174,7 @@ Welcome to nheko! The desktop client for the Matrix protocol. - Καλώς ήρθες στο nheko! + Καλώς ήρθες στο nheko! From bc4b47a5e30386df46f5d4d51a5be33b6a9d4c2f Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Sat, 7 Oct 2017 20:09:34 +0300 Subject: [PATCH 09/64] Use shared pointer for the modals --- include/ChatPage.h | 4 ++-- include/EmojiPickButton.h | 2 +- include/MainWindow.h | 4 ++-- include/RoomList.h | 4 ++-- include/TopRoomBar.h | 4 ++-- include/UserInfoWidget.h | 4 ++-- src/ChatPage.cc | 19 ++++++++++++------- src/EmojiPickButton.cc | 7 ++++--- src/MainWindow.cc | 36 ++++++++++++++++++------------------ src/RoomList.cc | 25 ++++++++++++++++--------- src/TopRoomBar.cc | 25 +++++++++++++++++-------- src/UserInfoWidget.cc | 13 +++++++------ 12 files changed, 85 insertions(+), 62 deletions(-) diff --git a/include/ChatPage.h b/include/ChatPage.h index 8332225b..f64d9589 100644 --- a/include/ChatPage.h +++ b/include/ChatPage.h @@ -110,8 +110,8 @@ private: // Keeps track of the users currently typing on each room. QMap> typingUsers_; - QuickSwitcher *quickSwitcher_ = nullptr; - OverlayModal *quickSwitcherModal_ = nullptr; + QSharedPointer quickSwitcher_; + QSharedPointer quickSwitcherModal_; // Matrix Client API provider. QSharedPointer client_; diff --git a/include/EmojiPickButton.h b/include/EmojiPickButton.h index 041e25a6..d4226165 100644 --- a/include/EmojiPickButton.h +++ b/include/EmojiPickButton.h @@ -43,5 +43,5 @@ private: // Horizontal distance from panel's bottom right corner. int horizontal_distance_ = 70; - EmojiPanel *panel_; + QSharedPointer panel_; }; diff --git a/include/MainWindow.h b/include/MainWindow.h index 85cd5a70..a7a2b2e6 100644 --- a/include/MainWindow.h +++ b/include/MainWindow.h @@ -83,8 +83,8 @@ private: ChatPage *chat_page_; // Used to hide undefined states between page transitions. - OverlayModal *progress_modal_; - LoadingIndicator *spinner_; + QSharedPointer progressModal_; + QSharedPointer spinner_; // Matrix Client API provider. QSharedPointer client_; diff --git a/include/RoomList.h b/include/RoomList.h index c2f4255d..d7b201f1 100644 --- a/include/RoomList.h +++ b/include/RoomList.h @@ -76,8 +76,8 @@ private: OverlayModal *joinRoomModal_; JoinRoomDialog *joinRoomDialog_; - OverlayModal *leaveRoomModal; - LeaveRoomDialog *leaveRoomDialog_; + QSharedPointer leaveRoomModal_; + QSharedPointer leaveRoomDialog_; QMap> rooms_; diff --git a/include/TopRoomBar.h b/include/TopRoomBar.h index 5d8b394e..2c7af218 100644 --- a/include/TopRoomBar.h +++ b/include/TopRoomBar.h @@ -77,8 +77,8 @@ private: FlatButton *settingsBtn_; - OverlayModal *leaveRoomModal; - LeaveRoomDialog *leaveRoomDialog_; + QSharedPointer leaveRoomModal_; + QSharedPointer leaveRoomDialog_; Avatar *avatar_; diff --git a/include/UserInfoWidget.h b/include/UserInfoWidget.h index 25dc2265..b2c7b876 100644 --- a/include/UserInfoWidget.h +++ b/include/UserInfoWidget.h @@ -68,8 +68,8 @@ private: QImage avatar_image_; - OverlayModal *logoutModal_; - LogoutDialog *logoutDialog_; + QSharedPointer logoutModal_; + QSharedPointer logoutDialog_; int logoutButtonSize_; }; diff --git a/src/ChatPage.cc b/src/ChatPage.cc index 0800fe32..dbffc6d0 100644 --- a/src/ChatPage.cc +++ b/src/ChatPage.cc @@ -572,21 +572,26 @@ ChatPage::keyPressEvent(QKeyEvent *event) void ChatPage::showQuickSwitcher() { - if (quickSwitcher_ == nullptr) { - quickSwitcher_ = new QuickSwitcher(this); + if (quickSwitcher_.isNull()) { + quickSwitcher_ = QSharedPointer( + new QuickSwitcher(this), + [=](QuickSwitcher *switcher) { switcher->deleteLater(); }); - connect(quickSwitcher_, + connect(quickSwitcher_.data(), &QuickSwitcher::roomSelected, room_list_, &RoomList::highlightSelectedRoom); - connect(quickSwitcher_, &QuickSwitcher::closing, this, [=]() { - if (this->quickSwitcherModal_ != nullptr) + + connect(quickSwitcher_.data(), &QuickSwitcher::closing, this, [=]() { + if (!this->quickSwitcherModal_.isNull()) this->quickSwitcherModal_->fadeOut(); }); } - if (quickSwitcherModal_ == nullptr) { - quickSwitcherModal_ = new OverlayModal(MainWindow::instance(), quickSwitcher_); + if (quickSwitcherModal_.isNull()) { + quickSwitcherModal_ = QSharedPointer( + new OverlayModal(MainWindow::instance(), quickSwitcher_.data()), + [=](OverlayModal *modal) { modal->deleteLater(); }); quickSwitcherModal_->setDuration(0); quickSwitcherModal_->setColor(QColor(30, 30, 30, 170)); } diff --git a/src/EmojiPickButton.cc b/src/EmojiPickButton.cc index f3e30661..4f7dd59e 100644 --- a/src/EmojiPickButton.cc +++ b/src/EmojiPickButton.cc @@ -30,9 +30,10 @@ EmojiPickButton::enterEvent(QEvent *e) { Q_UNUSED(e); - if (panel_ == nullptr) { - panel_ = new EmojiPanel(this); - connect(panel_, &EmojiPanel::emojiSelected, this, &EmojiPickButton::emojiSelected); + if (panel_.isNull()) { + panel_ = QSharedPointer(new EmojiPanel(this)); + connect( + panel_.data(), &EmojiPanel::emojiSelected, this, &EmojiPickButton::emojiSelected); } QPoint pos(rect().x(), rect().y()); diff --git a/src/MainWindow.cc b/src/MainWindow.cc index 43f4a949..80726683 100644 --- a/src/MainWindow.cc +++ b/src/MainWindow.cc @@ -15,8 +15,8 @@ * along with this program. If not, see . */ -#include "MainWindow.h" #include "Config.h" +#include "MainWindow.h" #include #include @@ -29,7 +29,7 @@ MainWindow *MainWindow::instance_ = nullptr; MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) - , progress_modal_{ nullptr } + , progressModal_{ nullptr } , spinner_{ nullptr } { QSizePolicy sizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); @@ -132,18 +132,14 @@ MainWindow::removeOverlayProgressBar() connect(timer, &QTimer::timeout, [=]() { timer->deleteLater(); - if (progress_modal_ != nullptr) { - progress_modal_->deleteLater(); - progress_modal_->fadeOut(); - } + if (!progressModal_.isNull()) + progressModal_->fadeOut(); - if (spinner_ != nullptr) - spinner_->deleteLater(); + if (!spinner_.isNull()) + spinner_->stop(); - spinner_->stop(); - - progress_modal_ = nullptr; - spinner_ = nullptr; + progressModal_.reset(); + spinner_.reset(); }); timer->start(500); @@ -166,18 +162,22 @@ MainWindow::showChatPage(QString userid, QString homeserver, QString token) QTimer::singleShot( modalOpacityDuration + 100, this, [=]() { pageStack_->setCurrentWidget(chat_page_); }); - if (spinner_ == nullptr) { - spinner_ = new LoadingIndicator(this); + if (spinner_.isNull()) { + spinner_ = QSharedPointer( + new LoadingIndicator(this), + [=](LoadingIndicator *indicator) { indicator->deleteLater(); }); spinner_->setColor("#acc7dc"); spinner_->setFixedHeight(120); spinner_->setFixedWidth(120); spinner_->start(); } - if (progress_modal_ == nullptr) { - progress_modal_ = new OverlayModal(this, spinner_); - progress_modal_->fadeIn(); - progress_modal_->setDuration(modalOpacityDuration); + if (progressModal_.isNull()) { + progressModal_ = + QSharedPointer(new OverlayModal(this, spinner_.data()), + [=](OverlayModal *modal) { modal->deleteLater(); }); + progressModal_->fadeIn(); + progressModal_->setDuration(modalOpacityDuration); } login_page_->reset(); diff --git a/src/RoomList.cc b/src/RoomList.cc index 0383be6e..90d8b83c 100644 --- a/src/RoomList.cc +++ b/src/RoomList.cc @@ -168,16 +168,23 @@ RoomList::setInitialRooms(const QMap> &set void RoomList::openLeaveRoomDialog(const QString &room_id) { - leaveRoomDialog_ = new LeaveRoomDialog(this); - connect(leaveRoomDialog_, &LeaveRoomDialog::closing, this, [=](bool leaving) { - closeLeaveRoomDialog(leaving, room_id); - }); + if (leaveRoomDialog_.isNull()) { + leaveRoomDialog_ = QSharedPointer(new LeaveRoomDialog(this)); - leaveRoomModal = new OverlayModal(MainWindow::instance(), leaveRoomDialog_); - leaveRoomModal->setDuration(0); - leaveRoomModal->setColor(QColor(55, 55, 55, 170)); + connect(leaveRoomDialog_.data(), + &LeaveRoomDialog::closing, + this, + [=](bool leaving) { closeLeaveRoomDialog(leaving, room_id); }); + } - leaveRoomModal->fadeIn(); + if (leaveRoomModal_.isNull()) { + leaveRoomModal_ = QSharedPointer( + new OverlayModal(MainWindow::instance(), leaveRoomDialog_.data())); + leaveRoomModal_->setDuration(0); + leaveRoomModal_->setColor(QColor(30, 30, 30, 170)); + } + + leaveRoomModal_->fadeIn(); } void @@ -266,7 +273,7 @@ RoomList::closeJoinRoomDialog(bool isJoining, QString roomAlias) void RoomList::closeLeaveRoomDialog(bool leaving, const QString &room_id) { - leaveRoomModal->fadeOut(); + leaveRoomModal_->fadeOut(); if (leaving) { client_->leaveRoom(room_id); diff --git a/src/TopRoomBar.cc b/src/TopRoomBar.cc index f8a7e600..3f93cad3 100644 --- a/src/TopRoomBar.cc +++ b/src/TopRoomBar.cc @@ -86,15 +86,24 @@ TopRoomBar::TopRoomBar(QWidget *parent) leaveRoom_ = new QAction(tr("Leave room"), this); connect(leaveRoom_, &QAction::triggered, this, [=]() { - leaveRoomDialog_ = new LeaveRoomDialog(this); - connect( - leaveRoomDialog_, SIGNAL(closing(bool)), this, SLOT(closeLeaveRoomDialog(bool))); + if (leaveRoomDialog_.isNull()) { + leaveRoomDialog_ = + QSharedPointer(new LeaveRoomDialog(this)); - leaveRoomModal = new OverlayModal(MainWindow::instance(), leaveRoomDialog_); - leaveRoomModal->setDuration(100); - leaveRoomModal->setColor(QColor(55, 55, 55, 170)); + connect(leaveRoomDialog_.data(), + SIGNAL(closing(bool)), + this, + SLOT(closeLeaveRoomDialog(bool))); + } - leaveRoomModal->fadeIn(); + if (leaveRoomModal_.isNull()) { + leaveRoomModal_ = QSharedPointer( + new OverlayModal(MainWindow::instance(), leaveRoomDialog_.data())); + leaveRoomModal_->setDuration(0); + leaveRoomModal_->setColor(QColor(30, 30, 30, 170)); + } + + leaveRoomModal_->fadeIn(); }); menu_->addAction(toggleNotifications_); @@ -117,7 +126,7 @@ TopRoomBar::TopRoomBar(QWidget *parent) void TopRoomBar::closeLeaveRoomDialog(bool leaving) { - leaveRoomModal->fadeOut(); + leaveRoomModal_->fadeOut(); if (leaving) { emit leaveRoom(); diff --git a/src/UserInfoWidget.cc b/src/UserInfoWidget.cc index b65329da..09a75a3b 100644 --- a/src/UserInfoWidget.cc +++ b/src/UserInfoWidget.cc @@ -93,18 +93,19 @@ UserInfoWidget::UserInfoWidget(QWidget *parent) // Show the confirmation dialog. connect(logoutButton_, &QPushButton::clicked, this, [=]() { - if (logoutDialog_ == nullptr) { - logoutDialog_ = new LogoutDialog(this); - connect(logoutDialog_, + if (logoutDialog_.isNull()) { + logoutDialog_ = QSharedPointer(new LogoutDialog(this)); + connect(logoutDialog_.data(), SIGNAL(closing(bool)), this, SLOT(closeLogoutDialog(bool))); } - if (logoutModal_ == nullptr) { - logoutModal_ = new OverlayModal(MainWindow::instance(), logoutDialog_); + if (logoutModal_.isNull()) { + logoutModal_ = QSharedPointer( + new OverlayModal(MainWindow::instance(), logoutDialog_.data())); logoutModal_->setDuration(0); - logoutModal_->setColor(QColor(55, 55, 55, 170)); + logoutModal_->setColor(QColor(30, 30, 30, 170)); } logoutModal_->fadeIn(); From 6e1285bb0e1f1d2b4aa443c72521c9b1f3255bfb Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Sat, 7 Oct 2017 20:50:32 +0300 Subject: [PATCH 10/64] Prevent FOUC --- include/ChatPage.h | 1 + include/TimelineView.h | 9 +++++++++ include/TimelineViewManager.h | 3 +++ src/ChatPage.cc | 13 +++++++++++-- src/TimelineViewManager.cc | 10 ++++++++++ 5 files changed, 34 insertions(+), 2 deletions(-) diff --git a/include/ChatPage.h b/include/ChatPage.h index f64d9589..0a6d303b 100644 --- a/include/ChatPage.h +++ b/include/ChatPage.h @@ -96,6 +96,7 @@ private: TextInputWidget *text_input_; TypingDisplay *typingDisplay_; + QTimer *consensusTimer_; QTimer *sync_timer_; int sync_interval_; diff --git a/include/TimelineView.h b/include/TimelineView.h index 4b5a2f77..9b81485d 100644 --- a/include/TimelineView.h +++ b/include/TimelineView.h @@ -97,6 +97,9 @@ public slots: // Add old events at the top of the timeline. void addBackwardsEvents(const QString &room_id, const RoomMessages &msgs); + // Whether or not the initial batch has been loaded. + bool hasLoaded(); + signals: void updateLastTimelineMessage(const QString &user, const DescInfo &info); @@ -163,3 +166,9 @@ TimelineView::isDuplicate(const QString &event_id) { return eventIds_.contains(event_id); } + +inline bool +TimelineView::hasLoaded() +{ + return scroll_layout_->count() > 1 || isTimelineFinished; +} diff --git a/include/TimelineViewManager.h b/include/TimelineViewManager.h index 35dcac5a..91fda996 100644 --- a/include/TimelineViewManager.h +++ b/include/TimelineViewManager.h @@ -47,6 +47,9 @@ public: void sync(const Rooms &rooms); void clearAll(); + // Check if all the timelines have been loaded. + bool hasLoaded() const; + static QString chooseRandomColor(); static QString displayName(const QString &userid); diff --git a/src/ChatPage.cc b/src/ChatPage.cc index dbffc6d0..d3f60494 100644 --- a/src/ChatPage.cc +++ b/src/ChatPage.cc @@ -213,6 +213,15 @@ ChatPage::ChatPage(QSharedPointer client, QWidget *parent) this, SLOT(removeRoom(const QString &))); + consensusTimer_ = new QTimer(this); + connect(consensusTimer_, &QTimer::timeout, this, [=]() { + if (view_manager_->hasLoaded()) { + // Remove the spinner overlay. + emit contentLoaded(); + consensusTimer_->stop(); + } + }); + AvatarProvider::init(client); } @@ -554,8 +563,8 @@ ChatPage::loadStateFromCache() // Initialize room list from the restored state and settings. room_list_->setInitialRooms(settingsManager_, state_manager_); - // Remove the spinner overlay. - emit contentLoaded(); + // Check periodically if the timelines have been loaded. + consensusTimer_->start(500); sync_timer_->start(sync_interval_); } diff --git a/src/TimelineViewManager.cc b/src/TimelineViewManager.cc index 1969ae5b..9f8137fc 100644 --- a/src/TimelineViewManager.cc +++ b/src/TimelineViewManager.cc @@ -256,3 +256,13 @@ TimelineViewManager::displayName(const QString &userid) return userid; } + +bool +TimelineViewManager::hasLoaded() const +{ + for (const auto &view : views_) + if (!view->hasLoaded()) + return false; + + return true; +} From ebe36b5713c5c85b8b85d1c15653f3f66b342823 Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Sun, 8 Oct 2017 21:35:37 +0300 Subject: [PATCH 11/64] Drop the loading screen if consensus can't be achieved --- include/ChatPage.h | 9 +++++++-- src/ChatPage.cc | 33 ++++++++++++++++++++++----------- src/MatrixClient.cc | 2 +- 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/include/ChatPage.h b/include/ChatPage.h index 0a6d303b..ad1ec9e3 100644 --- a/include/ChatPage.h +++ b/include/ChatPage.h @@ -34,6 +34,10 @@ #include "TypingDisplay.h" #include "UserInfoWidget.h" +constexpr int CONSENSUS_TIMEOUT = 1000; +constexpr int SHOW_CONTENT_TIMEOUT = 3000; +constexpr int SYNC_INTERVAL = 2000; + class ChatPage : public QWidget { Q_OBJECT @@ -96,9 +100,10 @@ private: TextInputWidget *text_input_; TypingDisplay *typingDisplay_; + // Safety net if consensus is not possible or too slow. + QTimer *showContentTimer_; QTimer *consensusTimer_; - QTimer *sync_timer_; - int sync_interval_; + QTimer *syncTimer_; QString current_room_; QMap room_avatars_; diff --git a/src/ChatPage.cc b/src/ChatPage.cc index d3f60494..5648a830 100644 --- a/src/ChatPage.cc +++ b/src/ChatPage.cc @@ -33,7 +33,6 @@ namespace events = matrix::events; ChatPage::ChatPage(QSharedPointer client, QWidget *parent) : QWidget(parent) - , sync_interval_(2000) , client_(client) { setStyleSheet("background-color: #fff;"); @@ -109,9 +108,9 @@ ChatPage::ChatPage(QSharedPointer client, QWidget *parent) user_info_widget_ = new UserInfoWidget(sideBarTopWidget_); sideBarTopWidgetLayout_->addWidget(user_info_widget_); - sync_timer_ = new QTimer(this); - sync_timer_->setSingleShot(true); - connect(sync_timer_, SIGNAL(timeout()), this, SLOT(startSync())); + syncTimer_ = new QTimer(this); + syncTimer_->setSingleShot(true); + connect(syncTimer_, SIGNAL(timeout()), this, SLOT(startSync())); connect(user_info_widget_, SIGNAL(logout()), client_.data(), SLOT(logout())); connect(client_.data(), SIGNAL(loggedOut()), this, SLOT(logout())); @@ -213,11 +212,19 @@ ChatPage::ChatPage(QSharedPointer client, QWidget *parent) this, SLOT(removeRoom(const QString &))); + showContentTimer_ = new QTimer(this); + showContentTimer_->setSingleShot(true); + connect(showContentTimer_, &QTimer::timeout, this, [=]() { + consensusTimer_->stop(); + emit contentLoaded(); + }); + consensusTimer_ = new QTimer(this); connect(consensusTimer_, &QTimer::timeout, this, [=]() { if (view_manager_->hasLoaded()) { // Remove the spinner overlay. emit contentLoaded(); + showContentTimer_->stop(); consensusTimer_->stop(); } }); @@ -228,7 +235,7 @@ ChatPage::ChatPage(QSharedPointer client, QWidget *parent) void ChatPage::logout() { - sync_timer_->stop(); + syncTimer_->stop(); // Delete all config parameters. QSettings settings; @@ -307,7 +314,7 @@ ChatPage::syncFailed(const QString &msg) return; qWarning() << "Sync error:" << msg; - sync_timer_->start(sync_interval_); + syncTimer_->start(SYNC_INTERVAL); } // TODO: Should be moved in another class that manages this global list. @@ -404,7 +411,7 @@ ChatPage::syncCompleted(const SyncResponse &response) room_list_->sync(state_manager_); view_manager_->sync(response.rooms()); - sync_timer_->start(sync_interval_); + syncTimer_->start(SYNC_INTERVAL); } void @@ -457,7 +464,7 @@ ChatPage::initialSyncCompleted(const SyncResponse &response) // Initialize room list. room_list_->setInitialRooms(settingsManager_, state_manager_); - sync_timer_->start(sync_interval_); + syncTimer_->start(SYNC_INTERVAL); emit contentLoaded(); } @@ -564,9 +571,13 @@ ChatPage::loadStateFromCache() room_list_->setInitialRooms(settingsManager_, state_manager_); // Check periodically if the timelines have been loaded. - consensusTimer_->start(500); + consensusTimer_->start(CONSENSUS_TIMEOUT); - sync_timer_->start(sync_interval_); + // Show the content if consensus can't be achieved. + showContentTimer_->start(SHOW_CONTENT_TIMEOUT); + + // Start receiving events. + syncTimer_->start(SYNC_INTERVAL); } void @@ -665,5 +676,5 @@ ChatPage::updateTypingUsers(const QString &roomid, const QList &user_id ChatPage::~ChatPage() { - sync_timer_->stop(); + syncTimer_->stop(); } diff --git a/src/MatrixClient.cc b/src/MatrixClient.cc index 8c10a0a2..708e1176 100644 --- a/src/MatrixClient.cc +++ b/src/MatrixClient.cc @@ -627,7 +627,7 @@ MatrixClient::sync() noexcept QUrlQuery query; query.addQueryItem("set_presence", "online"); query.addQueryItem("filter", QJsonDocument(filter).toJson(QJsonDocument::Compact)); - query.addQueryItem("timeout", "30000"); + query.addQueryItem("timeout", "15000"); query.addQueryItem("access_token", token_); if (next_batch_.isEmpty()) { From ac525970b086443bdb17899ff314aef6a5bdba06 Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Sun, 8 Oct 2017 16:49:56 +0300 Subject: [PATCH 12/64] Add snackbar --- CMakeLists.txt | 32 +++++----- include/ui/SnackBar.h | 80 +++++++++++++++++++++++ src/ui/SnackBar.cc | 143 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 237 insertions(+), 18 deletions(-) create mode 100644 include/ui/SnackBar.h create mode 100644 src/ui/SnackBar.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 99ae1d62..6415b912 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -171,6 +171,7 @@ set(SRC_FILES src/ui/FlatButton.cc src/ui/OverlayModal.cc src/ui/ScrollBar.cc + src/ui/SnackBar.cc src/ui/RaisedButton.cc src/ui/Ripple.cc src/ui/RippleOverlay.cc @@ -249,6 +250,7 @@ qt5_wrap_cpp(MOC_HEADERS include/ui/FlatButton.h include/ui/OverlayWidget.h include/ui/ScrollBar.h + include/ui/SnackBar.h include/ui/RaisedButton.h include/ui/Ripple.h include/ui/RippleOverlay.h @@ -292,9 +294,6 @@ if (APPLE) endif() if (BUILD_TESTS) - # - # Build tests. - # enable_testing() find_package(GTest REQUIRED) @@ -312,26 +311,23 @@ if (BUILD_TESTS) add_test(MatrixEvents events_test) add_test(MatrixEventCollection event_collection_test) add_test(MatrixMessageEvents message_events) -else() - # - # Build the executable. - # +endif() + if(APPVEYOR_BUILD) set (NHEKO_LIBS matrix_events Qt5::Widgets Qt5::Network lmdb) else() set (NHEKO_LIBS matrix_events Qt5::Widgets Qt5::Network ${LMDB_LIBRARY}) endif() - set (NHEKO_DEPS ${OS_BUNDLE} ${SRC_FILES} ${UI_HEADERS} ${MOC_HEADERS} ${QRC} ${LANG_QRC} ${QM_SRC}) +set (NHEKO_DEPS ${OS_BUNDLE} ${SRC_FILES} ${UI_HEADERS} ${MOC_HEADERS} ${QRC} ${LANG_QRC} ${QM_SRC}) - if(APPLE) - add_executable (nheko ${NHEKO_DEPS}) - target_link_libraries (nheko ${NHEKO_LIBS} Qt5::MacExtras) - elseif(WIN32) - add_executable (nheko ${ICON_FILE} ${NHEKO_DEPS}) - target_link_libraries (nheko ${NTDLIB} ${NHEKO_LIBS} Qt5::WinMain) - else() - add_executable (nheko ${NHEKO_DEPS}) - target_link_libraries (nheko ${NHEKO_LIBS}) - endif() +if(APPLE) + add_executable (nheko ${NHEKO_DEPS}) + target_link_libraries (nheko ${NHEKO_LIBS} Qt5::MacExtras) +elseif(WIN32) + add_executable (nheko ${ICON_FILE} ${NHEKO_DEPS}) + target_link_libraries (nheko ${NTDLIB} ${NHEKO_LIBS} Qt5::WinMain) +else() + add_executable (nheko ${NHEKO_DEPS}) + target_link_libraries (nheko ${NHEKO_LIBS}) endif() diff --git a/include/ui/SnackBar.h b/include/ui/SnackBar.h new file mode 100644 index 00000000..076b7105 --- /dev/null +++ b/include/ui/SnackBar.h @@ -0,0 +1,80 @@ +#pragma once + +#include +#include +#include +#include + +#include "OverlayWidget.h" + +enum class SnackBarPosition { + Bottom, + Top, +}; + +class SnackBar : public OverlayWidget +{ + Q_OBJECT + +public: + explicit SnackBar(QWidget *parent); + ~SnackBar(); + + inline void setBackgroundColor(const QColor &color); + inline void setTextColor(const QColor &color); + inline void setPosition(SnackBarPosition pos); + +public slots: + void showMessage(const QString &msg); + +protected: + void paintEvent(QPaintEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + +private slots: + void onTimeout(); + void hideMessage(); + +private: + void stopTimers(); + void start(); + + QColor bgColor_; + QColor textColor_; + + qreal bgOpacity_; + qreal offset_; + + QList messages_; + + QTimer *showTimer_; + QTimer *hideTimer_; + + int duration_; + int boxWidth_; + int boxHeight_; + int boxPadding_; + + SnackBarPosition position_; +}; + +inline void +SnackBar::setPosition(SnackBarPosition pos) +{ + position_ = pos; + update(); +} + +inline void +SnackBar::setBackgroundColor(const QColor &color) +{ + bgColor_ = color; + update(); +} + +inline void +SnackBar::setTextColor(const QColor &color) +{ + textColor_ = color; + update(); +} diff --git a/src/ui/SnackBar.cc b/src/ui/SnackBar.cc new file mode 100644 index 00000000..673c2f93 --- /dev/null +++ b/src/ui/SnackBar.cc @@ -0,0 +1,143 @@ +#include +#include + +#include "SnackBar.h" + +constexpr int STARTING_OFFSET = 1; + +SnackBar::SnackBar(QWidget *parent) + : OverlayWidget(parent) +{ + bgOpacity_ = 0.9; + duration_ = 6000; + boxWidth_ = 400; + boxHeight_ = 40; + boxPadding_ = 10; + textColor_ = QColor("white"); + bgColor_ = QColor("#333"); + offset_ = STARTING_OFFSET; + position_ = SnackBarPosition::Top; + + QFont font("Open Sans", 14, QFont::Medium); + setFont(font); + + showTimer_ = new QTimer(); + hideTimer_ = new QTimer(); + hideTimer_->setSingleShot(true); + + connect(showTimer_, SIGNAL(timeout()), this, SLOT(onTimeout())); + connect(hideTimer_, SIGNAL(timeout()), this, SLOT(hideMessage())); +} + +SnackBar::~SnackBar() +{ + stopTimers(); + + delete showTimer_; + delete hideTimer_; +} + +void +SnackBar::start() +{ + show(); + raise(); + + showTimer_->start(10); +} + +void +SnackBar::hideMessage() +{ + stopTimers(); + hide(); + + // Moving on to the next message. + messages_.removeFirst(); + + // Reseting the starting position of the widget. + offset_ = STARTING_OFFSET; + + if (!messages_.isEmpty()) + start(); +} + +void +SnackBar::stopTimers() +{ + showTimer_->stop(); + hideTimer_->stop(); +} + +void +SnackBar::showMessage(const QString &msg) +{ + messages_.push_back(msg); + + // There is already an active message. + if (isVisible()) + return; + + start(); +} + +void +SnackBar::onTimeout() +{ + offset_ -= 0.5; + + if (offset_ <= 0.0) { + showTimer_->stop(); + hideTimer_->start(duration_); + } + + update(); +} + +void +SnackBar::mousePressEvent(QMouseEvent *) +{ + hideMessage(); +} + +void +SnackBar::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event) + + if (messages_.isEmpty()) + return; + + auto message_ = messages_.first(); + + QPainter p(this); + p.setRenderHint(QPainter::Antialiasing); + + QBrush brush; + brush.setStyle(Qt::SolidPattern); + brush.setColor(bgColor_); + p.setBrush(brush); + p.setOpacity(bgOpacity_); + + QRect r(0, 0, boxWidth_, boxHeight_); + + p.setPen(Qt::white); + QRect br = p.boundingRect(r, Qt::AlignHCenter | Qt::AlignTop | Qt::TextWordWrap, message_); + + p.setPen(Qt::NoPen); + r = br.united(r).adjusted(-boxPadding_, -boxPadding_, boxPadding_, boxPadding_); + + const qreal s = 1 - offset_; + + if (position_ == SnackBarPosition::Bottom) + p.translate((width() - (r.width() - 2 * boxPadding_)) / 2, + height() - boxPadding_ - s * (r.height())); + else + p.translate((width() - (r.width() - 2 * boxPadding_)) / 2, + s * (r.height()) - 2 * boxPadding_); + + br.moveCenter(r.center()); + p.drawRoundedRect(r.adjusted(0, 0, 0, 3), 3, 3); + p.setPen(textColor_); + p.drawText(br, Qt::AlignHCenter | Qt::AlignTop | Qt::TextWordWrap, message_); +} From 76ddfb792b8e99a14b2adae23cfc221314e98f00 Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Sun, 8 Oct 2017 22:38:38 +0300 Subject: [PATCH 13/64] Add /join command support --- include/ChatPage.h | 1 + include/MainWindow.h | 4 ++++ include/MatrixClient.h | 1 + include/TextInputWidget.h | 3 +++ src/ChatPage.cc | 17 +++++++++++------ src/MainWindow.cc | 11 ++++++++++- src/MatrixClient.cc | 18 ++++++++++++++---- src/TextInputWidget.cc | 16 ++++++++++++++++ src/ui/SnackBar.cc | 2 +- 9 files changed, 61 insertions(+), 12 deletions(-) diff --git a/include/ChatPage.h b/include/ChatPage.h index ad1ec9e3..04464bc5 100644 --- a/include/ChatPage.h +++ b/include/ChatPage.h @@ -54,6 +54,7 @@ signals: void close(); void changeWindowTitle(const QString &msg); void unreadMessages(int count); + void showNotification(const QString &msg); private slots: void showUnreadMessageNotification(int count); diff --git a/include/MainWindow.h b/include/MainWindow.h index a7a2b2e6..0c2316a3 100644 --- a/include/MainWindow.h +++ b/include/MainWindow.h @@ -26,6 +26,7 @@ #include "MatrixClient.h" #include "OverlayModal.h" #include "RegisterPage.h" +#include "SnackBar.h" #include "TrayIcon.h" #include "WelcomePage.h" @@ -91,4 +92,7 @@ private: // Tray icon that shows the unread message count. TrayIcon *trayIcon_; + + // Notifications display. + QSharedPointer snackBar_; }; diff --git a/include/MatrixClient.h b/include/MatrixClient.h index c87f0668..927fe3a6 100644 --- a/include/MatrixClient.h +++ b/include/MatrixClient.h @@ -93,6 +93,7 @@ signals: void initialSyncCompleted(const SyncResponse &response); void syncCompleted(const SyncResponse &response); void syncFailed(const QString &msg); + void joinFailed(const QString &msg); void messageSent(const QString &event_id, const QString &roomid, const int txn_id); void emoteSent(const QString &event_id, const QString &roomid, const int txn_id); void messagesRetrieved(const QString &room_id, const RoomMessages &msgs); diff --git a/include/TextInputWidget.h b/include/TextInputWidget.h index 732f4f61..772bdd46 100644 --- a/include/TextInputWidget.h +++ b/include/TextInputWidget.h @@ -30,6 +30,7 @@ namespace msgs = matrix::events::messages; static const QString EMOTE_COMMAND("/me "); +static const QString JOIN_COMMAND("/join "); class FilteredTextEdit : public QTextEdit { @@ -63,10 +64,12 @@ signals: void sendTextMessage(QString msg); void sendEmoteMessage(QString msg); void uploadImage(QString filename); + void sendJoinRoomRequest(const QString &room); private: void showUploadSpinner(); QString parseEmoteCommand(const QString &cmd); + QString parseJoinCommand(const QString &cmd); QHBoxLayout *topLayout_; FilteredTextEdit *input_; diff --git a/src/ChatPage.cc b/src/ChatPage.cc index 5648a830..92692fc1 100644 --- a/src/ChatPage.cc +++ b/src/ChatPage.cc @@ -166,10 +166,16 @@ ChatPage::ChatPage(QSharedPointer client, QWidget *parent) view_manager_, SLOT(sendEmoteMessage(const QString &))); + connect(text_input_, + &TextInputWidget::sendJoinRoomRequest, + client_.data(), + &MatrixClient::joinRoom); + connect(text_input_, &TextInputWidget::uploadImage, this, [=](QString filename) { client_->uploadImage(current_room_, filename); }); + connect(client_.data(), &MatrixClient::joinFailed, this, &ChatPage::showNotification); connect(client_.data(), &MatrixClient::imageUploaded, this, @@ -203,10 +209,9 @@ ChatPage::ChatPage(QSharedPointer client, QWidget *parent) SIGNAL(ownAvatarRetrieved(const QPixmap &)), this, SLOT(setOwnAvatar(const QPixmap &))); - connect(client_.data(), - SIGNAL(joinedRoom(const QString &)), - this, - SLOT(addRoom(const QString &))); + connect(client_.data(), &MatrixClient::joinedRoom, this, [=]() { + emit showNotification("You joined the room."); + }); connect(client_.data(), SIGNAL(leftRoom(const QString &)), this, @@ -636,9 +641,9 @@ ChatPage::addRoom(const QString &room_id) QSharedPointer(new RoomSettings(room_id))); room_list_->addRoom(settingsManager_[room_id], state_manager_[room_id], room_id); - - this->changeTopRoomInfo(room_id); room_list_->highlightSelectedRoom(room_id); + + changeTopRoomInfo(room_id); } } diff --git a/src/MainWindow.cc b/src/MainWindow.cc index 80726683..06f8245c 100644 --- a/src/MainWindow.cc +++ b/src/MainWindow.cc @@ -15,8 +15,8 @@ * along with this program. If not, see . */ -#include "Config.h" #include "MainWindow.h" +#include "Config.h" #include #include @@ -142,6 +142,15 @@ MainWindow::removeOverlayProgressBar() spinner_.reset(); }); + // FIXME: Snackbar doesn't work if it's initialized in the constructor. + QTimer::singleShot(100, this, [=]() { + snackBar_ = QSharedPointer(new SnackBar(this)); + connect(chat_page_, + &ChatPage::showNotification, + snackBar_.data(), + &SnackBar::showMessage); + }); + timer->start(500); } diff --git a/src/MatrixClient.cc b/src/MatrixClient.cc index 708e1176..e9e47fcb 100644 --- a/src/MatrixClient.cc +++ b/src/MatrixClient.cc @@ -477,13 +477,22 @@ MatrixClient::onJoinRoomResponse(QNetworkReply *reply) int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if (status == 0 || status >= 400) { - qWarning() << reply->errorString(); + auto data = reply->readAll(); + auto response = QJsonDocument::fromJson(data); + auto json = response.object(); + + if (json.contains("error")) + emit joinFailed(json["error"].toString()); + else + qDebug() << reply->errorString(); + return; } - auto data = reply->readAll(); - QJsonDocument response = QJsonDocument::fromJson(data); - QString room_id = response.object()["room_id"].toString(); + auto data = reply->readAll(); + auto response = QJsonDocument::fromJson(data); + auto room_id = response.object()["room_id"].toString(); + emit joinedRoom(room_id); } @@ -899,6 +908,7 @@ MatrixClient::joinRoom(const QString &roomIdOrAlias) QNetworkReply *reply = post(request, "{}"); reply->setProperty("endpoint", static_cast(Endpoint::JoinRoom)); + reply->setProperty("room", roomIdOrAlias); } void diff --git a/src/TextInputWidget.cc b/src/TextInputWidget.cc index f894a247..b90a7caa 100644 --- a/src/TextInputWidget.cc +++ b/src/TextInputWidget.cc @@ -148,6 +148,11 @@ TextInputWidget::onSendButtonClicked() if (!text.isEmpty()) emit sendEmoteMessage(text); + } else if (msgText.startsWith(JOIN_COMMAND)) { + auto room = parseJoinCommand(msgText); + + if (!room.isEmpty()) + emit sendJoinRoomRequest(room); } else { emit sendTextMessage(msgText); } @@ -155,6 +160,17 @@ TextInputWidget::onSendButtonClicked() input_->clear(); } +QString +TextInputWidget::parseJoinCommand(const QString &cmd) +{ + auto room = cmd.right(cmd.size() - JOIN_COMMAND.size()).trimmed(); + + if (!room.isEmpty()) + return room; + + return QString(""); +} + QString TextInputWidget::parseEmoteCommand(const QString &cmd) { diff --git a/src/ui/SnackBar.cc b/src/ui/SnackBar.cc index 673c2f93..39566e99 100644 --- a/src/ui/SnackBar.cc +++ b/src/ui/SnackBar.cc @@ -84,7 +84,7 @@ SnackBar::showMessage(const QString &msg) void SnackBar::onTimeout() { - offset_ -= 0.5; + offset_ -= 1.1; if (offset_ <= 0.0) { showTimer_->stop(); From ac8e42b9265bbdc2b3a386e01787a6ee9cfb0180 Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Mon, 9 Oct 2017 01:32:25 +0300 Subject: [PATCH 14/64] Reduce timeline flickering --- include/TimelineView.h | 7 +++---- src/TimelineView.cc | 35 +++++++++++++++++++++-------------- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/include/TimelineView.h b/include/TimelineView.h index 9b81485d..da73b00a 100644 --- a/include/TimelineView.h +++ b/include/TimelineView.h @@ -140,10 +140,9 @@ private: bool isPaginationInProgress_ = false; // Keeps track whether or not the user has visited the view. - bool isInitialized = false; - bool isTimelineFinished = false; - bool isInitialSync = true; - bool isPaginationScrollPending_ = false; + bool isInitialized = false; + bool isTimelineFinished = false; + bool isInitialSync = true; const int SCROLL_BAR_GAP = 400; diff --git a/src/TimelineView.cc b/src/TimelineView.cc index 032d1310..08b46fdd 100644 --- a/src/TimelineView.cc +++ b/src/TimelineView.cc @@ -85,19 +85,15 @@ TimelineView::sliderRangeChanged(int min, int max) if (max - scroll_area_->verticalScrollBar()->value() < SCROLL_BAR_GAP) scroll_area_->verticalScrollBar()->setValue(max); - if (isPaginationScrollPending_) { - isPaginationScrollPending_ = false; + int currentHeight = scroll_widget_->size().height(); + int diff = currentHeight - oldHeight_; + int newPosition = oldPosition_ + diff; - int currentHeight = scroll_widget_->size().height(); - int diff = currentHeight - oldHeight_; - int newPosition = oldPosition_ + diff; + // Keep the scroll bar to the bottom if it hasn't been activated yet. + if (oldPosition_ == 0 && !scroll_area_->verticalScrollBar()->isVisible()) + newPosition = max; - // Keep the scroll bar to the bottom if it hasn't been activated yet. - if (oldPosition_ == 0 && !scroll_area_->verticalScrollBar()->isVisible()) - newPosition = max; - - scroll_area_->verticalScrollBar()->setValue(newPosition); - } + scroll_area_->verticalScrollBar()->setValue(newPosition); } void @@ -173,6 +169,9 @@ TimelineView::addBackwardsEvents(const QString &room_id, const RoomMessages &msg isTimelineFinished = false; QList items; + scroll_widget_->adjustSize(); + scroll_widget_->update(); + // Parse in reverse order to determine where we should not show sender's // name. auto ii = msgs.chunk().size(); @@ -195,9 +194,8 @@ TimelineView::addBackwardsEvents(const QString &room_id, const RoomMessages &msg for (const auto &item : items) addTimelineItem(item, TimelineDirection::Top); - prev_batch_token_ = msgs.end(); - isPaginationInProgress_ = false; - isPaginationScrollPending_ = true; + prev_batch_token_ = msgs.end(); + isPaginationInProgress_ = false; // Exclude the top stretch. if (!msgs.chunk().isEmpty() && scroll_layout_->count() > 1) @@ -465,6 +463,9 @@ TimelineView::addTimelineItem(TimelineItem *item, TimelineDirection direction) scroll_layout_->addWidget(item); else scroll_layout_->insertWidget(1, item); + + scroll_widget_->adjustSize(); + scroll_widget_->update(); } void @@ -488,6 +489,9 @@ TimelineView::addUserMessage(matrix::events::MessageEventType ty, const QString TimelineItem *view_item = new TimelineItem(ty, user_id, body, with_sender, scroll_widget_); scroll_layout_->addWidget(view_item); + scroll_widget_->adjustSize(); + scroll_widget_->update(); + lastSender_ = user_id; PendingMessage message(txn_id, body, "", view_item); @@ -506,6 +510,9 @@ TimelineView::addUserMessage(const QString &url, const QString &filename, int tx TimelineItem *view_item = new TimelineItem(image, user_id, with_sender, scroll_widget_); scroll_layout_->addWidget(view_item); + scroll_widget_->adjustSize(); + scroll_widget_->update(); + lastSender_ = user_id; PendingMessage message(txn_id, url, "", view_item); From 513f69e88a210a90df82fcac213bb171a7d573ff Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Mon, 9 Oct 2017 13:59:44 +0300 Subject: [PATCH 15/64] Scroll to the bottom on new messages Bug introduced on the last commit --- src/TimelineView.cc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/TimelineView.cc b/src/TimelineView.cc index 08b46fdd..615127a4 100644 --- a/src/TimelineView.cc +++ b/src/TimelineView.cc @@ -82,8 +82,10 @@ TimelineView::sliderRangeChanged(int min, int max) // If the scrollbar is close to the bottom and a new message // is added we move the scrollbar. - if (max - scroll_area_->verticalScrollBar()->value() < SCROLL_BAR_GAP) + if (max - scroll_area_->verticalScrollBar()->value() < SCROLL_BAR_GAP) { scroll_area_->verticalScrollBar()->setValue(max); + return; + } int currentHeight = scroll_widget_->size().height(); int diff = currentHeight - oldHeight_; @@ -94,6 +96,9 @@ TimelineView::sliderRangeChanged(int min, int max) newPosition = max; scroll_area_->verticalScrollBar()->setValue(newPosition); + + scroll_widget_->adjustSize(); + scroll_widget_->update(); } void From 1c53fed36dde89e3da6a2f4f26b166994b60c3af Mon Sep 17 00:00:00 2001 From: Vitaly Zaitsev Date: Tue, 10 Oct 2017 00:09:01 +0500 Subject: [PATCH 16/64] Added Fedora installation instructions. (#92) Signed-off-by: Vitaly Zaitsev --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index b30d8790..90f2832f 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,13 @@ sudo apt-get update sudo apt-get install qt58base qt58tools cmake liblmdb-dev ``` +##### Fedora +On Fedora you can install package from COPR repository: +```bash +sudo dnf copr enable xvitaly/matrix +sudo dnf install nheko +``` + ##### OSX (Xcode 8 or later) ```bash From 77485291629735d2ea55a590c5ce1328c0786034 Mon Sep 17 00:00:00 2001 From: Vitaly Zaitsev Date: Tue, 10 Oct 2017 00:09:23 +0500 Subject: [PATCH 17/64] Fixed Version in desktop file. (#91) Signed-off-by: Vitaly Zaitsev --- resources/nheko.desktop | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/nheko.desktop b/resources/nheko.desktop index f8a20fea..aa8de57d 100644 --- a/resources/nheko.desktop +++ b/resources/nheko.desktop @@ -1,6 +1,6 @@ [Desktop Entry] Name=nheko -Version=0.1 +Version=1.0 Comment=Desktop client for Matrix Exec=nheko Icon=nheko From 33fb3ea0917c6410e9f77aafe52802712378a0a8 Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Wed, 11 Oct 2017 17:46:23 +0300 Subject: [PATCH 18/64] Update readme --- README.md | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 90f2832f..83ebad72 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,11 @@ feels more like a mainstream chat app ([Riot], Telegram etc) and less like an IR ### Features Most of the features you would expect from a chat application are missing right now -but you can of course receive and send messages in the rooms that you are a member of. +but we are getting close to a more feature complete client. +Specifically there is support for: +- Joining & leaving rooms +- Sending & receiving images and emoji. +- Receiving typing notifications. ### Installation @@ -21,6 +25,12 @@ but you can of course receive and send messages in the rooms that you are a memb pacaur -S nheko-git ``` +#### Fedora +```bash +sudo dnf copr enable xvitaly/matrix +sudo dnf install nheko +``` + #### Gentoo Linux ```bash sudo layman -a matrix @@ -29,7 +39,7 @@ sudo emerge -a nheko #### Windows -You can find a NSIS installer [here](https://ci.appveyor.com/project/mujx/nheko/branch/master/artifacts). +You can find an installer [here](https://ci.appveyor.com/project/mujx/nheko/branch/master/artifacts). ### Build Requirements @@ -62,13 +72,6 @@ sudo apt-get update sudo apt-get install qt58base qt58tools cmake liblmdb-dev ``` -##### Fedora -On Fedora you can install package from COPR repository: -```bash -sudo dnf copr enable xvitaly/matrix -sudo dnf install nheko -``` - ##### OSX (Xcode 8 or later) ```bash From 8390ff253d3688c3f76e3149d43d3120f9549359 Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Sun, 15 Oct 2017 22:08:51 +0300 Subject: [PATCH 19/64] Fix icons for retina displays --- .clang-format | 1 - CMakeLists.txt | 2 + README.md | 1 + include/ChatPage.h | 2 + include/Config.h | 94 +++++++++--------- include/EmojiProvider.h | 3 +- include/MatrixClient.h | 3 +- include/RoomInfoListItem.h | 3 +- include/SideBarActions.h | 23 +++++ include/TimelineView.h | 9 +- include/Versions.h | 3 +- include/events/AliasesEventContent.h | 6 +- include/events/AvatarEventContent.h | 6 +- include/events/CanonicalAliasEventContent.h | 6 +- include/events/CreateEventContent.h | 6 +- include/events/Event.h | 7 +- .../events/HistoryVisibilityEventContent.h | 7 +- include/events/JoinRulesEventContent.h | 7 +- include/events/MemberEventContent.h | 7 +- include/events/MessageEvent.h | 10 +- include/events/MessageEventContent.h | 7 +- include/events/NameEventContent.h | 6 +- include/events/PowerLevelsEventContent.h | 7 +- include/events/RoomEvent.h | 6 +- include/events/StateEvent.h | 6 +- include/events/TopicEventContent.h | 6 +- include/events/messages/Audio.h | 10 +- include/events/messages/Emote.h | 9 +- include/events/messages/File.h | 10 +- include/events/messages/Image.h | 10 +- include/events/messages/Location.h | 10 +- include/events/messages/Notice.h | 9 +- include/events/messages/Text.h | 9 +- include/events/messages/Video.h | 10 +- include/ui/SnackBar.h | 3 +- include/ui/Theme.h | 53 ++++++++-- resources/icons/add-file.png | Bin 1124 -> 0 bytes resources/icons/clip-dark.png | Bin 1124 -> 0 bytes resources/icons/cog.png | Bin 952 -> 0 bytes resources/icons/emoji-categories/activity.png | Bin 659 -> 603 bytes resources/icons/emoji-categories/activity.svg | 25 ----- .../icons/emoji-categories/activity@2x.png | Bin 0 -> 1252 bytes .../icons/emoji-categories/diversity.svg | 30 ------ resources/icons/emoji-categories/flags.png | Bin 472 -> 416 bytes resources/icons/emoji-categories/flags.svg | 29 ------ resources/icons/emoji-categories/flags@2x.png | Bin 0 -> 824 bytes resources/icons/emoji-categories/foods.png | Bin 599 -> 537 bytes resources/icons/emoji-categories/foods.svg | 25 ----- resources/icons/emoji-categories/foods@2x.png | Bin 0 -> 1159 bytes resources/icons/emoji-categories/nature.png | Bin 725 -> 667 bytes resources/icons/emoji-categories/nature.svg | 36 ------- .../icons/emoji-categories/nature@2x.png | Bin 0 -> 1409 bytes resources/icons/emoji-categories/objects.png | Bin 640 -> 606 bytes resources/icons/emoji-categories/objects.svg | 30 ------ .../icons/emoji-categories/objects@2x.png | Bin 0 -> 1218 bytes resources/icons/emoji-categories/people.png | Bin 637 -> 581 bytes resources/icons/emoji-categories/people.svg | 37 ------- .../icons/emoji-categories/people@2x.png | Bin 0 -> 1222 bytes resources/icons/emoji-categories/recent.svg | 29 ------ resources/icons/emoji-categories/symbols.png | Bin 560 -> 504 bytes resources/icons/emoji-categories/symbols.svg | 46 --------- .../icons/emoji-categories/symbols@2x.png | Bin 0 -> 1001 bytes resources/icons/emoji-categories/travel.png | Bin 535 -> 439 bytes resources/icons/emoji-categories/travel.svg | 34 ------- .../icons/emoji-categories/travel@2x.png | Bin 0 -> 840 bytes resources/icons/emoji-categories/unicode9.svg | 30 ------ resources/icons/error.png | Bin 621 -> 0 bytes resources/icons/left-angle.png | Bin 225 -> 0 bytes resources/icons/left-chevron.png | Bin 590 -> 0 bytes resources/icons/plus-symbol.png | Bin 453 -> 0 bytes resources/icons/power-button-off.png | Bin 773 -> 0 bytes resources/icons/search.png | Bin 939 -> 0 bytes resources/icons/send-button.png | Bin 799 -> 0 bytes resources/icons/share-dark.png | Bin 799 -> 0 bytes resources/icons/smile.png | Bin 631 -> 0 bytes resources/icons/ui/add-square-button.png | Bin 0 -> 965 bytes resources/icons/ui/add-square-button@2x.png | Bin 0 -> 1386 bytes resources/icons/ui/angle-pointing-to-left.png | Bin 0 -> 663 bytes .../icons/ui/angle-pointing-to-left@2x.png | Bin 0 -> 877 bytes .../ui/cloud-storage-uploading-option.png | Bin 0 -> 1008 bytes .../ui/cloud-storage-uploading-option@2x.png | Bin 0 -> 1566 bytes resources/icons/ui/cursor.png | Bin 0 -> 811 bytes resources/icons/ui/cursor@2x.png | Bin 0 -> 1148 bytes resources/icons/ui/paper-clip-outline.png | Bin 0 -> 1121 bytes resources/icons/ui/paper-clip-outline@2x.png | Bin 0 -> 1852 bytes resources/icons/ui/plus-black-symbol.png | Bin 0 -> 735 bytes resources/icons/ui/plus-black-symbol@2x.png | Bin 0 -> 967 bytes resources/icons/ui/power-button-off.png | Bin 0 -> 1174 bytes resources/icons/ui/power-button-off@2x.png | Bin 0 -> 2044 bytes resources/icons/ui/settings.png | Bin 0 -> 1363 bytes resources/icons/ui/settings@2x.png | Bin 0 -> 2573 bytes resources/icons/ui/smile.png | Bin 0 -> 1317 bytes resources/icons/ui/smile@2x.png | Bin 0 -> 2536 bytes .../ui/speech-bubbles-comment-option.png | Bin 0 -> 1122 bytes .../ui/speech-bubbles-comment-option@2x.png | Bin 0 -> 1838 bytes resources/icons/ui/vertical-ellipsis.png | Bin 0 -> 830 bytes resources/icons/ui/vertical-ellipsis@2x.png | Bin 0 -> 1041 bytes resources/icons/user-shape.png | Bin 730 -> 0 bytes resources/icons/vertical-ellipsis.png | Bin 509 -> 0 bytes resources/login.png | Bin 0 -> 5523 bytes resources/login@2x.png | Bin 0 -> 11395 bytes resources/register.png | Bin 0 -> 5523 bytes resources/register@2x.png | Bin 0 -> 11395 bytes resources/res.qrc | 61 ++++++++---- resources/splash.png | Bin 0 -> 11395 bytes resources/splash@2x.png | Bin 0 -> 24776 bytes src/Cache.cc | 3 +- src/ChatPage.cc | 3 + src/Deserializable.cc | 3 +- src/EmojiPanel.cc | 26 +++-- src/EmojiPickButton.cc | 3 +- src/Login.cc | 3 +- src/LoginPage.cc | 10 +- src/MainWindow.cc | 4 +- src/QuickSwitcher.cc | 3 +- src/Register.cc | 3 +- src/RegisterPage.cc | 11 +- src/SideBarActions.cc | 65 ++++++++++++ src/TextInputWidget.cc | 13 +-- src/TopRoomBar.cc | 4 +- src/UserInfoWidget.cc | 8 +- src/WelcomePage.cc | 9 +- 122 files changed, 358 insertions(+), 602 deletions(-) create mode 100644 include/SideBarActions.h delete mode 100644 resources/icons/add-file.png delete mode 100644 resources/icons/clip-dark.png delete mode 100644 resources/icons/cog.png delete mode 100644 resources/icons/emoji-categories/activity.svg create mode 100644 resources/icons/emoji-categories/activity@2x.png delete mode 100755 resources/icons/emoji-categories/diversity.svg delete mode 100644 resources/icons/emoji-categories/flags.svg create mode 100644 resources/icons/emoji-categories/flags@2x.png delete mode 100644 resources/icons/emoji-categories/foods.svg create mode 100644 resources/icons/emoji-categories/foods@2x.png delete mode 100644 resources/icons/emoji-categories/nature.svg create mode 100644 resources/icons/emoji-categories/nature@2x.png delete mode 100644 resources/icons/emoji-categories/objects.svg create mode 100644 resources/icons/emoji-categories/objects@2x.png delete mode 100644 resources/icons/emoji-categories/people.svg create mode 100644 resources/icons/emoji-categories/people@2x.png delete mode 100644 resources/icons/emoji-categories/recent.svg delete mode 100644 resources/icons/emoji-categories/symbols.svg create mode 100644 resources/icons/emoji-categories/symbols@2x.png delete mode 100644 resources/icons/emoji-categories/travel.svg create mode 100644 resources/icons/emoji-categories/travel@2x.png delete mode 100755 resources/icons/emoji-categories/unicode9.svg delete mode 100644 resources/icons/error.png delete mode 100644 resources/icons/left-angle.png delete mode 100644 resources/icons/left-chevron.png delete mode 100644 resources/icons/plus-symbol.png delete mode 100644 resources/icons/power-button-off.png delete mode 100644 resources/icons/search.png delete mode 100644 resources/icons/send-button.png delete mode 100644 resources/icons/share-dark.png delete mode 100644 resources/icons/smile.png create mode 100644 resources/icons/ui/add-square-button.png create mode 100644 resources/icons/ui/add-square-button@2x.png create mode 100644 resources/icons/ui/angle-pointing-to-left.png create mode 100644 resources/icons/ui/angle-pointing-to-left@2x.png create mode 100644 resources/icons/ui/cloud-storage-uploading-option.png create mode 100644 resources/icons/ui/cloud-storage-uploading-option@2x.png create mode 100644 resources/icons/ui/cursor.png create mode 100644 resources/icons/ui/cursor@2x.png create mode 100644 resources/icons/ui/paper-clip-outline.png create mode 100644 resources/icons/ui/paper-clip-outline@2x.png create mode 100644 resources/icons/ui/plus-black-symbol.png create mode 100644 resources/icons/ui/plus-black-symbol@2x.png create mode 100644 resources/icons/ui/power-button-off.png create mode 100644 resources/icons/ui/power-button-off@2x.png create mode 100644 resources/icons/ui/settings.png create mode 100644 resources/icons/ui/settings@2x.png create mode 100644 resources/icons/ui/smile.png create mode 100644 resources/icons/ui/smile@2x.png create mode 100644 resources/icons/ui/speech-bubbles-comment-option.png create mode 100644 resources/icons/ui/speech-bubbles-comment-option@2x.png create mode 100644 resources/icons/ui/vertical-ellipsis.png create mode 100644 resources/icons/ui/vertical-ellipsis@2x.png delete mode 100644 resources/icons/user-shape.png delete mode 100644 resources/icons/vertical-ellipsis.png create mode 100644 resources/login.png create mode 100644 resources/login@2x.png create mode 100644 resources/register.png create mode 100644 resources/register@2x.png create mode 100644 resources/splash.png create mode 100644 resources/splash@2x.png create mode 100644 src/SideBarActions.cc diff --git a/.clang-format b/.clang-format index d5606cdf..42d7b6f7 100644 --- a/.clang-format +++ b/.clang-format @@ -5,7 +5,6 @@ AlignAfterOpenBracket: Align AlignConsecutiveAssignments: true AllowShortFunctionsOnASingleLine: Empty BasedOnStyle: Mozilla -BreakBeforeBraces: Linux ColumnLimit: 100 IndentCaseLabels: false IndentWidth: 8 diff --git a/CMakeLists.txt b/CMakeLists.txt index 6415b912..22673e66 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -151,6 +151,7 @@ set(SRC_FILES src/RoomList.cc src/RoomMessages.cc src/RoomState.cc + src/SideBarActions.cc src/Splitter.cc src/Sync.cc src/TextInputWidget.cc @@ -233,6 +234,7 @@ qt5_wrap_cpp(MOC_HEADERS include/RegisterPage.h include/RoomInfoListItem.h include/RoomList.h + include/SideBarActions.h include/Splitter.h include/TextInputWidget.h include/TimelineItem.h diff --git a/README.md b/README.md index 83ebad72..26356ef6 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,7 @@ Here is a screen shot to get a feel for the UI, but things will probably change. ### Third party - [Emoji One](http://emojione.com) +- [Font Awesome](http://fontawesome.io/) - [Open Sans](https://fonts.google.com/specimen/Open+Sans) [Matrix]:https://matrix.org diff --git a/include/ChatPage.h b/include/ChatPage.h index 04464bc5..be8fc12c 100644 --- a/include/ChatPage.h +++ b/include/ChatPage.h @@ -27,6 +27,7 @@ #include "RoomList.h" #include "RoomSettings.h" #include "RoomState.h" +#include "SideBarActions.h" #include "Splitter.h" #include "TextInputWidget.h" #include "TimelineViewManager.h" @@ -96,6 +97,7 @@ private: RoomList *room_list_; TimelineViewManager *view_manager_; + SideBarActions *sidebarActions_; TopRoomBar *top_bar_; TextInputWidget *text_input_; diff --git a/include/Config.h b/include/Config.h index 2412a4d0..b5e876c0 100644 --- a/include/Config.h +++ b/include/Config.h @@ -4,72 +4,70 @@ // // Font sizes are in pixels. -namespace conf -{ +namespace conf { // Global settings. -static const int fontSize = 12; -static const int textInputFontSize = 14; -static const int emojiSize = 14; -static const int headerFontSize = 21; -static const int typingNotificationFontSize = 11; +static constexpr int fontSize = 12; +static constexpr int textInputFontSize = 14; +static constexpr int emojiSize = 14; +static constexpr int headerFontSize = 21; +static constexpr int typingNotificationFontSize = 11; // Window geometry. -namespace window -{ -static const int height = 600; -static const int width = 1066; +namespace window { +static constexpr int height = 600; +static constexpr int width = 1066; -static const int minHeight = 600; -static const int minWidth = 950; +static constexpr int minHeight = height; +static constexpr int minWidth = 950; +} // namespace window + +namespace textInput { +static constexpr int height = 50; +} + +namespace sidebarActions { +static constexpr int height = textInput::height; +static constexpr int iconSize = 28; } // Button settings. -namespace btn -{ -static const int fontSize = 20; -static const int cornerRadius = 3; -} +namespace btn { +static constexpr int fontSize = 20; +static constexpr int cornerRadius = 3; +} // namespace btn // RoomList specific. -namespace roomlist -{ -namespace fonts -{ -static const int heading = 13; -static const int badge = 10; -static const int bubble = 20; +namespace roomlist { +namespace fonts { +static constexpr int heading = 13; +static constexpr int badge = 10; +static constexpr int bubble = 20; } // namespace fonts } // namespace roomlist -namespace userInfoWidget -{ -namespace fonts -{ -static const int displayName = 16; -static const int userid = 14; +namespace userInfoWidget { +namespace fonts { +static constexpr int displayName = 16; +static constexpr int userid = 14; } // namespace fonts } // namespace userInfoWidget -namespace topRoomBar -{ -namespace fonts -{ -static const int roomName = 15; -static const int roomDescription = 13; +namespace topRoomBar { +namespace fonts { +static constexpr int roomName = 15; +static constexpr int roomDescription = 13; } // namespace fonts } // namespace topRoomBar -namespace timeline -{ -static const int msgMargin = 11; -static const int avatarSize = 36; -static const int headerSpacing = 5; -static const int headerLeftMargin = 15; +namespace timeline { +static constexpr int msgMargin = 11; +static constexpr int avatarSize = 36; +static constexpr int headerSpacing = 5; +static constexpr int headerLeftMargin = 15; -namespace fonts -{ -static const int timestamp = 9; -} -} +namespace fonts { +static constexpr int timestamp = 9; +} // namespace fonts +} // namespace timeline } // namespace conf diff --git a/include/EmojiProvider.h b/include/EmojiProvider.h index 5b6f7c88..3f91f2b3 100644 --- a/include/EmojiProvider.h +++ b/include/EmojiProvider.h @@ -21,7 +21,8 @@ #include #include -struct Emoji { +struct Emoji +{ // Unicode code. QString unicode; // Keyboard shortcut e.g :emoji: diff --git a/include/MatrixClient.h b/include/MatrixClient.h index 927fe3a6..01e2c319 100644 --- a/include/MatrixClient.h +++ b/include/MatrixClient.h @@ -104,7 +104,8 @@ private slots: void onResponse(QNetworkReply *reply); private: - enum class Endpoint { + enum class Endpoint + { GetOwnAvatar, GetOwnProfile, GetProfile, diff --git a/include/RoomInfoListItem.h b/include/RoomInfoListItem.h index 03023038..d7009a85 100644 --- a/include/RoomInfoListItem.h +++ b/include/RoomInfoListItem.h @@ -26,7 +26,8 @@ #include "RoomSettings.h" #include "RoomState.h" -struct DescInfo { +struct DescInfo +{ QString username; QString userid; QString body; diff --git a/include/SideBarActions.h b/include/SideBarActions.h new file mode 100644 index 00000000..60975a03 --- /dev/null +++ b/include/SideBarActions.h @@ -0,0 +1,23 @@ +#include +#include + +#include + +class SideBarActions : public QWidget +{ + Q_OBJECT + +public: + SideBarActions(QWidget *parent = nullptr); + ~SideBarActions(); + +protected: + void resizeEvent(QResizeEvent *event) override; + +private: + QHBoxLayout *layout_; + + FlatButton *settingsBtn_; + FlatButton *createRoomBtn_; + FlatButton *joinRoomBtn_; +}; diff --git a/include/TimelineView.h b/include/TimelineView.h index da73b00a..6ca91211 100644 --- a/include/TimelineView.h +++ b/include/TimelineView.h @@ -39,7 +39,8 @@ namespace events = matrix::events; // Contains info about a message shown in the history view // but not yet confirmed by the homeserver through sync. -struct PendingMessage { +struct PendingMessage +{ int txn_id; QString body; QString event_id; @@ -50,12 +51,12 @@ struct PendingMessage { , body(body) , event_id(event_id) , widget(widget) - { - } + {} }; // In which place new TimelineItems should be inserted. -enum class TimelineDirection { +enum class TimelineDirection +{ Top, Bottom, }; diff --git a/include/Versions.h b/include/Versions.h index 62584eb7..31d8af82 100644 --- a/include/Versions.h +++ b/include/Versions.h @@ -30,7 +30,8 @@ public: bool isVersionSupported(unsigned int major, unsigned int minor, unsigned int patch); private: - struct Version_ { + struct Version_ + { unsigned int major_; unsigned int minor_; unsigned int patch_; diff --git a/include/events/AliasesEventContent.h b/include/events/AliasesEventContent.h index a60da9e8..49e51275 100644 --- a/include/events/AliasesEventContent.h +++ b/include/events/AliasesEventContent.h @@ -22,10 +22,8 @@ #include "Deserializable.h" -namespace matrix -{ -namespace events -{ +namespace matrix { +namespace events { class AliasesEventContent : public Deserializable , public Serializable diff --git a/include/events/AvatarEventContent.h b/include/events/AvatarEventContent.h index d46f0420..98e2f12d 100644 --- a/include/events/AvatarEventContent.h +++ b/include/events/AvatarEventContent.h @@ -22,10 +22,8 @@ #include "Deserializable.h" -namespace matrix -{ -namespace events -{ +namespace matrix { +namespace events { /* * A picture that is associated with the room. */ diff --git a/include/events/CanonicalAliasEventContent.h b/include/events/CanonicalAliasEventContent.h index 210e4a34..9c961d42 100644 --- a/include/events/CanonicalAliasEventContent.h +++ b/include/events/CanonicalAliasEventContent.h @@ -22,10 +22,8 @@ #include "CanonicalAliasEventContent.h" #include "Deserializable.h" -namespace matrix -{ -namespace events -{ +namespace matrix { +namespace events { /* * This event is used to inform the room about which alias should be considered * the canonical one. This could be for display purposes or as suggestion to diff --git a/include/events/CreateEventContent.h b/include/events/CreateEventContent.h index 7db2d367..8edc4d24 100644 --- a/include/events/CreateEventContent.h +++ b/include/events/CreateEventContent.h @@ -21,10 +21,8 @@ #include "Deserializable.h" -namespace matrix -{ -namespace events -{ +namespace matrix { +namespace events { /* * This is the first event in a room and cannot be changed. It acts as the root * of all other events. diff --git a/include/events/Event.h b/include/events/Event.h index c6d2da2f..43ab4cc6 100644 --- a/include/events/Event.h +++ b/include/events/Event.h @@ -22,11 +22,10 @@ #include "Deserializable.h" -namespace matrix +namespace matrix { +namespace events { +enum class EventType { -namespace events -{ -enum class EventType { /// m.room.aliases RoomAliases, /// m.room.avatar diff --git a/include/events/HistoryVisibilityEventContent.h b/include/events/HistoryVisibilityEventContent.h index 53405087..94563d55 100644 --- a/include/events/HistoryVisibilityEventContent.h +++ b/include/events/HistoryVisibilityEventContent.h @@ -21,11 +21,10 @@ #include "Deserializable.h" -namespace matrix +namespace matrix { +namespace events { +enum class HistoryVisibility { -namespace events -{ -enum class HistoryVisibility { Invited, Joined, Shared, diff --git a/include/events/JoinRulesEventContent.h b/include/events/JoinRulesEventContent.h index 738be9d3..b9b6848d 100644 --- a/include/events/JoinRulesEventContent.h +++ b/include/events/JoinRulesEventContent.h @@ -21,11 +21,10 @@ #include "Deserializable.h" -namespace matrix +namespace matrix { +namespace events { +enum class JoinRule { -namespace events -{ -enum class JoinRule { // A user who wishes to join the room must first receive // an invite to the room from someone already inside of the room. Invite, diff --git a/include/events/MemberEventContent.h b/include/events/MemberEventContent.h index 5af19218..1dd703a0 100644 --- a/include/events/MemberEventContent.h +++ b/include/events/MemberEventContent.h @@ -22,11 +22,10 @@ #include "Deserializable.h" -namespace matrix +namespace matrix { +namespace events { +enum class Membership { -namespace events -{ -enum class Membership { // The user is banned. Ban, diff --git a/include/events/MessageEvent.h b/include/events/MessageEvent.h index 1b105d62..7e7493e7 100644 --- a/include/events/MessageEvent.h +++ b/include/events/MessageEvent.h @@ -20,10 +20,8 @@ #include "MessageEventContent.h" #include "RoomEvent.h" -namespace matrix -{ -namespace events -{ +namespace matrix { +namespace events { template class MessageEvent : public RoomEvent { @@ -52,9 +50,9 @@ MessageEvent::deserialize(const QJsonValue &data) msg_content_.deserialize(data.toObject().value("content").toObject()); } -namespace messages +namespace messages { +struct ThumbnailInfo { -struct ThumbnailInfo { int h; int w; int size; diff --git a/include/events/MessageEventContent.h b/include/events/MessageEventContent.h index 90f6cfaf..c096f155 100644 --- a/include/events/MessageEventContent.h +++ b/include/events/MessageEventContent.h @@ -21,11 +21,10 @@ #include "Deserializable.h" -namespace matrix +namespace matrix { +namespace events { +enum class MessageEventType { -namespace events -{ -enum class MessageEventType { // m.audio Audio, diff --git a/include/events/NameEventContent.h b/include/events/NameEventContent.h index d2cf86da..413c28cb 100644 --- a/include/events/NameEventContent.h +++ b/include/events/NameEventContent.h @@ -21,10 +21,8 @@ #include "Deserializable.h" -namespace matrix -{ -namespace events -{ +namespace matrix { +namespace events { /* * A human-friendly room name designed to be displayed to the end-user. */ diff --git a/include/events/PowerLevelsEventContent.h b/include/events/PowerLevelsEventContent.h index 7cfeadf1..04afa473 100644 --- a/include/events/PowerLevelsEventContent.h +++ b/include/events/PowerLevelsEventContent.h @@ -22,11 +22,10 @@ #include "Deserializable.h" -namespace matrix +namespace matrix { +namespace events { +enum class PowerLevels { -namespace events -{ -enum class PowerLevels { User = 0, Moderator = 50, Admin = 100, diff --git a/include/events/RoomEvent.h b/include/events/RoomEvent.h index 540fafaf..a1e88807 100644 --- a/include/events/RoomEvent.h +++ b/include/events/RoomEvent.h @@ -22,10 +22,8 @@ #include "Event.h" -namespace matrix -{ -namespace events -{ +namespace matrix { +namespace events { template class RoomEvent : public Event { diff --git a/include/events/StateEvent.h b/include/events/StateEvent.h index 75bf1bbb..146e96bd 100644 --- a/include/events/StateEvent.h +++ b/include/events/StateEvent.h @@ -21,10 +21,8 @@ #include "RoomEvent.h" -namespace matrix -{ -namespace events -{ +namespace matrix { +namespace events { template class StateEvent : public RoomEvent { diff --git a/include/events/TopicEventContent.h b/include/events/TopicEventContent.h index b6c376e7..d059e489 100644 --- a/include/events/TopicEventContent.h +++ b/include/events/TopicEventContent.h @@ -21,10 +21,8 @@ #include "Deserializable.h" -namespace matrix -{ -namespace events -{ +namespace matrix { +namespace events { /* * A topic is a short message detailing what is currently being discussed in the * room. diff --git a/include/events/messages/Audio.h b/include/events/messages/Audio.h index 41bc2496..8abdf7f2 100644 --- a/include/events/messages/Audio.h +++ b/include/events/messages/Audio.h @@ -21,13 +21,11 @@ #include "Deserializable.h" -namespace matrix +namespace matrix { +namespace events { +namespace messages { +struct AudioInfo { -namespace events -{ -namespace messages -{ -struct AudioInfo { uint64_t duration; int size; diff --git a/include/events/messages/Emote.h b/include/events/messages/Emote.h index 98d049e7..a11b7c8d 100644 --- a/include/events/messages/Emote.h +++ b/include/events/messages/Emote.h @@ -21,12 +21,9 @@ #include "Deserializable.h" -namespace matrix -{ -namespace events -{ -namespace messages -{ +namespace matrix { +namespace events { +namespace messages { class Emote : public Deserializable { public: diff --git a/include/events/messages/File.h b/include/events/messages/File.h index a4e8b6a4..ff5eb0a6 100644 --- a/include/events/messages/File.h +++ b/include/events/messages/File.h @@ -22,13 +22,11 @@ #include "Deserializable.h" #include "MessageEvent.h" -namespace matrix +namespace matrix { +namespace events { +namespace messages { +struct FileInfo { -namespace events -{ -namespace messages -{ -struct FileInfo { int size; QString mimetype; diff --git a/include/events/messages/Image.h b/include/events/messages/Image.h index 93a598e2..14889ff1 100644 --- a/include/events/messages/Image.h +++ b/include/events/messages/Image.h @@ -22,13 +22,11 @@ #include "Deserializable.h" #include "MessageEvent.h" -namespace matrix +namespace matrix { +namespace events { +namespace messages { +struct ImageInfo { -namespace events -{ -namespace messages -{ -struct ImageInfo { int h; int w; int size; diff --git a/include/events/messages/Location.h b/include/events/messages/Location.h index 4b523878..e51c6799 100644 --- a/include/events/messages/Location.h +++ b/include/events/messages/Location.h @@ -22,13 +22,11 @@ #include "Deserializable.h" #include "MessageEvent.h" -namespace matrix +namespace matrix { +namespace events { +namespace messages { +struct LocationInfo { -namespace events -{ -namespace messages -{ -struct LocationInfo { QString thumbnail_url; ThumbnailInfo thumbnail_info; }; diff --git a/include/events/messages/Notice.h b/include/events/messages/Notice.h index b303dd79..66f4386d 100644 --- a/include/events/messages/Notice.h +++ b/include/events/messages/Notice.h @@ -21,12 +21,9 @@ #include "Deserializable.h" -namespace matrix -{ -namespace events -{ -namespace messages -{ +namespace matrix { +namespace events { +namespace messages { class Notice : public Deserializable { public: diff --git a/include/events/messages/Text.h b/include/events/messages/Text.h index 2c787d04..c3182dc5 100644 --- a/include/events/messages/Text.h +++ b/include/events/messages/Text.h @@ -21,12 +21,9 @@ #include "Deserializable.h" -namespace matrix -{ -namespace events -{ -namespace messages -{ +namespace matrix { +namespace events { +namespace messages { class Text : public Deserializable { public: diff --git a/include/events/messages/Video.h b/include/events/messages/Video.h index 37056c27..d167b8c1 100644 --- a/include/events/messages/Video.h +++ b/include/events/messages/Video.h @@ -22,13 +22,11 @@ #include "Deserializable.h" #include "MessageEvent.h" -namespace matrix +namespace matrix { +namespace events { +namespace messages { +struct VideoInfo { -namespace events -{ -namespace messages -{ -struct VideoInfo { int h; int w; int size; diff --git a/include/ui/SnackBar.h b/include/ui/SnackBar.h index 076b7105..8a35c327 100644 --- a/include/ui/SnackBar.h +++ b/include/ui/SnackBar.h @@ -7,7 +7,8 @@ #include "OverlayWidget.h" -enum class SnackBarPosition { +enum class SnackBarPosition +{ Bottom, Top, }; diff --git a/include/ui/Theme.h b/include/ui/Theme.h index c2e809e5..c6c39553 100644 --- a/include/ui/Theme.h +++ b/include/ui/Theme.h @@ -4,12 +4,15 @@ #include #include -namespace ui +namespace ui { +enum class AvatarType { -enum class AvatarType { Icon, Image, Letter }; + Icon, + Image, + Letter +}; -namespace sidebar -{ +namespace sidebar { static const int SmallSize = 60; static const int NormalSize = 300; } @@ -19,19 +22,47 @@ const int FontSize = 16; // Default avatar size. Width and height. const int AvatarSize = 40; -enum class ButtonPreset { FlatPreset, CheckablePreset }; +enum class ButtonPreset +{ + FlatPreset, + CheckablePreset +}; -enum class RippleStyle { CenteredRipple, PositionedRipple, NoRipple }; +enum class RippleStyle +{ + CenteredRipple, + PositionedRipple, + NoRipple +}; -enum class OverlayStyle { NoOverlay, TintedOverlay, GrayOverlay }; +enum class OverlayStyle +{ + NoOverlay, + TintedOverlay, + GrayOverlay +}; -enum class Role { Default, Primary, Secondary }; +enum class Role +{ + Default, + Primary, + Secondary +}; -enum class ButtonIconPlacement { LeftIcon, RightIcon }; +enum class ButtonIconPlacement +{ + LeftIcon, + RightIcon +}; -enum class ProgressType { DeterminateProgress, IndeterminateProgress }; +enum class ProgressType +{ + DeterminateProgress, + IndeterminateProgress +}; -enum class Color { +enum class Color +{ Black, BrightWhite, FadedWhite, diff --git a/resources/icons/add-file.png b/resources/icons/add-file.png deleted file mode 100644 index 806b166d26789b0a6d3ac9caf1c7383ba74a3aab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1124 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+0817l2pPlzi}!T+mS|F5V0 zy`J_DicHjZf{=bn5kp)V@l%@T@n(_Zy22}i7>i;uo|8Ie` z0MY;b4IuIw(5STk*Fl2+|2O=<3N$nI|8=n7<*fhr6aHTVE54ZyvJqrmI#3p*=33hS z^9lcMr2PXjfW}@;{|6NR&j51dwY0xLaiD<^^+5KGwEuU3YLbD1An~g}sq}wf2mim3 z^#58qhzl|q=pLYBK<@v46RZ;GJCJ+Q|KH99+Xb{2EPFc_qU--Pu*+{Jf;8Sp2ignv z4$y@lH~haE3N{&L6g2dJR)9o+0UK+@_yI^2l?3?(Gw>8IzxnaVBu}UNO9b=(9awht z?^8paAAg!zHBY}~IU6z0crBAblFvk04#_wAy^1?3qXPsUZR66~&)9x##W%gZrF-~Q z9>11L-1U=iEM7~ z0ou-(%tiFY)wsWq-&l&BQLzcUQ;^sK?jS z#WBR<^wP`W;Y@}CtPcVm*R1I7QiwRfWag40Dk{h-ofP>-v^xL$fBPLL-vu%2pL2e8 z?&Nn9OU0zn;OKOt>(NpxoJ`FeS3eVbSi4%(VEfi`kA}6u1^2J=R`)yeo{KvWs&}W> zVZR)^OOn6`iJPa71aL~QxviKb!CF)GrN7Y7E|5j7QB$sl(K_bxJ0n4svXgF0X9PSQYLgyCg5MT~yn)%~K~RA&K2%H}}!J zxHl`4;>{S@Lt7^WmPctk3en-)Ss?L9qu57jqFKxTB^8e?3ug(3=mq)A`RhN&xbWfC zmUgXt)x7$D3zhSyj(9cFS|H7u^?41zbJk7I~ysWA_h-aKbLh*2~7ac(-?99 diff --git a/resources/icons/clip-dark.png b/resources/icons/clip-dark.png deleted file mode 100644 index c3c34fac97ec4ed2bfe112bf8f67c6ee9f70b810..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1124 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+0817l2pPlzi}L1$UaPW4Mp4opslbJNn&z*<1M92y#ciUX5V z1C!DMK<1=107X*4BB`nVNh$t`i3te_L1}3L$ti(J=|C;MKxIiFHw1#nw19*J5D64d zOY=_zDF8YZs1e8jiKnFcfz=0sL{dW2(twTuOM%scT@K{>B_%<$1g50=C!_@c6@#q^ z2AT=97i%~DXlQ6)aypO; z(hKq?NDyQZ*x5kWetIbp2&9Tig8YIRc#4o>%1uanJV-$S>CS5KluHn;ZxZD&mKc6VXu zV3qX%aySb-B8!2^={g8AI%&+V01C2~c>21sKV+5`U}Ee@wpE-Zn zCPM+%2Z4@jR&;kML>yo;b4d{u6=ao8ihLtlo&Wv6{f?9Gf|&KsIX^ph^1F$pVp3>u zbh^>?XsH!Wre=<-pNT!JT`g*`eQUW#!`k42`&W6Z`<;2u#T^LMyHo41Uyj`+N#KLT z&C^E$I3?KJR?L!Mt*QFbUub9-$fDM$DOba29dr4ekswQ21pnDjp)F5?4B5}PU)jf? zqvzVlAbz@1pp#KMt6zDSS2ky?3ipv+l9$*ns%_imsS}it#O|@1`)FR=o0UoNW{m8i zt&;-Fqck3c={&)yzj; zD(j|x;FX^fn3CUmNa9^X^(r-y2aPRCE9{LwcsM0qKO+3WCrNSdo=M-&vaNoxxUlif z@Aj%FkC^$KZ}pcyZ+t)hgMN2?%K0`2k$eV2U|6e`xJHzuB$lLFB^RXvDF!10 zLvvjNV_hTj5JNL710yQ~Lv0XgupnrWEsBQR{FKbJO57SQ@=G-WHAsSN2+mI{DNig) lWhgH%*UQYyE>2D?NY%?PN}v7CMhd8i!PC{xWt~$(695QKmJI*^ diff --git a/resources/icons/cog.png b/resources/icons/cog.png deleted file mode 100644 index 8431116658c829aa8cdb70b8d236bf2050587ba5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 952 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10f+@+{-GzZ+Rj;xUkjGiz z5m^kh={g8AI%&+V01C2~c>21sKV+5`;IeMpmL(3<)aL2p7~*k!>Qrxk;Xsk&|Cj47 zGjqza4%X&4=3c3yakXRDVS&pZ7(IMC+@|ID#$-G?cKq7Z_yfnU?d_BnU%|-|)M@&3 zXM@m^;{0f3#?CFpF(p=Ka?_iN(+!``ss6XW`q{ai#w`6CMKgbQDW1N0=l$D%kLQVh zVA{faqv(N8#Q|-9#)1iCZ#ROLngbJfitKvG`%M ztg7?|(ah5iW&ZT8DeLvg=4Wcz;BP6gQeTdt;fdaT$E(w8^ey~Mk4Jm>Jf0|T>wC25 zlp0gRjf|VSWK$TIT)Zd}f3aG6*w z>bap*$*Ja-H;yfgez&sru;=kVI`=LctvwRvDRCioUSUx6Vz(!?b#FIGzE-)Rx@WQL z$q;9m3HvYHoAs(rYV+Bm2A`C>LAL#$|MQhQ%vx!Aq-BeB!d=x{it4kRixw@FW^fRY z*N%S@=a(pRT~^qAkK4J6>~W8dUMSxkVtHmW*KQ~NNAFLt%y_~Qry0wzYvtL9X^Us2 zE}6ykCF^O^z2wDjxZZt?mY@AUpnfI8sphScE;S6RY-Y{a#watzewN)L_x|}?LyERv zWcR7BJ=It{nUUKe?W@_RzLSeqFL|(m_gu-ln+yt>A2g;hFsSXluLwYP^+H00)|WTsW( z){tKv3d&`YARB`7(@M${i&7cN%ggmL^RkPR6AM!H@{7`Ezq647Dq`?-^>bP0l+XkK DXjYA7 diff --git a/resources/icons/emoji-categories/activity.png b/resources/icons/emoji-categories/activity.png index 7824765df3367cf29e0e49a292d28f45ae06ba1a..2d360762371055915dc0970ede31c7f152cee525 100644 GIT binary patch delta 26 fcmbQtdYff}3O`$tx4R3&e-K=-clqRv*&0j$f6xk3 delta 82 zcmcc3GMROPiaSfOlV=DA5Y%v_bTBY5a29w(7BevLodRJ-!$nR{fr64Ht`Q}{`DrEP hiAAXl<>lpinR(g8$%zH2dih1^v)|cBZ8X+k0sx)(7`^}i diff --git a/resources/icons/emoji-categories/activity.svg b/resources/icons/emoji-categories/activity.svg deleted file mode 100644 index d3d713e9..00000000 --- a/resources/icons/emoji-categories/activity.svg +++ /dev/null @@ -1,25 +0,0 @@ - -image/svg+xml \ No newline at end of file diff --git a/resources/icons/emoji-categories/activity@2x.png b/resources/icons/emoji-categories/activity@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d8f8871158c09a0933db139e1e2b712a745e568b GIT binary patch literal 1252 zcmVczl8EvrsQ4sm;*Z2fcmrCXKKKiPqG?nDMN3;EB==r@m|fX^cV>2a zcT37QnTOf)J!j6D-8(bq%&kxk76DHHtAQS12~Y#>h~-nj6<`GT88{9c0WOv_eoLqU z+khe9-xk{-ur;=I%g_lN1g;fuT-y}z0kC*B9iI=p227UdxVCG++rWado$m&|^9XobLjCKEX*q#736m|Y3pl&h# zH?SS3dUW#wNKlP!jazi;z(5h_U9WgkfPQEI2{w7gn{g2R*k?XzCxJ?d0aP%*A=h|y zV120b-DJP{>p(-EJy&7?I{S@yO#MwP%X7X;cC~2&&jW|$`Cy3woD}Y-@tC?&oyIKZ zPVBS{ygQaHC;Jv2Eg^u%gx3I8#IiRnc3yWGT8!Bd)8x;D^R>u7Dj|SF!uv8Ms{mh! zd=lue89Hbob_(Zd;amq676{-jvfuiX!s#MAu?N&pjqKTGhh@Ndz^}sFSRjDS!WjV~ z3vZY3rjP^RRw0by-lX;m*Yz2N1fZYKJ_oNF`;_KxNfY!)ywhO_;Bnzz#=M!C1~3m8 z6V6)CxNiveL?Y;5`f}C4f2?i8Bqw>RQC2FApHjebaeMV6JZLiY4imcS}y(J03C(us7)LC zq<;)dWmykNn#iQl2K>bwIm{)(K{2+=V@QwFmmC9^eVpDc++%Pv`#B`!S8#7`Pf7n< z{HI748nLd=c7iJKvON1F-KmkyJ8enLY^RhLu&0#UgqKMv!PMuK)Fqr}g!2z@XMq3~ zl1=LMEwq#U)ZVihvz1jo7tS{#|Ip!BXaE2o3-7Cxtb!@+q~#Nsg~y5=mVteREng-U z8o+AdHMzOtzGbnq-D8XSnDU((w3QZ5IBd)`0LT^<4g)WcEiIlRr?d#>W@JJXZN=UG z5(C&MTw8JXD%s@Ai4FM6>qC@ZUe^L|y48VaL!D1B;4yeBFF2aOi!=27e+*!M6XTdF zvF|o9G=K%ME!P!O%g!fQPxd#Fw2QzyWcQ1Z0J?xZu??H;tF-XhxUh`u>ZENLco(x` z*{)ev#j^eQuWXamCin*%iZ;wex9e?`(UObqSuHYj0PmCCwTrYVOwHf9!_Ee3m=gDK z{I~9|*2El%U!_1@DsL?hMLVz#lce-u%7*ta{qHuHF{jP+-)$}x9r!<%2l9@IU}o3= O0000 - - - - - image/svg+xml - - - - - - - - diff --git a/resources/icons/emoji-categories/flags.png b/resources/icons/emoji-categories/flags.png index 47f68375033f2a7b3223c9d65fc1e778b8a99deb..9a52000fd1562d9acaad273c80ff6da9181ffe3a 100644 GIT binary patch delta 26 fcmcb?ynuOv3O`$tx4R3&e-K=-clqRv*}04Wfr<*A delta 82 zcmZ3$e1my{iaSfOlV=DA5Y%v_bTBY5a29w(7BevLodRJ-!$nR{fr64Ht`Q}{`DrEP hiAAXl<>lpinR(g8$%zH2dih1^v)|cBZ8Xkh1OT8s83_OY diff --git a/resources/icons/emoji-categories/flags.svg b/resources/icons/emoji-categories/flags.svg deleted file mode 100644 index bc9ec6fd..00000000 --- a/resources/icons/emoji-categories/flags.svg +++ /dev/null @@ -1,29 +0,0 @@ - -image/svg+xml \ No newline at end of file diff --git a/resources/icons/emoji-categories/flags@2x.png b/resources/icons/emoji-categories/flags@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..45350593bd9a1e8ba65706e64aa0a5a67fb4d08a GIT binary patch literal 824 zcmV-81IPS{P)e=x;Cj~xkEli;5&h+i~|G0GR7_73-C!? zI{|DK^0P7y449~5k;k~(IxchpeoJVfOyI5VXTLJ~+#Om!QgDm`D`KsH5^Pp_qR=pkok8o@Aq|gOQw*qV};VYy<9wfM;7QL?Y zx*voke2oIj_Ffoff(PzdX=& zK1iR6i^}Mx(l?3+x`ZI!wpAhP03#)9eIAtNL0SUef%if_LVVOrFcdtvi@R-Sg)U$S zcyL}B-Bx$K^qt=j6UT$Vy-dcqin;z)m4=^f@_`2U!V#drI2` z92as_C@ecdC2N0LpNez1b$7?;+|nlw0Ix}}|3%=kns0h9(%26C5e9FK3Ct)*(?Zt< zt`a}F(t@7__GGNvQFEs(!Mqd(tH3UU&6IMKlw#hyt}(4_W-JNZQbrdHHi5EP2Ch-; zu1kc<&eRXiD5HB8BmDP?|2*jT6asib{2Wf8`#}~Wh)uy6o+d7xjm zYJ)X_NXcQfVfe=u;3w;4G~a)EGw~GnX|%82aAH_X2zr0TX#@|-Bw&55XbjrKe?>@2 z=wrR%6vGo-9_+6-oO~q&4dg-D1cs6aOXNHi`NTgnB%_-)bqp5(0000_7&KZsuEZ;CDVhk6=jRxy&B>r^-AD~t+alTT+!(fikDr;{A@Lcu< z^D0b+va-Si1HqgiUGof!Don6AnBQvBd54|~6XdL!8eWC6y21oG>vauxLaA4nAV0>p zVMto9Pw1-v-WRM4YjfkxFr2C&!RcU)w;P9~>phE2|9Ao47>owibUKel<|(ZD2l#Tl z560m?1$5&^1iXS;61lI(RB!2{RLP=Bz2nYy#2xN!=000SaNLh0L01nCk01nClF``1{0000P zbVXQnQ*UN;cVTj60C#tHE@^ISb7Ns}WiD@WXPfRk8UO$R)Ja4^R7i=f)jwzyQ4j|3 zpXZ+gFNK0tP_T$%v=OyRu(6XgQ4z%|1TAbW#7+?{jACaav469%QV}s|rwBGy(SS${ zA|#@PL{zvW-dgM|w_eP0Yj$9GJM(6~@4eYK?@=tLFok)XD~MYYT1u`W>Chv&`2)Cv z4`||b8Sp~^*u)4x434rkyEe2$4r1?3yY`_SQnxGRD62^-;TI9{Swkl3x% z@G3CR(B9Ta1>l*CY9DDI!|1D!U}s=X!>=$J6%zCW=6}zcv}s^Ng#;N^vxb>4c2`KS zC&rl?o`$ikIaVP@V|)htr1koSO%;G|4y*)wGv`D&#wsAV9$1g_&i&H%Uc$gCzy|_j zQEv5_Tn^@0Z2u4Nt -image/svg+xml \ No newline at end of file diff --git a/resources/icons/emoji-categories/foods@2x.png b/resources/icons/emoji-categories/foods@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..bbdd2a3c82555a12f008c7d0509bf268abb0b052 GIT binary patch literal 1159 zcmV;21bF+2P)gb=8RefKJFqXtD+2&; z$1=>p_;x|3wJrkKg9VYztFo;WgYOXrd94cQwB`!UoQCPc%b1kdEK=M}wqtUY(6N@u z2RneLaUuR2z@|q&e5V#$qs*dF6Z+^Pp+8Sm++X_y*v_21yYV4z#U+@L)HOOO*Mpz1 z0q^0Xq^_1!!|lS9R%YHWvo5SlyF$y&qn}-N|DAER;QWpL)VPjTQVV~?^ z9!xr{am(~zUL*LK@;7LQgoS7^PDfW_KNiz)KfY`Ncnj`J%8pIy>PqTfocPMyYV>0E zK=3Ew$DBfM;mk&FF7(p}aIgUUAeJgOMf@wu@_nrd#)%UA$6+<JqK&HQ@!)8c{V> z6N*3Xp_-%_Qkqy1Oe>lYJ@2bI8v5wh%!*)IQOvfZCTwlfT38WGE4nUv-U8Rkj_fYS zo<_>z()E&J>&D~%vrLBhN~(DEYf`0^T7GB z(fY{fQx#k`lu!pgQO~wM&v3eIq~YFb7z?#THi9u@AmcY8<6h+s^zG6avQd)NMFl1a z(`dTDCd0Eo8Yyg_<0*bpQ6a)DV|>70rP}e(Aw;}mj$6uBurjCc@*&3@SWo8o&xBHn zG)_1?#Fadzy)Ds!$8-E{)51CBH0;c&>@|#P0;uc@j}pFe>~AMdElnNi93iM0SGrm8 zu63Gl0RKBl@8H}5`&sxr$Iou{mxwLt0{PME!$LUndLimIL0C(c3b!?}{Q)5eJUy}P zPRg#8AL;&G&T8ALrU?HJ5j$yD+A14t7_>&?InCX$G@JSoAESpFKCL;z;?@Xezi>Tt z2mUHyzfrh1Yf6Ke5-$=iBjyRS#0X)f+=cgrh3L1$wi|ch%H;3y!cFEc!bRR1VGuX1 Z{{f*}bJ&R$|GWSI002ovPDHLkV1jAZKV1L- literal 0 HcmV?d00001 diff --git a/resources/icons/emoji-categories/nature.png b/resources/icons/emoji-categories/nature.png index 7a131ba25a8b92eefcc4d2dba35ad9c44bc19040..eb1786cf16e521f25c6907b130fda9cfa24bedfc 100644 GIT binary patch delta 642 zcmV-|0)73}1)BwsB!32COGiWi{{a60|De66lK=n$P)S5VR7i=f)ys>IQ5XmCk83Cs z3dy7l7OdS98c{B}Wh40ml+_q3D`i|4ZV4M@ZBQf&MIvG0wn36I6f#Ec_n*aiUcYxv zzhM~PI=%06`##UPJm*kFA2g#4FR>SO{*1xc6ETm`gxehiYs%NM2Aae78HR-JXy~@X?GWaKabw6A(5o8>df{Tk zYzSRr$T#412!BQPt06D$26=&{5pyNvVIg;r*1m%NQB9F>#9t&B88NSeV_?W%Nhhb6 z!K(PbAAwu(4lOlEbK8T6U5U~0{|+98&?uen3f4u?+mPSXNRZpshumH&^Z^`KU9SpG zscvpd5N)ZEAh-3#Y1MwJ;212@xoIZXAnPtBWLygi!+&bL3gxUGB$yN&AEd7Cgu`kd z&w8n2uS@&&P`Zb9OPg87Y}FnDeM`o+_)f%`lKupyWJC>^iW!)m%>eEdit@F3C%!W? zvb@SN>NB~x%g)zwu9GgPFBntOFHt>V<3jP+nkoRqWefE*NwG=Cgy7bGb|A(M)R>W5P(vi1lE(TZ32 zg0IoPhC|_##~lyOpV$?K4_J+9F_Jv6yVe#lYtRw8?IrnyN@epyw;bQ1?Z#Y;!T=0H zBNk#0zK7qkh<{xW@@F&&f&xdvVJM!5ZhbzNb+9pfpJ7Poj)rar+zw$*7&nD{5xshl zpcgJh%!bg-3;71Lhfrp34S7iq$V)7Zn5!WV3;7Lc?hEvfYRZHo{vg4~h-nXwfgyh( z?VLiI`<2nZAA#HO9xZi9bK8T6U4hZje-95sm@n<`0)J~G=v~Nf>mq352Df9uH zP@As;r_~m>C5X1xNs!xmM{IeX3LJyQx*nRzb;x>(2^rU-(y$7zLpkdo5=;t?4(U{P z!Qn7AXZl9z#9o)y>!EZH?U5FYez`%zw!8DywMB -image/svg+xml \ No newline at end of file diff --git a/resources/icons/emoji-categories/nature@2x.png b/resources/icons/emoji-categories/nature@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..81db5c08e342db8a41b4af6e392f48af20a06e04 GIT binary patch literal 1409 zcmV-{1%CR8P)#{D2WasTyM zz#he08VL^p{!qdPJe{pjoPSDmKEgs{%eKAT!+S(=`i<|*Rf0j>sWVS;^2ondF6bGM z2hLZV$-r*KnFA8P(?zD%eBHmN=~cz7!n=W&2_s_`nNRIlVC!0BalKEiC= z5Utl?i;}O+05cS6i>f!P`iH=GWpG&ITVTCvyF%4FRegqU0KKYyj;g;=xNpJGwslC| zq3Xwh8v*p({zb{N4t(g0=Iq8kf_NP|Ka~WX2 zGE!yzzLK%Ie;+Vpepe$AtPh!XZK|_4B=A&b zaW~^}weqD43A6v9N-2DgIK19!J0h^NTHs=2Va+spQQ9#CRnW} ztOo#crDwwzfizly#|Y1phJgEk7gBg9sruFs<28&uSZqq+UrT(XNGmNVw%00t@O7r0 z9}Oa*HV(dfo6z!QiXS?>&kP-a-UUxf;OeWELdwnryIIB6Cq*Fs%YLrvN!KT(9534w zWcqBrx)8$`Fn07m;a~6oCn6h8pAh5WstDMgmTZnZb#-Zs_%0-TPE4?K8E!@}ub2T$!@Y7G0&c-gts~4w_K~;& z^cap0lYo1XwAt2qvl!r z#o(a;r}7l`@G%;|?*dLK)%gfFkUY0_@nyBXcZImJj)bSAVlf?f8uu1Mmp!;Q-g)3W z+&d^;+L1+EQB)!C$F~Eo<9_Ws4bNW8J*Mq$#oY_Mg+!(?Y4UhNKTSp1YZEev9^fL= zR;RcF7$Bnlq&XIscwr?#})NUNTfdeDK^D P00000NkvXXu0mjf6+xlF literal 0 HcmV?d00001 diff --git a/resources/icons/emoji-categories/objects.png b/resources/icons/emoji-categories/objects.png index 7f6a8d13534e1af29a716117c1f0a33a2e2ef944..45c6eb37970d1fce36bac64e9f1b656b5cc035b7 100644 GIT binary patch literal 606 zcmV-k0-^nhP)}p6w?}%zsR98^F4S?BoQT;Av%Y+Y~5n8w>cAPXQW*sOy7blH-S?+hT zHv2^Assp0>t+10@?mOYWpJM_qob~oQNgj)w*Iro?Q zCSj89WNrWt2_JEg&|wq4mY1bPLTSo4O7&5l+mU{Q=g0#z2oqYoa2kdGd!m*o^aGdy zR{i}La5=@X>Nt*Oml7v+2Va3UFzWAXfRT8?#PFQ3i#@`0A9kDbwTy7I|2GtPLdd?{ sIc?xEX}4Y_&H-8Oo>JQLPZ4&=Mh<4f~7$aC|nSg>21#yFknsi}37T=ey`*(7$?6p(b>O*Te<-&DqqH}51-MUo%mLCw zM`SD8Y2+<+;{g3nNGba(s)j6ZinOu$VVlbQ9jH|hegm!mpQIlIF7yiC>SX-$x9#Ls zRrC>P0{f&d13}jztHM)NwMp9cJ(3C83ew5x3kM#ln(j$8fro7GineeHcm@2FyaZ16 a3jPcKuB4VD)x(GY0000 -image/svg+xml \ No newline at end of file diff --git a/resources/icons/emoji-categories/objects@2x.png b/resources/icons/emoji-categories/objects@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..01fd5cb4df5e814dafa090d5e6cd0b966c002ceb GIT binary patch literal 1218 zcmV;z1U>tSP)56%Ltv|A0@yU4!WRQ?lptJ}*MS>4Eq*yLmLXyc*a@u1&4U|# z>GRKaHt9bKR5~esB4D%%ybJWU!PN_l_`abbRk{<43BS2X;NAkftAR5C-cjKC0=x-? z#{3~*Sqa<~&!zeIl)w$RXcH3ORh9wI6>?&zC~-tGR8CUPo7bp#?nYbRQ*fQwt_Z6X z8}rSun&iZOvIyZ(MfOfXfvsSlvUwyg(MpnUznxH-7ySXdcjh5_UJ-p&lE7AQLfL*c zt)XO)45<4L0kHNVBXZV=Tr|df4BQ{+yGBIbHX^5t$SDzd3%D=PZk;jaGb3`zh@8dR zE&lm_&6lAw75Xi#2k_$KhH@ELTk9kuFRS`msUotY&K^Y@jmY1s&6Ka*plpxjao~a? z=}B=QHr9>;29Xz1#Q)=jwNC(IM7{~s*Bt0ow$JBr;4eke4S=;gevtlbtAA4QwJ?QUiJ&pZyyDv~9QmByI;O2DkTKSvY|VeMmnj5pT(cFnyp`;_gQ zlfPucz@j|1b1WKh+wFe25UC^sh>(QQO# zQXKf5xv)T2uV-VC)hZsna-kJqXD6B#;Fp%=1b zD6{s_z#C0Vzc4m2%ckmM$#6I;r|B)|6BtWND<56Mq^ zh;qI}g{1fx*j)m59g7H`e@;?tP7IJ7Hnm+?nO_)i2U}kUj8$=qQGXj;{UoDJt(>HT zr*q4IV=0DX+ye9xE*0p*WtH9jdV{aii&SGd3M}oE_!<`B#%G#{yyaET-4;v%N8$o~ zL&&A2aExt0I}VlRd{h<@umU&Ehj3&57T{K2Hx2xXJ8Am__|!jJQ0Orn$|}>aD$qX{ z%ANyX;*MXA;Zl$p|NLg)F5I!?R^XnpwERDb7aIS02g-~>)408G4{$dw0xkD-=Y9P+ z?tjCJxG1%t2>o{Ynd}sJ4epPk8;Z(6AWyXZj9Uj&0>r^>Z*9Dh{*Z-f9FaTEL`ZY7@a g*L!ifQmLW)D%CYqSOk=Avj6}907*qoM6N<$f+6ZV9RL6T literal 0 HcmV?d00001 diff --git a/resources/icons/emoji-categories/people.png b/resources/icons/emoji-categories/people.png index 8c826fa9ba4975627d8d198de8b68da7bfb7916d..710e808a7b823d5d7a383a63ab76b38f1e51e5b3 100644 GIT binary patch delta 26 fcmey%a+GC)3O`$tx4R3&e-K=-clqRv*+NVJh(8LD delta 82 zcmX@g@|R_TiaSfOlV=DA5Y%v_bTBY5a29w(7BevLodRJ-!$nR{fr64Ht`Q}{`DrEP hiAAXl<>lpinR(g8$%zH2dih1^v)|cBZ8R2Q0syy>82bPK diff --git a/resources/icons/emoji-categories/people.svg b/resources/icons/emoji-categories/people.svg deleted file mode 100644 index 31849020..00000000 --- a/resources/icons/emoji-categories/people.svg +++ /dev/null @@ -1,37 +0,0 @@ - -image/svg+xml \ No newline at end of file diff --git a/resources/icons/emoji-categories/people@2x.png b/resources/icons/emoji-categories/people@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..142ba09e980353b674a4d0c30a30837d67111b65 GIT binary patch literal 1222 zcmV;%1UdVOP)a07JkNKpp5#`j>%u;2Q8Va0$2w{8`cXg-{2M z0p9^@1>0rdXku&K&<~se?vx;|+cNMz(AQ4loxpKmsY2qq-2vVPx~e8W2>j?7xB*N9 z?*K0X!@$GIq&t(}!@wwT3fTh3h+&K)a zI*k9F^ld6T0J2=h5ih03Sp~+58b0hWxCVSmd%VULU@BwG+>;o^e)22Gq}q*%WGwwA z4#pZ7#9s4vf#<7c>cReGOuZ*|gcLVndz%&3mwbx-*l%JM@oJfv#9n{vv23z%#KGr{ zEa5&RBI*sh(Kt&hrzN1*C3r>&&Lf^-qftlPw#n^1myphFd`o8lIEQSkMc@l$^T#5u zzFtbpC|7<=3Cxilqy}&Yom)v?4EenJUDy$O!W7Uc@uY{qImh5>`z&GtMfFcA*>BU1 zUZ81oH1I#kMGt|6hzS(c@1r%iHi4cRun*`k1TP~?s0h{y>c!|^2WE`x4q#sm*sBD; z2@LYJr|+wPZdv_{>UwXgWWL5w3Ok5wI9G<+AJ8qU|B}+V0Pvf-IUGS^0NHR0Nt=o! zUsnGq#q}!KF71&VMAjQyXytkn;J&(9#UZ6#=%SVDz1qg|DUTWwmQ8fK1pg;^NL?@0 zfZOV(Gt)Db$PPqhTPP4}ZvWH}FJvN=l-&sQmqEz(kh;EFLu!>Kb_a}H%S9<4Lbjh) z*H>ytk;%kJz{vSrl=9)rK>u^7I~eA_Y3DkaFLI7qgeypB}NVoHh{+mZC(MuzPLB9=`@NFc!u zBqr?2=x+kwBWc16a0QuMdfMwoyw6S~T^~VGPGd*yGHn^D`DaGhcA$=IeT&y)gf7 ka})TT*1y~QRd(S2000rJE_WD0y8r+H07*qoM6N<$g4b0 -image/svg+xml \ No newline at end of file diff --git a/resources/icons/emoji-categories/symbols.png b/resources/icons/emoji-categories/symbols.png index 69a39754b6074486a615ab3f072297f7f88c98ef..08184de1b3117ba0cc38f7c2f6628dce8b4d45a4 100644 GIT binary patch delta 26 fcmdnM@`HJT3O`$tx4R3&e-K=-clqRv*+&@xhJylpinR(g8$%zH2dih1^v)|cBZ8Sd02mr9_8Ik}1 diff --git a/resources/icons/emoji-categories/symbols.svg b/resources/icons/emoji-categories/symbols.svg deleted file mode 100644 index 75f30ee6..00000000 --- a/resources/icons/emoji-categories/symbols.svg +++ /dev/null @@ -1,46 +0,0 @@ - -image/svg+xml \ No newline at end of file diff --git a/resources/icons/emoji-categories/symbols@2x.png b/resources/icons/emoji-categories/symbols@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..b5e7cc6c2ab6292babcdbbba37b5c41e6511f94b GIT binary patch literal 1001 zcmV3fJx^3-`Y zXX-!j-g#C-<|Hz=%mLbw$dr)=Oa^uUWaICvUSz`f_(EVOQVuKw@_->E#=imX`Mlo` zRM~D8{G7{N*Mklg-NB&yd3otbxez65^kq<@;IPEpLO9R+}L<3#?J{Zt|2NnjH%IKJD-5K428w%Jv zB>8}Z%vxpsQ9uK_mG=vPwTkYC1^M_WhtZ|POy#AUF2G^nt}-7qJF0nO0Ql{*286us zeEd`uml$o~b!EOrnXmO@tVlg(_ZddqOeL>dcT~5*Hf2#iu+*6=L0Ufd{HA~gY_>3< zI8pK-{c)?ZxCv>V`xL!(KdQ(n1Njpc>ihd9YxpCP7d;_*dNZws4_*So_8h{Gd{3Hj!J>Ta5pJXIz z^#SW5CT~@})+rNhjIA_tkqD%^lM31mtcZxbb@i(Hd8Nmu`N$1{39tqKUcrVu1gKs+ z6y-y_?~AtMo#^f$_n(Q7FW0P9tJgw2s@FyhU=^?gn1b9O>;;|~zQho)GD7k&($2=m zDqyvu>jv0wAQohN!R$VV0+5FQ)oTvsud5q>fR7q|D3)~snjT+d4mBjMLZY^zs5n)*_tk@>-^8e9(Yb6WztJv3mUr XcDfa-utW;F00000NkvXXu0mjf>O{{R literal 0 HcmV?d00001 diff --git a/resources/icons/emoji-categories/travel.png b/resources/icons/emoji-categories/travel.png index ab50f7cbfd1ac005569b53786f51565b5ee976bf..93da773e8f235bbd2ee9642c5a7cdc64325e5a92 100644 GIT binary patch delta 426 zcmV;b0agB&1h)f_7k?NC1^@s6;CDUv00006VoOIv0RI600RN!9r;`8x0c=S`K~zYI z-PAuz98nYn@E>c0blL?iB0*vySZim%6bZIjh>(1tqz`_8wa{7xqg5mj(8kUb`Ll_z zl{Sf1$*#rR(b;8Zc1GRC1DDIY@7#0Vn|beXUs1i(Wf zH!xW0(fE;_{G6Hij@k_jYSEkO?85^*#3mkL8c!sr=r;IQY(|^cQlJoo*yLen6l*jGTTno21QA{sd8XpS`^0;qf+^C@y#mUI?k(VPc z6d2@jQ>|9kU?TFD$Xh+*e#qmdS}jN-6kSLs^Tw6=y^q>SAL8!a($q?mJ^*2h1wz;m-AOI55gXos7k{wzC-U6hR zya9x2c%TrlsWWLYs%lz^&p>+(a93@>lg>^BB`g7tR9fAx1MX_ubN&%nbT_(ycl@&) zY@ooP!6=GNCx39`*+?}2cXjUJT#HQ<*aC7i{`Q!b6Eg~M^p9ZE4$-W>fT2H(#?+9Q z#SrkS;EUFN-~@1h1EALeiYdndj)6YKeas708&mKSf15j&0=q_TTJdrDsSu?Lai6AJ z9ee-@#YZd!hP5VuPhdcdnBqCJfTIq=z@Fl#mI7y5_kVy^b>Nr<@^ai+C#l@QKCo^n zu!jE*77SKCY;*u{1&mstUgd$gfOxqBfLWUN9n1jq0QXIR--%sTTpI=X5&3VH5xprV zBEKL%9+ccc<*W8u#!?^?kY5xlP|LuBCpimDSPD$y|93x7`nQzz4GInAuZ!hprT_o{ N07*qoL -image/svg+xml \ No newline at end of file diff --git a/resources/icons/emoji-categories/travel@2x.png b/resources/icons/emoji-categories/travel@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..2f72a2813a09d6e697b1a03c5dda346b995b3d07 GIT binary patch literal 840 zcmV-O1GoH%P)n zJ>UDz-22{r0}V9LKo}eWo&t+N2`C$~60iU~0lMN6KL8XBigvRiaLKgzUf^#X#H*7y zZA!cae6FMTihTl7CMIxR5SM`KK#K`-2Q9!2s@K&y6Xa8Z^rZ<>7rYX+rfMZT1gs0v z{#r>ra8}e>2M*Ul_)rkeW9WD3f)wyo)O-+zpdGu7dGrfw)Sy^5jJnj$0~3x$E!u&f zF3FX4cE%{Yf=;S`wyG@w`*exVJ(9}6bj-q4O#5hgrb}A&Ng9e-w2C1gEq`=LOCAZo zf!4THD`<6Uy6IWeB@H+MUBY>WY% ztwoqX6y6^qe$2u?1E}c0`_yIm#G}HGHwEk$zW*DLyc=!GpbX(#9Ry){_u|o=FCutc ziKPP+x*TBwVR=u8*qllx7t{q&Y|Nv`y$Ir?@Lm-0j2e{RW3Fn7rLkLDaBPNXcRnib zHKB0=HeVy;)3`%J@YXTY2+Aup<-lI~0LwmulK`n{i{=sh7gW-07~DIs6R-@M6PN=w zwoglFY~XC8O^&08{pqe3*tJcff?^f8gR_l#J-W-{T8{OL`@smmrE~5icuk-nuH`PE zA|Ii)5q_@;dX%-DMI(+#ZPzaOsvM-#{o*x%|W(f{F*2j0{|UI+dLV#h!H*RF^P SWG2=C0000 - - - - - image/svg+xml - - - - - - - - diff --git a/resources/icons/error.png b/resources/icons/error.png deleted file mode 100644 index 295dbf06ddad543dc02ed4694d2ec69e4c80e8f8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 621 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10f+@+{-GzZ+Rj;xUkjGiz z5m^kh={g8AI%&+V01C2~c>21sKV)ZRS5lvRbagfZ1EaO4i(`n#@wHP8^O_w5T;->2 zy%Lf)_a1MqpP=`hW@d$T4;DuVI+^V7do68YvMyHH{gz0>VLs_C88=_LeNHd`H-9eY zhPk@(dp1AswY~7c_ptc@}|Ikq+W)tC=(k#Ej z@}TJ~>Gqdz-+jJo{=t3BUHt_$nE%Cc_F-lEp_(8o?o0z|IuIDYJD`+52ILN=avE_7_* zc8^-!qwB7xv@3Gq#68hBe;cp;Fn2HeDK6!+)hn95N4$@FxHZB`Z%^25g;$H7W}V-b zu=hWcyH0@X9)6eiz>rlfag8WRNi0dVN-jzTQVd20hUU5kKx7(XU~Xk>Vr6WpZD3?& zU{Jic)(b^LZhlH;S|x4`O#hC418R^2*$|wcR#Ki=l*&+EUaps!mtCBkSdglhUz9%k SosASw5re0zpUXO@geCwgb?b8g diff --git a/resources/icons/left-angle.png b/resources/icons/left-angle.png deleted file mode 100644 index 715432864bd589e4093da4c2bd3d65e9b753cda8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 225 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaN3?zjj6;1;w=3*z$5DpHG+YkL80J)q69+AZi z42)VJ%-GU1Hy$V`S>hT|5}cn_Ql40p%1~Zju9umYU7Va)kgAtols@~NjTBH3e}GSj z>;M1%flN3Ew9fhf^$82SdtaUH}Q0F43W5;oY27Frpm}>Ay5|BuvyY~ zg2f@db4(sWZCfTJI8AbRxjpE6Pzb}08+?%-a?{s<4EJ>Pb6Mw<&;$UX CMLaS9 diff --git a/resources/icons/left-chevron.png b/resources/icons/left-chevron.png deleted file mode 100644 index 386395fb5092539d13c5f865aa05f817ee2964e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 590 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10B00b(#1%+0?B6fCaFyty zRX|c?!AcMt%oSO<8p>X{iectVu|;6+f>r!Wmx?R^D+a3+n7>{Ote#=jD#4knL>8_D zav2&LK!QL91H(_gHy?nMd`XaBFat}@rK6Fn+t;LAUi*f%Z{1nJ^=CGm7T9=-jlcFJ z)5GK?wX=Xq8I!!-T^Kr8Wj%l#&H|6fVqg$l2Vq7hjoB4ILG}_)Usv{r%%TD&qQ9%S zrvZhUJzX3_EKbk8{F<*>frm9=M&7++d*YYB{=I+cu^GoQZYq9xf1O3c*6Xgfeba}P zZ%}*8H2dC}2 z_x<^2o!8+0!23}8BS*{9hVwOXAtT_eK~Lo+K=BP%0AZ39Cq1A|K}=Q&U`NSwWJ?9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fgAs65i;tHgH{QC9d=g+TS zzXHkczkUJH4<>&pI+S(=&2N}c2E0-#Wsr;B5V#`)v~2Ihr*e6DON z2i{z~crmF#<-jdQ7Df-I9EHXfM$rU5Zy;djoWdx%l8r}6R^z}#N9NE4oeP2OC7#SFv>lzsA z8kvU}7+V<_S(#dB14#pezg)ZDqiD#@PsvQH#I2zxoH-b%K@wy`aDG}zd16s2LwR|* gUS?i)adKios$PCk`s{Z$Qb0uvp00i_>zopr01}&sk^lez diff --git a/resources/icons/power-button-off.png b/resources/icons/power-button-off.png deleted file mode 100644 index 2409032f4999116c870fe83dd80d657869d485c1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 773 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_*1mUKs7M+SzC{oH>NS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5lweBoc6VW5Sk5Uj7}P}D}aLRC7!;n>6h8q?WW4O@;uvCax_7c|_U!Yc1_bk{phb7~TuCGtoyxh?2r zob(f(g~zVFT#y}Jw)B#5%)-z6&hLGGhyMh}-3!xy1)2Z2e<4;ks>Iwoc;=DMS7z{g zD6TjiFWkZPD(D15)2^N2*OyHfbL3jKeBIgFfOsah-LK|IUAS79wilIEuo_9y>JBEmE>%*U9X3Ps@elT&Gu^;of8DjkmSC*xV8O)YZXFWafwZ8e= zUqvkp1vzGuMWaqy6-DKy=ca7cUB2y)m`2BUA;o`l$|o4B-eYDxXUyPs@9myX@9W(d z-cJ)$Ik#wy1iypcoC>}T)F8+3`eE2S>pMxZLd%&pRI*4h)q?;S_{kKR|bI*D5d2`f{G<}5wGo%;zWzPF) zSo_h$htc5HkFOKATgKK`eyS_ou)#^_p!8SH@{?;Gm&d7>wsgPRU{m?hB=ZtWP0h#d zcz zhCy^%87RdeH00)|WTsW()}Up5ZYNNKB*=!~{Irtt#G+J&^73-M%)IR4k4UEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10f+@+{-GzZ+Rj;xUkjGiz z5m^kh={g8AI%&+V01C2~c>21sKV+5`;I^K>HaZNbsnXNMF~sBe)G6n^r5#0%@6Y#j zO6t~Bi(F&xpy)1I^f}~%f~}?oM_e_BhjXUCQ}wzllNT&#Q>x1On8RQ7QC`4>U+#)o zfG^KY-QZKr(AcT!<@udpytJ#IR03g7PU@A@iwr?+v~Wo{Q7IM1 zSgT~u#dg6^NBZhwrl3C4Dn*F}_aEKzTim*WB}6tmkFAMux}inBS$mzQ!$r@vJ_ZVA zU3}HX>8Heggv|KD;BYSb`6~9^O6RAR{{M6;Sck*fU&P_K+`Bh6D|3G^XGl7iMe%Dt zZ<@9uI8JAl;j`P*rHcjPHP_qj4m{wXDfzEY&+wD!r0(l?9%V*OTYOO_gn#?*&ksa% zHAPe79ywcG%hyld7XQ02+4+{$if_9a7$)3$VzPem&1E~z&7akLOF}P7@JVd6SKG3T zS#3Mn180<^&Ased@q1t4)w?)FK#IZ0z|dUR zz*yJFJjBq<%Fxit)Kc5P$jZRrnC}Q!>*kacgMUX$Q!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10;!=Q5h%1o(eOq)Dnab4DIoHG0*Jhv z^6wf@5Ul2U>i>(eAiPX_4&farL!g;RlNQ->h%Y;AHFv?aD|1 zzp4D(_+tvtJjNt%cNc~ZR#^`qhqJ&VvKSaG*Fl)kNn>^eP>{XE)7O>#A+t0Sr(w{W zD}l^O>D<`ZKNU^ufMY_vI{PGz+XAA370JxcHzfzto23-b`1vvux<+nedZo zxqVpdBjY9SfPPUeag8WRNi0dVN-jzTQVd20hUU5k#=1u4AqGZP29{PPmf9eO!KQ_i zwxejs%}>cptHiD0@EBu OF?hQAxvX!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10;!=Q5h%1oxOG@(0&Gk=B z4Mv zkP!$r*`WahfPz3-|74K8!3pUwl_2-{fi(iX!=X_+0Z6%&1o;IssI$A@mMP%XbqGnX z?wN2v+MJPjZ0@uJgRv8ZPe=zYCn8$Zpdj@aQ(-^^ypQ~ z71djZpMF#Mx$(yopm~f*-tI089jvk*Kn`btM`STDTCRgIqm#z$3ZNi+iKnkC`$J}F z0akWBN3M9F&;d^u#}JFtb1yyTYIYD{dEjl_*}3fdz3TUS&;GU#_7miJ_15v({Y~;R zZ+MgIk3L`iosq>^B|t$?=E5O{B?gVrEy{tWV%JMz{SW9)T3Nz&?uOFr=5(Ej*SQ!J zyHq=ZpDkdL3R!7ezbI_^_ce+)H!ri@ers;t`u8?dOcNIH?De-bYX9i+BQiL7#@VCy z_Zl3Sq;cky)$|QT*?eZ_oc>HJJAJV4)qOb&8O;K#$A?bD6fQm}%P+Ozxi{05?JOJm zc_#d1T5cZ}`^b37JD^`wOI#yLQW8s2t&)pUffR$0fuXssfw8WUd5EEzm4T_1fw{JU zk(GggVq8xhiiX_$l+3hB+!_LyO%4GyNP=t#&QB{TPb^AhC@(M9%goCzPEIUH)ypqR UpZ(583aE&|)78&qol`;+0MT6@ZU6uP diff --git a/resources/icons/smile.png b/resources/icons/smile.png deleted file mode 100644 index f226a5f317cc4cdeb8d22a9194f1cae9eb22c2f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 631 zcmV--0*L*IP)*8l(j8FWQhbW?9;ba!ELWdL_~cP?peYja~^ zaAhuUa%Y?FJQ@H10rW{kK~zYIwbn66l~EK2@ZWvekPRZykb_W=L37md+61S-rB*|5 zPud-gmI^l~YpkWADF_;}K@p`b2((!Q3B;-AZTQZEulMfvo(f*L-22^g&i{YUdFP(< zQqEb^NGXlsDIVe$ZiZ+NyZDB;Ip;r(AUWs0i3zOY2zkgn3f+k!Rsnn(`(fO{7c65o zh$mvd9I{U6?qhl&0iM+iexY5uRgXBl!ETc8oH)8gMswBr1{0ywMZibFUCUojRVc5--jRHYYjtBUQ zV~n>{d43gNbI$99Og_`dId9-|cr({hJtElb(S06go3Za5gXCMWwqaZ4=InWY+(P&s z1?Dy!#z*lbo|E84U>>&45#}?Ema1^U{kkBfbUUT=WEen7>2XTwPS5eZSod10s@O+> zmw2C2njZi@j}Lg!K(O0VmE01+$5>ZVN>c^!DXhfjrv`#`njhLFRk~&T#uDzvT#B=d zAGqA~&>BlcoL|F^=3cOa>oxuUQZcO9VFU~Kt}0>&v0uQ*sfyi&m+PA1qKj%cHG`RO z@vJTLpnmg4f*_g1T4chorhgo|lO?RwZ3?B4QX0oxgrW4eWgYKw&WBAH{{S0R&jYCR RakKyc002ovPDHLkV1iEQAFKcX diff --git a/resources/icons/ui/add-square-button.png b/resources/icons/ui/add-square-button.png new file mode 100644 index 0000000000000000000000000000000000000000..a4933c16c9097df85f9d165f70a9fab3860f006d GIT binary patch literal 965 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}oCO|{#S9Fh z9U#njDSry^7odplSvNn+hu+GdHy)QK2F?C$HG5 z!d3~a!V1U+3F|8Y3;nDA{o-C@9zzrKDK}xwt{K19`Se z86_nJR{Hwo<>h+i#(Mch>H3D2mX`VkM*2oZx=P7{9O-#x!EwNQn0$BtHPO|}r3j51k_V##lVOtdm< z7+K{VbX8gt5|gF4T_2Y_sri|md^c%t_S440b9YuhKT}XYt(vpsBjzQi z0&dlAJTcw-2dlGc-z$%3%PFgwUH7<4@SZqbuQ0txtnC5Eo`%^MJ~V!0+P_vg+1lri zjZC>!=$qW1uP^xj*8Qh7r)hGdhHj?U?X*zKsk;jvSl!<^{pw8NC2M8ET8hfwe3Sau zVpZIP(UUgTe~DWM4fMCgO9 literal 0 HcmV?d00001 diff --git a/resources/icons/ui/add-square-button@2x.png b/resources/icons/ui/add-square-button@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..b2c4a44b8a3e15f47f2f636f5e5174a00fb08745 GIT binary patch literal 1386 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|I14-?iy0V1 zJ3yH6$aRMWKm{q8ArU1JzCKpT`MG+DDfvmMdKI|^K-CNkHWgMtW^QUpqC!P(PF}H9 zg{=};g%ywu64qBz04piUwpEJo4N!2-FG^J~)icpEP_pAvP*AWbN=dT{a&d!d2l8x{ zGD=Dctn~HE%ggo3jrH=2()A53EiLs8jP#9+bb%^#i!1X=5-W7`ij^UTz|3(;Elw`V zEGWs$&r<-Io0ybeT4JlD1hNPYpzh2qfLoMT4D}VzfBF@P1^R}12KsQ5ff{XqezbBf zN=+=uFAB-e&#?nJEi*5>I61K(734r0eRLI2D{b^a?m%)E#M59=ps($?fI49zXvcNu z__ACE2Il#mE{-7;jBjV}&J%VNX%kimVe8t%pvgE(TV{Ke`t7)TST?(7UVOib`Nlm|%U!@hz zFN(!)nR90q^Yp79f}4*_U$)@oB?IA%4|^LQc?(}in^h2V^gORv{Hy~$4zKeM_8+lc zm~-We{}wU(9_wFbIxmak|L^|#F|O79(5?@zGmhNQ3@SdvnYccIHEMcGw8?_Y+y`&J zKE3KZ*G`t0hi@{SjBhO$V|j10-)XVW<7E}mY6~Ra26y`@FZ(Sgl`2)>-kf^qk(p?9 z-wyUwwFR#x$2I+SZcpK7{&D*r*PYY0HoUPsAH>XL)OWU*AA2=rxuf@!EukGJ^26^N zA97qMGe3FC{_F2KXFHwJ*dA#4%z#~x2`Y8})2 zg=ZM@TVGxc%GN*D9m%tCRdV9y_=515qtojC9$xkS#)1F;|D3ujZP9Ba7hVzG`XE$g ziB3%T*{;o65r1EuJA9POWJXOAU!(IivEvi?p9v*9ywx^8ej`E8kjb>d8ov3L%hThD4k8b)%#CIavYvIj;w{gK-2SD{Zl7zo()Y|P z&YHRCcJj)feaUPUx4&eq+$#R~>ORNZsp^i9pr|>Mq)~D>=75d*2LqR#uO82meYEnb z)M1{6h*t@smF8a;oU2&J#>W_Eym8UDt213yl;UmpvcKFv&~V&s;+HwWru(o3)3%5o{CsFn#tX}9OQfRySGoOKF-iVEv+#wE zYo~TBc(d%nlD)PoSaUN<9xqzuJBKsF>GrfmtHbW(NtNh7f1lxa*<+*k+VvdgS>vou zoMiwf`jtIJreTq9)Gb3l{rb~%LAYR(vQHAvKD+;3ES0tMq6^dhuymT{Uq8?vcO6s! Ndb;|#taD0e0stp1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xps5^5D;1=Z-LwyDGpMFJRfxe-hfj-=1phg>@AFZ5= zQWHz^i$e1AbL>D)%goCzPEIUH1v$`0A6*61N*jHUJCNK3@ibTz=xaMJpiWo_+Hu`E zzAP6QsAoJ~977^F--a7<9X8-GnLFENK}qI|33YqoUtUmjUU=b~;n_@!4+6&}i#{nG zOmucAw@bA2@)Tyibgyx;+TW^c+;;mL>_4!aYlyya(ld{VhyMY`31xfBlex9bduQ!+ z`N#Tkt?EySCtB=YrvDoaa>94c^V)Vhl>2t-HvO+<+3WSTl`G6@*t7ojKjxUm*hd?@ z#S$kiQ+=>=R@{C8NA?|VmRWu~Y>t}>>ctwlTwq+6z1{g=be!-*qZt85F5P};HY?C2 kj_>=!DN1KLeINhisXDPV#B!MwCn&Xey85}Sb4q9e0PEi7ivR!s literal 0 HcmV?d00001 diff --git a/resources/icons/ui/angle-pointing-to-left@2x.png b/resources/icons/ui/angle-pointing-to-left@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..f2fa93c441598709be07e4461326403871d1a52f GIT binary patch literal 877 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|I14-?iy0Us zUV$*DSry^7odplSvNn+hu+GdHy)QK2F?C$HG5 z!d3~a!V1U+3F|8Y3;nDA{o-C@9zzrKDK}xwt{K19`Se z86_nJR{Hwo<>h+i#(Mch>H3D2mX`VkM*2oZx=P7{9O-#x!EwNQn0$BtHPF=$ElmG-g%<3dHLJz$bh&2*yQ4P8)gRhF6dupytQGsCejq=R<3!Vcwhzq*g$*Wb z{WD$HKjABrVs_=O8S+_0i;lD|V~+p+jd7E|_y>2P_5Qo}mK>VQrvLWaB~Jf)Z|~kV zp0fMW!<{D?Ics};|L!f>Hr=n{(u1XU@A>jM&(xN9!f(9kcjYX;_k!2#9bZM+eVM6s zTm1Tc)dTej@Am!4=z5&k)jjL*tS8P3Bs_O9wM?JYA~??>Tu_ED*QSS)eO7nhM$3Z_ z8J_ttc`UG!UUAs*Tfi1I$Fk!YLQRY>aym{*E^k1-+Z|0E~1r5Jvw3w*} zypz47b8y$pIS*c4Xk$Jr%lzR_A6RuRAN%Bj-SZY#85N)A_{l4Bb@56a&MD`gWeK&- zTFNGDZa6da`&19k4~bXwID`6^H@EDzpTD2!gUhz#hgc<>KzYE^)z4*}Q$iB}d$~|^ literal 0 HcmV?d00001 diff --git a/resources/icons/ui/cloud-storage-uploading-option.png b/resources/icons/ui/cloud-storage-uploading-option.png new file mode 100644 index 0000000000000000000000000000000000000000..f679d67646d099f584ec874197ffd57130c8dd2b GIT binary patch literal 1008 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}oCO|{#S9EQ zuR)mc={1*2Km{q8ArU1JzCKpT`MG+DDfvmMdKI|^K-CNkHWgMtW^QUpqC!P(PF}H9 zg{=};g%ywu64qBz04piUwpEJo4N!2-FG^J~)icpEP_pAvP*AWbN=dT{a&d!d2l8x{ zGD=Dctn~HE%ggo3jrH=2()A53EiLs8jP#9+bb%^#i!1X=5-W7`ij^UTz|3(;Elw`V zEGWs$&r<-Io0ybeT4JlD1hNPYpzh2qfLoMT4D}VzfBF@P1^R}12KsQ5ff{XqezbBf zN=+=uFAB-e&#?nJEi*5>I61K(734r0eRLI2D{b^a?m%)E#M59=ps($?fI49zXvcNu z__ACE1}0Tc7srqY&bQMHy=5Flj_b?0xGZiGFkI0pCZxp1S=Qb2Wm38-v4=;X1lDIsxTD~Fhp;{}%^%2^$*SAP7Cek#QNrdRmy@j*Lr`c2Q0Fe(@K(==Mb60aaH+)@bB-p ze|))H#+u2p^-A5Qr0@10eD8({o)@(0oOq|}mdY|IxkKD9TEjYWxm{*t-1(-w@pQqw z%NOp=Y|!G^l(xrd-fxvUd4(+pB5RuO?fS=X(Xb{(Xi9;H;Qo)Vn>?zU-8CJ(^PD`b zj0FvsnP)g2jq2R1t@upceC`FMxM+ukVd{6pH)Q?Y7EyG$OY0HC@#D|lZ10zdV6tEN z?&}*j#UoqPmmR#v5dMC~N6QPZE022V{${(!8^HcGfP0}l>`cwJxN@#&(o5iwMwb~}ne*gIXmS;(b!)E*9_V1aZ2Bp0%QGUkT^OG084HjMC zyR=s<%TwmAacu3&nB!_X6%$`fZ{$kePZu-=j@S+dtW54a{g+*v~2gvmvb&C k$2I?*-F5f)|7h(3PUHW6Kd!ymrUA-^p00i_>zopr0O+!xr2qf` literal 0 HcmV?d00001 diff --git a/resources/icons/ui/cloud-storage-uploading-option@2x.png b/resources/icons/ui/cloud-storage-uploading-option@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..162bc9549ecfb1a1fb93d7a8b17166d10996f1ee GIT binary patch literal 1566 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|I14-?iy0Vr zUV|{>(`zo5fC^GFLn2Bde0{8v^KP? zWt5Z@Sn2DRmzV368|&p4rRy77T3YHG80i}s=>k>g7FXt#Bv$C=6)QswftllyTAW;z zSx}OhpQivaH!&%{w8U0P31kr*K;4;J0JkWw80ssa|MV*o3-k^34D{h912x(J{b=P} zl$uzQUlfv`pJNAdT4r8$adKioD#(F0`sga4R@&%;+=1jSh^N7#KwsN&0d>Mc(2ncQ z@nyLT3@pK(E{-7;jBlf&JA4Br2qsiv&+9(zFwbyEiQZg z*Z=>&udR)&o~x<7IZbo}R|M-CrZC3U4lA_`4k~;s`Z13)ggLk|FoEL-PY(lY;x5)1 z++n!~q7oznxI>sGH|{^c|3LYJ)C<c6Z}d2d!MCO?Es5 zVTT3EzyEFiDBf72j6dV3TrU_e?*{C zu$BLGZ>t7Ve6Bg~WN|?@!F11sodp_-pOg+cA6adn=`^F%L0PDh>w}obrEf|R?q&~^ zro3(tW%&Gq^TWZP3f%Xsj=iz5IekVYa-Y*3wg);NL{Es%p6p!Mb3>-$FUu3|yiIep zPb}bG6aS)G;6k4D!$9$q_dk?M?O1bhxv@FdHrWeVb3Z)e5&Amg$#)B*$Db@$>8~%` z6ZKlQFiG~4^E1{5q6Q}{%`4T8wr|iB^fPSWo6W0U_`Y7W^5oem9Dg|sT$_$)&Ni%& z;4nJ9$}aASs=lz&hu%PS#z#|PdOyBgrS$x|Sj(O#%c5DH?TMIkTvOU`1~-rWrgxtc zPk7f^xjqQp9U$Yb8*MltyFM>{Cuh~w#S^B>ly-jL<6C_s^G1;nn+$6kgZ`pb#f2xl z=UKVzP&s#K+GWE9+yN6mXJ2-V`_!wBlKC!LsmYC1<4_TD$m_JHrhGNITI(79>~_F+0V);u=)Jdj-3

nZ_-7HFJguD}Izz?P@>8nBM$)hH!y&M#0w2UV)YS=85uN z zNxb~}kAdgEo;-e@wc>`%Rquwvn9O%fc28cOKHI(Iz8iDL$@|tuZi?@i-XFVdQ&MBb@09`DSry^7odplSvNn+hu+GdHy)QK2F?C$HG5 z!d3~a!V1U+3F|8Y3;nDA{o-C@9zzrKDK}xwt{K19`Se z86_nJR{Hwo<>h+i#(Mch>H3D2mX`VkM*2oZx=P7{9O-#x!EwNQn0$BtHPq<;xTayvjlq+`>nL4|)3k3ZR_`A+k zUgGm<-WK7=)mx9(H!oG!e|_+NHFJgQ`ttuDxt>Vfaz1=e_9x5vg`y^TJNnP&SPAZF zId>u7`%TMDj>gO#!_>rjp@QOt(UAalW&-Y z%&`;KQQLHx+hXZerm2?NwY(c7JiOwCeV8x4%x+$MIlA%cm)b4QnYDfvd3xQK`SZqJ zm`^}np=5sgzVBC(f61RH%GvO9JO76U)>$*0l;Z=B*Q(8P*xYCOUF`CH$2jJ^!dIsA zKQ6phto!4p@QE)wAA43RUzVRTONsf`y=ajYYxQ3`oNx%K%1cO9(Aqn-@x_-gwndkt zfiB2TRM4!=Wj^^bTiU}fKI_0=&iIWpt6mA-kb9^0*7WVy>A?rwcU8Tz{Q7^o>VLod j8?B6e7x+$9t@+RRul(=f_MKWHpcL%s>gTe~DWM4fg;g?E literal 0 HcmV?d00001 diff --git a/resources/icons/ui/cursor@2x.png b/resources/icons/ui/cursor@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..d02d29fbccc75914b31a6fa87f67256ed27831fa GIT binary patch literal 1148 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 O+~w)&=d#Wzp$PyGesz8T literal 0 HcmV?d00001 diff --git a/resources/icons/ui/power-button-off.png b/resources/icons/ui/power-button-off.png new file mode 100644 index 0000000000000000000000000000000000000000..7763de0f5144b135c1d94aa3ac2e476254f6513c GIT binary patch literal 1174 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}oCO|{#S9F5 zuR)lx@NDB$pn{alkcg59UmvUF{9L`nl>DSry^7odplSvNn+hu+GdHy)QK2F?C$HG5 z!d3~a!V1U+3F|8Y3;nDA{o-C@9zzrKDK}xwt{K19`Se z86_nJR{Hwo<>h+i#(Mch>H3D2mX`VkM*2oZx=P7{9O-#x!EwNQn0$BtHPf-29_`#i|o~4?h>>+S+{f3~R z+Z|<5ODMD{OQqwT?!RCOPb@@isH;UiQJ3iMzY|R9=2Z zC3m~6f3hSlBq!vS@W*(+tSzpGIaMQf#9m}OHmOnl8G9Ie@kFsLQu>X&zsBH)PHl8vP&q@@i>(RuM|X?u-YU&oKe_Ib z?={&5v#OaNA6}`L9M~!Oc8PQR5?MdBS$q!G#%Gq*rL^48|GNAI`@f7@trvO4498|1 zJzPD>F^uiM)TjLDiLc9Mh8CU`lU;Y_(L+bKLhH8-r&axa=w01^6=(wOv#dP^yTlE*0TV0xQ5GMK{VnXK1VRH^^c@9~dH_s_a`b?pIW z{rA3NGmkp|2>oiy(!O<-|L)T3Li~GdPAF||QI7f<98@n-K5Z9Uv_ZG~EAKG*Ft19T zWoPG<#m1L!KEShirMzTHtA(DaY(lNOjOgv8klr&caVa559@f{7W_;NDW%HvA5v<={ zEHwXgV0I7FQrC5#&fOE@7m0oPXzR4?N7)zc^@0qQkV12 zUsm4AsCZ~8V79tCEJrxtdNX_6+ymxMm;CTr!`=C9Mxt0hYw}*1bDiZ!-zSN59Fw#C z($~@3A$Mq*Vx7+$(eE8KP6d%QdUnF`U28weuRbpw!w}hW2vk^jy85}Sb4q9e0BE%5 AHvj+t literal 0 HcmV?d00001 diff --git a/resources/icons/ui/power-button-off@2x.png b/resources/icons/ui/power-button-off@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a462944604b6bfe418dabf81f1651d562364ed6f GIT binary patch literal 2044 zcmZ8i4LH+l8{ghHVn|1ltkb(z`6%0bhGEH;+7XTR)|A8;WB#_b38iC2*=V8Gl*8-b z_y|qjw0(u?*n=e z69@ zNH~$iCV3q~2h#y$Pzaq&LB;|MH5vqRiAAejfWi)f#R8F0EOe|Z{0joD_E*CwIP43A zO>>2lya+HXok@Y&Bkhn@a0~VFfe88SHR+6l}F_5Sbp$c7?-N8+{+& zcmj;?jiOlJRH^l$VuKhcYorzGdu4T^i&`TZ%cKOc=}aP>9*J3{79K@o1qU%Gs{~z8 z|Bv|A(gn53<9~R475ycuR*iwEGygh43}m3C2mpaJt?{^h#8~iDmS=gu5yKX`tFK7j z@N&=AQF!+?E%)O*?ggvPs;wJ#m|of8ap-AdBiCZ~rtv|dOUf>T#9jCTE*Cek5ttvl zTq>yIA1ygBu~PqVE_&$vADDAfa|;jsm)pccPPB@Ko`gGdeaiW(S9CS!Zm5iAhqv}( z_AMR`yj?l~-#W|XS32=EN~d+CsE3L$-CU>CWTKx>Rpf6LI7yZ27kM>bAQVgo;k!C1z6&0W9On)Ld03|hRhL>k7V zLYp^9#Las+ubgp5uZHpVrqWYr5-d54lz-?QPg2mnbkgiI)lK_NjmKE+Tuqx9JCKWy zl-DRcbh6No?mxBJY1D=lr)dggK;w2^Hr4$^8L9B?X9+V5%G&jUVAu-k@@Im)$L1J z&t97v>5TRJ{p)y?otFbAdqnl2S%q~7^~&YjhAiN(XfUUoM{B5OxW5*TYo;d+45f&Q z6npG_`gN0x+#TA2AL&-itmE}?&pmF+A+;@WxI)7cUf)lTic9$9@>J2%$xoL|j6FM_ zY<5H1MH-w0DuIaIO9R!Ub;8V?ruV~xu(g0aNRPVjNcqfTZ$PFe=WN;fE(zY$N8OjN99^iu;^3_YM_0>Yc9_(?859 z^RxkrXC+dgaW=~-BkRuT=?AAaFUDtzDX*ac5Es!*heVzg7zg*+e&4$Z0KHofwhaBY zSbK~32znWBluIzS%R^{HOeV-@pbX!L>L)S01G+zN%M7CoY%zJ;Yur=}=}f9NEU{LA zmfi9aA_TbJ6E;EYWIfr zD$xxmANW2fcewCkG*?wUu}G@&30@hL0HEuL7^xHu$*D6Oz)8JZ-_~f!lTAMJnQ0&C zCXOR#J*r!_tAeafRDI>$2==p-4<#)w6nBb)F26ZtmqR1C^JQo~F<7WH#~Z1}^t<%4 z933qP!FR!ym@`G8v_4)B)gzSm*Q^zLKINFNGoc1<4Hh8<6=xIxb>!!k3vO4E*)Sm@A3058sv?VH7N-R|tR zeWERZ?3y0NB;I0dM;~mZFF`=a7br$rr8qsAuXa3L*==q1`OYpY&N*x zT9ru-7=J5$q*GOgcUdX1I5%;w_vuuz=BX)%}oap?^+qu?Q_JO%F+(m{`_ zw^zCmpBG4(GRHe*>NBhikKB+MaM$`++d>Y>G%!7wiVZHS#Yy9ew}#~94V);{268z6 z>~1F?Y=uQq+XBYBrwkoB&q-Y0z*0=j>ZE}Ux7zQ=F_b@hFCLjJ{b2-dR+p?PKiGD* z^1rP9mo8+n=`|Yl^d*6-sdtJn-xAXdH?4&3|3&`%rXT?Es`K+G`^QZPg_)U==RbH* ziN0isJWRa@7LnfHed&BSdwnLaOi~Dcy;F&pg`O^LAxjGfRWtfd*chbERP5C1myF-< KjT5;CUi=du3$!Hw literal 0 HcmV?d00001 diff --git a/resources/icons/ui/settings.png b/resources/icons/ui/settings.png new file mode 100644 index 0000000000000000000000000000000000000000..6e013f75a5c053b11fe8b7123632b750988302c2 GIT binary patch literal 1363 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

pI$ag(J{XnA;EaF zP*?X&Q8W|{3GoogLYB&26k8RT2EOS|-!5Z+koOpnS26wi9B6WX&`=(*Y}s7Y`*hO2 zCZ)SG%Eq-GQS{09V&blh4sda--S1tJZFV}=WSkmXb2(!@?ZUu?aPuWb-kj5@%#hZ{ zUkD`9h&`CRufJEktT;u{=nGTm);ssuVc6RMKTw&O9poQeFE*_o80H8mnaH5e3MuEc`YfnTU#$%}6-_~8VqzYT! zcbFn?WN@=TOSen4mtW&T9)sh%hrjv?H6>C3qh0do#%3?=V47cLq@_5SBIwzG3kpae zO$2}hax>u>ig@NeNYHEA`B5d-#!L=%DDDOO8+zt}8gF7TR{`{;^EeWfc#qdV-)iLH zYi&xMNFB1D>22a=8T$sr(MvapUFdZaQFGA8WkuE``)8BVQb!Jy-aix7HH866oe5{< zOIM_29V46>xSO?N~A@)Ng*v(^`2UX42yuwD?wk z)$G|l$!*CA27VVG6gHU@8}Fp8o(@nVNZ(0$AZ@2Kk{+GU##gpL7Vof6KCFhqG{|jv z$HGEZsK-^yMj5)vb#rXouum|MjSkfVuX;MaL@TwjsbK2vl#fu-d=;E z(V;3fN9|BO5O3{qy(B{X*u9-AoQ!)qjvEywmRQc&Mo^tdU>sBvb`$@(>U5PxU3moy-iOw+7MLgM_1c-dlNgI@pvWwUu)NMvaO(QVv|>jjxMS!I75BpTe(~1a7Oe}C@PZEU3z}|jZUS@(T>sOJy;dEvc6o5(`L@?O}upGjNn!#qKt7mPre!eiF!p%dW zTfB1#Bln?Pw0LW{Ix|9g7hHOGs8Jr+rZi_U;gTbhvoPlTBUwr&PNLK>{8-}_xysLv zr`?e3KJro(Fn&pi7pYiQkp6Nq*%@wh)NgsG#j;(#J%;*bp)fa>{p6Nd(=A$eT}2Vz zuD)y$^pJw8Rvq+cdW&h7z$B)=`=bK}V?vdm%`bqGj(Aa6AqmS14=vwnrRMY%{osEAEJa5f# z*uHp+h|icv;%WzN#I+!u(N*r9fEm+omU?n_?pV36jhF-1+Sy8+(r{yY+#8h1yq9F~ zPT+o>RE#`_>><#?9*T?ScvubVP74rHMI78ijt@rJ2s`|&qeu)#JR6$5Cd`qefO3Lv z1G%da#1|IPna2xXG(XL;_1lf=yV%)$ie$^ROJ&v6C)cv>W4Ri5`w*PiV`WaQLtGTF z-aC_pufm-Ps}|Gj+_C9Hr~N)R6(s!A38h}|Xw#xVNs4Ye`u6ZZJ&g_E0=B4qlpQJzSWAxO8y4LB5?8!E#)g+%I(=st(8J54k8Mul6oc)1uXO3%hUUV zJR`~x3Xfl9A7|#V%$!wqC0nlEOnHRL{A~ziseWP&(1E~Yh!zH%-vIAUJI{Do9s7DC T7iE!n@JGX8?XBuhd&mD5SK5cA literal 0 HcmV?d00001 diff --git a/resources/icons/ui/smile.png b/resources/icons/ui/smile.png new file mode 100644 index 0000000000000000000000000000000000000000..161d46d4c62f59951962924b22d15a9df25de7cf GIT binary patch literal 1317 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}oCO|{#S9Dr zY9P$myv;`xs30XXB%&n3*T*V3KUXg?B|j-uuOhbqsG5Pnrosxy%uOvxRH(?!$t$+1 zuvG%9umZ9{!um=IU?nBlwn`Dc0SeCfMX3s=dM0`XN_Jcd3JNwwDQQ+gE^bimK%T8q zMoCG5mA-y?dAVM>v0i>ry1t>MrKP@sk-m|UE>MMTab;dfVufyAu`tKo-FP)SbBnaEtPap}qq8Pro9uK;KZ$Kp$>0P@@gdk5y^xri8eqgDQcR2imX$^P%AyZfVt8R(B=MLHPX880*6^HfC4UuTRpLnli-52T0 z=N|t$yY*O?fx|TsA8vnx{d~U@<=^P7Tzh`TER*9>=?^v;^jPg(wfqcwqC0o!l@+Gy zVoAE$+gMsBS~S;kZo6cmpLdR9i`$j#T^#wzF+aoK1Q%@G(6%+EW$W9(PQ5*s0~W51 z>wY#xGhyuxVfhW^XLgJK-+R&|Z|Zj23w?!*_u9)JINsnm1~m8hod(mz`}T@0Y`@9B z;(_HI9%g5Wi7$Q#zH77mBI&)=x%0%V^QU&~eG_@ybd85i^!Ibp{gWdy^wOl3|5B1$ zyi@a!rQ4Z#xzY=6zxGr=u=Yz_qVAucTN19VJztuAaK)#mJME;ZIjV2%&b9A(d$m`k z{rp$ko~ieb-ka3zHGkuUnZaN8-Ei);eH)u2HL)bhowe+tHGl2w?$Gdz63O7j*XC&5 zQ2xcMwIjx=&QR{c#&a9m_r|pBy{&e-v)$c1CcotJfjGT{OVdBpA9%(ZbD$#g_a7^7 zYx!*tJ-)Zu%sYQ|RxoR|%i2Qm0PRp|$?sFjtYp$Y3E4G8Uv^-QKI@!uZX#E&?HkoB zy$8HsmI|1ie(1efR<2e3Q^L`hvxXUcJ1rNr16}r~0+AeCy_pS__oqY0fM4|YcHnF*lc`HLL z`Ys#)m;DnI6g_{VR>bL9*S<9$iJSW3xHY?|*>9E44xg4z4s92H$7GiOa`Kuh;_>SX zvtzezU#Hx3TH>r^W8u?lGI^%)iFY`K{~r1FB=trU*0V$_4ip+^3@kh4{5A9 w7%AcV>EaBjtmdKI;Vst0HC8c`Tzg` literal 0 HcmV?d00001 diff --git a/resources/icons/ui/smile@2x.png b/resources/icons/ui/smile@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..acc439e9961b4f8cc76c2ff72c3bf6804d882163 GIT binary patch literal 2536 zcmZ9O2{hDeAIE3vB4f#t<%&$Bh?vDl&15NAS`Z=(sWD*~VEoEIqj_mjIOa&5&@U}o!zKz zcD867i3r7BCVBfn=|r-K1^`UxXwi`9L&bvW#K0g5nr;gDL7+wBtz{Sl`~#s9Od)P| z4&W1{U?1=ys1XznF$00YV3Xj>zGx>)>!0DGnJENMrIOJw7>!1Q(vVP6upbP8LZM)A zLztnVfrv1mgauKtbb}y@*566~$z$n5!3F!1ss5xO@K#=|Hz|Z_3W01D`uF*%lSuxz zP!Q#(E0I4K9ZQBGpm5l~nMHvnB9G`3!9G|jDcG4r3N+i2<{w0$;IL$$ErBMm|0jNy zG=XjD_#a(=oBpvXa%~0@MgFscW}vZ-Cqw{XhsG((?pnL5-NV|bT{0s)MA_J^x6ryVjf-_iqukcmz&uojPR5s1Wbe5>C#& zSL>~3)AoGyGOMq8sY^C7SpZW8LH#yYB{i=foKcrd*!v1O2MAp1!f++XsI2XH&eY9U(C#yBj z@8^Bu$UQf%+C2f0%0Uzb3;f1BN14O)QQ+Hc;pVXG+Qa?{T20;@iPTTHvP!H6S;?!2 z`33uJzQ(fXzisKo*x`Mgp^Wupu2-E{s#w^aXT2wwa3dTp^MrdOe_}qYSt*1VzhIsW z`@L0+?`>Zg&k`pT%CJ-Jo!H)0K2y3N&UZGJ>=}?TSBY-KW&xHxTvvG@3bG@eUF~*G z+4J?`CPCYGrv6SF*TO(M){~N%;qA6XD`p>Eq_R5(*LiQrN>?R+bI+Fch^<+Bmu9v| zl*XkrtA}HY7#(jC)aHZK>aa!=ae;dJt*g@a9?02^(SRYji8Tn7^>!h{2kuI_RWlHJ z{Qm9vf=fmfjcv^eS&SEfZ2Sr)@zze0S_jumMuf00y&c->E$$RLkRDTB6552Xk{=#= z{4&Y{_8Naz_{TY(mTY0>yu6QyQWfts9dXO6mgUA+NB7j_B<0DU)mqU7No{yY%@p~T zWt#&l)OMc!goI&J*^u5vo3T|GBrKUCHoiIzGXAV0&J~vrXg4*{Lm~kCKLF zv13DBmt+PMW=g{V=;2@TTIM$ymEWKHHD=D`_OQ9bENE4)G2iD8w~%h-0fh~>1%cu! zvaoF!^Raf$CSjEOV9r-?dVTv>4^PR>n2_9hnAewU>|;7@;omh@&vINN!r>w}h)>f!WXU69vAL|cJ< zQ_X?=C}k6!y)eb`rfUVIFm5<}@p3%S*$V0#pgX2^ZrA;cYu86~km>0CU9Z2+ zl0veJZx)m!O$~0O7PU-G=gGBJza2ucQoSQ4_x=e zvQg<-!;5Xqxtr5aklsikret_TVoon=QhIgAlD~!UX_%askbrgmChp#C(HEQDBj}y@?h2o1 zypdtCn|RJF(WlcFzFdy|iY<*BpO8{yszI-zIKSkN!^JB%wffHrjTkF(Sr%W8Kec-^ zxaVX0o9l>kW+jVoTg!^=*3pTt+V?zppgfkwf~~E`{29lk3SM`%-mNO_e3pdm)vigr z9gR3}KfH=&I;%7#tg`cwyQ;2eH~eh zzJ#-s&#{H7?sXZRzV9sriSXymA`jHVyH z$^{cI?$jVC@h=AzW<4anoKj72ipy)?ryuarKQ6E{zl1J2pH!~mpmKVKi_ZipMA^E3 z6og9~U*P-A4#4+$##T!?4e$F34@H^0hw}s6QqxeG;U4hau)=fRS}R`dX~$^E_BeH* za;qsz-aIm6I&kQ`XCgaNHCk!?qT%f6toresOeq~2(8wC$8@P-ND$}@5iX3QCHCj5H zH<^o5`}}*Ug-3*b>{`MV@68A*G)5!#V3Eh=fW}108JixJ^tU{9e6Z*;vAU15uXsL! zFHRC1Ug~<8RaGAA45^jn=dK)iVKfM+znFXTE_IxBgr~(y8CM>GMW|Zd`x|f>USxcknocVD%jxcgd zfG(#5gMRb7dA5SlYzlU^eg^YQDN=l$tH;@c#uBXxw87f`yatAlVISi$sxM%|#yAYlWF)RN literal 0 HcmV?d00001 diff --git a/resources/icons/ui/speech-bubbles-comment-option.png b/resources/icons/ui/speech-bubbles-comment-option.png new file mode 100644 index 0000000000000000000000000000000000000000..3ec0165dbf07076626228cb4ce49dfd3fb2d36d4 GIT binary patch literal 1122 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 OPmJj0k8kvdO#ch0yEAA2 literal 0 HcmV?d00001 diff --git a/resources/icons/ui/vertical-ellipsis.png b/resources/icons/ui/vertical-ellipsis.png new file mode 100644 index 0000000000000000000000000000000000000000..5ce9d78f26a1fc9d0cf445e10c2d458e45114f0f GIT binary patch literal 830 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}oCO|{#S9FB z8$g)xg!DSry^7odplSvNn+hu+GdHy)QK2F?C$HG5 z!d3~a!V1U+3F|8Y3;nDA{o-C@9zzrKDK}xwt{K19`Se z86_nJR{Hwo<>h+i#(Mch>H3D2mX`VkM*2oZx=P7{9O-#x!EwNQn0$BtHP@oQ?x4bNW&!@Rh#Lsf*ZtT*(bE2~*BGAXLn|h%K+BRkU|%r&NS;asJKc%*R$t zyf}U8)DO(lc`casAINQv`5{`voZ52h>WZrVkIu>G9`gNJZSD8Ze_5>6f$wQdcDvGl z9I0Xax8rx@@|?dsf7t$do~Z5#4CXn%;*I#X;;a;x?F;jNFKrd+ed@}qaeU39mpNd2 zWdq837a9H!WWJLHbWN`5q>pcI8(dj8S-0$M7JJ|((^zJp`CHfhGOTKB6#elIXr|~< ziK{O`Ydl+u;I)X whl@)#M=SmL_=G>>@3r6B$yY>NuigKw%(*N>UsB(GFDOxay85}Sb4q9e0OAfg{{R30 literal 0 HcmV?d00001 diff --git a/resources/icons/ui/vertical-ellipsis@2x.png b/resources/icons/ui/vertical-ellipsis@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9af0c042684fe607641381b415a36f11601a145e GIT binary patch literal 1041 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=jKx9jP7LeL$-D$|I14-?iy0UM zH-IqX3H7fbKm{q8ArU1JzCKpT`MG+DDfvmMdKI|^K-CNkHWgMtW^QUpqC!P(PF}H9 zg{=};g%ywu64qBz04piUwpEJo4N!2-FG^J~)icpEP_pAvP*AWbN=dT{a&d!d2l8x{ zGD=Dctn~HE%ggo3jrH=2()A53EiLs8jP#9+bb%^#i!1X=5-W7`ij^UTz|3(;Elw`V zEGWs$&r<-Io0ybeT4JlD1hNPYpzh2qfLoMT4D}VzfBF@P1^R}12KsQ5ff{XqezbBf zN=+=uFAB-e&#?nJEi*5>I61K(734r0eRLI2D{b^a?m%)E#M59=ps($?fI49zXvcNu z__ACE1|~O87srqa#<$VlUd)aHyKe3Lk zUuI%)uxI!wu6L1rD6{x^ha{YQeNmagW^CCDtEV&a|JceMVa;dx3Ls zmXyp=OP+7`4bQa|YR~)c^@}eu$z6WpAFGGxZTaQ=wZ6HBr~I8>)7CD2KyU-|h8z2P ze(quXe!%nsW8y*w4FRqerZBFaQqddEjOh;NLLbO}e%B!VKr-XrvlR~*U&KETKL67B z{<2=}{~SxYrXG9g&1by%9`_vO&wrcVfH`6f?|%keejrf1LCoQ?M|AVzSqwG#(`Nyl z*Idfp(YTkVg&{6%QJCCC#;sGn3tivEZ1PP?N@mf$YfLpAaQ}YDS%1aE!mC)~&HFlI zlO3DtEzTKUe_M9r3v-&MO4*@XaveG^1^AsC0~AC)EIkkr-Y9u0?)L1(dybxszI*B9 z`Y%?sFK%zy`Mk4UEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10f+@+{-GzZ+Rj;xUkjGiz z5m^kh={g8AI%&+V01C2~c>21sKV+5`ke3in`CG`qz_`NG#WBR=_}a+_y_f<;+Wr@> zJ>c5yzb=VpsA zRlQ!Rbn=J))Ax^?9V`PLG;7LabjwZPnYmW~_T$|jECZGXSWXC8dP4BXyd81YLJge^ zK8ziabq>i%^~ypH!i*xk1=%m!U;WP06`GKFI&(&x-(T^M`vb)um8$c5i7vG=^N3%y zQt8CW&s84+9nbWX z?E3H0mb>rK`H6kE<&SKa-FIN&`76p>Kd>Y?7v1=>H*Q&fTf_Cxt9^4luWuEzzVGc5 z!lSfSXzD`dx?@2(3?C-Nv573W-t*+Zq&Q{~hibQ?3NO|ZU78MIHtdnf?2-HvOxRTR z3a0hA&-hq$!I1R;_au{ESrbO4l)m?wr(NdVTiVE!;gDLA{CaoDvV-qU&t2|(quk!A zxg-9gP2NC6cwc)I$ztaD0e0svKR8sz{0 diff --git a/resources/icons/vertical-ellipsis.png b/resources/icons/vertical-ellipsis.png deleted file mode 100644 index 82714451d31182dff0ac1748d05c1a0ffb0d860f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 509 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE0wix1Z>k4UEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10f+@+{-GzZ+Rj;xUkjGiz z5m^kh={g8AI%&+V01C2~c>21sKV%i-H5HI%SYiqkI_>G=7~*mK?9`2%hYbW=>wBb} z)e5BVq%u2d^*@mHQ9EUnx7pEZv1klyjKm@nB?b58f0Q+z?A&;+oL^2D?NY%?PN}v7CMhd8i!PC{xWt~$(695Rp BspbFx diff --git a/resources/login.png b/resources/login.png new file mode 100644 index 0000000000000000000000000000000000000000..e65084ef91b9e8d4e51072ff352941fb72c189c1 GIT binary patch literal 5523 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 J1aAHE{{g+5m}~$5 literal 0 HcmV?d00001 diff --git a/resources/register.png b/resources/register.png new file mode 100644 index 0000000000000000000000000000000000000000..e65084ef91b9e8d4e51072ff352941fb72c189c1 GIT binary patch literal 5523 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 J1aAHE{{g+5m}~$5 literal 0 HcmV?d00001 diff --git a/resources/res.qrc b/resources/res.qrc index 13a7d309..59d6559d 100644 --- a/resources/res.qrc +++ b/resources/res.qrc @@ -1,33 +1,58 @@ - icons/left-chevron.png - icons/left-angle.png - icons/add-file.png - icons/send-button.png - icons/cog.png - icons/search.png - icons/plus-symbol.png - icons/clip-dark.png - icons/share-dark.png - icons/user-shape.png - icons/power-button-off.png - icons/smile.png - icons/error.png + icons/ui/cursor.png + icons/ui/cursor@2x.png + icons/ui/settings.png + icons/ui/settings@2x.png + icons/ui/smile.png + icons/ui/smile@2x.png + icons/ui/speech-bubbles-comment-option.png + icons/ui/speech-bubbles-comment-option@2x.png + icons/ui/vertical-ellipsis.png + icons/ui/vertical-ellipsis@2x.png + icons/ui/power-button-off.png + icons/ui/power-button-off@2x.png + icons/ui/plus-black-symbol.png + icons/ui/plus-black-symbol@2x.png + icons/ui/add-square-button.png + icons/ui/add-square-button@2x.png + icons/ui/cloud-storage-uploading-option.png + icons/ui/cloud-storage-uploading-option@2x.png + icons/ui/paper-clip-outline.png + icons/ui/paper-clip-outline@2x.png + icons/ui/angle-pointing-to-left.png + icons/ui/angle-pointing-to-left@2x.png icons/emoji-categories/people.png + icons/emoji-categories/people@2x.png icons/emoji-categories/nature.png + icons/emoji-categories/nature@2x.png icons/emoji-categories/foods.png + icons/emoji-categories/foods@2x.png icons/emoji-categories/activity.png + icons/emoji-categories/activity@2x.png icons/emoji-categories/travel.png + icons/emoji-categories/travel@2x.png icons/emoji-categories/objects.png + icons/emoji-categories/objects@2x.png icons/emoji-categories/symbols.png + icons/emoji-categories/symbols@2x.png icons/emoji-categories/flags.png - - icons/vertical-ellipsis.png + icons/emoji-categories/flags@2x.png nheko.png + + splash.png + splash@2x.png + + register.png + register@2x.png + + login.png + login@2x.png + nheko-512.png nheko-256.png nheko-128.png @@ -37,16 +62,10 @@ - fonts/OpenSans/OpenSans-Light.ttf - fonts/OpenSans/OpenSans-LightItalic.ttf fonts/OpenSans/OpenSans-Regular.ttf fonts/OpenSans/OpenSans-Italic.ttf fonts/OpenSans/OpenSans-Bold.ttf - fonts/OpenSans/OpenSans-BoldItalic.ttf fonts/OpenSans/OpenSans-Semibold.ttf - fonts/OpenSans/OpenSans-SemiboldItalic.ttf - fonts/OpenSans/OpenSans-ExtraBold.ttf - fonts/OpenSans/OpenSans-ExtraBoldItalic.ttf fonts/EmojiOne/emojione-android.ttf diff --git a/resources/splash.png b/resources/splash.png new file mode 100644 index 0000000000000000000000000000000000000000..4f89112fa44eb62df87da26329605b65b2f0db41 GIT binary patch literal 11395 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 J1aAHE{{g+5m}~$5 literal 0 HcmV?d00001 diff --git a/resources/splash@2x.png b/resources/splash@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..3c39b0be499118d6350c783bbbd428ab2899abd8 GIT binary patch literal 24776 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 client, QWidget *parent) sideBarMainLayout_->setSpacing(0); sideBarMainLayout_->setMargin(0); + sidebarActions_ = new SideBarActions(this); + sideBarLayout_->addLayout(sideBarTopLayout_); sideBarLayout_->addLayout(sideBarMainLayout_); + sideBarLayout_->addWidget(sidebarActions_); sideBarTopWidget_ = new QWidget(sideBar_); sideBarTopWidget_->setStyleSheet("background-color: #d6dde3; color: #ebebeb;"); diff --git a/src/Deserializable.cc b/src/Deserializable.cc index 9bef7d68..6033f898 100644 --- a/src/Deserializable.cc +++ b/src/Deserializable.cc @@ -23,8 +23,7 @@ DeserializationException::DeserializationException(const std::string &msg) : msg_(msg) -{ -} +{} const char * DeserializationException::what() const noexcept diff --git a/src/EmojiPanel.cc b/src/EmojiPanel.cc index 2730ddb5..16299ace 100644 --- a/src/EmojiPanel.cc +++ b/src/EmojiPanel.cc @@ -63,43 +63,53 @@ EmojiPanel::EmojiPanel(QWidget *parent) categoriesLayout->setSpacing(6); categoriesLayout->setMargin(5); + QIcon icon; + auto peopleCategory = new FlatButton(emojiCategories); - peopleCategory->setIcon(QIcon(":/icons/icons/emoji-categories/people.png")); + icon.addFile(":/icons/icons/emoji-categories/people.png"); + peopleCategory->setIcon(icon); peopleCategory->setIconSize(QSize(categoryIconSize_, categoryIconSize_)); peopleCategory->setForegroundColor("gray"); auto natureCategory_ = new FlatButton(emojiCategories); - natureCategory_->setIcon(QIcon(":/icons/icons/emoji-categories/nature.png")); + icon.addFile(":/icons/icons/emoji-categories/nature.png"); + natureCategory_->setIcon(icon); natureCategory_->setIconSize(QSize(categoryIconSize_, categoryIconSize_)); natureCategory_->setForegroundColor("gray"); auto foodCategory_ = new FlatButton(emojiCategories); - foodCategory_->setIcon(QIcon(":/icons/icons/emoji-categories/foods.png")); + icon.addFile(":/icons/icons/emoji-categories/foods.png"); + foodCategory_->setIcon(icon); foodCategory_->setIconSize(QSize(categoryIconSize_, categoryIconSize_)); foodCategory_->setForegroundColor("gray"); auto activityCategory = new FlatButton(emojiCategories); - activityCategory->setIcon(QIcon(":/icons/icons/emoji-categories/activity.png")); + icon.addFile(":/icons/icons/emoji-categories/activity.png"); + activityCategory->setIcon(icon); activityCategory->setIconSize(QSize(categoryIconSize_, categoryIconSize_)); activityCategory->setForegroundColor("gray"); auto travelCategory = new FlatButton(emojiCategories); - travelCategory->setIcon(QIcon(":/icons/icons/emoji-categories/travel.png")); + icon.addFile(":/icons/icons/emoji-categories/travel.png"); + travelCategory->setIcon(icon); travelCategory->setIconSize(QSize(categoryIconSize_, categoryIconSize_)); travelCategory->setForegroundColor("gray"); auto objectsCategory = new FlatButton(emojiCategories); - objectsCategory->setIcon(QIcon(":/icons/icons/emoji-categories/objects.png")); + icon.addFile(":/icons/icons/emoji-categories/objects.png"); + objectsCategory->setIcon(icon); objectsCategory->setIconSize(QSize(categoryIconSize_, categoryIconSize_)); objectsCategory->setForegroundColor("gray"); auto symbolsCategory = new FlatButton(emojiCategories); - symbolsCategory->setIcon(QIcon(":/icons/icons/emoji-categories/symbols.png")); + icon.addFile(":/icons/icons/emoji-categories/symbols.png"); + symbolsCategory->setIcon(icon); symbolsCategory->setIconSize(QSize(categoryIconSize_, categoryIconSize_)); symbolsCategory->setForegroundColor("gray"); auto flagsCategory = new FlatButton(emojiCategories); - flagsCategory->setIcon(QIcon(":/icons/icons/emoji-categories/flags.png")); + icon.addFile(":/icons/icons/emoji-categories/flags.png"); + flagsCategory->setIcon(icon); flagsCategory->setIconSize(QSize(categoryIconSize_, categoryIconSize_)); flagsCategory->setForegroundColor("gray"); diff --git a/src/EmojiPickButton.cc b/src/EmojiPickButton.cc index 4f7dd59e..06d97d4c 100644 --- a/src/EmojiPickButton.cc +++ b/src/EmojiPickButton.cc @@ -22,8 +22,7 @@ EmojiPickButton::EmojiPickButton(QWidget *parent) : FlatButton(parent) , panel_{ nullptr } -{ -} +{} void EmojiPickButton::enterEvent(QEvent *e) diff --git a/src/Login.cc b/src/Login.cc index d0dd1ea9..16c6f172 100644 --- a/src/Login.cc +++ b/src/Login.cc @@ -27,8 +27,7 @@ LoginRequest::LoginRequest() {} LoginRequest::LoginRequest(QString username, QString password) : user_(username) , password_(password) -{ -} +{} QByteArray LoginRequest::serialize() noexcept diff --git a/src/LoginPage.cc b/src/LoginPage.cc index fd7fede3..bbe2a134 100644 --- a/src/LoginPage.cc +++ b/src/LoginPage.cc @@ -42,16 +42,16 @@ LoginPage::LoginPage(QSharedPointer client, QWidget *parent) top_bar_layout_->addStretch(1); QIcon icon; - icon.addFile(":/icons/icons/left-angle.png", QSize(), QIcon::Normal, QIcon::Off); + icon.addFile(":/icons/icons/ui/angle-pointing-to-left.png"); back_button_->setIcon(icon); - back_button_->setIconSize(QSize(24, 24)); + back_button_->setIconSize(QSize(32, 32)); - QIcon advanced_settings_icon; - advanced_settings_icon.addFile(":/icons/icons/cog.png", QSize(), QIcon::Normal, QIcon::Off); + QIcon logo; + logo.addFile(":/logos/login.png"); logo_ = new QLabel(this); - logo_->setPixmap(QPixmap(":/logos/nheko-128.png")); + logo_->setPixmap(logo.pixmap(128)); logo_layout_ = new QHBoxLayout(); logo_layout_->setContentsMargins(0, 0, 0, 20); diff --git a/src/MainWindow.cc b/src/MainWindow.cc index 06f8245c..8cb2b562 100644 --- a/src/MainWindow.cc +++ b/src/MainWindow.cc @@ -176,8 +176,8 @@ MainWindow::showChatPage(QString userid, QString homeserver, QString token) new LoadingIndicator(this), [=](LoadingIndicator *indicator) { indicator->deleteLater(); }); spinner_->setColor("#acc7dc"); - spinner_->setFixedHeight(120); - spinner_->setFixedWidth(120); + spinner_->setFixedHeight(100); + spinner_->setFixedWidth(100); spinner_->start(); } diff --git a/src/QuickSwitcher.cc b/src/QuickSwitcher.cc index 29876f99..5b459eaf 100644 --- a/src/QuickSwitcher.cc +++ b/src/QuickSwitcher.cc @@ -24,8 +24,7 @@ RoomSearchInput::RoomSearchInput(QWidget *parent) : TextField(parent) -{ -} +{} bool RoomSearchInput::focusNextPrevChild(bool next) diff --git a/src/Register.cc b/src/Register.cc index 2e2c227c..db3ce4f9 100644 --- a/src/Register.cc +++ b/src/Register.cc @@ -25,8 +25,7 @@ RegisterRequest::RegisterRequest(const QString &username, const QString &password) : user_(username) , password_(password) -{ -} +{} QByteArray RegisterRequest::serialize() noexcept diff --git a/src/RegisterPage.cc b/src/RegisterPage.cc index e485de7d..bcd02be0 100644 --- a/src/RegisterPage.cc +++ b/src/RegisterPage.cc @@ -38,17 +38,20 @@ RegisterPage::RegisterPage(QSharedPointer client, QWidget *parent) back_button_->setMinimumSize(QSize(30, 30)); QIcon icon; - icon.addFile(":/icons/icons/left-angle.png", QSize(), QIcon::Normal, QIcon::Off); + icon.addFile(":/icons/icons/ui/angle-pointing-to-left.png"); back_button_->setIcon(icon); - back_button_->setIconSize(QSize(24, 24)); + back_button_->setIconSize(QSize(32, 32)); back_layout_->addWidget(back_button_, 0, Qt::AlignLeft | Qt::AlignVCenter); back_layout_->addStretch(1); + QIcon logo; + logo.addFile(":/logos/register.png"); + logo_ = new Avatar(this); - logo_->setImage(QImage(":/logos/nheko-128.png")); - logo_->setSize(80); + logo_->setIcon(logo); + logo_->setSize(128); logo_layout_ = new QHBoxLayout(); logo_layout_->setMargin(0); diff --git a/src/SideBarActions.cc b/src/SideBarActions.cc new file mode 100644 index 00000000..d4874c6a --- /dev/null +++ b/src/SideBarActions.cc @@ -0,0 +1,65 @@ +#include +#include + +#include "Config.h" +#include "Theme.h" +#include + +SideBarActions::SideBarActions(QWidget *parent) + : QWidget{ parent } +{ + setFixedHeight(conf::sidebarActions::height); + + QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + setSizePolicy(sizePolicy); + + layout_ = new QHBoxLayout(this); + layout_->setMargin(0); + + QIcon settingsIcon; + settingsIcon.addFile(":/icons/icons/ui/settings.png"); + + QIcon createRoomIcon; + createRoomIcon.addFile(":/icons/icons/ui/add-square-button.png"); + + QIcon joinRoomIcon; + joinRoomIcon.addFile(":/icons/icons/ui/speech-bubbles-comment-option.png"); + + settingsBtn_ = new FlatButton(this); + settingsBtn_->setIcon(settingsIcon); + settingsBtn_->setCornerRadius(conf::sidebarActions::iconSize / 2); + settingsBtn_->setIconSize( + QSize(conf::sidebarActions::iconSize, conf::sidebarActions::iconSize)); + + createRoomBtn_ = new FlatButton(this); + createRoomBtn_->setIcon(createRoomIcon); + createRoomBtn_->setCornerRadius(conf::sidebarActions::iconSize / 2); + createRoomBtn_->setIconSize( + QSize(conf::sidebarActions::iconSize, conf::sidebarActions::iconSize)); + + joinRoomBtn_ = new FlatButton(this); + joinRoomBtn_->setIcon(joinRoomIcon); + joinRoomBtn_->setCornerRadius(conf::sidebarActions::iconSize / 2); + joinRoomBtn_->setIconSize( + QSize(conf::sidebarActions::iconSize, conf::sidebarActions::iconSize)); + + layout_->addWidget(createRoomBtn_); + layout_->addWidget(joinRoomBtn_); + layout_->addWidget(settingsBtn_); +} + +SideBarActions::~SideBarActions() {} + +void +SideBarActions::resizeEvent(QResizeEvent *event) +{ + Q_UNUSED(event); + + if (width() <= ui::sidebar::SmallSize) { + joinRoomBtn_->hide(); + createRoomBtn_->hide(); + } else { + joinRoomBtn_->show(); + createRoomBtn_->show(); + } +} diff --git a/src/TextInputWidget.cc b/src/TextInputWidget.cc index b90a7caa..0d5e1102 100644 --- a/src/TextInputWidget.cc +++ b/src/TextInputWidget.cc @@ -45,7 +45,7 @@ TextInputWidget::TextInputWidget(QWidget *parent) { setFont(QFont("Emoji One")); - setFixedHeight(50); + setFixedHeight(conf::textInput::height); setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); setCursor(Qt::ArrowCursor); setStyleSheet("background-color: #fff;"); @@ -55,15 +55,13 @@ TextInputWidget::TextInputWidget(QWidget *parent) topLayout_->setContentsMargins(15, 0, 15, 5); QIcon send_file_icon; - send_file_icon.addFile(":/icons/icons/clip-dark.png", QSize(), QIcon::Normal, QIcon::Off); + send_file_icon.addFile(":/icons/icons/ui/paper-clip-outline.png"); sendFileBtn_ = new FlatButton(this); - sendFileBtn_->setForegroundColor(QColor("#acc7dc")); sendFileBtn_->setIcon(send_file_icon); sendFileBtn_->setIconSize(QSize(24, 24)); spinner_ = new LoadingIndicator(this); - spinner_->setColor("#acc7dc"); spinner_->setFixedHeight(32); spinner_->setFixedWidth(32); spinner_->hide(); @@ -79,19 +77,16 @@ TextInputWidget::TextInputWidget(QWidget *parent) input_->setStyleSheet("color: #333333; border: none; padding-top: 5px; margin: 0 5px"); sendMessageBtn_ = new FlatButton(this); - sendMessageBtn_->setForegroundColor(QColor("#acc7dc")); QIcon send_message_icon; - send_message_icon.addFile( - ":/icons/icons/share-dark.png", QSize(), QIcon::Normal, QIcon::Off); + send_message_icon.addFile(":/icons/icons/ui/cursor.png"); sendMessageBtn_->setIcon(send_message_icon); sendMessageBtn_->setIconSize(QSize(24, 24)); emojiBtn_ = new EmojiPickButton(this); - emojiBtn_->setForegroundColor(QColor("#acc7dc")); QIcon emoji_icon; - emoji_icon.addFile(":/icons/icons/smile.png", QSize(), QIcon::Normal, QIcon::Off); + emoji_icon.addFile(":/icons/icons/ui/smile.png"); emojiBtn_->setIcon(emoji_icon); emojiBtn_->setIconSize(QSize(24, 24)); diff --git a/src/TopRoomBar.cc b/src/TopRoomBar.cc index 3f93cad3..5a1f2d25 100644 --- a/src/TopRoomBar.cc +++ b/src/TopRoomBar.cc @@ -62,13 +62,11 @@ TopRoomBar::TopRoomBar(QWidget *parent) textLayout_->addWidget(topicLabel_); settingsBtn_ = new FlatButton(this); - settingsBtn_->setForegroundColor(QColor("#acc7dc")); settingsBtn_->setFixedSize(buttonSize_, buttonSize_); settingsBtn_->setCornerRadius(buttonSize_ / 2); QIcon settings_icon; - settings_icon.addFile( - ":/icons/icons/vertical-ellipsis.png", QSize(), QIcon::Normal, QIcon::Off); + settings_icon.addFile(":/icons/icons/ui/vertical-ellipsis.png"); settingsBtn_->setIcon(settings_icon); settingsBtn_->setIconSize(QSize(buttonSize_ / 2, buttonSize_ / 2)); diff --git a/src/UserInfoWidget.cc b/src/UserInfoWidget.cc index 09a75a3b..04cfec74 100644 --- a/src/UserInfoWidget.cc +++ b/src/UserInfoWidget.cc @@ -29,7 +29,7 @@ UserInfoWidget::UserInfoWidget(QWidget *parent) , user_id_("@user:homeserver.org") , logoutModal_{ nullptr } , logoutDialog_{ nullptr } - , logoutButtonSize_{ 32 } + , logoutButtonSize_{ 20 } { QSizePolicy sizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); setSizePolicy(sizePolicy); @@ -77,15 +77,13 @@ UserInfoWidget::UserInfoWidget(QWidget *parent) buttonLayout_->setMargin(0); logoutButton_ = new FlatButton(this); - logoutButton_->setForegroundColor(QColor("#555459")); - logoutButton_->setFixedSize(logoutButtonSize_, logoutButtonSize_); logoutButton_->setCornerRadius(logoutButtonSize_ / 2); QIcon icon; - icon.addFile(":/icons/icons/power-button-off.png", QSize(), QIcon::Normal, QIcon::Off); + icon.addFile(":/icons/icons/ui/power-button-off.png"); logoutButton_->setIcon(icon); - logoutButton_->setIconSize(QSize(logoutButtonSize_ / 2, logoutButtonSize_ / 2)); + logoutButton_->setIconSize(QSize(logoutButtonSize_, logoutButtonSize_)); buttonLayout_->addWidget(logoutButton_); diff --git a/src/WelcomePage.cc b/src/WelcomePage.cc index 5ea145f7..1fc0c19b 100644 --- a/src/WelcomePage.cc +++ b/src/WelcomePage.cc @@ -29,11 +29,14 @@ WelcomePage::WelcomePage(QWidget *parent) auto topLayout_ = new QVBoxLayout(this); topLayout_->setSpacing(20); - QFont headingFont("Open Sans", 23); - QFont subTitleFont("Open Sans", 22); + QFont headingFont("Open Sans", 22); + QFont subTitleFont("Open Sans", 21); + + QIcon icon; + icon.addFile(":/logos/splash.png"); auto logo_ = new QLabel(this); - logo_->setPixmap(QPixmap(":/logos/nheko-256.png")); + logo_->setPixmap(icon.pixmap(256)); logo_->setAlignment(Qt::AlignCenter); QString heading(tr("Welcome to nheko! The desktop client for the Matrix protocol.")); From 8299a74775bcdf917d58038fd35b2fdd208d4154 Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Thu, 19 Oct 2017 19:04:51 +0300 Subject: [PATCH 20/64] Elide room topic --- include/TopRoomBar.h | 13 +++++-------- src/TopRoomBar.cc | 16 +++++++++++++--- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/include/TopRoomBar.h b/include/TopRoomBar.h index 2c7af218..87037574 100644 --- a/include/TopRoomBar.h +++ b/include/TopRoomBar.h @@ -83,6 +83,9 @@ private: Avatar *avatar_; int buttonSize_; + + QString roomName_; + QString roomTopic_; }; inline void @@ -102,9 +105,7 @@ TopRoomBar::updateRoomAvatar(const QIcon &icon) inline void TopRoomBar::updateRoomName(const QString &name) { - QString elidedText = - QFontMetrics(nameLabel_->font()).elidedText(name, Qt::ElideRight, width() * 0.8); - nameLabel_->setText(elidedText); + roomName_ = name; update(); } @@ -112,10 +113,6 @@ inline void TopRoomBar::updateRoomTopic(QString topic) { topic.replace(URL_REGEX, URL_HTML); - - QString elidedText = - QFontMetrics(topicLabel_->font()).elidedText(topic, Qt::ElideRight, width() * 0.6); - - topicLabel_->setText(topic); + roomTopic_ = topic; update(); } diff --git a/src/TopRoomBar.cc b/src/TopRoomBar.cc index 5a1f2d25..8b2e338b 100644 --- a/src/TopRoomBar.cc +++ b/src/TopRoomBar.cc @@ -71,9 +71,8 @@ TopRoomBar::TopRoomBar(QWidget *parent) settingsBtn_->setIconSize(QSize(buttonSize_ / 2, buttonSize_ / 2)); topLayout_->addWidget(avatar_); - topLayout_->addLayout(textLayout_); - topLayout_->addStretch(1); - topLayout_->addWidget(settingsBtn_); + topLayout_->addLayout(textLayout_, 1); + topLayout_->addWidget(settingsBtn_, 0, Qt::AlignRight); menu_ = new Menu(this); @@ -149,6 +148,9 @@ TopRoomBar::reset() nameLabel_->setText(""); topicLabel_->setText(""); avatar_->setLetter(QChar('?')); + + roomName_.clear(); + roomTopic_.clear(); } void @@ -161,6 +163,14 @@ TopRoomBar::paintEvent(QPaintEvent *event) QPainter painter(this); style()->drawPrimitive(QStyle::PE_Widget, &option, &painter, this); + + QString elidedText = + QFontMetrics(nameLabel_->font()).elidedText(roomName_, Qt::ElideRight, width()); + nameLabel_->setText(elidedText); + + elidedText = + QFontMetrics(topicLabel_->font()).elidedText(roomTopic_, Qt::ElideRight, width()); + topicLabel_->setText(elidedText); } void From 3205e5fdd303ccfc7f5a4bd5d236d2fbb5add2f6 Mon Sep 17 00:00:00 2001 From: Rokas Kupstys Date: Fri, 20 Oct 2017 20:58:23 +0300 Subject: [PATCH 21/64] Make sidebar topic expand on click and fix html formatting of elided text. (#96) Fixes #95 --- CMakeLists.txt | 2 ++ include/TopRoomBar.h | 5 +++-- include/ui/Label.h | 26 ++++++++++++++++++++++++++ src/TopRoomBar.cc | 31 +++++++++++++++++++++++++++---- src/ui/Label.cc | 44 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 102 insertions(+), 6 deletions(-) create mode 100644 include/ui/Label.h create mode 100644 src/ui/Label.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 22673e66..b513e297 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -170,6 +170,7 @@ set(SRC_FILES src/ui/Badge.cc src/ui/LoadingIndicator.cc src/ui/FlatButton.cc + src/ui/Label.cc src/ui/OverlayModal.cc src/ui/ScrollBar.cc src/ui/SnackBar.cc @@ -250,6 +251,7 @@ qt5_wrap_cpp(MOC_HEADERS include/ui/Badge.h include/ui/LoadingIndicator.h include/ui/FlatButton.h + include/ui/Label.h include/ui/OverlayWidget.h include/ui/ScrollBar.h include/ui/SnackBar.h diff --git a/include/TopRoomBar.h b/include/TopRoomBar.h index 87037574..1b1d148c 100644 --- a/include/TopRoomBar.h +++ b/include/TopRoomBar.h @@ -29,6 +29,7 @@ #include "Avatar.h" #include "FlatButton.h" +#include "Label.h" #include "LeaveRoomDialog.h" #include "Menu.h" #include "OverlayModal.h" @@ -58,6 +59,7 @@ signals: protected: void paintEvent(QPaintEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; private slots: void closeLeaveRoomDialog(bool leaving); @@ -67,7 +69,7 @@ private: QVBoxLayout *textLayout_; QLabel *nameLabel_; - QLabel *topicLabel_; + Label *topicLabel_; QSharedPointer roomSettings_; @@ -112,7 +114,6 @@ TopRoomBar::updateRoomName(const QString &name) inline void TopRoomBar::updateRoomTopic(QString topic) { - topic.replace(URL_REGEX, URL_HTML); roomTopic_ = topic; update(); } diff --git a/include/ui/Label.h b/include/ui/Label.h new file mode 100644 index 00000000..66e98115 --- /dev/null +++ b/include/ui/Label.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +class Label : public QLabel +{ + Q_OBJECT + +public: + explicit Label(QWidget *parent = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags()); + explicit Label(const QString &text, + QWidget *parent = Q_NULLPTR, + Qt::WindowFlags f = Qt::WindowFlags()); + ~Label() override {} + +signals: + void clicked(QMouseEvent *e); + void pressed(QMouseEvent *e); + void released(QMouseEvent *e); + +protected: + void mousePressEvent(QMouseEvent *e) override; + void mouseReleaseEvent(QMouseEvent *e) override; + + QPoint pressPosition_; +}; diff --git a/src/TopRoomBar.cc b/src/TopRoomBar.cc index 8b2e338b..4d1f4195 100644 --- a/src/TopRoomBar.cc +++ b/src/TopRoomBar.cc @@ -52,11 +52,15 @@ TopRoomBar::TopRoomBar(QWidget *parent) QFont descriptionFont("Open Sans"); descriptionFont.setPixelSize(conf::topRoomBar::fonts::roomDescription); - topicLabel_ = new QLabel(this); + topicLabel_ = new Label(this); topicLabel_->setFont(descriptionFont); topicLabel_->setTextFormat(Qt::RichText); topicLabel_->setTextInteractionFlags(Qt::TextBrowserInteraction); topicLabel_->setOpenExternalLinks(true); + connect(topicLabel_, &Label::clicked, [=](QMouseEvent *e) { + if (e->button() == Qt::LeftButton && !topicLabel_->hasSelectedText()) + topicLabel_->setWordWrap(!topicLabel_->wordWrap()); + }); textLayout_->addWidget(nameLabel_); textLayout_->addWidget(topicLabel_); @@ -164,15 +168,34 @@ TopRoomBar::paintEvent(QPaintEvent *event) QPainter painter(this); style()->drawPrimitive(QStyle::PE_Widget, &option, &painter, this); + // Number of pixels that we can move sidebar splitter per frame. If label contains text + // which fills entire it's width then label starts blocking it's layout from shrinking. + // Making label little bit shorter leaves some space for it to shrink. + const auto perFrameResize = 20; + QString elidedText = - QFontMetrics(nameLabel_->font()).elidedText(roomName_, Qt::ElideRight, width()); + QFontMetrics(nameLabel_->font()) + .elidedText(roomName_, Qt::ElideRight, nameLabel_->width() - perFrameResize); nameLabel_->setText(elidedText); - elidedText = - QFontMetrics(topicLabel_->font()).elidedText(roomTopic_, Qt::ElideRight, width()); + if (topicLabel_->wordWrap()) + elidedText = roomTopic_; + else + elidedText = + QFontMetrics(topicLabel_->font()) + .elidedText(roomTopic_, Qt::ElideRight, topicLabel_->width() - perFrameResize); + elidedText.replace(URL_REGEX, URL_HTML); topicLabel_->setText(elidedText); } +void +TopRoomBar::mousePressEvent(QMouseEvent *event) +{ + if (childAt(event->pos()) == topicLabel_) { + event->accept(); + } +} + void TopRoomBar::setRoomSettings(QSharedPointer settings) { diff --git a/src/ui/Label.cc b/src/ui/Label.cc new file mode 100644 index 00000000..8bd8c54e --- /dev/null +++ b/src/ui/Label.cc @@ -0,0 +1,44 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "Label.h" +#include + +Label::Label(QWidget *parent, Qt::WindowFlags f) + : QLabel(parent, f) +{} + +Label::Label(const QString &text, QWidget *parent, Qt::WindowFlags f) + : QLabel(text, parent, f) +{} + +void +Label::mousePressEvent(QMouseEvent *e) +{ + pressPosition_ = e->pos(); + emit pressed(e); + QLabel::mousePressEvent(e); +} + +void +Label::mouseReleaseEvent(QMouseEvent *e) +{ + emit released(e); + if (pressPosition_ == e->pos()) + emit clicked(e); + QLabel::mouseReleaseEvent(e); +} From 9b60fdd620d5091377cca2163b97f856aedd7dca Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Fri, 20 Oct 2017 21:21:04 +0300 Subject: [PATCH 22/64] Remove sync timer --- include/ChatPage.h | 3 --- src/ChatPage.cc | 25 +++++-------------------- 2 files changed, 5 insertions(+), 23 deletions(-) diff --git a/include/ChatPage.h b/include/ChatPage.h index be8fc12c..e8a40ade 100644 --- a/include/ChatPage.h +++ b/include/ChatPage.h @@ -37,7 +37,6 @@ constexpr int CONSENSUS_TIMEOUT = 1000; constexpr int SHOW_CONTENT_TIMEOUT = 3000; -constexpr int SYNC_INTERVAL = 2000; class ChatPage : public QWidget { @@ -66,7 +65,6 @@ private slots: void syncCompleted(const SyncResponse &response); void syncFailed(const QString &msg); void changeTopRoomInfo(const QString &room_id); - void startSync(); void logout(); void addRoom(const QString &room_id); void removeRoom(const QString &room_id); @@ -106,7 +104,6 @@ private: // Safety net if consensus is not possible or too slow. QTimer *showContentTimer_; QTimer *consensusTimer_; - QTimer *syncTimer_; QString current_room_; QMap room_avatars_; diff --git a/src/ChatPage.cc b/src/ChatPage.cc index 24844183..514494ea 100644 --- a/src/ChatPage.cc +++ b/src/ChatPage.cc @@ -111,10 +111,6 @@ ChatPage::ChatPage(QSharedPointer client, QWidget *parent) user_info_widget_ = new UserInfoWidget(sideBarTopWidget_); sideBarTopWidgetLayout_->addWidget(user_info_widget_); - syncTimer_ = new QTimer(this); - syncTimer_->setSingleShot(true); - connect(syncTimer_, SIGNAL(timeout()), this, SLOT(startSync())); - connect(user_info_widget_, SIGNAL(logout()), client_.data(), SLOT(logout())); connect(client_.data(), SIGNAL(loggedOut()), this, SLOT(logout())); @@ -243,8 +239,6 @@ ChatPage::ChatPage(QSharedPointer client, QWidget *parent) void ChatPage::logout() { - syncTimer_->stop(); - // Delete all config parameters. QSettings settings; settings.beginGroup("auth"); @@ -302,12 +296,6 @@ ChatPage::bootstrap(QString userid, QString homeserver, QString token) client_->initialSync(); } -void -ChatPage::startSync() -{ - client_->sync(); -} - void ChatPage::setOwnAvatar(const QPixmap &img) { @@ -322,7 +310,7 @@ ChatPage::syncFailed(const QString &msg) return; qWarning() << "Sync error:" << msg; - syncTimer_->start(SYNC_INTERVAL); + client_->sync(); } // TODO: Should be moved in another class that manages this global list. @@ -419,7 +407,7 @@ ChatPage::syncCompleted(const SyncResponse &response) room_list_->sync(state_manager_); view_manager_->sync(response.rooms()); - syncTimer_->start(SYNC_INTERVAL); + client_->sync(); } void @@ -472,7 +460,7 @@ ChatPage::initialSyncCompleted(const SyncResponse &response) // Initialize room list. room_list_->setInitialRooms(settingsManager_, state_manager_); - syncTimer_->start(SYNC_INTERVAL); + client_->sync(); emit contentLoaded(); } @@ -585,7 +573,7 @@ ChatPage::loadStateFromCache() showContentTimer_->start(SHOW_CONTENT_TIMEOUT); // Start receiving events. - syncTimer_->start(SYNC_INTERVAL); + client_->sync(); } void @@ -682,7 +670,4 @@ ChatPage::updateTypingUsers(const QString &roomid, const QList &user_id typingUsers_.insert(roomid, users); } -ChatPage::~ChatPage() -{ - syncTimer_->stop(); -} +ChatPage::~ChatPage() {} From 8a9a513ecd352768a7c14d30252394c6973a352e Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Fri, 20 Oct 2017 21:39:05 +0300 Subject: [PATCH 23/64] Move ctrl-k callback to the MainWindow --- include/ChatPage.h | 5 +---- include/MainWindow.h | 1 + src/ChatPage.cc | 9 --------- src/MainWindow.cc | 9 +++++++++ 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/include/ChatPage.h b/include/ChatPage.h index e8a40ade..4c8ed05b 100644 --- a/include/ChatPage.h +++ b/include/ChatPage.h @@ -48,6 +48,7 @@ public: // Initialize all the components of the UI. void bootstrap(QString userid, QString homeserver, QString token); + void showQuickSwitcher(); signals: void contentLoaded(); @@ -69,14 +70,10 @@ private slots: void addRoom(const QString &room_id); void removeRoom(const QString &room_id); -protected: - void keyPressEvent(QKeyEvent *event) override; - private: void updateTypingUsers(const QString &roomid, const QList &user_ids); void updateDisplayNames(const RoomState &state); void loadStateFromCache(); - void showQuickSwitcher(); QHBoxLayout *topLayout_; Splitter *splitter; diff --git a/include/MainWindow.h b/include/MainWindow.h index 0c2316a3..95935c46 100644 --- a/include/MainWindow.h +++ b/include/MainWindow.h @@ -43,6 +43,7 @@ public: protected: void closeEvent(QCloseEvent *event); + void keyPressEvent(QKeyEvent *event); private slots: // Handle interaction with the tray icon. diff --git a/src/ChatPage.cc b/src/ChatPage.cc index 514494ea..d8280a4a 100644 --- a/src/ChatPage.cc +++ b/src/ChatPage.cc @@ -576,15 +576,6 @@ ChatPage::loadStateFromCache() client_->sync(); } -void -ChatPage::keyPressEvent(QKeyEvent *event) -{ - if (event->key() == Qt::Key_K) { - if (event->modifiers() == Qt::ControlModifier) - showQuickSwitcher(); - } -} - void ChatPage::showQuickSwitcher() { diff --git a/src/MainWindow.cc b/src/MainWindow.cc index 8cb2b562..f6276967 100644 --- a/src/MainWindow.cc +++ b/src/MainWindow.cc @@ -100,6 +100,15 @@ MainWindow::MainWindow(QWidget *parent) } } +void +MainWindow::keyPressEvent(QKeyEvent *e) +{ + if ((e->key() == Qt::Key_K) && (e->modifiers().testFlag(Qt::ControlModifier))) + chat_page_->showQuickSwitcher(); + else + QMainWindow::keyPressEvent(e); +} + void MainWindow::restoreWindowSize() { From 13e526c27d91c5cfa61ef709b7d1b0e709ba59fb Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Fri, 20 Oct 2017 22:32:48 +0300 Subject: [PATCH 24/64] Retry initial sync (#19) --- include/ChatPage.h | 7 +++++ include/LoginPage.h | 7 ++--- include/MatrixClient.h | 1 + src/ChatPage.cc | 58 +++++++++++++++++++++++++++++++----------- src/MainWindow.cc | 4 +++ src/MatrixClient.cc | 2 +- 6 files changed, 60 insertions(+), 19 deletions(-) diff --git a/include/ChatPage.h b/include/ChatPage.h index 4c8ed05b..bac83ece 100644 --- a/include/ChatPage.h +++ b/include/ChatPage.h @@ -56,6 +56,7 @@ signals: void changeWindowTitle(const QString &msg); void unreadMessages(int count); void showNotification(const QString &msg); + void showLoginPage(const QString &msg); private slots: void showUnreadMessageNotification(int count); @@ -74,6 +75,8 @@ private: void updateTypingUsers(const QString &roomid, const QList &user_ids); void updateDisplayNames(const RoomState &state); void loadStateFromCache(); + void deleteConfigs(); + void resetUI(); QHBoxLayout *topLayout_; Splitter *splitter; @@ -121,4 +124,8 @@ private: // LMDB wrapper. QSharedPointer cache_; + + // If the number of failures exceeds a certain threshold we + // return to the login page. + int initialSyncFailures = 0; }; diff --git a/include/LoginPage.h b/include/LoginPage.h index 5caa3f1e..88cffaf3 100644 --- a/include/LoginPage.h +++ b/include/LoginPage.h @@ -43,6 +43,10 @@ public: signals: void backButtonClicked(); +public slots: + // Displays errors produced during the login. + void loginError(QString error_message); + private slots: // Callback for the back button. void onBackButtonClicked(); @@ -56,9 +60,6 @@ private slots: // Callback for probing the manually entered server void onServerAddressEntered(); - // Displays errors produced during the login. - void loginError(QString error_message); - // Callback for errors produced during server probing void versionError(QString error_message); diff --git a/include/MatrixClient.h b/include/MatrixClient.h index 01e2c319..0185bf64 100644 --- a/include/MatrixClient.h +++ b/include/MatrixClient.h @@ -91,6 +91,7 @@ signals: // Returned profile data for the user's account. void getOwnProfileResponse(const QUrl &avatar_url, const QString &display_name); void initialSyncCompleted(const SyncResponse &response); + void initialSyncFailed(const QString &msg); void syncCompleted(const SyncResponse &response); void syncFailed(const QString &msg); void joinFailed(const QString &msg); diff --git a/src/ChatPage.cc b/src/ChatPage.cc index d8280a4a..cf78d8a1 100644 --- a/src/ChatPage.cc +++ b/src/ChatPage.cc @@ -29,6 +29,8 @@ #include "StateEvent.h" +constexpr int MAX_INITIAL_SYNC_FAILURES = 5; + namespace events = matrix::events; ChatPage::ChatPage(QSharedPointer client, QWidget *parent) @@ -192,6 +194,24 @@ ChatPage::ChatPage(QSharedPointer client, QWidget *parent) SIGNAL(initialSyncCompleted(const SyncResponse &)), this, SLOT(initialSyncCompleted(const SyncResponse &))); + connect(client_.data(), &MatrixClient::initialSyncFailed, this, [=](const QString &msg) { + initialSyncFailures += 1; + + if (initialSyncFailures >= MAX_INITIAL_SYNC_FAILURES) { + initialSyncFailures = 0; + + deleteConfigs(); + + emit showLoginPage(msg); + emit contentLoaded(); + return; + } + + qWarning() << msg; + qWarning() << "Retrying initial sync"; + + client_->initialSync(); + }); connect(client_.data(), SIGNAL(syncCompleted(const SyncResponse &)), this, @@ -239,7 +259,29 @@ ChatPage::ChatPage(QSharedPointer client, QWidget *parent) void ChatPage::logout() { - // Delete all config parameters. + deleteConfigs(); + + resetUI(); + + emit close(); +} + +void +ChatPage::resetUI() +{ + room_avatars_.clear(); + room_list_->clear(); + settingsManager_.clear(); + state_manager_.clear(); + top_bar_->reset(); + user_info_widget_->reset(); + view_manager_->clearAll(); + AvatarProvider::clear(); +} + +void +ChatPage::deleteConfigs() +{ QSettings settings; settings.beginGroup("auth"); settings.remove(""); @@ -253,21 +295,7 @@ ChatPage::logout() cache_->deleteData(); - // Clear the environment. - room_list_->clear(); - view_manager_->clearAll(); - - top_bar_->reset(); - user_info_widget_->reset(); client_->reset(); - - state_manager_.clear(); - settingsManager_.clear(); - room_avatars_.clear(); - - AvatarProvider::clear(); - - emit close(); } void diff --git a/src/MainWindow.cc b/src/MainWindow.cc index f6276967..b6033eaf 100644 --- a/src/MainWindow.cc +++ b/src/MainWindow.cc @@ -73,6 +73,10 @@ MainWindow::MainWindow(QWidget *parent) connect( chat_page_, SIGNAL(changeWindowTitle(QString)), this, SLOT(setWindowTitle(QString))); connect(chat_page_, SIGNAL(unreadMessages(int)), trayIcon_, SLOT(setUnreadCount(int))); + connect(chat_page_, &ChatPage::showLoginPage, this, [=](const QString &msg) { + login_page_->loginError(msg); + showLoginPage(); + }); connect(trayIcon_, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), diff --git a/src/MatrixClient.cc b/src/MatrixClient.cc index e9e47fcb..4ececd01 100644 --- a/src/MatrixClient.cc +++ b/src/MatrixClient.cc @@ -221,7 +221,7 @@ MatrixClient::onInitialSyncResponse(QNetworkReply *reply) int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); if (status == 0 || status >= 400) { - qWarning() << reply->errorString(); + emit initialSyncFailed(reply->errorString()); return; } From a3fd83192d4360ec2357944c263f4dfc13081fa9 Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Sat, 21 Oct 2017 12:22:12 +0300 Subject: [PATCH 25/64] Update snap --- README.md | 1 + snap/snapcraft.yaml | 12 ++++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 26356ef6..2d81d9df 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ nheko [![Chat on Matrix](https://img.shields.io/badge/chat-on%20matrix-blue.svg)](https://matrix.to/#/#nheko:matrix.org) [![License: GPL v3](https://img.shields.io/badge/license-GPL%20v3-red.svg)](https://www.gnu.org/licenses/gpl-3.0) [![AUR: nheko-git](https://img.shields.io/badge/AUR-nheko--git-blue.svg)](https://aur.archlinux.org/packages/nheko-git) +[![Snap Status](https://build.snapcraft.io/badge/mujx/nheko.svg)](https://build.snapcraft.io/user/mujx/nheko) The motivation behind the project is to provide a native desktop app for [Matrix] that feels more like a mainstream chat app ([Riot], Telegram etc) and less like an IRC client. diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index a25f1013..b50f7e5b 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -1,5 +1,5 @@ name: nheko -version: master +version: 0.1.0 summary: Desktop client for the Matrix protocol description: | The motivation behind the project is to provide a native desktop app for @@ -18,6 +18,14 @@ parts: nheko: source: . plugin: cmake - build-packages: [gcc] + configflags: + - -DCMAKE_BUILD_TYPE=RelWithDebInfo + - -DBUILD_TESTS=OFF + build-packages: + - gcc + - g++ + - liblmdb-dev + - liblmdb0 + - qt5tools5-dev-tools artifacts: [nheko] after: [desktop-qt5] From 168342e4a5d31304a7af7a85f6bc86e7ea186bb4 Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Sat, 21 Oct 2017 12:30:26 +0300 Subject: [PATCH 26/64] Use correct package for qt tools --- snap/snapcraft.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index b50f7e5b..8dbd3d52 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -26,6 +26,6 @@ parts: - g++ - liblmdb-dev - liblmdb0 - - qt5tools5-dev-tools + - qttools5-dev-tools artifacts: [nheko] after: [desktop-qt5] From 622772a44b025cb111085c3cd2f8b0d5ee8a2bc2 Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Sat, 21 Oct 2017 12:59:38 +0300 Subject: [PATCH 27/64] Remove snap Incompatible Qt version 5.5.1 [ci skip] --- README.md | 1 - snap/snapcraft.yaml | 31 ------------------------------- 2 files changed, 32 deletions(-) delete mode 100644 snap/snapcraft.yaml diff --git a/README.md b/README.md index 2d81d9df..26356ef6 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,6 @@ nheko [![Chat on Matrix](https://img.shields.io/badge/chat-on%20matrix-blue.svg)](https://matrix.to/#/#nheko:matrix.org) [![License: GPL v3](https://img.shields.io/badge/license-GPL%20v3-red.svg)](https://www.gnu.org/licenses/gpl-3.0) [![AUR: nheko-git](https://img.shields.io/badge/AUR-nheko--git-blue.svg)](https://aur.archlinux.org/packages/nheko-git) -[![Snap Status](https://build.snapcraft.io/badge/mujx/nheko.svg)](https://build.snapcraft.io/user/mujx/nheko) The motivation behind the project is to provide a native desktop app for [Matrix] that feels more like a mainstream chat app ([Riot], Telegram etc) and less like an IRC client. diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml deleted file mode 100644 index 8dbd3d52..00000000 --- a/snap/snapcraft.yaml +++ /dev/null @@ -1,31 +0,0 @@ -name: nheko -version: 0.1.0 -summary: Desktop client for the Matrix protocol -description: | - The motivation behind the project is to provide a native desktop app for - Matrix that feels more like a mainstream chat app (Riot, Telegram etc) and - less like an IRC client. - -grade: devel # must be 'stable' to release into candidate/stable channels -confinement: strict - -apps: - nheko: - command: desktop-launch $SNAP/nheko - plugs: [network, network-bind, x11, unity7, home] - -parts: - nheko: - source: . - plugin: cmake - configflags: - - -DCMAKE_BUILD_TYPE=RelWithDebInfo - - -DBUILD_TESTS=OFF - build-packages: - - gcc - - g++ - - liblmdb-dev - - liblmdb0 - - qttools5-dev-tools - artifacts: [nheko] - after: [desktop-qt5] From 47d1546adfee1ee1c6b44906b7f3a80aecd6f354 Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Sat, 21 Oct 2017 16:46:11 +0300 Subject: [PATCH 28/64] Clean unread count when the user logs out fixes #60 --- src/ChatPage.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ChatPage.cc b/src/ChatPage.cc index cf78d8a1..6d8c7cea 100644 --- a/src/ChatPage.cc +++ b/src/ChatPage.cc @@ -277,6 +277,8 @@ ChatPage::resetUI() user_info_widget_->reset(); view_manager_->clearAll(); AvatarProvider::clear(); + + showUnreadMessageNotification(0); } void From 3cae6c39831fe6b1e9661fb2af7034105a5f289a Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Sat, 21 Oct 2017 18:53:15 +0300 Subject: [PATCH 29/64] Remove ui flickering when adding new timeline events --- Makefile | 2 +- include/TimelineView.h | 2 +- src/ChatPage.cc | 5 +++++ src/TimelineView.cc | 20 +++++++------------- 4 files changed, 14 insertions(+), 15 deletions(-) diff --git a/Makefile b/Makefile index ba116307..b4f3f470 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,7 @@ debug: @cmake --build build release-debug: - @cmake -DBUILD_TESTS=OFF -H. -Bbuild -DCMAKE_BUILD_TYPE=RelWithDebInfo + @cmake -DBUILD_TESTS=OFF -H. -GNinja -Bbuild -DCMAKE_BUILD_TYPE=RelWithDebInfo @cmake --build build test: diff --git a/include/TimelineView.h b/include/TimelineView.h index 6ca91211..f25bc570 100644 --- a/include/TimelineView.h +++ b/include/TimelineView.h @@ -145,7 +145,7 @@ private: bool isTimelineFinished = false; bool isInitialSync = true; - const int SCROLL_BAR_GAP = 400; + const int SCROLL_BAR_GAP = 200; QTimer *paginationTimer_; diff --git a/src/ChatPage.cc b/src/ChatPage.cc index 6d8c7cea..5df8dec2 100644 --- a/src/ChatPage.cc +++ b/src/ChatPage.cc @@ -15,6 +15,7 @@ * along with this program. If not, see . */ +#include #include #include @@ -413,6 +414,8 @@ ChatPage::syncCompleted(const SyncResponse &response) if (it.key() == current_room_) changeTopRoomInfo(it.key()); + + QApplication::processEvents(); } auto leave = response.rooms().leave(); @@ -472,6 +475,8 @@ ChatPage::initialSyncCompleted(const SyncResponse &response) if (!url.toString().isEmpty()) AvatarProvider::setAvatarUrl(uid, url); } + + QApplication::processEvents(); } try { diff --git a/src/TimelineView.cc b/src/TimelineView.cc index 615127a4..5463064f 100644 --- a/src/TimelineView.cc +++ b/src/TimelineView.cc @@ -15,6 +15,7 @@ * along with this program. If not, see . */ +#include #include #include #include @@ -96,9 +97,6 @@ TimelineView::sliderRangeChanged(int min, int max) newPosition = max; scroll_area_->verticalScrollBar()->setValue(newPosition); - - scroll_widget_->adjustSize(); - scroll_widget_->update(); } void @@ -174,9 +172,6 @@ TimelineView::addBackwardsEvents(const QString &room_id, const RoomMessages &msg isTimelineFinished = false; QList items; - scroll_widget_->adjustSize(); - scroll_widget_->update(); - // Parse in reverse order to determine where we should not show sender's // name. auto ii = msgs.chunk().size(); @@ -199,6 +194,8 @@ TimelineView::addBackwardsEvents(const QString &room_id, const RoomMessages &msg for (const auto &item : items) addTimelineItem(item, TimelineDirection::Top); + QApplication::processEvents(); + prev_batch_token_ = msgs.end(); isPaginationInProgress_ = false; @@ -352,6 +349,8 @@ TimelineView::addEvents(const Timeline &timeline) } } + QApplication::processEvents(); + if (isInitialSync) { prev_batch_token_ = timeline.previousBatch(); isInitialSync = false; @@ -468,9 +467,6 @@ TimelineView::addTimelineItem(TimelineItem *item, TimelineDirection direction) scroll_layout_->addWidget(item); else scroll_layout_->insertWidget(1, item); - - scroll_widget_->adjustSize(); - scroll_widget_->update(); } void @@ -494,8 +490,7 @@ TimelineView::addUserMessage(matrix::events::MessageEventType ty, const QString TimelineItem *view_item = new TimelineItem(ty, user_id, body, with_sender, scroll_widget_); scroll_layout_->addWidget(view_item); - scroll_widget_->adjustSize(); - scroll_widget_->update(); + QApplication::processEvents(); lastSender_ = user_id; @@ -515,8 +510,7 @@ TimelineView::addUserMessage(const QString &url, const QString &filename, int tx TimelineItem *view_item = new TimelineItem(image, user_id, with_sender, scroll_widget_); scroll_layout_->addWidget(view_item); - scroll_widget_->adjustSize(); - scroll_widget_->update(); + QApplication::processEvents(); lastSender_ = user_id; From 160fe1d668d9c1fa8f089cec34df29c2ba31a56f Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Sat, 21 Oct 2017 21:17:01 +0300 Subject: [PATCH 30/64] Remove cache updates from the main thread --- CMakeLists.txt | 5 +++-- src/Cache.cc | 17 ++++++++++++----- src/ChatPage.cc | 25 +++++++++---------------- src/MatrixClient.cc | 6 +++--- 4 files changed, 27 insertions(+), 26 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b513e297..6f8c167c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,6 +41,7 @@ endif() find_package(Qt5Widgets REQUIRED) find_package(Qt5Network REQUIRED) find_package(Qt5LinguistTools REQUIRED) +find_package(Qt5Concurrent REQUIRED) if (APPLE) find_package(Qt5MacExtras REQUIRED) @@ -318,9 +319,9 @@ if (BUILD_TESTS) endif() if(APPVEYOR_BUILD) - set (NHEKO_LIBS matrix_events Qt5::Widgets Qt5::Network lmdb) + set (NHEKO_LIBS matrix_events Qt5::Widgets Qt5::Network Qt5::Concurrent lmdb) else() - set (NHEKO_LIBS matrix_events Qt5::Widgets Qt5::Network ${LMDB_LIBRARY}) + set (NHEKO_LIBS matrix_events Qt5::Widgets Qt5::Network Qt5::Concurrent ${LMDB_LIBRARY}) endif() set (NHEKO_DEPS ${OS_BUNDLE} ${SRC_FILES} ${UI_HEADERS} ${MOC_HEADERS} ${QRC} ${LANG_QRC} ${QM_SRC}) diff --git a/src/Cache.cc b/src/Cache.cc index 3f7b141b..010b4aa9 100644 --- a/src/Cache.cc +++ b/src/Cache.cc @@ -102,14 +102,21 @@ Cache::setState(const QString &nextBatchToken, const QMap &s if (!isMounted_) return; - auto txn = lmdb::txn::begin(env_); + try { + auto txn = lmdb::txn::begin(env_); - setNextBatchToken(txn, nextBatchToken); + setNextBatchToken(txn, nextBatchToken); - for (auto it = states.constBegin(); it != states.constEnd(); it++) - insertRoomState(txn, it.key(), it.value()); + for (auto it = states.constBegin(); it != states.constEnd(); it++) + insertRoomState(txn, it.key(), it.value()); - txn.commit(); + txn.commit(); + } catch (const lmdb::error &e) { + qCritical() << "The cache couldn't be updated: " << e.what(); + + unmount(); + deleteData(); + } } void diff --git a/src/ChatPage.cc b/src/ChatPage.cc index 5df8dec2..65fef9de 100644 --- a/src/ChatPage.cc +++ b/src/ChatPage.cc @@ -18,6 +18,7 @@ #include #include #include +#include #include "AvatarProvider.h" #include "ChatPage.h" @@ -196,6 +197,11 @@ ChatPage::ChatPage(QSharedPointer client, QWidget *parent) this, SLOT(initialSyncCompleted(const SyncResponse &))); connect(client_.data(), &MatrixClient::initialSyncFailed, this, [=](const QString &msg) { + if (client_->getHomeServer().isEmpty()) { + deleteConfigs(); + return; + } + initialSyncFailures += 1; if (initialSyncFailures >= MAX_INITIAL_SYNC_FAILURES) { @@ -426,14 +432,7 @@ ChatPage::syncCompleted(const SyncResponse &response) } } - try { - cache_->setState(response.nextBatch(), state_manager_); - } catch (const lmdb::error &e) { - qCritical() << "The cache couldn't be updated: " << e.what(); - // TODO: Notify the user. - cache_->unmount(); - cache_->deleteData(); - } + QtConcurrent::run(cache_.data(), &Cache::setState, response.nextBatch(), state_manager_); client_->setNextBatchToken(response.nextBatch()); @@ -479,16 +478,10 @@ ChatPage::initialSyncCompleted(const SyncResponse &response) QApplication::processEvents(); } - try { - cache_->setState(response.nextBatch(), state_manager_); - } catch (const lmdb::error &e) { - qCritical() << "The cache couldn't be initialized: " << e.what(); - cache_->unmount(); - cache_->deleteData(); - } - client_->setNextBatchToken(response.nextBatch()); + QtConcurrent::run(cache_.data(), &Cache::setState, response.nextBatch(), state_manager_); + // Populate timelines with messages. view_manager_->initialize(response.rooms()); diff --git a/src/MatrixClient.cc b/src/MatrixClient.cc index 4ececd01..f0b3bd26 100644 --- a/src/MatrixClient.cc +++ b/src/MatrixClient.cc @@ -56,9 +56,9 @@ MatrixClient::MatrixClient(QString server, QObject *parent) void MatrixClient::reset() noexcept { - next_batch_ = ""; - server_ = ""; - token_ = ""; + next_batch_.clear(); + server_.clear(); + token_.clear(); txn_id_ = 0; } From c0e55378c31321e2ab7a117cefb3bd63a609e474 Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Sun, 22 Oct 2017 19:03:55 +0300 Subject: [PATCH 31/64] Remove extra inline keywords --- .clang-format | 2 +- include/Cache.h | 19 +-- include/EmojiCategory.h | 11 +- include/Login.h | 40 +---- include/MatrixClient.h | 51 +----- include/Profile.h | 16 +- include/Register.h | 40 +---- include/RoomInfoListItem.h | 44 +---- include/RoomMessages.h | 24 +-- include/RoomState.h | 24 +-- include/Sync.h | 160 +++--------------- include/TextInputWidget.h | 8 +- include/TimelineItem.h | 8 +- include/TimelineView.h | 16 +- include/TopRoomBar.h | 36 +--- include/events/AliasesEventContent.h | 7 +- include/events/AvatarEventContent.h | 7 +- include/events/CanonicalAliasEventContent.h | 7 +- include/events/CreateEventContent.h | 7 +- include/events/Event.h | 4 +- .../events/HistoryVisibilityEventContent.h | 7 +- include/events/JoinRulesEventContent.h | 7 +- include/events/MemberEventContent.h | 23 +-- include/events/MessageEvent.h | 2 +- include/events/MessageEventContent.h | 7 +- include/events/NameEventContent.h | 7 +- include/events/PowerLevelsEventContent.h | 55 +----- include/events/RoomEvent.h | 8 +- include/events/StateEvent.h | 4 +- include/events/TopicEventContent.h | 7 +- include/events/messages/Audio.h | 16 +- include/events/messages/File.h | 25 +-- include/events/messages/Image.h | 16 +- include/events/messages/Location.h | 16 +- include/events/messages/Video.h | 16 +- src/Cache.cc | 9 + src/RoomInfoListItem.cc | 15 ++ src/TopRoomBar.cc | 28 +++ 38 files changed, 159 insertions(+), 640 deletions(-) diff --git a/.clang-format b/.clang-format index 42d7b6f7..6dc5274b 100644 --- a/.clang-format +++ b/.clang-format @@ -3,7 +3,7 @@ Language: Cpp AccessModifierOffset: -8 AlignAfterOpenBracket: Align AlignConsecutiveAssignments: true -AllowShortFunctionsOnASingleLine: Empty +AllowShortFunctionsOnASingleLine: true BasedOnStyle: Mozilla ColumnLimit: 100 IndentCaseLabels: false diff --git a/include/Cache.h b/include/Cache.h index 3a98fddc..69d880f5 100644 --- a/include/Cache.h +++ b/include/Cache.h @@ -33,8 +33,8 @@ public: QString nextBatchToken() const; QMap states(); - inline void deleteData(); - inline void unmount(); + void deleteData(); + void unmount() { isMounted_ = false; }; void removeRoom(const QString &roomid); void setup(); @@ -52,18 +52,3 @@ private: QString userId_; QString cacheDirectory_; }; - -inline void -Cache::unmount() -{ - isMounted_ = false; -} - -inline void -Cache::deleteData() -{ - qInfo() << "Deleting cache data"; - - if (!cacheDirectory_.isEmpty()) - QDir(cacheDirectory_).removeRecursively(); -} diff --git a/include/EmojiCategory.h b/include/EmojiCategory.h index e17b110b..154ad8f4 100644 --- a/include/EmojiCategory.h +++ b/include/EmojiCategory.h @@ -39,7 +39,10 @@ signals: void emojiSelected(const QString &emoji); private slots: - inline void clickIndex(const QModelIndex &); + void clickIndex(const QModelIndex &index) + { + emit emojiSelected(index.data(Qt::UserRole).toString()); + }; private: QVBoxLayout *mainLayout_; @@ -52,9 +55,3 @@ private: QLabel *category_; }; - -inline void -EmojiCategory::clickIndex(const QModelIndex &index) -{ - emit emojiSelected(index.data(Qt::UserRole).toString()); -} diff --git a/include/Login.h b/include/Login.h index ceca4ebf..2f74fab2 100644 --- a/include/Login.h +++ b/include/Login.h @@ -29,55 +29,25 @@ public: QByteArray serialize() noexcept; - inline void setPassword(QString password); - inline void setUser(QString username); + void setPassword(QString password) { password_ = password; }; + void setUser(QString username) { user_ = username; }; private: QString user_; QString password_; }; -inline void -LoginRequest::setPassword(QString password) -{ - password_ = password; -} - -inline void -LoginRequest::setUser(QString username) -{ - user_ = username; -} - class LoginResponse : public Deserializable { public: void deserialize(const QJsonDocument &data) override; - inline QString getAccessToken(); - inline QString getHomeServer(); - inline QString getUserId(); + QString getAccessToken() { return access_token_; }; + QString getHomeServer() { return home_server_; }; + QString getUserId() { return user_id_; }; private: QString access_token_; QString home_server_; QString user_id_; }; - -inline QString -LoginResponse::getAccessToken() -{ - return access_token_; -} - -inline QString -LoginResponse::getHomeServer() -{ - return home_server_; -} - -inline QString -LoginResponse::getUserId() -{ - return user_id_; -} diff --git a/include/MatrixClient.h b/include/MatrixClient.h index 0185bf64..7596cf4e 100644 --- a/include/MatrixClient.h +++ b/include/MatrixClient.h @@ -55,9 +55,9 @@ public: void joinRoom(const QString &roomIdOrAlias); void leaveRoom(const QString &roomId); - inline QUrl getHomeServer(); - inline int transactionId(); - inline void incrementTransactionId(); + QUrl getHomeServer() { return server_; }; + int transactionId() { return txn_id_; }; + void incrementTransactionId() { txn_id_ += 1; }; void reset() noexcept; @@ -65,9 +65,12 @@ public slots: void getOwnProfile() noexcept; void logout() noexcept; - inline void setServer(const QString &server); - inline void setAccessToken(const QString &token); - inline void setNextBatchToken(const QString &next_batch); + void setServer(const QString &server) + { + server_ = QUrl(QString("https://%1").arg(server)); + }; + void setAccessToken(const QString &token) { token_ = token; }; + void setNextBatchToken(const QString &next_batch) { next_batch_ = next_batch; }; signals: void loginError(const QString &error); @@ -162,39 +165,3 @@ private: // Token to be used for the next sync. QString next_batch_; }; - -inline QUrl -MatrixClient::getHomeServer() -{ - return server_; -} - -inline int -MatrixClient::transactionId() -{ - return txn_id_; -} - -inline void -MatrixClient::setServer(const QString &server) -{ - server_ = QUrl(QString("https://%1").arg(server)); -} - -inline void -MatrixClient::setAccessToken(const QString &token) -{ - token_ = token; -} - -inline void -MatrixClient::setNextBatchToken(const QString &next_batch) -{ - next_batch_ = next_batch; -} - -inline void -MatrixClient::incrementTransactionId() -{ - txn_id_ += 1; -} diff --git a/include/Profile.h b/include/Profile.h index c460efbb..e2868e62 100644 --- a/include/Profile.h +++ b/include/Profile.h @@ -27,22 +27,10 @@ class ProfileResponse : public Deserializable public: void deserialize(const QJsonDocument &data) override; - inline QUrl getAvatarUrl(); - inline QString getDisplayName(); + QUrl getAvatarUrl() { return avatar_url_; }; + QString getDisplayName() { return display_name_; }; private: QUrl avatar_url_; QString display_name_; }; - -inline QUrl -ProfileResponse::getAvatarUrl() -{ - return avatar_url_; -} - -inline QString -ProfileResponse::getDisplayName() -{ - return display_name_; -} diff --git a/include/Register.h b/include/Register.h index eb3a3d81..83f538b9 100644 --- a/include/Register.h +++ b/include/Register.h @@ -29,55 +29,25 @@ public: QByteArray serialize() noexcept; - inline void setPassword(QString password); - inline void setUser(QString username); + void setPassword(QString password) { password_ = password; }; + void setUser(QString username) { user_ = username; }; private: QString user_; QString password_; }; -inline void -RegisterRequest::setPassword(QString password) -{ - password_ = password; -} - -inline void -RegisterRequest::setUser(QString username) -{ - user_ = username; -} - class RegisterResponse : public Deserializable { public: void deserialize(const QJsonDocument &data) override; - inline QString getAccessToken(); - inline QString getHomeServer(); - inline QString getUserId(); + QString getAccessToken() { return access_token_; }; + QString getHomeServer() { return home_server_; }; + QString getUserId() { return user_id_; }; private: QString access_token_; QString home_server_; QString user_id_; }; - -inline QString -RegisterResponse::getAccessToken() -{ - return access_token_; -} - -inline QString -RegisterResponse::getHomeServer() -{ - return home_server_; -} - -inline QString -RegisterResponse::getUserId() -{ - return user_id_; -} diff --git a/include/RoomInfoListItem.h b/include/RoomInfoListItem.h index d7009a85..8975af79 100644 --- a/include/RoomInfoListItem.h +++ b/include/RoomInfoListItem.h @@ -50,11 +50,12 @@ public: void clearUnreadMessageCount(); void setState(const RoomState &state); - inline bool isPressed() const; - inline RoomState state() const; - inline void setAvatar(const QImage &avatar_image); - inline int unreadMessageCount() const; - inline void setDescriptionMessage(const DescInfo &info); + bool isPressed() const { return isPressed_; }; + RoomState state() const { return state_; }; + int unreadMessageCount() const { return unreadMsgCount_; }; + + void setAvatar(const QImage &avatar_image); + void setDescriptionMessage(const DescInfo &info); signals: void clicked(const QString &room_id); @@ -97,36 +98,3 @@ private: int maxHeight_; int unreadMsgCount_ = 0; }; - -inline int -RoomInfoListItem::unreadMessageCount() const -{ - return unreadMsgCount_; -} - -inline bool -RoomInfoListItem::isPressed() const -{ - return isPressed_; -} - -inline RoomState -RoomInfoListItem::state() const -{ - return state_; -} - -inline void -RoomInfoListItem::setAvatar(const QImage &img) -{ - roomAvatar_ = QPixmap::fromImage( - img.scaled(IconSize, IconSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); - update(); -} - -inline void -RoomInfoListItem::setDescriptionMessage(const DescInfo &info) -{ - lastMsgInfo_ = info; - update(); -} diff --git a/include/RoomMessages.h b/include/RoomMessages.h index 89d546a8..7aee2e24 100644 --- a/include/RoomMessages.h +++ b/include/RoomMessages.h @@ -27,30 +27,12 @@ class RoomMessages : public Deserializable public: void deserialize(const QJsonDocument &data) override; - inline QString start() const; - inline QString end() const; - inline QJsonArray chunk() const; + QString start() const { return start_; }; + QString end() const { return end_; }; + QJsonArray chunk() const { return chunk_; }; private: QString start_; QString end_; QJsonArray chunk_; }; - -inline QString -RoomMessages::start() const -{ - return start_; -} - -inline QString -RoomMessages::end() const -{ - return end_; -} - -inline QJsonArray -RoomMessages::chunk() const -{ - return chunk_; -} diff --git a/include/RoomState.h b/include/RoomState.h index a07fcbed..57955e56 100644 --- a/include/RoomState.h +++ b/include/RoomState.h @@ -49,9 +49,9 @@ public: void resolveAvatar(); void parse(const QJsonObject &object); - inline QUrl getAvatar() const; - inline QString getName() const; - inline QString getTopic() const; + QUrl getAvatar() const { return avatar_; }; + QString getName() const { return name_; }; + QString getTopic() const { return topic.content().topic().simplified(); }; void removeLeaveMemberships(); void update(const RoomState &state); @@ -81,21 +81,3 @@ private: // avatar event this should be empty. QString userAvatar_; }; - -inline QString -RoomState::getTopic() const -{ - return topic.content().topic().simplified(); -} - -inline QString -RoomState::getName() const -{ - return name_; -} - -inline QUrl -RoomState::getAvatar() const -{ - return avatar_; -} diff --git a/include/Sync.h b/include/Sync.h index c49d7101..ae61015e 100644 --- a/include/Sync.h +++ b/include/Sync.h @@ -27,15 +27,15 @@ class Event : public Deserializable { public: - inline QJsonObject content() const; - inline QJsonObject unsigned_content() const; + QJsonObject content() const { return content_; }; + QJsonObject unsigned_content() const { return unsigned_; }; - inline QString sender() const; - inline QString state_key() const; - inline QString type() const; - inline QString eventId() const; + QString sender() const { return sender_; }; + QString state_key() const { return state_key_; }; + QString type() const { return type_; }; + QString eventId() const { return event_id_; }; - inline uint64_t timestamp() const; + uint64_t timestamp() const { return origin_server_ts_; }; void deserialize(const QJsonValue &data) override; @@ -51,70 +51,22 @@ private: uint64_t origin_server_ts_; }; -inline QJsonObject -Event::content() const -{ - return content_; -} - -inline QJsonObject -Event::unsigned_content() const -{ - return unsigned_; -} - -inline QString -Event::sender() const -{ - return sender_; -} - -inline QString -Event::state_key() const -{ - return state_key_; -} - -inline QString -Event::type() const -{ - return type_; -} - -inline QString -Event::eventId() const -{ - return event_id_; -} - -inline uint64_t -Event::timestamp() const -{ - return origin_server_ts_; -} - class State : public Deserializable { public: void deserialize(const QJsonValue &data) override; - inline QJsonArray events() const; + QJsonArray events() const { return events_; }; private: QJsonArray events_; }; -inline QJsonArray -State::events() const -{ - return events_; -} - class Timeline : public Deserializable { public: - inline QJsonArray events() const; - inline QString previousBatch() const; - inline bool limited() const; + QJsonArray events() const { return events_; }; + QString previousBatch() const { return prev_batch_; }; + bool limited() const { return limited_; }; void deserialize(const QJsonValue &data) override; @@ -124,31 +76,13 @@ private: bool limited_; }; -inline QJsonArray -Timeline::events() const -{ - return events_; -} - -inline QString -Timeline::previousBatch() const -{ - return prev_batch_; -} - -inline bool -Timeline::limited() const -{ - return limited_; -} - // TODO: Add support for account_data, undread_notifications class JoinedRoom : public Deserializable { public: - inline State state() const; - inline Timeline timeline() const; - inline QList typingUserIDs() const; + State state() const { return state_; }; + Timeline timeline() const { return timeline_; }; + QList typingUserIDs() const { return typingUserIDs_; }; void deserialize(const QJsonValue &data) override; @@ -160,29 +94,11 @@ private: /* UnreadNotifications unread_notifications_; */ }; -inline QList -JoinedRoom::typingUserIDs() const -{ - return typingUserIDs_; -} - -inline State -JoinedRoom::state() const -{ - return state_; -} - -inline Timeline -JoinedRoom::timeline() const -{ - return timeline_; -} - class LeftRoom : public Deserializable { public: - inline State state() const; - inline Timeline timeline() const; + State state() const { return state_; }; + Timeline timeline() const { return timeline_; }; void deserialize(const QJsonValue &data) override; @@ -191,24 +107,12 @@ private: Timeline timeline_; }; -inline State -LeftRoom::state() const -{ - return state_; -} - -inline Timeline -LeftRoom::timeline() const -{ - return timeline_; -} - // TODO: Add support for invited and left rooms. class Rooms : public Deserializable { public: - inline QMap join() const; - inline QMap leave() const; + QMap join() const { return join_; }; + QMap leave() const { return leave_; }; void deserialize(const QJsonValue &data) override; private: @@ -216,38 +120,14 @@ private: QMap leave_; }; -inline QMap -Rooms::join() const -{ - return join_; -} - -inline QMap -Rooms::leave() const -{ - return leave_; -} - class SyncResponse : public Deserializable { public: void deserialize(const QJsonDocument &data) override; - inline QString nextBatch() const; - inline Rooms rooms() const; + QString nextBatch() const { return next_batch_; }; + Rooms rooms() const { return rooms_; }; private: QString next_batch_; Rooms rooms_; }; - -inline Rooms -SyncResponse::rooms() const -{ - return rooms_; -} - -inline QString -SyncResponse::nextBatch() const -{ - return next_batch_; -} diff --git a/include/TextInputWidget.h b/include/TextInputWidget.h index 772bdd46..08b62f45 100644 --- a/include/TextInputWidget.h +++ b/include/TextInputWidget.h @@ -55,7 +55,7 @@ public slots: void onSendButtonClicked(); void openFileSelection(); void hideUploadSpinner(); - inline void focusLineEdit(); + void focusLineEdit() { input_->setFocus(); }; private slots: void addSelectedEmoji(const QString &emoji); @@ -80,9 +80,3 @@ private: FlatButton *sendMessageBtn_; EmojiPickButton *emojiBtn_; }; - -inline void -TextInputWidget::focusLineEdit() -{ - input_->setFocus(); -} diff --git a/include/TimelineItem.h b/include/TimelineItem.h index 0a0538f9..ef021cfe 100644 --- a/include/TimelineItem.h +++ b/include/TimelineItem.h @@ -65,7 +65,7 @@ public: QWidget *parent); void setUserAvatar(const QImage &pixmap); - inline DescInfo descriptionMessage() const; + DescInfo descriptionMessage() const { return descriptionMsg_; }; ~TimelineItem(); @@ -98,9 +98,3 @@ private: QLabel *userName_; QLabel *body_; }; - -inline DescInfo -TimelineItem::descriptionMessage() const -{ - return descriptionMsg_; -} diff --git a/include/TimelineView.h b/include/TimelineView.h index f25bc570..55f25687 100644 --- a/include/TimelineView.h +++ b/include/TimelineView.h @@ -99,7 +99,7 @@ public slots: void addBackwardsEvents(const QString &room_id, const RoomMessages &msgs); // Whether or not the initial batch has been loaded. - bool hasLoaded(); + bool hasLoaded() { return scroll_layout_->count() > 1 || isTimelineFinished; }; signals: void updateLastTimelineMessage(const QString &user, const DescInfo &info); @@ -120,7 +120,7 @@ private: const QString &userid); void removePendingMessage(const QString &eventid, const QString &body); - inline bool isDuplicate(const QString &event_id); + bool isDuplicate(const QString &event_id) { return eventIds_.contains(event_id); }; // Return nullptr if the event couldn't be parsed. TimelineItem *parseMessageEvent(const QJsonObject &event, TimelineDirection direction); @@ -160,15 +160,3 @@ private: QList pending_msgs_; QSharedPointer client_; }; - -inline bool -TimelineView::isDuplicate(const QString &event_id) -{ - return eventIds_.contains(event_id); -} - -inline bool -TimelineView::hasLoaded() -{ - return scroll_layout_->count() > 1 || isTimelineFinished; -} diff --git a/include/TopRoomBar.h b/include/TopRoomBar.h index 1b1d148c..eb941b3b 100644 --- a/include/TopRoomBar.h +++ b/include/TopRoomBar.h @@ -45,10 +45,10 @@ public: TopRoomBar(QWidget *parent = 0); ~TopRoomBar(); - inline void updateRoomAvatar(const QImage &avatar_image); - inline void updateRoomAvatar(const QIcon &icon); - inline void updateRoomName(const QString &name); - inline void updateRoomTopic(QString topic); + void updateRoomAvatar(const QImage &avatar_image); + void updateRoomAvatar(const QIcon &icon); + void updateRoomName(const QString &name); + void updateRoomTopic(QString topic); void updateRoomAvatarFromName(const QString &name); void setRoomSettings(QSharedPointer settings); @@ -89,31 +89,3 @@ private: QString roomName_; QString roomTopic_; }; - -inline void -TopRoomBar::updateRoomAvatar(const QImage &avatar_image) -{ - avatar_->setImage(avatar_image); - update(); -} - -inline void -TopRoomBar::updateRoomAvatar(const QIcon &icon) -{ - avatar_->setIcon(icon); - update(); -} - -inline void -TopRoomBar::updateRoomName(const QString &name) -{ - roomName_ = name; - update(); -} - -inline void -TopRoomBar::updateRoomTopic(QString topic) -{ - roomTopic_ = topic; - update(); -} diff --git a/include/events/AliasesEventContent.h b/include/events/AliasesEventContent.h index 49e51275..7784fad7 100644 --- a/include/events/AliasesEventContent.h +++ b/include/events/AliasesEventContent.h @@ -32,16 +32,11 @@ public: void deserialize(const QJsonValue &data) override; QJsonObject serialize() const override; - inline QList aliases() const; + QList aliases() const { return aliases_; }; private: QList aliases_; }; -inline QList -AliasesEventContent::aliases() const -{ - return aliases_; -} } // namespace events } // namespace matrix diff --git a/include/events/AvatarEventContent.h b/include/events/AvatarEventContent.h index 98e2f12d..55284aa4 100644 --- a/include/events/AvatarEventContent.h +++ b/include/events/AvatarEventContent.h @@ -36,16 +36,11 @@ public: void deserialize(const QJsonValue &data) override; QJsonObject serialize() const override; - inline QUrl url() const; + QUrl url() const { return url_; }; private: QUrl url_; }; -inline QUrl -AvatarEventContent::url() const -{ - return url_; -} } // namespace events } // namespace matrix diff --git a/include/events/CanonicalAliasEventContent.h b/include/events/CanonicalAliasEventContent.h index 9c961d42..6322c001 100644 --- a/include/events/CanonicalAliasEventContent.h +++ b/include/events/CanonicalAliasEventContent.h @@ -38,16 +38,11 @@ public: void deserialize(const QJsonValue &data) override; QJsonObject serialize() const override; - inline QString alias() const; + QString alias() const { return alias_; }; private: QString alias_; }; -inline QString -CanonicalAliasEventContent::alias() const -{ - return alias_; -} } // namespace events } // namespace matrix diff --git a/include/events/CreateEventContent.h b/include/events/CreateEventContent.h index 8edc4d24..0a47860e 100644 --- a/include/events/CreateEventContent.h +++ b/include/events/CreateEventContent.h @@ -36,17 +36,12 @@ public: void deserialize(const QJsonValue &data) override; QJsonObject serialize() const override; - inline QString creator() const; + QString creator() const { return creator_; }; private: // The user_id of the room creator. This is set by the homeserver. QString creator_; }; -inline QString -CreateEventContent::creator() const -{ - return creator_; -} } // namespace events } // namespace matrix diff --git a/include/events/Event.h b/include/events/Event.h index 43ab4cc6..84c21907 100644 --- a/include/events/Event.h +++ b/include/events/Event.h @@ -66,8 +66,8 @@ class Event , public Serializable { public: - inline Content content() const; - inline EventType eventType() const; + Content content() const; + EventType eventType() const; void deserialize(const QJsonValue &data) override; QJsonObject serialize() const override; diff --git a/include/events/HistoryVisibilityEventContent.h b/include/events/HistoryVisibilityEventContent.h index 94563d55..1c39ae03 100644 --- a/include/events/HistoryVisibilityEventContent.h +++ b/include/events/HistoryVisibilityEventContent.h @@ -36,7 +36,7 @@ class HistoryVisibilityEventContent , public Serializable { public: - inline HistoryVisibility historyVisibility() const; + HistoryVisibility historyVisibility() const { return history_visibility_; }; void deserialize(const QJsonValue &data) override; QJsonObject serialize() const override; @@ -45,10 +45,5 @@ private: HistoryVisibility history_visibility_; }; -inline HistoryVisibility -HistoryVisibilityEventContent::historyVisibility() const -{ - return history_visibility_; -} } // namespace events } // namespace matrix diff --git a/include/events/JoinRulesEventContent.h b/include/events/JoinRulesEventContent.h index b9b6848d..4ed9e65f 100644 --- a/include/events/JoinRulesEventContent.h +++ b/include/events/JoinRulesEventContent.h @@ -51,16 +51,11 @@ public: void deserialize(const QJsonValue &data) override; QJsonObject serialize() const override; - inline JoinRule joinRule() const; + JoinRule joinRule() const { return join_rule_; }; private: JoinRule join_rule_; }; -inline JoinRule -JoinRulesEventContent::joinRule() const -{ - return join_rule_; -} } // namespace events } // namespace matrix diff --git a/include/events/MemberEventContent.h b/include/events/MemberEventContent.h index 1dd703a0..8b7b1576 100644 --- a/include/events/MemberEventContent.h +++ b/include/events/MemberEventContent.h @@ -54,9 +54,9 @@ public: void deserialize(const QJsonValue &data) override; QJsonObject serialize() const override; - inline QUrl avatarUrl() const; - inline QString displayName() const; - inline Membership membershipState() const; + QUrl avatarUrl() const { return avatar_url_; }; + QString displayName() const { return display_name_; }; + Membership membershipState() const { return membership_state_; }; private: QUrl avatar_url_; @@ -64,22 +64,5 @@ private: Membership membership_state_; }; -inline QUrl -MemberEventContent::avatarUrl() const -{ - return avatar_url_; -} - -inline QString -MemberEventContent::displayName() const -{ - return display_name_; -} - -inline Membership -MemberEventContent::membershipState() const -{ - return membership_state_; -} } // namespace events } // namespace matrix diff --git a/include/events/MessageEvent.h b/include/events/MessageEvent.h index 7e7493e7..3c4f83e9 100644 --- a/include/events/MessageEvent.h +++ b/include/events/MessageEvent.h @@ -26,7 +26,7 @@ template class MessageEvent : public RoomEvent { public: - inline MsgContent msgContent() const; + MsgContent msgContent() const; void deserialize(const QJsonValue &data) override; diff --git a/include/events/MessageEventContent.h b/include/events/MessageEventContent.h index c096f155..aa08c066 100644 --- a/include/events/MessageEventContent.h +++ b/include/events/MessageEventContent.h @@ -64,16 +64,11 @@ public: void deserialize(const QJsonValue &data) override; QJsonObject serialize() const override; - inline QString body() const; + QString body() const { return body_; }; private: QString body_; }; -inline QString -MessageEventContent::body() const -{ - return body_; -} } // namespace events } // namespace matrix diff --git a/include/events/NameEventContent.h b/include/events/NameEventContent.h index 413c28cb..378f689d 100644 --- a/include/events/NameEventContent.h +++ b/include/events/NameEventContent.h @@ -35,16 +35,11 @@ public: void deserialize(const QJsonValue &data) override; QJsonObject serialize() const override; - inline QString name() const; + QString name() const { return name_; }; private: QString name_; }; -inline QString -NameEventContent::name() const -{ - return name_; -} } // namespace events } // namespace matrix diff --git a/include/events/PowerLevelsEventContent.h b/include/events/PowerLevelsEventContent.h index 04afa473..63998871 100644 --- a/include/events/PowerLevelsEventContent.h +++ b/include/events/PowerLevelsEventContent.h @@ -43,14 +43,14 @@ public: void deserialize(const QJsonValue &data) override; QJsonObject serialize() const override; - inline int banLevel() const; - inline int inviteLevel() const; - inline int kickLevel() const; - inline int redactLevel() const; + int banLevel() const { return ban_; }; + int inviteLevel() const { return invite_; }; + int kickLevel() const { return kick_; }; + int redactLevel() const { return redact_; }; - inline int eventsDefaultLevel() const; - inline int stateDefaultLevel() const; - inline int usersDefaultLevel() const; + int eventsDefaultLevel() const { return events_default_; }; + int stateDefaultLevel() const { return state_default_; }; + int usersDefaultLevel() const { return users_default_; }; int eventLevel(QString event_type) const; int userLevel(QString user_id) const; @@ -69,46 +69,5 @@ private: QMap users_; }; -inline int -PowerLevelsEventContent::banLevel() const -{ - return ban_; -} - -inline int -PowerLevelsEventContent::inviteLevel() const -{ - return invite_; -} - -inline int -PowerLevelsEventContent::kickLevel() const -{ - return kick_; -} - -inline int -PowerLevelsEventContent::redactLevel() const -{ - return redact_; -} - -inline int -PowerLevelsEventContent::eventsDefaultLevel() const -{ - return events_default_; -} - -inline int -PowerLevelsEventContent::stateDefaultLevel() const -{ - return state_default_; -} - -inline int -PowerLevelsEventContent::usersDefaultLevel() const -{ - return users_default_; -} } // namespace events } // namespace matrix diff --git a/include/events/RoomEvent.h b/include/events/RoomEvent.h index a1e88807..d80951c7 100644 --- a/include/events/RoomEvent.h +++ b/include/events/RoomEvent.h @@ -28,10 +28,10 @@ template class RoomEvent : public Event { public: - inline QString eventId() const; - inline QString roomId() const; - inline QString sender() const; - inline uint64_t timestamp() const; + QString eventId() const; + QString roomId() const; + QString sender() const; + uint64_t timestamp() const; void deserialize(const QJsonValue &data) override; QJsonObject serialize() const override; diff --git a/include/events/StateEvent.h b/include/events/StateEvent.h index 146e96bd..19342a48 100644 --- a/include/events/StateEvent.h +++ b/include/events/StateEvent.h @@ -27,8 +27,8 @@ template class StateEvent : public RoomEvent { public: - inline QString stateKey() const; - inline Content previousContent() const; + QString stateKey() const; + Content previousContent() const; void deserialize(const QJsonValue &data); QJsonObject serialize() const; diff --git a/include/events/TopicEventContent.h b/include/events/TopicEventContent.h index d059e489..67e21208 100644 --- a/include/events/TopicEventContent.h +++ b/include/events/TopicEventContent.h @@ -36,16 +36,11 @@ public: void deserialize(const QJsonValue &data) override; QJsonObject serialize() const override; - inline QString topic() const; + QString topic() const { return topic_; }; private: QString topic_; }; -inline QString -TopicEventContent::topic() const -{ - return topic_; -} } // namespace events } // namespace matrix diff --git a/include/events/messages/Audio.h b/include/events/messages/Audio.h index 8abdf7f2..8466e0e4 100644 --- a/include/events/messages/Audio.h +++ b/include/events/messages/Audio.h @@ -35,8 +35,8 @@ struct AudioInfo class Audio : public Deserializable { public: - inline QString url() const; - inline AudioInfo info() const; + QString url() const { return url_; }; + AudioInfo info() const { return info_; }; void deserialize(const QJsonObject &object) override; @@ -45,18 +45,6 @@ private: AudioInfo info_; }; -inline QString -Audio::url() const -{ - return url_; -} - -inline AudioInfo -Audio::info() const -{ - return info_; -} - } // namespace messages } // namespace events } // namespace matrix diff --git a/include/events/messages/File.h b/include/events/messages/File.h index ff5eb0a6..2fb2aa83 100644 --- a/include/events/messages/File.h +++ b/include/events/messages/File.h @@ -37,10 +37,9 @@ struct FileInfo class File : public Deserializable { public: - inline QString url() const; - inline QString filename() const; - - inline FileInfo info() const; + QString url() const { return url_; }; + QString filename() const { return filename_; }; + FileInfo info() const { return info_; }; void deserialize(const QJsonObject &object) override; @@ -51,24 +50,6 @@ private: FileInfo info_; }; -inline QString -File::filename() const -{ - return filename_; -} - -inline QString -File::url() const -{ - return url_; -} - -inline FileInfo -File::info() const -{ - return info_; -} - } // namespace messages } // namespace events } // namespace matrix diff --git a/include/events/messages/Image.h b/include/events/messages/Image.h index 14889ff1..1e709579 100644 --- a/include/events/messages/Image.h +++ b/include/events/messages/Image.h @@ -39,8 +39,8 @@ struct ImageInfo class Image : public Deserializable { public: - inline QString url() const; - inline ImageInfo info() const; + QString url() const { return url_; }; + ImageInfo info() const { return info_; }; void deserialize(const QJsonObject &object) override; @@ -49,18 +49,6 @@ private: ImageInfo info_; }; -inline QString -Image::url() const -{ - return url_; -} - -inline ImageInfo -Image::info() const -{ - return info_; -} - } // namespace messages } // namespace events } // namespace matrix diff --git a/include/events/messages/Location.h b/include/events/messages/Location.h index e51c6799..27722b37 100644 --- a/include/events/messages/Location.h +++ b/include/events/messages/Location.h @@ -34,8 +34,8 @@ struct LocationInfo class Location : public Deserializable { public: - inline QString geoUri() const; - inline LocationInfo info() const; + QString geoUri() const { return geo_uri_; }; + LocationInfo info() const { return info_; }; void deserialize(const QJsonObject &object) override; @@ -45,18 +45,6 @@ private: LocationInfo info_; }; -inline QString -Location::geoUri() const -{ - return geo_uri_; -} - -inline LocationInfo -Location::info() const -{ - return info_; -} - } // namespace messages } // namespace events } // namespace matrix diff --git a/include/events/messages/Video.h b/include/events/messages/Video.h index d167b8c1..79713546 100644 --- a/include/events/messages/Video.h +++ b/include/events/messages/Video.h @@ -40,8 +40,8 @@ struct VideoInfo class Video : public Deserializable { public: - inline QString url() const; - inline VideoInfo info() const; + QString url() const { return url_; }; + VideoInfo info() const { return info_; }; void deserialize(const QJsonObject &object) override; @@ -50,18 +50,6 @@ private: VideoInfo info_; }; -inline QString -Video::url() const -{ - return url_; -} - -inline VideoInfo -Video::info() const -{ - return info_; -} - } // namespace messages } // namespace events } // namespace matrix diff --git a/src/Cache.cc b/src/Cache.cc index 010b4aa9..befb3a0a 100644 --- a/src/Cache.cc +++ b/src/Cache.cc @@ -272,3 +272,12 @@ Cache::nextBatchToken() const return QString::fromUtf8(token.data(), token.size()); } + +void +Cache::deleteData() +{ + qInfo() << "Deleting cache data"; + + if (!cacheDirectory_.isEmpty()) + QDir(cacheDirectory_).removeRecursively(); +} diff --git a/src/RoomInfoListItem.cc b/src/RoomInfoListItem.cc index cd15d839..588ee5c8 100644 --- a/src/RoomInfoListItem.cc +++ b/src/RoomInfoListItem.cc @@ -313,4 +313,19 @@ RoomInfoListItem::mousePressEvent(QMouseEvent *event) ripple_overlay_->addRipple(ripple); } +void +RoomInfoListItem::setAvatar(const QImage &img) +{ + roomAvatar_ = QPixmap::fromImage( + img.scaled(IconSize, IconSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); + update(); +} + +void +RoomInfoListItem::setDescriptionMessage(const DescInfo &info) +{ + lastMsgInfo_ = info; + update(); +} + RoomInfoListItem::~RoomInfoListItem() {} diff --git a/src/TopRoomBar.cc b/src/TopRoomBar.cc index 4d1f4195..72aef92a 100644 --- a/src/TopRoomBar.cc +++ b/src/TopRoomBar.cc @@ -202,4 +202,32 @@ TopRoomBar::setRoomSettings(QSharedPointer settings) roomSettings_ = settings; } +void +TopRoomBar::updateRoomAvatar(const QImage &avatar_image) +{ + avatar_->setImage(avatar_image); + update(); +} + +void +TopRoomBar::updateRoomAvatar(const QIcon &icon) +{ + avatar_->setIcon(icon); + update(); +} + +void +TopRoomBar::updateRoomName(const QString &name) +{ + roomName_ = name; + update(); +} + +void +TopRoomBar::updateRoomTopic(QString topic) +{ + roomTopic_ = topic; + update(); +} + TopRoomBar::~TopRoomBar() {} From 143ed5176a891916b088b3c768b3dd3883629812 Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Sun, 22 Oct 2017 22:51:50 +0300 Subject: [PATCH 32/64] Use callbacks on MatrixClient --- include/MatrixClient.h | 42 -- src/MatrixClient.cc | 962 ++++++++++++++++++----------------------- 2 files changed, 413 insertions(+), 591 deletions(-) diff --git a/include/MatrixClient.h b/include/MatrixClient.h index 7596cf4e..7a415e82 100644 --- a/include/MatrixClient.h +++ b/include/MatrixClient.h @@ -104,49 +104,7 @@ signals: void joinedRoom(const QString &room_id); void leftRoom(const QString &room_id); -private slots: - void onResponse(QNetworkReply *reply); - private: - enum class Endpoint - { - GetOwnAvatar, - GetOwnProfile, - GetProfile, - Image, - InitialSync, - ImageUpload, - Login, - Logout, - Messages, - Register, - RoomAvatar, - SendRoomMessage, - Sync, - UserAvatar, - Versions, - JoinRoom, - LeaveRoom, - }; - - // Response handlers. - void onGetOwnAvatarResponse(QNetworkReply *reply); - void onGetOwnProfileResponse(QNetworkReply *reply); - void onImageResponse(QNetworkReply *reply); - void onInitialSyncResponse(QNetworkReply *reply); - void onImageUploadResponse(QNetworkReply *reply); - void onLoginResponse(QNetworkReply *reply); - void onLogoutResponse(QNetworkReply *reply); - void onMessagesResponse(QNetworkReply *reply); - void onRegisterResponse(QNetworkReply *reply); - void onRoomAvatarResponse(QNetworkReply *reply); - void onSendRoomMessage(QNetworkReply *reply); - void onSyncResponse(QNetworkReply *reply); - void onUserAvatarResponse(QNetworkReply *reply); - void onVersionsResponse(QNetworkReply *reply); - void onJoinRoomResponse(QNetworkReply *reply); - void onLeaveRoomResponse(QNetworkReply *reply); - // Client API prefix. QString clientApiUrl_; diff --git a/src/MatrixClient.cc b/src/MatrixClient.cc index f0b3bd26..afd99995 100644 --- a/src/MatrixClient.cc +++ b/src/MatrixClient.cc @@ -43,7 +43,6 @@ MatrixClient::MatrixClient(QString server, QObject *parent) QSettings settings; txn_id_ = settings.value("client/transaction_id", 1).toInt(); - connect(this, SIGNAL(finished(QNetworkReply *)), this, SLOT(onResponse(QNetworkReply *))); connect(this, &QNetworkAccessManager::networkAccessibleChanged, this, @@ -63,512 +62,6 @@ MatrixClient::reset() noexcept txn_id_ = 0; } -void -MatrixClient::onVersionsResponse(QNetworkReply *reply) -{ - reply->deleteLater(); - - int status_code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - - if (status_code == 404) { - emit versionError("Versions endpoint was not found on the server. Possibly " - "not a Matrix server"); - return; - } - - if (status_code >= 400) { - qWarning() << "API version error: " << reply->errorString(); - emit versionError("An unknown error occured. Please try again."); - return; - } - - auto data = reply->readAll(); - auto json = QJsonDocument::fromJson(data); - - VersionsResponse response; - - try { - response.deserialize(json); - if (!response.isVersionSupported(0, 2, 0)) - emit versionError("Server does not support required API version."); - else - emit versionSuccess(); - } catch (DeserializationException &e) { - qWarning() << "Malformed JSON response" << e.what(); - emit versionError("Malformed response. Possibly not a Matrix server"); - } -} - -void -MatrixClient::onLoginResponse(QNetworkReply *reply) -{ - reply->deleteLater(); - - int status_code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - - if (status_code == 403) { - emit loginError(tr("Wrong username or password")); - return; - } - - if (status_code == 404) { - emit loginError(tr("Login endpoint was not found on the server")); - return; - } - - if (status_code >= 400) { - qWarning() << "Login error: " << reply->errorString(); - emit loginError(tr("An unknown error occured. Please try again.")); - return; - } - - auto data = reply->readAll(); - auto json = QJsonDocument::fromJson(data); - - LoginResponse response; - - try { - response.deserialize(json); - - auto hostname = server_.host(); - - if (server_.port() > 0) - hostname = QString("%1:%2").arg(server_.host()).arg(server_.port()); - - emit loginSuccess(response.getUserId(), hostname, response.getAccessToken()); - } catch (DeserializationException &e) { - qWarning() << "Malformed JSON response" << e.what(); - emit loginError(tr("Malformed response. Possibly not a Matrix server")); - } -} - -void -MatrixClient::onLogoutResponse(QNetworkReply *reply) -{ - reply->deleteLater(); - - int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - - if (status != 200) { - qWarning() << "Logout error: " << reply->errorString(); - return; - } - - emit loggedOut(); -} - -void -MatrixClient::onRegisterResponse(QNetworkReply *reply) -{ - reply->deleteLater(); - - int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - - auto data = reply->readAll(); - auto json = QJsonDocument::fromJson(data); - - if (status == 0 || status >= 400) { - if (json.isObject() && json.object().contains("error")) - emit registerError(json.object().value("error").toString()); - else - emit registerError(reply->errorString()); - - return; - } - - RegisterResponse response; - - try { - response.deserialize(json); - emit registerSuccess( - response.getUserId(), response.getHomeServer(), response.getAccessToken()); - } catch (DeserializationException &e) { - qWarning() << "Register" << e.what(); - emit registerError("Received malformed response."); - } -} - -void -MatrixClient::onGetOwnProfileResponse(QNetworkReply *reply) -{ - reply->deleteLater(); - - int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - - if (status >= 400) { - qWarning() << reply->errorString(); - return; - } - - auto data = reply->readAll(); - auto json = QJsonDocument::fromJson(data); - - ProfileResponse response; - - try { - response.deserialize(json); - emit getOwnProfileResponse(response.getAvatarUrl(), response.getDisplayName()); - } catch (DeserializationException &e) { - qWarning() << "Profile:" << e.what(); - } -} - -void -MatrixClient::onInitialSyncResponse(QNetworkReply *reply) -{ - reply->deleteLater(); - - int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - - if (status == 0 || status >= 400) { - emit initialSyncFailed(reply->errorString()); - return; - } - - auto data = reply->readAll(); - - if (data.isEmpty()) - return; - - auto json = QJsonDocument::fromJson(data); - - SyncResponse response; - - try { - response.deserialize(json); - } catch (DeserializationException &e) { - qWarning() << "Sync malformed response" << e.what(); - return; - } - - emit initialSyncCompleted(response); -} - -void -MatrixClient::onImageUploadResponse(QNetworkReply *reply) -{ - reply->deleteLater(); - - int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - - if (status == 0 || status >= 400) { - emit syncFailed(reply->errorString()); - return; - } - - auto data = reply->readAll(); - - if (data.isEmpty()) - return; - - auto json = QJsonDocument::fromJson(data); - - if (!json.isObject()) { - qDebug() << "Media upload: Response is not a json object."; - return; - } - - QJsonObject object = json.object(); - if (!object.contains("content_uri")) { - qDebug() << "Media upload: Missing content_uri key"; - qDebug() << object; - return; - } - - emit imageUploaded(reply->property("room_id").toString(), - reply->property("filename").toString(), - object.value("content_uri").toString()); -} - -void -MatrixClient::onSyncResponse(QNetworkReply *reply) -{ - reply->deleteLater(); - - int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - - if (status == 0 || status >= 400) { - emit syncFailed(reply->errorString()); - return; - } - - auto data = reply->readAll(); - - if (data.isEmpty()) - return; - - auto json = QJsonDocument::fromJson(data); - - SyncResponse response; - - try { - response.deserialize(json); - emit syncCompleted(response); - } catch (DeserializationException &e) { - qWarning() << "Sync malformed response" << e.what(); - } -} - -void -MatrixClient::onSendRoomMessage(QNetworkReply *reply) -{ - reply->deleteLater(); - - int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - - if (status == 0 || status >= 400) { - qWarning() << reply->errorString(); - return; - } - - auto data = reply->readAll(); - - if (data.isEmpty()) - return; - - auto json = QJsonDocument::fromJson(data); - - if (!json.isObject()) { - qDebug() << "Send message response is not a JSON object"; - return; - } - - auto object = json.object(); - - if (!object.contains("event_id")) { - qDebug() << "SendTextMessage: missing event_id from response"; - return; - } - - emit messageSent(object.value("event_id").toString(), - reply->property("roomid").toString(), - reply->property("txn_id").toInt()); -} - -void -MatrixClient::onRoomAvatarResponse(QNetworkReply *reply) -{ - reply->deleteLater(); - - int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - - if (status == 0 || status >= 400) { - qWarning() << reply->errorString(); - return; - } - - auto img = reply->readAll(); - - if (img.size() == 0) - return; - - auto roomid = reply->property("roomid").toString(); - - QPixmap pixmap; - pixmap.loadFromData(img); - - emit roomAvatarRetrieved(roomid, pixmap); -} - -void -MatrixClient::onUserAvatarResponse(QNetworkReply *reply) -{ - reply->deleteLater(); - - int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - - if (status == 0 || status >= 400) { - qWarning() << reply->errorString(); - return; - } - - auto data = reply->readAll(); - - if (data.size() == 0) - return; - - auto roomid = reply->property("userid").toString(); - - QImage img; - img.loadFromData(data); - - emit userAvatarRetrieved(roomid, img); -} -void -MatrixClient::onGetOwnAvatarResponse(QNetworkReply *reply) -{ - reply->deleteLater(); - - int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - - if (status == 0 || status >= 400) { - qWarning() << reply->errorString(); - return; - } - - auto img = reply->readAll(); - - if (img.size() == 0) - return; - - QPixmap pixmap; - pixmap.loadFromData(img); - - emit ownAvatarRetrieved(pixmap); -} - -void -MatrixClient::onImageResponse(QNetworkReply *reply) -{ - reply->deleteLater(); - - int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - - if (status == 0 || status >= 400) { - qWarning() << reply->errorString(); - return; - } - - auto img = reply->readAll(); - - if (img.size() == 0) - return; - - QPixmap pixmap; - pixmap.loadFromData(img); - - auto event_id = reply->property("event_id").toString(); - - emit imageDownloaded(event_id, pixmap); -} - -void -MatrixClient::onMessagesResponse(QNetworkReply *reply) -{ - reply->deleteLater(); - - int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - - if (status == 0 || status >= 400) { - qWarning() << reply->errorString(); - return; - } - - auto data = reply->readAll(); - auto room_id = reply->property("room_id").toString(); - - RoomMessages msgs; - - try { - msgs.deserialize(QJsonDocument::fromJson(data)); - } catch (const DeserializationException &e) { - qWarning() << "Room messages from" << room_id << e.what(); - return; - } - - emit messagesRetrieved(room_id, msgs); -} - -void -MatrixClient::onJoinRoomResponse(QNetworkReply *reply) -{ - reply->deleteLater(); - - int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - - if (status == 0 || status >= 400) { - auto data = reply->readAll(); - auto response = QJsonDocument::fromJson(data); - auto json = response.object(); - - if (json.contains("error")) - emit joinFailed(json["error"].toString()); - else - qDebug() << reply->errorString(); - - return; - } - - auto data = reply->readAll(); - auto response = QJsonDocument::fromJson(data); - auto room_id = response.object()["room_id"].toString(); - - emit joinedRoom(room_id); -} - -void -MatrixClient::onLeaveRoomResponse(QNetworkReply *reply) -{ - reply->deleteLater(); - - int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - - if (status == 0 || status >= 400) { - qWarning() << reply->errorString(); - return; - } - - QString room_id = reply->property("room_id").toString(); - emit leftRoom(room_id); -} - -void -MatrixClient::onResponse(QNetworkReply *reply) -{ - switch (static_cast(reply->property("endpoint").toInt())) { - case Endpoint::Versions: - onVersionsResponse(reply); - break; - case Endpoint::Login: - onLoginResponse(reply); - break; - case Endpoint::Logout: - onLogoutResponse(reply); - break; - case Endpoint::Register: - onRegisterResponse(reply); - break; - case Endpoint::GetOwnProfile: - onGetOwnProfileResponse(reply); - break; - case Endpoint::Image: - onImageResponse(reply); - break; - case Endpoint::InitialSync: - onInitialSyncResponse(reply); - break; - case Endpoint::ImageUpload: - onImageUploadResponse(reply); - break; - case Endpoint::Sync: - onSyncResponse(reply); - break; - case Endpoint::SendRoomMessage: - onSendRoomMessage(reply); - break; - case Endpoint::RoomAvatar: - onRoomAvatarResponse(reply); - break; - case Endpoint::UserAvatar: - onUserAvatarResponse(reply); - break; - case Endpoint::GetOwnAvatar: - onGetOwnAvatarResponse(reply); - break; - case Endpoint::Messages: - onMessagesResponse(reply); - break; - case Endpoint::JoinRoom: - onJoinRoomResponse(reply); - break; - case Endpoint::LeaveRoom: - onLeaveRoomResponse(reply); - break; - default: - break; - } -} - void MatrixClient::login(const QString &username, const QString &password) noexcept { @@ -580,8 +73,49 @@ MatrixClient::login(const QString &username, const QString &password) noexcept LoginRequest body(username, password); - QNetworkReply *reply = post(request, body.serialize()); - reply->setProperty("endpoint", static_cast(Endpoint::Login)); + auto reply = post(request, body.serialize()); + connect(reply, &QNetworkReply::finished, this, [this, reply]() { + reply->deleteLater(); + + int status_code = + reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + + if (status_code == 403) { + emit loginError(tr("Wrong username or password")); + return; + } + + if (status_code == 404) { + emit loginError(tr("Login endpoint was not found on the server")); + return; + } + + if (status_code >= 400) { + qWarning() << "Login error: " << reply->errorString(); + emit loginError(tr("An unknown error occured. Please try again.")); + return; + } + + auto data = reply->readAll(); + auto json = QJsonDocument::fromJson(data); + + LoginResponse response; + + try { + response.deserialize(json); + + auto hostname = server_.host(); + + if (server_.port() > 0) + hostname = QString("%1:%2").arg(server_.host()).arg(server_.port()); + + emit loginSuccess( + response.getUserId(), hostname, response.getAccessToken()); + } catch (DeserializationException &e) { + qWarning() << "Malformed JSON response" << e.what(); + emit loginError(tr("Malformed response. Possibly not a Matrix server")); + } + }); } void @@ -598,8 +132,20 @@ MatrixClient::logout() noexcept request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); QJsonObject body{}; - QNetworkReply *reply = post(request, QJsonDocument(body).toJson(QJsonDocument::Compact)); - reply->setProperty("endpoint", static_cast(Endpoint::Logout)); + auto reply = post(request, QJsonDocument(body).toJson(QJsonDocument::Compact)); + + connect(reply, &QNetworkReply::finished, this, [this, reply]() { + reply->deleteLater(); + + int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + + if (status != 200) { + qWarning() << "Logout error: " << reply->errorString(); + return; + } + + emit loggedOut(); + }); } void @@ -618,9 +164,37 @@ MatrixClient::registerUser(const QString &user, const QString &pass, const QStri request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); RegisterRequest body(user, pass); + auto reply = post(request, body.serialize()); - QNetworkReply *reply = post(request, body.serialize()); - reply->setProperty("endpoint", static_cast(Endpoint::Register)); + connect(reply, &QNetworkReply::finished, this, [this, reply]() { + reply->deleteLater(); + + int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + + auto data = reply->readAll(); + auto json = QJsonDocument::fromJson(data); + + if (status == 0 || status >= 400) { + if (json.isObject() && json.object().contains("error")) + emit registerError(json.object().value("error").toString()); + else + emit registerError(reply->errorString()); + + return; + } + + RegisterResponse response; + + try { + response.deserialize(json); + emit registerSuccess(response.getUserId(), + response.getHomeServer(), + response.getAccessToken()); + } catch (DeserializationException &e) { + qWarning() << "Register" << e.what(); + emit registerError("Received malformed response."); + } + }); } void @@ -654,8 +228,33 @@ MatrixClient::sync() noexcept QNetworkRequest request(QString(endpoint.toEncoded())); request.setRawHeader("Connection", "keep-alive"); - QNetworkReply *reply = get(request); - reply->setProperty("endpoint", static_cast(Endpoint::Sync)); + auto reply = get(request); + connect(reply, &QNetworkReply::finished, this, [this, reply]() { + reply->deleteLater(); + + int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + + if (status == 0 || status >= 400) { + emit syncFailed(reply->errorString()); + return; + } + + auto data = reply->readAll(); + + if (data.isEmpty()) + return; + + auto json = QJsonDocument::fromJson(data); + + SyncResponse response; + + try { + response.deserialize(json); + emit syncCompleted(response); + } catch (DeserializationException &e) { + qWarning() << "Sync malformed response" << e.what(); + } + }); } void @@ -693,10 +292,40 @@ MatrixClient::sendRoomMessage(matrix::events::MessageEventType ty, QNetworkRequest request(QString(endpoint.toEncoded())); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); - QNetworkReply *reply = put(request, QJsonDocument(body).toJson(QJsonDocument::Compact)); - reply->setProperty("endpoint", static_cast(Endpoint::SendRoomMessage)); - reply->setProperty("txn_id", txn_id_); - reply->setProperty("roomid", roomid); + auto reply = put(request, QJsonDocument(body).toJson(QJsonDocument::Compact)); + auto txnId = this->txn_id_; + + connect(reply, &QNetworkReply::finished, this, [this, reply, roomid, txnId]() { + reply->deleteLater(); + + int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + + if (status == 0 || status >= 400) { + qWarning() << reply->errorString(); + return; + } + + auto data = reply->readAll(); + + if (data.isEmpty()) + return; + + auto json = QJsonDocument::fromJson(data); + + if (!json.isObject()) { + qDebug() << "Send message response is not a JSON object"; + return; + } + + auto object = json.object(); + + if (!object.contains("event_id")) { + qDebug() << "SendTextMessage: missing event_id from response"; + return; + } + + emit messageSent(object.value("event_id").toString(), roomid, txnId); + }); incrementTransactionId(); } @@ -715,8 +344,35 @@ MatrixClient::initialSync() noexcept QNetworkRequest request(QString(endpoint.toEncoded())); request.setRawHeader("Connection", "keep-alive"); - QNetworkReply *reply = get(request); - reply->setProperty("endpoint", static_cast(Endpoint::InitialSync)); + auto reply = get(request); + connect(reply, &QNetworkReply::finished, this, [this, reply]() { + reply->deleteLater(); + + int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + + if (status == 0 || status >= 400) { + emit initialSyncFailed(reply->errorString()); + return; + } + + auto data = reply->readAll(); + + if (data.isEmpty()) + return; + + auto json = QJsonDocument::fromJson(data); + + SyncResponse response; + + try { + response.deserialize(json); + } catch (DeserializationException &e) { + qWarning() << "Sync malformed response" << e.what(); + return; + } + + emit initialSyncCompleted(response); + }); } void @@ -727,8 +383,39 @@ MatrixClient::versions() noexcept QNetworkRequest request(endpoint); - QNetworkReply *reply = get(request); - reply->setProperty("endpoint", static_cast(Endpoint::Versions)); + auto reply = get(request); + connect(reply, &QNetworkReply::finished, this, [this, reply]() { + reply->deleteLater(); + + int status_code = + reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + + if (status_code == 404) { + emit versionError("Versions endpoint was not found on the server. Possibly " + "not a Matrix server"); + return; + } + + if (status_code >= 400) { + emit versionError("An unknown error occured. Please try again."); + return; + } + + auto data = reply->readAll(); + auto json = QJsonDocument::fromJson(data); + + VersionsResponse response; + + try { + response.deserialize(json); + if (!response.isVersionSupported(0, 2, 0)) + emit versionError("Server does not support required API version."); + else + emit versionSuccess(); + } catch (DeserializationException &e) { + emit versionError("Malformed response. Possibly not a Matrix server"); + } + }); } void @@ -749,7 +436,29 @@ MatrixClient::getOwnProfile() noexcept QNetworkRequest request(QString(endpoint.toEncoded())); QNetworkReply *reply = get(request); - reply->setProperty("endpoint", static_cast(Endpoint::GetOwnProfile)); + connect(reply, &QNetworkReply::finished, this, [this, reply]() { + reply->deleteLater(); + + int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + + if (status >= 400) { + qWarning() << reply->errorString(); + return; + } + + auto data = reply->readAll(); + auto json = QJsonDocument::fromJson(data); + + ProfileResponse response; + + try { + response.deserialize(json); + emit getOwnProfileResponse(response.getAvatarUrl(), + response.getDisplayName()); + } catch (DeserializationException &e) { + qWarning() << "Profile:" << e.what(); + } + }); } void @@ -776,8 +485,26 @@ MatrixClient::fetchRoomAvatar(const QString &roomid, const QUrl &avatar_url) QNetworkRequest avatar_request(endpoint); QNetworkReply *reply = get(avatar_request); - reply->setProperty("roomid", roomid); - reply->setProperty("endpoint", static_cast(Endpoint::RoomAvatar)); + connect(reply, &QNetworkReply::finished, this, [this, reply, roomid]() { + reply->deleteLater(); + + int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + + if (status == 0 || status >= 400) { + qWarning() << reply->errorString(); + return; + } + + auto img = reply->readAll(); + + if (img.size() == 0) + return; + + QPixmap pixmap; + pixmap.loadFromData(img); + + emit roomAvatarRetrieved(roomid, pixmap); + }); } void @@ -803,9 +530,27 @@ MatrixClient::fetchUserAvatar(const QString &userId, const QUrl &avatarUrl) QNetworkRequest avatar_request(endpoint); - QNetworkReply *reply = get(avatar_request); - reply->setProperty("userid", userId); - reply->setProperty("endpoint", static_cast(Endpoint::UserAvatar)); + auto reply = get(avatar_request); + connect(reply, &QNetworkReply::finished, this, [this, reply, userId]() { + reply->deleteLater(); + + int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + + if (status == 0 || status >= 400) { + qWarning() << reply->errorString(); + return; + } + + auto data = reply->readAll(); + + if (data.size() == 0) + return; + + QImage img; + img.loadFromData(data); + + emit userAvatarRetrieved(userId, img); + }); } void @@ -813,9 +558,27 @@ MatrixClient::downloadImage(const QString &event_id, const QUrl &url) { QNetworkRequest image_request(url); - QNetworkReply *reply = get(image_request); - reply->setProperty("event_id", event_id); - reply->setProperty("endpoint", static_cast(Endpoint::Image)); + auto reply = get(image_request); + connect(reply, &QNetworkReply::finished, this, [this, reply, event_id]() { + reply->deleteLater(); + + int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + + if (status == 0 || status >= 400) { + qWarning() << reply->errorString(); + return; + } + + auto img = reply->readAll(); + + if (img.size() == 0) + return; + + QPixmap pixmap; + pixmap.loadFromData(img); + + emit imageDownloaded(event_id, pixmap); + }); } void @@ -841,12 +604,31 @@ MatrixClient::fetchOwnAvatar(const QUrl &avatar_url) QNetworkRequest avatar_request(endpoint); - QNetworkReply *reply = get(avatar_request); - reply->setProperty("endpoint", static_cast(Endpoint::GetOwnAvatar)); + auto reply = get(avatar_request); + connect(reply, &QNetworkReply::finished, this, [this, reply]() { + reply->deleteLater(); + + int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + + if (status == 0 || status >= 400) { + qWarning() << reply->errorString(); + return; + } + + auto img = reply->readAll(); + + if (img.size() == 0) + return; + + QPixmap pixmap; + pixmap.loadFromData(img); + + emit ownAvatarRetrieved(pixmap); + }); } void -MatrixClient::messages(const QString &room_id, const QString &from_token, int limit) noexcept +MatrixClient::messages(const QString &roomid, const QString &from_token, int limit) noexcept { QUrlQuery query; query.addQueryItem("access_token", token_); @@ -855,14 +637,35 @@ MatrixClient::messages(const QString &room_id, const QString &from_token, int li query.addQueryItem("limit", QString::number(limit)); QUrl endpoint(server_); - endpoint.setPath(clientApiUrl_ + QString("/rooms/%1/messages").arg(room_id)); + endpoint.setPath(clientApiUrl_ + QString("/rooms/%1/messages").arg(roomid)); endpoint.setQuery(query); QNetworkRequest request(QString(endpoint.toEncoded())); - QNetworkReply *reply = get(request); - reply->setProperty("endpoint", static_cast(Endpoint::Messages)); - reply->setProperty("room_id", room_id); + auto reply = get(request); + connect(reply, &QNetworkReply::finished, this, [this, reply, roomid]() { + reply->deleteLater(); + + int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + + if (status == 0 || status >= 400) { + qWarning() << reply->errorString(); + return; + } + + auto data = reply->readAll(); + + RoomMessages msgs; + + try { + msgs.deserialize(QJsonDocument::fromJson(data)); + } catch (const DeserializationException &e) { + qWarning() << "Room messages from" << roomid << e.what(); + return; + } + + emit messagesRetrieved(roomid, msgs); + }); } void @@ -887,10 +690,38 @@ MatrixClient::uploadImage(const QString &roomid, const QString &filename) request.setHeader(QNetworkRequest::ContentLengthHeader, file.size()); request.setHeader(QNetworkRequest::ContentTypeHeader, QString("image/%1").arg(imgFormat)); - QNetworkReply *reply = post(request, file.readAll()); - reply->setProperty("endpoint", static_cast(Endpoint::ImageUpload)); - reply->setProperty("room_id", roomid); - reply->setProperty("filename", filename); + auto reply = post(request, file.readAll()); + connect(reply, &QNetworkReply::finished, this, [this, reply, roomid, filename]() { + reply->deleteLater(); + + int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + + if (status == 0 || status >= 400) { + emit syncFailed(reply->errorString()); + return; + } + + auto data = reply->readAll(); + + if (data.isEmpty()) + return; + + auto json = QJsonDocument::fromJson(data); + + if (!json.isObject()) { + qDebug() << "Media upload: Response is not a json object."; + return; + } + + QJsonObject object = json.object(); + if (!object.contains("content_uri")) { + qDebug() << "Media upload: Missing content_uri key"; + qDebug() << object; + return; + } + + emit imageUploaded(roomid, filename, object.value("content_uri").toString()); + }); } void @@ -906,9 +737,31 @@ MatrixClient::joinRoom(const QString &roomIdOrAlias) QNetworkRequest request(endpoint); request.setHeader(QNetworkRequest::KnownHeaders::ContentTypeHeader, "application/json"); - QNetworkReply *reply = post(request, "{}"); - reply->setProperty("endpoint", static_cast(Endpoint::JoinRoom)); - reply->setProperty("room", roomIdOrAlias); + auto reply = post(request, "{}"); + connect(reply, &QNetworkReply::finished, this, [this, reply]() { + reply->deleteLater(); + + int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + + if (status == 0 || status >= 400) { + auto data = reply->readAll(); + auto response = QJsonDocument::fromJson(data); + auto json = response.object(); + + if (json.contains("error")) + emit joinFailed(json["error"].toString()); + else + qDebug() << reply->errorString(); + + return; + } + + auto data = reply->readAll(); + auto response = QJsonDocument::fromJson(data); + auto room_id = response.object()["room_id"].toString(); + + emit joinedRoom(room_id); + }); } void @@ -924,7 +777,18 @@ MatrixClient::leaveRoom(const QString &roomId) QNetworkRequest request(endpoint); request.setHeader(QNetworkRequest::KnownHeaders::ContentTypeHeader, "application/json"); - QNetworkReply *reply = post(request, "{}"); - reply->setProperty("room_id", roomId); - reply->setProperty("endpoint", static_cast(Endpoint::LeaveRoom)); + auto reply = post(request, "{}"); + + connect(reply, &QNetworkReply::finished, this, [this, reply, roomId]() { + reply->deleteLater(); + + int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + + if (status == 0 || status >= 400) { + qWarning() << reply->errorString(); + return; + } + + emit leftRoom(roomId); + }); } From c6e1068e0e1cb8ff5d5982ccd1683b1900dd7114 Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Mon, 23 Oct 2017 00:19:35 +0300 Subject: [PATCH 33/64] Lint --- include/RoomSettings.h | 5 +---- include/ui/LoadingIndicator.h | 20 ++++---------------- src/ChatPage.cc | 3 ++- src/EmojiItemDelegate.cc | 5 +---- src/ui/ThemeManager.cc | 5 +---- 5 files changed, 9 insertions(+), 29 deletions(-) diff --git a/include/RoomSettings.h b/include/RoomSettings.h index d9ecff99..d67e406a 100644 --- a/include/RoomSettings.h +++ b/include/RoomSettings.h @@ -35,10 +35,7 @@ public: settings.setValue(path_, isNotificationsEnabled_); }; - bool isNotificationsEnabled() - { - return isNotificationsEnabled_; - }; + bool isNotificationsEnabled() { return isNotificationsEnabled_; }; void toggleNotifications() { diff --git a/include/ui/LoadingIndicator.h b/include/ui/LoadingIndicator.h index 2641955a..75920dd8 100644 --- a/include/ui/LoadingIndicator.h +++ b/include/ui/LoadingIndicator.h @@ -19,23 +19,11 @@ public: void start(); void stop(); - QColor color() - { - return color_; - } - void setColor(QColor color) - { - color_ = color; - } + QColor color() { return color_; } + void setColor(QColor color) { color_ = color; } - int interval() - { - return interval_; - } - void setInterval(int interval) - { - interval_ = interval; - } + int interval() { return interval_; } + void setInterval(int interval) { interval_ = interval; } private slots: void onTimeout(); diff --git a/src/ChatPage.cc b/src/ChatPage.cc index 65fef9de..d087c3f3 100644 --- a/src/ChatPage.cc +++ b/src/ChatPage.cc @@ -32,6 +32,7 @@ #include "StateEvent.h" constexpr int MAX_INITIAL_SYNC_FAILURES = 5; +constexpr int SYNC_RETRY_TIMEOUT = 10000; namespace events = matrix::events; @@ -347,7 +348,7 @@ ChatPage::syncFailed(const QString &msg) return; qWarning() << "Sync error:" << msg; - client_->sync(); + QTimer::singleShot(SYNC_RETRY_TIMEOUT, this, [=]() { client_->sync(); }); } // TODO: Should be moved in another class that manages this global list. diff --git a/src/EmojiItemDelegate.cc b/src/EmojiItemDelegate.cc index 9fd4600d..691bee17 100644 --- a/src/EmojiItemDelegate.cc +++ b/src/EmojiItemDelegate.cc @@ -26,10 +26,7 @@ EmojiItemDelegate::EmojiItemDelegate(QObject *parent) data_ = new Emoji; } -EmojiItemDelegate::~EmojiItemDelegate() -{ - delete data_; -} +EmojiItemDelegate::~EmojiItemDelegate() { delete data_; } void EmojiItemDelegate::paint(QPainter *painter, diff --git a/src/ui/ThemeManager.cc b/src/ui/ThemeManager.cc index 172ddc41..7baed1f3 100644 --- a/src/ui/ThemeManager.cc +++ b/src/ui/ThemeManager.cc @@ -2,10 +2,7 @@ #include "ThemeManager.h" -ThemeManager::ThemeManager() -{ - setTheme(new Theme); -} +ThemeManager::ThemeManager() { setTheme(new Theme); } void ThemeManager::setTheme(Theme *theme) From 8e15a5080d0d2db77684e22d4cb7297a7aeff8f4 Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Fri, 27 Oct 2017 13:36:26 +0300 Subject: [PATCH 34/64] Adjust scrollbar only after pagination (#94) The scrollbar will stay in the same position if new messages are added to the bottom of the timeline. --- include/TimelineView.h | 2 ++ src/TimelineView.cc | 11 ++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/include/TimelineView.h b/include/TimelineView.h index 55f25687..400b0db0 100644 --- a/include/TimelineView.h +++ b/include/TimelineView.h @@ -155,6 +155,8 @@ private: int oldPosition_; int oldHeight_; + TimelineDirection lastMessageDirection_; + // The events currently rendered. Used for duplicate detection. QMap eventIds_; QList pending_msgs_; diff --git a/src/TimelineView.cc b/src/TimelineView.cc index 5463064f..2142f546 100644 --- a/src/TimelineView.cc +++ b/src/TimelineView.cc @@ -96,7 +96,8 @@ TimelineView::sliderRangeChanged(int min, int max) if (oldPosition_ == 0 && !scroll_area_->verticalScrollBar()->isVisible()) newPosition = max; - scroll_area_->verticalScrollBar()->setValue(newPosition); + if (lastMessageDirection_ == TimelineDirection::Top) + scroll_area_->verticalScrollBar()->setValue(newPosition); } void @@ -194,6 +195,8 @@ TimelineView::addBackwardsEvents(const QString &room_id, const RoomMessages &msg for (const auto &item : items) addTimelineItem(item, TimelineDirection::Top); + lastMessageDirection_ = TimelineDirection::Top; + QApplication::processEvents(); prev_batch_token_ = msgs.end(); @@ -349,6 +352,8 @@ TimelineView::addEvents(const Timeline &timeline) } } + lastMessageDirection_ = TimelineDirection::Bottom; + QApplication::processEvents(); if (isInitialSync) { @@ -490,6 +495,8 @@ TimelineView::addUserMessage(matrix::events::MessageEventType ty, const QString TimelineItem *view_item = new TimelineItem(ty, user_id, body, with_sender, scroll_widget_); scroll_layout_->addWidget(view_item); + lastMessageDirection_ = TimelineDirection::Bottom; + QApplication::processEvents(); lastSender_ = user_id; @@ -510,6 +517,8 @@ TimelineView::addUserMessage(const QString &url, const QString &filename, int tx TimelineItem *view_item = new TimelineItem(image, user_id, with_sender, scroll_widget_); scroll_layout_->addWidget(view_item); + lastMessageDirection_ = TimelineDirection::Bottom; + QApplication::processEvents(); lastSender_ = user_id; From 845228ac6ac549ec9af97ee0da3aaa71168f605e Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Fri, 27 Oct 2017 22:20:33 +0300 Subject: [PATCH 35/64] Add scroll-down button --- CMakeLists.txt | 2 + include/TimelineView.h | 4 + include/ui/FloatingButton.h | 26 ++++++ resources/icons/ui/angle-arrow-down.png | Bin 0 -> 698 bytes resources/icons/ui/angle-arrow-down@2x.png | Bin 0 -> 1013 bytes resources/res.qrc | 2 + src/TimelineView.cc | 23 +++++ src/ui/FloatingButton.cc | 95 +++++++++++++++++++++ 8 files changed, 152 insertions(+) create mode 100644 include/ui/FloatingButton.h create mode 100644 resources/icons/ui/angle-arrow-down.png create mode 100644 resources/icons/ui/angle-arrow-down@2x.png create mode 100644 src/ui/FloatingButton.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 6f8c167c..c15093ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -171,6 +171,7 @@ set(SRC_FILES src/ui/Badge.cc src/ui/LoadingIndicator.cc src/ui/FlatButton.cc + src/ui/FloatingButton.cc src/ui/Label.cc src/ui/OverlayModal.cc src/ui/ScrollBar.cc @@ -224,6 +225,7 @@ qt5_wrap_cpp(MOC_HEADERS include/EmojiItemDelegate.h include/EmojiPanel.h include/EmojiPickButton.h + include/ui/FloatingButton.h include/ImageItem.h include/ImageOverlayDialog.h include/JoinRoomDialog.h diff --git a/include/TimelineView.h b/include/TimelineView.h index 400b0db0..83247948 100644 --- a/include/TimelineView.h +++ b/include/TimelineView.h @@ -34,6 +34,8 @@ #include "RoomInfoListItem.h" #include "Text.h" +class FloatingButton; + namespace msgs = matrix::events::messages; namespace events = matrix::events; @@ -155,6 +157,8 @@ private: int oldPosition_; int oldHeight_; + FloatingButton *scrollDownBtn_; + TimelineDirection lastMessageDirection_; // The events currently rendered. Used for duplicate detection. diff --git a/include/ui/FloatingButton.h b/include/ui/FloatingButton.h new file mode 100644 index 00000000..91e99ebb --- /dev/null +++ b/include/ui/FloatingButton.h @@ -0,0 +1,26 @@ +#pragma once + +#include "RaisedButton.h" + +constexpr int DIAMETER = 40; +constexpr int ICON_SIZE = 18; + +constexpr int OFFSET_X = 30; +constexpr int OFFSET_Y = 20; + +class FloatingButton : public RaisedButton +{ + Q_OBJECT + +public: + FloatingButton(const QIcon &icon, QWidget *parent = nullptr); + + QSize sizeHint() const override { return QSize(DIAMETER, DIAMETER); }; + QRect buttonGeometry() const; + +protected: + bool event(QEvent *event) override; + bool eventFilter(QObject *obj, QEvent *event) override; + + void paintEvent(QPaintEvent *event) override; +}; diff --git a/resources/icons/ui/angle-arrow-down.png b/resources/icons/ui/angle-arrow-down.png new file mode 100644 index 0000000000000000000000000000000000000000..e40ebca54e3d7c27820e67ec65d850a3bafbfd2a GIT binary patch literal 698 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz#^NA%Cx&(BWL^R}oCO|{#S9D* zuRxe_zrLtDP(ey&NJL45ua8x7ey(0(N`6wRUPW#JP&EUCO@$SZnVVXYs8ErclUHn2 zVXFjIVFhG^g!Ppaz)DK8ZIvQ?0~DO|i&7O#^-S~(l1AfjnEK zjFOT9D}DX)@^Za$W4-*MbbUihOG|wNBYh(yU7!lx;>x^|#0uTKVr7USFmqf|i<65o z3raHc^AtelCMM;Vme?vOfh>Xps5^5D;1=Z-LwyDGpMFJRfxe-hfj-=1phg>@AFZ5= zQWHz^i$e1AbL>D)%goCzPEIUH1v$`0A6*61N*jHUJCNK3@ibTz=xaMJpiWo_+Hu`E zzAP6QsP8>p977^F-%i=ccgTRpWpmRH<|7PDo%zq5BN{q+?bzOF`Q=Usa>^>-v~=}k zldrq2LW8?H_*~e3@#z@Ed=1_%D4CMF@z@)ly9<-cj+Y&}l`_FKOy- zuchP`o=~x#%htUsGxw3S9^cKE3>$T$Lbe$NHcj>{6OWFu_31fy^30)IN8$@CHyT}J zncgYO7TlLC6r&bpoye4Ln#eW1^VZ{n#?wb`6u#NmDfRE+7x{DSry^7odplSvNn+hu+GdHy)QK2F?C$HG5 z!d3~a!V1U+3F|8Y3;nDA{o-C@9zzrKDK}xwt{K19`Se z86_nJR{Hwo<>h+i#(Mch>H3D2mX`VkM*2oZx=P7{9O-#x!EwNQn0$BtHPdNb z#z)V83oQOxC9D2Ttxw&08k_a&H^<&rKC&?B6*W#^JJTR(z?^n~X9gpPb3jb$2Csqq z!_C}B6d&ncSpRWxFwZZZk9|KJwoDWLVxwFqFuy~)<(m`RrU{)nDLi6jdz2~`9WlQ( zOSRxlih$g;l$gl^P1E%=rwTNQFB9cFZt&?*DXj)WYLgo6OB^{h#q)oyS?5c2CslTbg`fe}Q#UuGbE?D`6IDeY$34+xyR?{MTmx zFr@!%A2zTw{v|p1&muHTQk}UT5hS)V_`~TKMA3@1i0Vo}H0WsawA* z?(Pd#i~j;rrUx*zxI^-icons/ui/paper-clip-outline@2x.png icons/ui/angle-pointing-to-left.png icons/ui/angle-pointing-to-left@2x.png + icons/ui/angle-arrow-down.png + icons/ui/angle-arrow-down@2x.png icons/emoji-categories/people.png icons/emoji-categories/people@2x.png diff --git a/src/TimelineView.cc b/src/TimelineView.cc index 2142f546..13209062 100644 --- a/src/TimelineView.cc +++ b/src/TimelineView.cc @@ -27,6 +27,7 @@ #include "MessageEvent.h" #include "MessageEventContent.h" +#include "FloatingButton.h" #include "ImageItem.h" #include "TimelineItem.h" #include "TimelineView.h" @@ -140,6 +141,16 @@ TimelineView::sliderMoved(int position) if (!scroll_area_->verticalScrollBar()->isVisible()) return; + const int maxScroll = scroll_area_->verticalScrollBar()->maximum(); + const int currentScroll = scroll_area_->verticalScrollBar()->value(); + + if (maxScroll - currentScroll > SCROLL_BAR_GAP) { + scrollDownBtn_->show(); + scrollDownBtn_->raise(); + } else { + scrollDownBtn_->hide(); + } + // The scrollbar is high enough so we can start retrieving old events. if (position < SCROLL_BAR_GAP) { if (isTimelineFinished) @@ -376,6 +387,18 @@ TimelineView::init() QSettings settings; local_user_ = settings.value("auth/user_id").toString(); + QIcon icon; + icon.addFile(":/icons/icons/ui/angle-arrow-down.png"); + scrollDownBtn_ = new FloatingButton(icon, this); + scrollDownBtn_->setBackgroundColor(QColor("#F5F5F5")); + scrollDownBtn_->setForegroundColor(QColor("black")); + scrollDownBtn_->hide(); + + connect(scrollDownBtn_, &QPushButton::clicked, this, [=]() { + const int max = scroll_area_->verticalScrollBar()->maximum(); + scroll_area_->verticalScrollBar()->setValue(max); + }); + top_layout_ = new QVBoxLayout(this); top_layout_->setSpacing(0); top_layout_->setMargin(0); diff --git a/src/ui/FloatingButton.cc b/src/ui/FloatingButton.cc new file mode 100644 index 00000000..74dcd482 --- /dev/null +++ b/src/ui/FloatingButton.cc @@ -0,0 +1,95 @@ +#include + +#include "FloatingButton.h" + +FloatingButton::FloatingButton(const QIcon &icon, QWidget *parent) + : RaisedButton(parent) +{ + setFixedSize(DIAMETER, DIAMETER); + setGeometry(buttonGeometry()); + + if (parentWidget()) + parentWidget()->installEventFilter(this); + + setFixedRippleRadius(50); + setIcon(icon); + raise(); +} + +QRect +FloatingButton::buttonGeometry() const +{ + QWidget *parent = parentWidget(); + + if (!parent) + return QRect(); + + return QRect(parent->width() - (OFFSET_X + DIAMETER), + parent->height() - (OFFSET_Y + DIAMETER), + DIAMETER, + DIAMETER); +} + +bool +FloatingButton::event(QEvent *event) +{ + if (!parent()) + return RaisedButton::event(event); + + switch (event->type()) { + case QEvent::ParentChange: { + parent()->installEventFilter(this); + setGeometry(buttonGeometry()); + break; + } + case QEvent::ParentAboutToChange: { + parent()->installEventFilter(this); + break; + } + default: + break; + } + + return RaisedButton::event(event); +} + +bool +FloatingButton::eventFilter(QObject *obj, QEvent *event) +{ + const QEvent::Type type = event->type(); + + if (QEvent::Move == type || QEvent::Resize == type) + setGeometry(buttonGeometry()); + + return RaisedButton::eventFilter(obj, event); +} + +void +FloatingButton::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event); + + QRect square = QRect(0, 0, DIAMETER, DIAMETER); + square.moveCenter(rect().center()); + + QPainter p(this); + p.setRenderHints(QPainter::Antialiasing); + + QBrush brush; + brush.setStyle(Qt::SolidPattern); + brush.setColor(backgroundColor()); + + p.setBrush(brush); + p.setPen(Qt::NoPen); + p.drawEllipse(square); + + QRect iconGeometry(0, 0, ICON_SIZE, ICON_SIZE); + iconGeometry.moveCenter(square.center()); + + QPixmap pixmap = icon().pixmap(QSize(ICON_SIZE, ICON_SIZE)); + QPainter icon(&pixmap); + icon.setCompositionMode(QPainter::CompositionMode_SourceIn); + icon.fillRect(pixmap.rect(), foregroundColor()); + + p.drawPixmap(iconGeometry, pixmap); +} From a17b6dffafb450c48c1c1f5034e85763aa7a6a5e Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Sat, 28 Oct 2017 15:46:39 +0300 Subject: [PATCH 36/64] Clean up headers --- include/AvatarProvider.h | 5 ++--- include/Cache.h | 2 +- include/ChatPage.h | 30 +++++++++++++++++------------- include/Deserializable.h | 1 - include/EmojiCategory.h | 7 +++---- include/EmojiPanel.h | 4 ++-- include/EmojiPickButton.h | 3 ++- include/EmojiProvider.h | 2 -- include/ImageItem.h | 1 + include/InputValidator.h | 1 - include/JoinRoomDialog.h | 2 +- include/LeaveRoomDialog.h | 2 +- include/LoginPage.h | 15 +++++++-------- include/LogoutDialog.h | 2 +- include/MainWindow.h | 20 +++++++++++--------- include/MatrixClient.h | 9 +++++---- include/Profile.h | 2 +- include/Register.h | 2 +- include/RegisterPage.h | 14 ++++++-------- include/RoomInfoListItem.h | 7 ++++--- include/RoomList.h | 17 ++++++++++------- include/SideBarActions.h | 5 ++++- include/Sync.h | 2 -- include/TimelineItem.h | 8 +++----- include/TimelineView.h | 15 +++++++-------- include/TimelineViewManager.h | 14 +++++++------- include/TopRoomBar.h | 17 ++++++++--------- include/TrayIcon.h | 1 - include/UserInfoWidget.h | 14 ++++++-------- include/Versions.h | 2 +- include/WelcomePage.h | 4 +++- include/ui/FlatButton.h | 2 +- src/AvatarProvider.cc | 2 ++ src/Cache.cc | 2 +- src/ChatPage.cc | 14 ++++++++++++-- src/Deserializable.cc | 4 ---- src/EmojiCategory.cc | 1 - src/EmojiPanel.cc | 2 -- src/EmojiPickButton.cc | 3 +-- src/EmojiProvider.cc | 5 ----- src/ImageItem.cc | 1 - src/ImageOverlayDialog.cc | 3 --- src/JoinRoomDialog.cc | 1 + src/LeaveRoomDialog.cc | 1 + src/Login.cc | 4 ---- src/LoginPage.cc | 8 ++++++-- src/LogoutDialog.cc | 1 + src/MainWindow.cc | 16 ++++++++++++---- src/MatrixClient.cc | 3 +++ src/Profile.cc | 4 ---- src/QuickSwitcher.cc | 1 - src/Register.cc | 4 ---- src/RegisterPage.cc | 8 +++++--- src/RoomInfoListItem.cc | 5 +++-- src/RoomList.cc | 8 ++++++-- src/RoomState.cc | 1 - src/SideBarActions.cc | 3 +-- src/Sync.cc | 5 ----- src/TimelineItem.cc | 4 +++- src/TimelineView.cc | 13 ++++--------- src/TimelineViewManager.cc | 4 ++-- src/TopRoomBar.cc | 7 +++++++ src/TrayIcon.cc | 1 + src/TypingDisplay.cc | 1 - src/UserInfoWidget.cc | 4 +++- src/Versions.cc | 3 --- src/WelcomePage.cc | 1 + src/main.cc | 1 + src/ui/Avatar.cc | 2 -- src/ui/LoadingIndicator.cc | 1 - src/ui/OverlayModal.cc | 1 - 71 files changed, 193 insertions(+), 192 deletions(-) diff --git a/include/AvatarProvider.h b/include/AvatarProvider.h index 0a37ffb9..a58ef0b7 100644 --- a/include/AvatarProvider.h +++ b/include/AvatarProvider.h @@ -18,12 +18,11 @@ #pragma once #include -#include #include #include -#include "MatrixClient.h" -#include "TimelineItem.h" +class MatrixClient; +class TimelineItem; class AvatarProvider : public QObject { diff --git a/include/Cache.h b/include/Cache.h index 69d880f5..c16654d0 100644 --- a/include/Cache.h +++ b/include/Cache.h @@ -20,7 +20,7 @@ #include #include -#include "RoomState.h" +class RoomState; class Cache { diff --git a/include/ChatPage.h b/include/ChatPage.h index bac83ece..d3790f78 100644 --- a/include/ChatPage.h +++ b/include/ChatPage.h @@ -17,23 +17,27 @@ #pragma once +#include +#include #include #include #include -#include "Cache.h" -#include "MatrixClient.h" -#include "QuickSwitcher.h" -#include "RoomList.h" -#include "RoomSettings.h" -#include "RoomState.h" -#include "SideBarActions.h" -#include "Splitter.h" -#include "TextInputWidget.h" -#include "TimelineViewManager.h" -#include "TopRoomBar.h" -#include "TypingDisplay.h" -#include "UserInfoWidget.h" +class Cache; +class MatrixClient; +class OverlayModal; +class QuickSwitcher; +class RoomList; +class RoomSettings; +class RoomState; +class SideBarActions; +class Splitter; +class SyncResponse; +class TextInputWidget; +class TimelineViewManager; +class TopRoomBar; +class TypingDisplay; +class UserInfoWidget; constexpr int CONSENSUS_TIMEOUT = 1000; constexpr int SHOW_CONTENT_TIMEOUT = 3000; diff --git a/include/Deserializable.h b/include/Deserializable.h index 0d0b2882..0b97ce29 100644 --- a/include/Deserializable.h +++ b/include/Deserializable.h @@ -21,7 +21,6 @@ #include #include -#include class DeserializationException : public std::exception { diff --git a/include/EmojiCategory.h b/include/EmojiCategory.h index 154ad8f4..06099f3d 100644 --- a/include/EmojiCategory.h +++ b/include/EmojiCategory.h @@ -17,15 +17,14 @@ #pragma once -#include #include +#include #include #include -#include -#include #include "EmojiItemDelegate.h" -#include "EmojiProvider.h" + +class EmojiProvider; class EmojiCategory : public QWidget { diff --git a/include/EmojiPanel.h b/include/EmojiPanel.h index 360e7006..211d916b 100644 --- a/include/EmojiPanel.h +++ b/include/EmojiPanel.h @@ -20,11 +20,11 @@ #include #include #include -#include -#include "EmojiCategory.h" #include "EmojiProvider.h" +class EmojiCategory; + class EmojiPanel : public QWidget { Q_OBJECT diff --git a/include/EmojiPickButton.h b/include/EmojiPickButton.h index d4226165..8ef9be9e 100644 --- a/include/EmojiPickButton.h +++ b/include/EmojiPickButton.h @@ -20,9 +20,10 @@ #include #include -#include "EmojiPanel.h" #include "FlatButton.h" +class EmojiPanel; + class EmojiPickButton : public FlatButton { Q_OBJECT diff --git a/include/EmojiProvider.h b/include/EmojiProvider.h index 3f91f2b3..847157fd 100644 --- a/include/EmojiProvider.h +++ b/include/EmojiProvider.h @@ -17,9 +17,7 @@ #pragma once -#include #include -#include struct Emoji { diff --git a/include/ImageItem.h b/include/ImageItem.h index 20e0772d..c4f6998a 100644 --- a/include/ImageItem.h +++ b/include/ImageItem.h @@ -24,6 +24,7 @@ #include "Image.h" #include "MatrixClient.h" +#include "MessageEvent.h" namespace events = matrix::events; namespace msgs = matrix::events::messages; diff --git a/include/InputValidator.h b/include/InputValidator.h index 4f77033e..da1c121e 100644 --- a/include/InputValidator.h +++ b/include/InputValidator.h @@ -17,7 +17,6 @@ #pragma once -#include #include class InputValidator diff --git a/include/JoinRoomDialog.h b/include/JoinRoomDialog.h index 6c3fbdcf..84184733 100644 --- a/include/JoinRoomDialog.h +++ b/include/JoinRoomDialog.h @@ -3,7 +3,7 @@ #include #include -#include "FlatButton.h" +class FlatButton; class JoinRoomDialog : public QFrame { diff --git a/include/LeaveRoomDialog.h b/include/LeaveRoomDialog.h index 1639a578..231556dc 100644 --- a/include/LeaveRoomDialog.h +++ b/include/LeaveRoomDialog.h @@ -2,7 +2,7 @@ #include -#include "FlatButton.h" +class FlatButton; class LeaveRoomDialog : public QFrame { diff --git a/include/LoginPage.h b/include/LoginPage.h index 88cffaf3..d5f46f76 100644 --- a/include/LoginPage.h +++ b/include/LoginPage.h @@ -17,18 +17,17 @@ #pragma once -#include #include +#include #include -#include #include -#include "FlatButton.h" -#include "LoadingIndicator.h" -#include "MatrixClient.h" -#include "OverlayModal.h" -#include "RaisedButton.h" -#include "TextField.h" +class FlatButton; +class LoadingIndicator; +class MatrixClient; +class OverlayModal; +class RaisedButton; +class TextField; class LoginPage : public QWidget { diff --git a/include/LogoutDialog.h b/include/LogoutDialog.h index 44dab17a..e081986e 100644 --- a/include/LogoutDialog.h +++ b/include/LogoutDialog.h @@ -19,7 +19,7 @@ #include -#include "FlatButton.h" +class FlatButton; class LogoutDialog : public QFrame { diff --git a/include/MainWindow.h b/include/MainWindow.h index 95935c46..f56592c2 100644 --- a/include/MainWindow.h +++ b/include/MainWindow.h @@ -19,16 +19,18 @@ #include #include +#include +#include -#include "ChatPage.h" -#include "LoadingIndicator.h" -#include "LoginPage.h" -#include "MatrixClient.h" -#include "OverlayModal.h" -#include "RegisterPage.h" -#include "SnackBar.h" -#include "TrayIcon.h" -#include "WelcomePage.h" +class ChatPage; +class LoadingIndicator; +class LoginPage; +class MatrixClient; +class OverlayModal; +class RegisterPage; +class SnackBar; +class TrayIcon; +class WelcomePage; class MainWindow : public QMainWindow { diff --git a/include/MatrixClient.h b/include/MatrixClient.h index 7a415e82..2e45e397 100644 --- a/include/MatrixClient.h +++ b/include/MatrixClient.h @@ -17,12 +17,13 @@ #pragma once -#include +#include #include "MessageEvent.h" -#include "Profile.h" -#include "RoomMessages.h" -#include "Sync.h" + +class SyncResponse; +class Profile; +class RoomMessages; /* * MatrixClient provides the high level API to communicate with diff --git a/include/Profile.h b/include/Profile.h index e2868e62..d640db02 100644 --- a/include/Profile.h +++ b/include/Profile.h @@ -20,7 +20,7 @@ #include #include -#include "Deserializable.h" +class Deserializable; class ProfileResponse : public Deserializable { diff --git a/include/Register.h b/include/Register.h index 83f538b9..fc4a49e6 100644 --- a/include/Register.h +++ b/include/Register.h @@ -19,7 +19,7 @@ #include -#include "Deserializable.h" +class Deserializable; class RegisterRequest { diff --git a/include/RegisterPage.h b/include/RegisterPage.h index a42cbedd..e0a3b6d8 100644 --- a/include/RegisterPage.h +++ b/include/RegisterPage.h @@ -17,17 +17,15 @@ #pragma once -#include #include +#include #include -#include -#include -#include "Avatar.h" -#include "FlatButton.h" -#include "MatrixClient.h" -#include "RaisedButton.h" -#include "TextField.h" +class Avatar; +class FlatButton; +class MatrixClient; +class RaisedButton; +class TextField; class RegisterPage : public QWidget { diff --git a/include/RoomInfoListItem.h b/include/RoomInfoListItem.h index 8975af79..a137b37f 100644 --- a/include/RoomInfoListItem.h +++ b/include/RoomInfoListItem.h @@ -21,11 +21,12 @@ #include #include -#include "Menu.h" -#include "RippleOverlay.h" -#include "RoomSettings.h" #include "RoomState.h" +class Menu; +class RippleOverlay; +class RoomSettings; + struct DescInfo { QString username; diff --git a/include/RoomList.h b/include/RoomList.h index d7b201f1..df668ac6 100644 --- a/include/RoomList.h +++ b/include/RoomList.h @@ -17,19 +17,22 @@ #pragma once +#include #include #include #include #include #include -#include "JoinRoomDialog.h" -#include "LeaveRoomDialog.h" -#include "MatrixClient.h" -#include "OverlayModal.h" -#include "RoomInfoListItem.h" -#include "RoomState.h" -#include "Sync.h" +class JoinRoomDialog; +class LeaveRoomDialog; +class MatrixClient; +class OverlayModal; +class RoomInfoListItem; +class RoomSettings; +class RoomState; +class Sync; +struct DescInfo; class RoomList : public QWidget { diff --git a/include/SideBarActions.h b/include/SideBarActions.h index 60975a03..7b550578 100644 --- a/include/SideBarActions.h +++ b/include/SideBarActions.h @@ -1,7 +1,10 @@ +#pragma once + #include #include +#include -#include +#include "FlatButton.h" class SideBarActions : public QWidget { diff --git a/include/Sync.h b/include/Sync.h index ae61015e..d59a57dc 100644 --- a/include/Sync.h +++ b/include/Sync.h @@ -18,9 +18,7 @@ #pragma once #include -#include #include -#include #include "Deserializable.h" diff --git a/include/TimelineItem.h b/include/TimelineItem.h index ef021cfe..1adf574c 100644 --- a/include/TimelineItem.h +++ b/include/TimelineItem.h @@ -19,12 +19,7 @@ #include #include -#include -#include "ImageItem.h" -#include "Sync.h" - -#include "Avatar.h" #include "Emote.h" #include "Image.h" #include "MessageEvent.h" @@ -32,6 +27,9 @@ #include "RoomInfoListItem.h" #include "Text.h" +class ImageItem; +class Avatar; + namespace events = matrix::events; namespace msgs = matrix::events::messages; diff --git a/include/TimelineView.h b/include/TimelineView.h index 83247948..7e44db46 100644 --- a/include/TimelineView.h +++ b/include/TimelineView.h @@ -17,24 +17,23 @@ #pragma once -#include +#include #include #include -#include -#include - -#include "ScrollBar.h" -#include "Sync.h" -#include "TimelineItem.h" #include "Emote.h" #include "Image.h" #include "MessageEvent.h" #include "Notice.h" -#include "RoomInfoListItem.h" #include "Text.h" class FloatingButton; +class MatrixClient; +class RoomMessages; +class ScrollBar; +class Timeline; +class TimelineItem; +struct DescInfo; namespace msgs = matrix::events::messages; namespace events = matrix::events; diff --git a/include/TimelineViewManager.h b/include/TimelineViewManager.h index 91fda996..8ff49f20 100644 --- a/include/TimelineViewManager.h +++ b/include/TimelineViewManager.h @@ -17,16 +17,16 @@ #pragma once -#include +#include #include #include -#include -#include "MatrixClient.h" -#include "MessageEvent.h" -#include "RoomInfoListItem.h" -#include "Sync.h" -#include "TimelineView.h" +class JoinedRoom; +class MatrixClient; +class RoomInfoListItem; +class Rooms; +class TimelineView; +struct DescInfo; class TimelineViewManager : public QStackedWidget { diff --git a/include/TopRoomBar.h b/include/TopRoomBar.h index eb941b3b..f1e93d9d 100644 --- a/include/TopRoomBar.h +++ b/include/TopRoomBar.h @@ -18,22 +18,21 @@ #pragma once #include -#include #include #include #include +#include #include #include #include -#include -#include "Avatar.h" -#include "FlatButton.h" -#include "Label.h" -#include "LeaveRoomDialog.h" -#include "Menu.h" -#include "OverlayModal.h" -#include "RoomSettings.h" +class Avatar; +class FlatButton; +class Label; +class LeaveRoomDialog; +class Menu; +class OverlayModal; +class RoomSettings; static const QString URL_HTML = "\\1"; static const QRegExp URL_REGEX("((?:https?|ftp)://\\S+)"); diff --git a/include/TrayIcon.h b/include/TrayIcon.h index 6073ea69..a3536cc3 100644 --- a/include/TrayIcon.h +++ b/include/TrayIcon.h @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include diff --git a/include/UserInfoWidget.h b/include/UserInfoWidget.h index b2c7b876..111f5808 100644 --- a/include/UserInfoWidget.h +++ b/include/UserInfoWidget.h @@ -17,15 +17,13 @@ #pragma once -#include -#include -#include -#include +#include +#include -#include "Avatar.h" -#include "FlatButton.h" -#include "LogoutDialog.h" -#include "OverlayModal.h" +class Avatar; +class FlatButton; +class LogoutDialog; +class OverlayModal; class UserInfoWidget : public QWidget { diff --git a/include/Versions.h b/include/Versions.h index 31d8af82..a603e391 100644 --- a/include/Versions.h +++ b/include/Versions.h @@ -20,7 +20,7 @@ #include #include -#include "Deserializable.h" +class Deserializable; class VersionsResponse : public Deserializable { diff --git a/include/WelcomePage.h b/include/WelcomePage.h index 7cd83fd1..73b612a8 100644 --- a/include/WelcomePage.h +++ b/include/WelcomePage.h @@ -17,7 +17,9 @@ #pragma once -#include "RaisedButton.h" +#include + +class RaisedButton; class WelcomePage : public QWidget { diff --git a/include/ui/FlatButton.h b/include/ui/FlatButton.h index 816563e3..9c2bf425 100644 --- a/include/ui/FlatButton.h +++ b/include/ui/FlatButton.h @@ -5,9 +5,9 @@ #include #include -#include "RippleOverlay.h" #include "Theme.h" +class RippleOverlay; class FlatButton; class FlatButtonStateMachine : public QStateMachine diff --git a/src/AvatarProvider.cc b/src/AvatarProvider.cc index c4e5d19e..ab938cb1 100644 --- a/src/AvatarProvider.cc +++ b/src/AvatarProvider.cc @@ -16,6 +16,8 @@ */ #include "AvatarProvider.h" +#include "MatrixClient.h" +#include "TimelineItem.h" QSharedPointer AvatarProvider::client_; diff --git a/src/Cache.cc b/src/Cache.cc index befb3a0a..de2c7944 100644 --- a/src/Cache.cc +++ b/src/Cache.cc @@ -18,12 +18,12 @@ #include #include -#include #include #include #include "Cache.h" #include "MemberEventContent.h" +#include "RoomState.h" namespace events = matrix::events; diff --git a/src/ChatPage.cc b/src/ChatPage.cc index d087c3f3..150f6007 100644 --- a/src/ChatPage.cc +++ b/src/ChatPage.cc @@ -21,16 +21,26 @@ #include #include "AvatarProvider.h" +#include "Cache.h" #include "ChatPage.h" #include "MainWindow.h" +#include "MatrixClient.h" +#include "OverlayModal.h" +#include "QuickSwitcher.h" +#include "RoomList.h" +#include "RoomSettings.h" +#include "RoomState.h" +#include "SideBarActions.h" #include "Splitter.h" +#include "StateEvent.h" #include "Sync.h" +#include "TextInputWidget.h" #include "Theme.h" #include "TimelineViewManager.h" +#include "TopRoomBar.h" +#include "TypingDisplay.h" #include "UserInfoWidget.h" -#include "StateEvent.h" - constexpr int MAX_INITIAL_SYNC_FAILURES = 5; constexpr int SYNC_RETRY_TIMEOUT = 10000; diff --git a/src/Deserializable.cc b/src/Deserializable.cc index 6033f898..8bdbfc2c 100644 --- a/src/Deserializable.cc +++ b/src/Deserializable.cc @@ -15,10 +15,6 @@ * along with this program. If not, see . */ -#include -#include -#include - #include "Deserializable.h" DeserializationException::DeserializationException(const std::string &msg) diff --git a/src/EmojiCategory.cc b/src/EmojiCategory.cc index ea32ea9c..8546b807 100644 --- a/src/EmojiCategory.cc +++ b/src/EmojiCategory.cc @@ -15,7 +15,6 @@ * along with this program. If not, see . */ -#include #include #include "Config.h" diff --git a/src/EmojiPanel.cc b/src/EmojiPanel.cc index 16299ace..71e2526c 100644 --- a/src/EmojiPanel.cc +++ b/src/EmojiPanel.cc @@ -16,11 +16,9 @@ */ #include -#include #include #include -#include "Avatar.h" #include "DropShadow.h" #include "EmojiCategory.h" #include "EmojiPanel.h" diff --git a/src/EmojiPickButton.cc b/src/EmojiPickButton.cc index 06d97d4c..17716f6f 100644 --- a/src/EmojiPickButton.cc +++ b/src/EmojiPickButton.cc @@ -15,8 +15,7 @@ * along with this program. If not, see . */ -#include - +#include "EmojiPanel.h" #include "EmojiPickButton.h" EmojiPickButton::EmojiPickButton(QWidget *parent) diff --git a/src/EmojiProvider.cc b/src/EmojiProvider.cc index 11cdffcb..ed380ae0 100644 --- a/src/EmojiProvider.cc +++ b/src/EmojiProvider.cc @@ -16,11 +16,6 @@ */ #include -#include -#include -#include -#include -#include #include "EmojiProvider.h" diff --git a/src/ImageItem.cc b/src/ImageItem.cc index 5e95f8b2..39fa630f 100644 --- a/src/ImageItem.cc +++ b/src/ImageItem.cc @@ -19,7 +19,6 @@ #include #include #include -#include #include #include diff --git a/src/ImageOverlayDialog.cc b/src/ImageOverlayDialog.cc index 56b5707c..8d6db45e 100644 --- a/src/ImageOverlayDialog.cc +++ b/src/ImageOverlayDialog.cc @@ -16,11 +16,8 @@ */ #include -#include #include #include -#include -#include #include "ImageOverlayDialog.h" diff --git a/src/JoinRoomDialog.cc b/src/JoinRoomDialog.cc index c3ee289e..d071876a 100644 --- a/src/JoinRoomDialog.cc +++ b/src/JoinRoomDialog.cc @@ -2,6 +2,7 @@ #include #include "Config.h" +#include "FlatButton.h" #include "JoinRoomDialog.h" #include "Theme.h" diff --git a/src/LeaveRoomDialog.cc b/src/LeaveRoomDialog.cc index f7669f0d..cd4a3260 100644 --- a/src/LeaveRoomDialog.cc +++ b/src/LeaveRoomDialog.cc @@ -2,6 +2,7 @@ #include #include "Config.h" +#include "FlatButton.h" #include "LeaveRoomDialog.h" #include "Theme.h" diff --git a/src/Login.cc b/src/Login.cc index 16c6f172..69338f2c 100644 --- a/src/Login.cc +++ b/src/Login.cc @@ -15,10 +15,6 @@ * along with this program. If not, see . */ -#include -#include -#include - #include "Deserializable.h" #include "Login.h" diff --git a/src/LoginPage.cc b/src/LoginPage.cc index bbe2a134..c1ba352c 100644 --- a/src/LoginPage.cc +++ b/src/LoginPage.cc @@ -15,11 +15,15 @@ * along with this program. If not, see . */ -#include - #include "Config.h" +#include "FlatButton.h" #include "InputValidator.h" +#include "LoadingIndicator.h" #include "LoginPage.h" +#include "MatrixClient.h" +#include "OverlayModal.h" +#include "RaisedButton.h" +#include "TextField.h" LoginPage::LoginPage(QSharedPointer client, QWidget *parent) : QWidget(parent) diff --git a/src/LogoutDialog.cc b/src/LogoutDialog.cc index 768efcd3..7f2cdbd3 100644 --- a/src/LogoutDialog.cc +++ b/src/LogoutDialog.cc @@ -19,6 +19,7 @@ #include #include "Config.h" +#include "FlatButton.h" #include "LogoutDialog.h" #include "Theme.h" diff --git a/src/MainWindow.cc b/src/MainWindow.cc index b6033eaf..92388ae4 100644 --- a/src/MainWindow.cc +++ b/src/MainWindow.cc @@ -15,15 +15,23 @@ * along with this program. If not, see . */ -#include "MainWindow.h" -#include "Config.h" - #include #include #include #include #include -#include + +#include "ChatPage.h" +#include "Config.h" +#include "LoadingIndicator.h" +#include "LoginPage.h" +#include "MainWindow.h" +#include "MatrixClient.h" +#include "OverlayModal.h" +#include "RegisterPage.h" +#include "SnackBar.h" +#include "TrayIcon.h" +#include "WelcomePage.h" MainWindow *MainWindow::instance_ = nullptr; diff --git a/src/MatrixClient.cc b/src/MatrixClient.cc index afd99995..b1854dd8 100644 --- a/src/MatrixClient.cc +++ b/src/MatrixClient.cc @@ -30,8 +30,11 @@ #include "Login.h" #include "MatrixClient.h" +#include "MessageEvent.h" #include "Profile.h" #include "Register.h" +#include "RoomMessages.h" +#include "Sync.h" #include "Versions.h" MatrixClient::MatrixClient(QString server, QObject *parent) diff --git a/src/Profile.cc b/src/Profile.cc index dcd9c7a1..38795e49 100644 --- a/src/Profile.cc +++ b/src/Profile.cc @@ -15,10 +15,6 @@ * along with this program. If not, see . */ -#include -#include -#include - #include "Deserializable.h" #include "Profile.h" diff --git a/src/QuickSwitcher.cc b/src/QuickSwitcher.cc index 5b459eaf..542eebd9 100644 --- a/src/QuickSwitcher.cc +++ b/src/QuickSwitcher.cc @@ -16,7 +16,6 @@ */ #include -#include #include #include diff --git a/src/Register.cc b/src/Register.cc index db3ce4f9..0fe2ddce 100644 --- a/src/Register.cc +++ b/src/Register.cc @@ -15,10 +15,6 @@ * along with this program. If not, see . */ -#include -#include -#include - #include "Deserializable.h" #include "Register.h" diff --git a/src/RegisterPage.cc b/src/RegisterPage.cc index bcd02be0..d8186f65 100644 --- a/src/RegisterPage.cc +++ b/src/RegisterPage.cc @@ -15,12 +15,14 @@ * along with this program. If not, see . */ -#include -#include - +#include "Avatar.h" #include "Config.h" +#include "FlatButton.h" #include "InputValidator.h" +#include "MatrixClient.h" +#include "RaisedButton.h" #include "RegisterPage.h" +#include "TextField.h" RegisterPage::RegisterPage(QSharedPointer client, QWidget *parent) : QWidget(parent) diff --git a/src/RoomInfoListItem.cc b/src/RoomInfoListItem.cc index 588ee5c8..857189b5 100644 --- a/src/RoomInfoListItem.cc +++ b/src/RoomInfoListItem.cc @@ -15,14 +15,15 @@ * along with this program. If not, see . */ -#include #include #include #include "Config.h" +#include "Menu.h" #include "Ripple.h" +#include "RippleOverlay.h" #include "RoomInfoListItem.h" -#include "RoomState.h" +#include "RoomSettings.h" #include "Theme.h" RoomInfoListItem::RoomInfoListItem(QSharedPointer settings, diff --git a/src/RoomList.cc b/src/RoomList.cc index 90d8b83c..73e85ea8 100644 --- a/src/RoomList.cc +++ b/src/RoomList.cc @@ -16,12 +16,16 @@ */ #include -#include -#include +#include "JoinRoomDialog.h" +#include "LeaveRoomDialog.h" #include "MainWindow.h" +#include "MatrixClient.h" +#include "OverlayModal.h" #include "RoomInfoListItem.h" #include "RoomList.h" +#include "RoomSettings.h" +#include "RoomState.h" #include "Sync.h" RoomList::RoomList(QSharedPointer client, QWidget *parent) diff --git a/src/RoomState.cc b/src/RoomState.cc index de0ed6ea..8db9b2bc 100644 --- a/src/RoomState.cc +++ b/src/RoomState.cc @@ -15,7 +15,6 @@ * along with this program. If not, see . */ -#include #include #include diff --git a/src/SideBarActions.cc b/src/SideBarActions.cc index d4874c6a..1484bd00 100644 --- a/src/SideBarActions.cc +++ b/src/SideBarActions.cc @@ -1,9 +1,8 @@ -#include #include #include "Config.h" +#include "SideBarActions.h" #include "Theme.h" -#include SideBarActions::SideBarActions(QWidget *parent) : QWidget{ parent } diff --git a/src/Sync.cc b/src/Sync.cc index 39d84acb..965f7c3d 100644 --- a/src/Sync.cc +++ b/src/Sync.cc @@ -16,12 +16,7 @@ */ #include -#include -#include -#include -#include -#include "Deserializable.h" #include "Sync.h" void diff --git a/src/TimelineItem.cc b/src/TimelineItem.cc index 28cabc12..ba9e54bd 100644 --- a/src/TimelineItem.cc +++ b/src/TimelineItem.cc @@ -16,14 +16,16 @@ */ #include -#include #include #include +#include #include +#include "Avatar.h" #include "AvatarProvider.h" #include "Config.h" #include "ImageItem.h" +#include "Sync.h" #include "TimelineItem.h" #include "TimelineViewManager.h" diff --git a/src/TimelineView.cc b/src/TimelineView.cc index 13209062..ee98fe72 100644 --- a/src/TimelineView.cc +++ b/src/TimelineView.cc @@ -17,21 +17,16 @@ #include #include -#include -#include #include -#include -#include - -#include "Event.h" -#include "MessageEvent.h" -#include "MessageEventContent.h" +#include #include "FloatingButton.h" #include "ImageItem.h" +#include "RoomMessages.h" +#include "ScrollBar.h" +#include "Sync.h" #include "TimelineItem.h" #include "TimelineView.h" -#include "TimelineViewManager.h" namespace events = matrix::events; namespace msgs = matrix::events::messages; diff --git a/src/TimelineViewManager.cc b/src/TimelineViewManager.cc index 9f8137fc..44b626ed 100644 --- a/src/TimelineViewManager.cc +++ b/src/TimelineViewManager.cc @@ -21,9 +21,9 @@ #include #include #include -#include -#include +#include "MatrixClient.h" +#include "Sync.h" #include "TimelineView.h" #include "TimelineViewManager.h" diff --git a/src/TopRoomBar.cc b/src/TopRoomBar.cc index 72aef92a..0361bd47 100644 --- a/src/TopRoomBar.cc +++ b/src/TopRoomBar.cc @@ -17,8 +17,15 @@ #include +#include "Avatar.h" #include "Config.h" +#include "FlatButton.h" +#include "Label.h" +#include "LeaveRoomDialog.h" #include "MainWindow.h" +#include "Menu.h" +#include "OverlayModal.h" +#include "RoomSettings.h" #include "TopRoomBar.h" TopRoomBar::TopRoomBar(QWidget *parent) diff --git a/src/TrayIcon.cc b/src/TrayIcon.cc index 1360cc4e..ad644ed9 100644 --- a/src/TrayIcon.cc +++ b/src/TrayIcon.cc @@ -17,6 +17,7 @@ #include #include +#include #include #include "TrayIcon.h" diff --git a/src/TypingDisplay.cc b/src/TypingDisplay.cc index 7792d3e4..e3eb9db9 100644 --- a/src/TypingDisplay.cc +++ b/src/TypingDisplay.cc @@ -1,4 +1,3 @@ -#include #include #include diff --git a/src/UserInfoWidget.cc b/src/UserInfoWidget.cc index 04cfec74..1fadc8e7 100644 --- a/src/UserInfoWidget.cc +++ b/src/UserInfoWidget.cc @@ -15,12 +15,14 @@ * along with this program. If not, see . */ -#include #include +#include "Avatar.h" #include "Config.h" #include "FlatButton.h" +#include "LogoutDialog.h" #include "MainWindow.h" +#include "OverlayModal.h" #include "UserInfoWidget.h" UserInfoWidget::UserInfoWidget(QWidget *parent) diff --git a/src/Versions.cc b/src/Versions.cc index 8d0e2e9c..9de1aafe 100644 --- a/src/Versions.cc +++ b/src/Versions.cc @@ -16,9 +16,6 @@ */ #include -#include -#include -#include #include #include "Deserializable.h" diff --git a/src/WelcomePage.cc b/src/WelcomePage.cc index 1fc0c19b..f361ca05 100644 --- a/src/WelcomePage.cc +++ b/src/WelcomePage.cc @@ -19,6 +19,7 @@ #include #include "Config.h" +#include "RaisedButton.h" #include "WelcomePage.h" WelcomePage::WelcomePage(QWidget *parent) diff --git a/src/main.cc b/src/main.cc index fa89cc60..2d059788 100644 --- a/src/main.cc +++ b/src/main.cc @@ -19,6 +19,7 @@ #include #include #include +#include #include #include "MainWindow.h" diff --git a/src/ui/Avatar.cc b/src/ui/Avatar.cc index c2ee629b..e3987e7a 100644 --- a/src/ui/Avatar.cc +++ b/src/ui/Avatar.cc @@ -1,6 +1,4 @@ -#include #include -#include #include "Avatar.h" diff --git a/src/ui/LoadingIndicator.cc b/src/ui/LoadingIndicator.cc index 0fafaf23..71312d32 100644 --- a/src/ui/LoadingIndicator.cc +++ b/src/ui/LoadingIndicator.cc @@ -1,6 +1,5 @@ #include "LoadingIndicator.h" -#include #include #include diff --git a/src/ui/OverlayModal.cc b/src/ui/OverlayModal.cc index 1da009d4..05bd7d03 100644 --- a/src/ui/OverlayModal.cc +++ b/src/ui/OverlayModal.cc @@ -15,7 +15,6 @@ * along with this program. If not, see . */ -#include #include #include From c18a49915b4b98ac7f837a1feca3e243ac44940d Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Sat, 28 Oct 2017 20:46:34 +0300 Subject: [PATCH 37/64] Save the changes between syncs in cache - Fixes high cpu issues caused by the serialization of the whole in-memory state. - Display name changes are now visible in the timeline. --- include/ChatPage.h | 24 ++- include/RoomState.h | 6 +- src/Cache.cc | 2 +- src/ChatPage.cc | 297 +++++++++++++++++--------- src/RoomList.cc | 6 +- src/RoomState.cc | 3 + src/Sync.cc | 4 +- src/TimelineView.cc | 2 +- src/TimelineViewManager.cc | 4 +- src/events/PowerLevelsEventContent.cc | 8 +- src/ui/LoadingIndicator.cc | 2 +- src/ui/RippleOverlay.cc | 2 +- 12 files changed, 242 insertions(+), 118 deletions(-) diff --git a/include/ChatPage.h b/include/ChatPage.h index d3790f78..416f7870 100644 --- a/include/ChatPage.h +++ b/include/ChatPage.h @@ -23,6 +23,10 @@ #include #include +#include "MemberEventContent.h" +#include "MessageEvent.h" +#include "StateEvent.h" + class Cache; class MatrixClient; class OverlayModal; @@ -38,6 +42,8 @@ class TimelineViewManager; class TopRoomBar; class TypingDisplay; class UserInfoWidget; +class JoinedRoom; +class LeftRoom; constexpr int CONSENSUS_TIMEOUT = 1000; constexpr int SHOW_CONTENT_TIMEOUT = 3000; @@ -76,8 +82,24 @@ private slots: void removeRoom(const QString &room_id); private: + using UserID = QString; + using RoomStates = QMap; + using JoinedRooms = QMap; + using LeftRooms = QMap; + using Membership = matrix::events::StateEvent; + using Memberships = QMap; + + void removeLeftRooms(const LeftRooms &rooms); + void updateJoinedRooms(const JoinedRooms &rooms); + + Memberships getMemberships(const QJsonArray &events) const; + RoomStates generateMembershipDifference(const JoinedRooms &rooms, + const RoomStates &states) const; + void updateTypingUsers(const QString &roomid, const QList &user_ids); - void updateDisplayNames(const RoomState &state); + void updateUserMetadata(const QJsonArray &events); + void updateUserDisplayName(const Membership &event); + void updateUserAvatarUrl(const Membership &event); void loadStateFromCache(); void deleteConfigs(); void resetUI(); diff --git a/include/RoomState.h b/include/RoomState.h index 57955e56..db1cdc68 100644 --- a/include/RoomState.h +++ b/include/RoomState.h @@ -41,6 +41,9 @@ namespace events = matrix::events; class RoomState { public: + RoomState(); + RoomState(const QJsonArray &events); + // Calculate room data that are not immediatly accessible. Like room name and // avatar. // @@ -71,7 +74,8 @@ public: events::StateEvent topic; // Contains the m.room.member events for all the joined users. - QMap> memberships; + using UserID = QString; + QMap> memberships; private: QUrl avatar_; diff --git a/src/Cache.cc b/src/Cache.cc index de2c7944..99267343 100644 --- a/src/Cache.cc +++ b/src/Cache.cc @@ -107,7 +107,7 @@ Cache::setState(const QString &nextBatchToken, const QMap &s setNextBatchToken(txn, nextBatchToken); - for (auto it = states.constBegin(); it != states.constEnd(); it++) + for (auto it = states.constBegin(); it != states.constEnd(); ++it) insertRoomState(txn, it.key(), it.value()); txn.commit(); diff --git a/src/ChatPage.cc b/src/ChatPage.cc index 150f6007..a756a0f9 100644 --- a/src/ChatPage.cc +++ b/src/ChatPage.cc @@ -32,7 +32,6 @@ #include "RoomState.h" #include "SideBarActions.h" #include "Splitter.h" -#include "StateEvent.h" #include "Sync.h" #include "TextInputWidget.h" #include "Theme.h" @@ -361,95 +360,19 @@ ChatPage::syncFailed(const QString &msg) QTimer::singleShot(SYNC_RETRY_TIMEOUT, this, [=]() { client_->sync(); }); } -// TODO: Should be moved in another class that manages this global list. -void -ChatPage::updateDisplayNames(const RoomState &state) -{ - for (const auto member : state.memberships) { - auto displayName = member.content().displayName(); - - if (!displayName.isEmpty()) - TimelineViewManager::DISPLAY_NAMES.insert(member.stateKey(), displayName); - } -} - void ChatPage::syncCompleted(const SyncResponse &response) { - auto joined = response.rooms().join(); + updateJoinedRooms(response.rooms().join()); + removeLeftRooms(response.rooms().leave()); - for (auto it = joined.constBegin(); it != joined.constEnd(); it++) { - updateTypingUsers(it.key(), it.value().typingUserIDs()); - - RoomState room_state; - - // Merge the new updates for rooms that we are tracking. - if (state_manager_.contains(it.key())) { - room_state = state_manager_[it.key()]; - } - - room_state.updateFromEvents(it.value().state().events()); - room_state.updateFromEvents(it.value().timeline().events()); - - updateDisplayNames(room_state); - - if (state_manager_.contains(it.key())) { - // TODO: Use pointers instead of copying. - auto oldState = state_manager_[it.key()]; - oldState.update(room_state); - state_manager_.insert(it.key(), oldState); - } else { - RoomState room_state; - - // Build the current state from the timeline and state events. - room_state.updateFromEvents(it.value().state().events()); - room_state.updateFromEvents(it.value().timeline().events()); - - // Remove redundant memberships. - room_state.removeLeaveMemberships(); - - // Resolve room name and avatar. e.g in case of one-to-one chats. - room_state.resolveName(); - room_state.resolveAvatar(); - - updateDisplayNames(room_state); - - state_manager_.insert(it.key(), room_state); - settingsManager_.insert( - it.key(), QSharedPointer(new RoomSettings(it.key()))); - - for (const auto membership : room_state.memberships) { - auto uid = membership.sender(); - auto url = membership.content().avatarUrl(); - - if (!url.toString().isEmpty()) - AvatarProvider::setAvatarUrl(uid, url); - } - - view_manager_->addRoom(it.value(), it.key()); - } - - if (it.key() == current_room_) - changeTopRoomInfo(it.key()); - - QApplication::processEvents(); - } - - auto leave = response.rooms().leave(); - - for (auto it = leave.constBegin(); it != leave.constEnd(); it++) { - if (state_manager_.contains(it.key())) { - removeRoom(it.key()); - } - } - - QtConcurrent::run(cache_.data(), &Cache::setState, response.nextBatch(), state_manager_); - - client_->setNextBatchToken(response.nextBatch()); + auto stateDiff = generateMembershipDifference(response.rooms().join(), state_manager_); + QtConcurrent::run(cache_.data(), &Cache::setState, response.nextBatch(), stateDiff); room_list_->sync(state_manager_); view_manager_->sync(response.rooms()); + client_->setNextBatchToken(response.nextBatch()); client_->sync(); } @@ -458,7 +381,7 @@ ChatPage::initialSyncCompleted(const SyncResponse &response) { auto joined = response.rooms().join(); - for (auto it = joined.constBegin(); it != joined.constEnd(); it++) { + for (auto it = joined.constBegin(); it != joined.constEnd(); ++it) { RoomState room_state; // Build the current state from the timeline and state events. @@ -472,25 +395,18 @@ ChatPage::initialSyncCompleted(const SyncResponse &response) room_state.resolveName(); room_state.resolveAvatar(); - updateDisplayNames(room_state); - state_manager_.insert(it.key(), room_state); settingsManager_.insert(it.key(), QSharedPointer(new RoomSettings(it.key()))); for (const auto membership : room_state.memberships) { - auto uid = membership.sender(); - auto url = membership.content().avatarUrl(); - - if (!url.toString().isEmpty()) - AvatarProvider::setAvatarUrl(uid, url); + updateUserDisplayName(membership); + updateUserAvatarUrl(membership); } QApplication::processEvents(); } - client_->setNextBatchToken(response.nextBatch()); - QtConcurrent::run(cache_.data(), &Cache::setState, response.nextBatch(), state_manager_); // Populate timelines with messages. @@ -499,6 +415,7 @@ ChatPage::initialSyncCompleted(const SyncResponse &response) // Initialize room list. room_list_->setInitialRooms(settingsManager_, state_manager_); + client_->setNextBatchToken(response.nextBatch()); client_->sync(); emit contentLoaded(); @@ -571,7 +488,7 @@ ChatPage::loadStateFromCache() // Fetch all the joined room's state. auto rooms = cache_->states(); - for (auto it = rooms.constBegin(); it != rooms.constEnd(); it++) { + for (auto it = rooms.constBegin(); it != rooms.constEnd(); ++it) { RoomState room_state = it.value(); // Clean up and prepare state for use. @@ -579,9 +496,6 @@ ChatPage::loadStateFromCache() room_state.resolveName(); room_state.resolveAvatar(); - // Update the global list with user's display names. - updateDisplayNames(room_state); - // Save the current room state. state_manager_.insert(it.key(), room_state); @@ -591,11 +505,8 @@ ChatPage::loadStateFromCache() // Resolve user avatars. for (const auto membership : room_state.memberships) { - auto uid = membership.sender(); - auto url = membership.content().avatarUrl(); - - if (!url.toString().isEmpty()) - AvatarProvider::setAvatarUrl(uid, url); + updateUserDisplayName(membership); + updateUserAvatarUrl(membership); } } @@ -700,4 +611,188 @@ ChatPage::updateTypingUsers(const QString &roomid, const QList &user_id typingUsers_.insert(roomid, users); } +void +ChatPage::updateUserMetadata(const QJsonArray &events) +{ + events::EventType ty; + + for (const auto &event : events) { + try { + ty = events::extractEventType(event.toObject()); + } catch (const DeserializationException &e) { + qWarning() << e.what() << event; + continue; + } + + if (!events::isStateEvent(ty)) + continue; + + try { + switch (ty) { + case events::EventType::RoomMember: { + events::StateEvent member; + member.deserialize(event); + + updateUserAvatarUrl(member); + updateUserDisplayName(member); + + break; + } + default: { + continue; + } + } + } catch (const DeserializationException &e) { + qWarning() << e.what() << event; + continue; + } + } +} + +void +ChatPage::updateUserAvatarUrl(const events::StateEvent &membership) +{ + auto uid = membership.sender(); + auto url = membership.content().avatarUrl(); + + if (!url.toString().isEmpty()) + AvatarProvider::setAvatarUrl(uid, url); +} + +void +ChatPage::updateUserDisplayName(const events::StateEvent &membership) +{ + auto displayName = membership.content().displayName(); + + if (!displayName.isEmpty()) + TimelineViewManager::DISPLAY_NAMES.insert(membership.stateKey(), displayName); +} + +void +ChatPage::removeLeftRooms(const QMap &rooms) +{ + for (auto it = rooms.constBegin(); it != rooms.constEnd(); ++it) { + if (state_manager_.contains(it.key())) + removeRoom(it.key()); + } +} + +void +ChatPage::updateJoinedRooms(const QMap &rooms) +{ + for (auto it = rooms.constBegin(); it != rooms.constEnd(); ++it) { + updateTypingUsers(it.key(), it.value().typingUserIDs()); + + const auto newStateEvents = it.value().state().events(); + const auto newTimelineEvents = it.value().timeline().events(); + + // Merge the new updates for rooms that we are tracking. + if (state_manager_.contains(it.key())) { + auto oldState = &state_manager_[it.key()]; + oldState->updateFromEvents(newStateEvents); + oldState->updateFromEvents(newTimelineEvents); + oldState->resolveName(); + oldState->resolveAvatar(); + } else { + // Build the current state from the timeline and state events. + RoomState room_state; + room_state.updateFromEvents(newStateEvents); + room_state.updateFromEvents(newTimelineEvents); + + // Resolve room name and avatar. e.g in case of one-to-one chats. + room_state.resolveName(); + room_state.resolveAvatar(); + + state_manager_.insert(it.key(), room_state); + + // TODO Doesn't work on the sidebar. + settingsManager_.insert( + it.key(), QSharedPointer(new RoomSettings(it.key()))); + + view_manager_->addRoom(it.value(), it.key()); + } + + updateUserMetadata(newStateEvents); + updateUserMetadata(newTimelineEvents); + + if (it.key() == current_room_) + changeTopRoomInfo(it.key()); + + QApplication::processEvents(); + } +} + +QMap +ChatPage::generateMembershipDifference(const QMap &rooms, + const QMap &states) const +{ + QMap stateDiff; + + for (auto it = rooms.constBegin(); it != rooms.constEnd(); ++it) { + if (!states.contains(it.key())) + continue; + + auto events = it.value().state().events(); + + for (auto event : it.value().timeline().events()) + events.append(event); + + RoomState local; + local.aliases = states[it.key()].aliases; + local.avatar = states[it.key()].avatar; + local.canonical_alias = states[it.key()].canonical_alias; + local.history_visibility = states[it.key()].history_visibility; + local.join_rules = states[it.key()].join_rules; + local.name = states[it.key()].name; + local.power_levels = states[it.key()].power_levels; + local.topic = states[it.key()].topic; + local.memberships = getMemberships(events); + + stateDiff.insert(it.key(), local); + } + + return stateDiff; +} + +using Memberships = QMap>; + +Memberships +ChatPage::getMemberships(const QJsonArray &events) const +{ + Memberships memberships; + + events::EventType ty; + + for (const auto &event : events) { + try { + ty = events::extractEventType(event.toObject()); + } catch (const DeserializationException &e) { + qWarning() << e.what() << event; + continue; + } + + if (!events::isStateEvent(ty)) + continue; + + try { + switch (ty) { + case events::EventType::RoomMember: { + events::StateEvent member; + member.deserialize(event); + memberships.insert(member.stateKey(), member); + break; + } + default: { + continue; + } + } + } catch (const DeserializationException &e) { + qWarning() << e.what() << event; + continue; + } + } + + return memberships; +}; + ChatPage::~ChatPage() {} diff --git a/src/RoomList.cc b/src/RoomList.cc index 73e85ea8..3a95cb17 100644 --- a/src/RoomList.cc +++ b/src/RoomList.cc @@ -140,7 +140,7 @@ RoomList::setInitialRooms(const QMap> &set return; } - for (auto it = states.constBegin(); it != states.constEnd(); it++) { + for (auto it = states.constBegin(); it != states.constEnd(); ++it) { auto room_id = it.key(); auto state = it.value(); @@ -194,7 +194,7 @@ RoomList::openLeaveRoomDialog(const QString &room_id) void RoomList::sync(const QMap &states) { - for (auto it = states.constBegin(); it != states.constEnd(); it++) { + for (auto it = states.constBegin(); it != states.constEnd(); ++it) { auto room_id = it.key(); auto state = it.value(); @@ -231,7 +231,7 @@ RoomList::highlightSelectedRoom(const QString &room_id) calculateUnreadMessageCount(); - for (auto it = rooms_.constBegin(); it != rooms_.constEnd(); it++) { + for (auto it = rooms_.constBegin(); it != rooms_.constEnd(); ++it) { if (it.key() != room_id) { it.value()->setPressedState(false); } else { diff --git a/src/RoomState.cc b/src/RoomState.cc index 8db9b2bc..2b8bcdba 100644 --- a/src/RoomState.cc +++ b/src/RoomState.cc @@ -22,6 +22,9 @@ namespace events = matrix::events; +RoomState::RoomState() {} +RoomState::RoomState(const QJsonArray &events) { updateFromEvents(events); } + void RoomState::resolveName() { diff --git a/src/Sync.cc b/src/Sync.cc index 965f7c3d..416fa0c6 100644 --- a/src/Sync.cc +++ b/src/Sync.cc @@ -83,7 +83,7 @@ Rooms::deserialize(const QJsonValue &data) auto join = object.value("join").toObject(); - for (auto it = join.constBegin(); it != join.constEnd(); it++) { + for (auto it = join.constBegin(); it != join.constEnd(); ++it) { JoinedRoom tmp_room; try { tmp_room.deserialize(it.value()); @@ -108,7 +108,7 @@ Rooms::deserialize(const QJsonValue &data) } auto leave = object.value("leave").toObject(); - for (auto it = leave.constBegin(); it != leave.constEnd(); it++) { + for (auto it = leave.constBegin(); it != leave.constEnd(); ++it) { LeftRoom tmp_room; try { diff --git a/src/TimelineView.cc b/src/TimelineView.cc index ee98fe72..354a725c 100644 --- a/src/TimelineView.cc +++ b/src/TimelineView.cc @@ -577,7 +577,7 @@ TimelineView::isPendingMessage(const QString &eventid, void TimelineView::removePendingMessage(const QString &eventid, const QString &body) { - for (auto it = pending_msgs_.begin(); it != pending_msgs_.end(); it++) { + for (auto it = pending_msgs_.begin(); it != pending_msgs_.end(); ++it) { int index = std::distance(pending_msgs_.begin(), it); if (it->event_id == eventid || it->body == body) { diff --git a/src/TimelineViewManager.cc b/src/TimelineViewManager.cc index 44b626ed..01e7d9ab 100644 --- a/src/TimelineViewManager.cc +++ b/src/TimelineViewManager.cc @@ -100,7 +100,7 @@ TimelineViewManager::clearAll() void TimelineViewManager::initialize(const Rooms &rooms) { - for (auto it = rooms.join().constBegin(); it != rooms.join().constEnd(); it++) { + for (auto it = rooms.join().constBegin(); it != rooms.join().constEnd(); ++it) { addRoom(it.value(), it.key()); } } @@ -148,7 +148,7 @@ TimelineViewManager::addRoom(const QString &room_id) void TimelineViewManager::sync(const Rooms &rooms) { - for (auto it = rooms.join().constBegin(); it != rooms.join().constEnd(); it++) { + for (auto it = rooms.join().constBegin(); it != rooms.join().constEnd(); ++it) { auto roomid = it.key(); if (!views_.contains(roomid)) { diff --git a/src/events/PowerLevelsEventContent.cc b/src/events/PowerLevelsEventContent.cc index 02a6ee71..c796f81f 100644 --- a/src/events/PowerLevelsEventContent.cc +++ b/src/events/PowerLevelsEventContent.cc @@ -54,14 +54,14 @@ PowerLevelsEventContent::deserialize(const QJsonValue &data) if (object.value("users").isObject()) { auto users = object.value("users").toObject(); - for (auto it = users.constBegin(); it != users.constEnd(); it++) + for (auto it = users.constBegin(); it != users.constEnd(); ++it) users_.insert(it.key(), it.value().toInt()); } if (object.value("events").isObject()) { auto events = object.value("events").toObject(); - for (auto it = events.constBegin(); it != events.constEnd(); it++) + for (auto it = events.constBegin(); it != events.constEnd(); ++it) events_.insert(it.key(), it.value().toInt()); } } @@ -83,10 +83,10 @@ PowerLevelsEventContent::serialize() const QJsonObject users; QJsonObject events; - for (auto it = users_.constBegin(); it != users_.constEnd(); it++) + for (auto it = users_.constBegin(); it != users_.constEnd(); ++it) users.insert(it.key(), it.value()); - for (auto it = events_.constBegin(); it != events_.constEnd(); it++) + for (auto it = events_.constBegin(); it != events_.constEnd(); ++it) events.insert(it.key(), it.value()); object["users"] = users; diff --git a/src/ui/LoadingIndicator.cc b/src/ui/LoadingIndicator.cc index 71312d32..f64151ce 100644 --- a/src/ui/LoadingIndicator.cc +++ b/src/ui/LoadingIndicator.cc @@ -41,7 +41,7 @@ LoadingIndicator::paintEvent(QPaintEvent *e) int capsuleRadius = (outerRadius - innerRadius) / 2; - for (int i = 0; i < 8; i++) { + for (int i = 0; i < 8; ++i) { QColor color = color_; color.setAlphaF(1.0f - (i / 8.0f)); diff --git a/src/ui/RippleOverlay.cc b/src/ui/RippleOverlay.cc index 26d432f6..a3567db2 100644 --- a/src/ui/RippleOverlay.cc +++ b/src/ui/RippleOverlay.cc @@ -48,7 +48,7 @@ RippleOverlay::paintEvent(QPaintEvent *event) if (use_clip_) painter.setClipPath(clip_path_); - for (auto it = ripples_.constBegin(); it != ripples_.constEnd(); it++) + for (auto it = ripples_.constBegin(); it != ripples_.constEnd(); ++it) paintRipple(&painter, *it); } From e224440f5df4e39df3fed5f30e13c773a65fe01b Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Sat, 28 Oct 2017 21:11:40 +0300 Subject: [PATCH 38/64] Display user avatar changes --- include/AvatarProvider.h | 12 +++++++++--- src/AvatarProvider.cc | 42 ++++++++++++++++++++++------------------ src/EmojiPickButton.cc | 2 +- src/Login.cc | 2 +- src/LoginPage.cc | 2 +- src/Profile.cc | 1 + src/Register.cc | 1 + src/RegisterPage.cc | 2 +- 8 files changed, 38 insertions(+), 26 deletions(-) diff --git a/include/AvatarProvider.h b/include/AvatarProvider.h index a58ef0b7..906f2593 100644 --- a/include/AvatarProvider.h +++ b/include/AvatarProvider.h @@ -24,6 +24,12 @@ class MatrixClient; class TimelineItem; +struct AvatarData +{ + QImage img; + QUrl url; +}; + class AvatarProvider : public QObject { Q_OBJECT @@ -39,8 +45,8 @@ private: static void updateAvatar(const QString &uid, const QImage &img); static QSharedPointer client_; - static QMap> toBeResolved_; - static QMap userAvatars_; - static QMap avatarUrls_; + using UserID = QString; + static QMap avatars_; + static QMap> toBeResolved_; }; diff --git a/src/AvatarProvider.cc b/src/AvatarProvider.cc index ab938cb1..7e8c9e49 100644 --- a/src/AvatarProvider.cc +++ b/src/AvatarProvider.cc @@ -21,8 +21,7 @@ QSharedPointer AvatarProvider::client_; -QMap AvatarProvider::userAvatars_; -QMap AvatarProvider::avatarUrls_; +QMap AvatarProvider::avatars_; QMap> AvatarProvider::toBeResolved_; void @@ -46,45 +45,50 @@ AvatarProvider::updateAvatar(const QString &uid, const QImage &img) toBeResolved_.remove(uid); } - userAvatars_.insert(uid, img); + auto avatarData = avatars_[uid]; + avatarData.img = img; + + avatars_.insert(uid, avatarData); } void AvatarProvider::resolve(const QString &userId, TimelineItem *item) { - if (userAvatars_.contains(userId)) { - auto img = userAvatars_[userId]; + if (!avatars_.contains(userId)) + return; + auto img = avatars_[userId].img; + + if (!img.isNull()) { item->setUserAvatar(img); - return; } - if (avatarUrls_.contains(userId)) { - // Add the current timeline item to the waiting list for this avatar. - if (!toBeResolved_.contains(userId)) { - client_->fetchUserAvatar(userId, avatarUrls_[userId]); + // Add the current timeline item to the waiting list for this avatar. + if (!toBeResolved_.contains(userId)) { + client_->fetchUserAvatar(userId, avatars_[userId].url); - QList timelineItems; - timelineItems.push_back(item); + QList timelineItems; + timelineItems.push_back(item); - toBeResolved_.insert(userId, timelineItems); - } else { - toBeResolved_[userId].push_back(item); - } + toBeResolved_.insert(userId, timelineItems); + } else { + toBeResolved_[userId].push_back(item); } } void AvatarProvider::setAvatarUrl(const QString &userId, const QUrl &url) { - avatarUrls_.insert(userId, url); + AvatarData data; + data.url = url; + + avatars_.insert(userId, data); } void AvatarProvider::clear() { - userAvatars_.clear(); - avatarUrls_.clear(); + avatars_.clear(); toBeResolved_.clear(); } diff --git a/src/EmojiPickButton.cc b/src/EmojiPickButton.cc index 17716f6f..a63ca0b2 100644 --- a/src/EmojiPickButton.cc +++ b/src/EmojiPickButton.cc @@ -15,8 +15,8 @@ * along with this program. If not, see . */ -#include "EmojiPanel.h" #include "EmojiPickButton.h" +#include "EmojiPanel.h" EmojiPickButton::EmojiPickButton(QWidget *parent) : FlatButton(parent) diff --git a/src/Login.cc b/src/Login.cc index 69338f2c..a9e303f9 100644 --- a/src/Login.cc +++ b/src/Login.cc @@ -15,8 +15,8 @@ * along with this program. If not, see . */ -#include "Deserializable.h" #include "Login.h" +#include "Deserializable.h" LoginRequest::LoginRequest() {} diff --git a/src/LoginPage.cc b/src/LoginPage.cc index c1ba352c..528b7442 100644 --- a/src/LoginPage.cc +++ b/src/LoginPage.cc @@ -15,11 +15,11 @@ * along with this program. If not, see . */ +#include "LoginPage.h" #include "Config.h" #include "FlatButton.h" #include "InputValidator.h" #include "LoadingIndicator.h" -#include "LoginPage.h" #include "MatrixClient.h" #include "OverlayModal.h" #include "RaisedButton.h" diff --git a/src/Profile.cc b/src/Profile.cc index 38795e49..8eaafe07 100644 --- a/src/Profile.cc +++ b/src/Profile.cc @@ -16,6 +16,7 @@ */ #include "Deserializable.h" + #include "Profile.h" void diff --git a/src/Register.cc b/src/Register.cc index 0fe2ddce..7453c943 100644 --- a/src/Register.cc +++ b/src/Register.cc @@ -16,6 +16,7 @@ */ #include "Deserializable.h" + #include "Register.h" RegisterRequest::RegisterRequest(const QString &username, const QString &password) diff --git a/src/RegisterPage.cc b/src/RegisterPage.cc index d8186f65..304a7dc0 100644 --- a/src/RegisterPage.cc +++ b/src/RegisterPage.cc @@ -15,13 +15,13 @@ * along with this program. If not, see . */ +#include "RegisterPage.h" #include "Avatar.h" #include "Config.h" #include "FlatButton.h" #include "InputValidator.h" #include "MatrixClient.h" #include "RaisedButton.h" -#include "RegisterPage.h" #include "TextField.h" RegisterPage::RegisterPage(QSharedPointer client, QWidget *parent) From 91b8427795db943694333e5a9e56fae42c61d4b0 Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Sat, 28 Oct 2017 21:24:42 +0300 Subject: [PATCH 39/64] Add missing headers --- include/MatrixClient.h | 1 + src/ChatPage.cc | 2 +- src/MatrixClient.cc | 1 - src/RoomList.cc | 1 + 4 files changed, 3 insertions(+), 2 deletions(-) diff --git a/include/MatrixClient.h b/include/MatrixClient.h index 2e45e397..ef9e82e6 100644 --- a/include/MatrixClient.h +++ b/include/MatrixClient.h @@ -18,6 +18,7 @@ #pragma once #include +#include #include "MessageEvent.h" diff --git a/src/ChatPage.cc b/src/ChatPage.cc index a756a0f9..5fefd767 100644 --- a/src/ChatPage.cc +++ b/src/ChatPage.cc @@ -793,6 +793,6 @@ ChatPage::getMemberships(const QJsonArray &events) const } return memberships; -}; +} ChatPage::~ChatPage() {} diff --git a/src/MatrixClient.cc b/src/MatrixClient.cc index b1854dd8..e1085e82 100644 --- a/src/MatrixClient.cc +++ b/src/MatrixClient.cc @@ -25,7 +25,6 @@ #include #include #include -#include #include #include "Login.h" diff --git a/src/RoomList.cc b/src/RoomList.cc index 3a95cb17..c89e4e6e 100644 --- a/src/RoomList.cc +++ b/src/RoomList.cc @@ -16,6 +16,7 @@ */ #include +#include #include "JoinRoomDialog.h" #include "LeaveRoomDialog.h" From 287b5aa4c0d52e1ac80a0785ab136aa0f98b3e9f Mon Sep 17 00:00:00 2001 From: Thomas Herzog Date: Tue, 31 Oct 2017 19:11:49 +0100 Subject: [PATCH 40/64] Implemented sending of typing notifications (#105) --- include/ChatPage.h | 6 +++-- include/MatrixClient.h | 2 ++ include/TextInputWidget.h | 13 ++++++++++ src/ChatPage.cc | 29 +++++++++++++++++++++-- src/MatrixClient.cc | 50 +++++++++++++++++++++++++++++++++++++++ src/TextInputWidget.cc | 36 ++++++++++++++++++++++++++-- 6 files changed, 130 insertions(+), 6 deletions(-) diff --git a/include/ChatPage.h b/include/ChatPage.h index 416f7870..849d60e7 100644 --- a/include/ChatPage.h +++ b/include/ChatPage.h @@ -45,8 +45,9 @@ class UserInfoWidget; class JoinedRoom; class LeftRoom; -constexpr int CONSENSUS_TIMEOUT = 1000; -constexpr int SHOW_CONTENT_TIMEOUT = 3000; +constexpr int CONSENSUS_TIMEOUT = 1000; +constexpr int SHOW_CONTENT_TIMEOUT = 3000; +constexpr int TYPING_REFRESH_TIMEOUT = 10000; class ChatPage : public QWidget { @@ -141,6 +142,7 @@ private: // Keeps track of the users currently typing on each room. QMap> typingUsers_; + QTimer *typingRefresher_; QSharedPointer quickSwitcher_; QSharedPointer quickSwitcherModal_; diff --git a/include/MatrixClient.h b/include/MatrixClient.h index ef9e82e6..d6dd7162 100644 --- a/include/MatrixClient.h +++ b/include/MatrixClient.h @@ -56,6 +56,8 @@ public: void uploadImage(const QString &roomid, const QString &filename); void joinRoom(const QString &roomIdOrAlias); void leaveRoom(const QString &roomId); + void sendTypingNotification(const QString &roomid, int timeoutInMillis = 20000); + void removeTypingNotification(const QString &roomid); QUrl getHomeServer() { return server_; }; int transactionId() { return txn_id_; }; diff --git a/include/TextInputWidget.h b/include/TextInputWidget.h index 08b62f45..e32ce2ff 100644 --- a/include/TextInputWidget.h +++ b/include/TextInputWidget.h @@ -35,12 +35,20 @@ static const QString JOIN_COMMAND("/join "); class FilteredTextEdit : public QTextEdit { Q_OBJECT + +private: + QTimer *typingTimer_; + public: explicit FilteredTextEdit(QWidget *parent = nullptr); void keyPressEvent(QKeyEvent *event); + void stopTyping(); + signals: void enterPressed(); + void startedTyping(); + void stoppedTyping(); }; class TextInputWidget : public QFrame @@ -51,6 +59,8 @@ public: TextInputWidget(QWidget *parent = 0); ~TextInputWidget(); + void stopTyping(); + public slots: void onSendButtonClicked(); void openFileSelection(); @@ -66,6 +76,9 @@ signals: void uploadImage(QString filename); void sendJoinRoomRequest(const QString &room); + void startedTyping(); + void stoppedTyping(); + private: void showUploadSpinner(); QString parseEmoteCommand(const QString &cmd); diff --git a/src/ChatPage.cc b/src/ChatPage.cc index 5fefd767..d81b64fb 100644 --- a/src/ChatPage.cc +++ b/src/ChatPage.cc @@ -122,6 +122,9 @@ ChatPage::ChatPage(QSharedPointer client, QWidget *parent) contentLayout_->addWidget(typingDisplay_); contentLayout_->addWidget(text_input_); + typingRefresher_ = new QTimer(this); + typingRefresher_->setInterval(TYPING_REFRESH_TIMEOUT); + user_info_widget_ = new UserInfoWidget(sideBarTopWidget_); sideBarTopWidgetLayout_->addWidget(user_info_widget_); @@ -139,6 +142,7 @@ ChatPage::ChatPage(QSharedPointer client, QWidget *parent) 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, text_input_, &TextInputWidget::focusLineEdit); @@ -159,6 +163,20 @@ ChatPage::ChatPage(QSharedPointer client, QWidget *parent) room_list_->updateUnreadMessageCount(roomid, count); }); + connect(text_input_, &TextInputWidget::startedTyping, this, [=]() { + typingRefresher_->start(); + client_->sendTypingNotification(current_room_); + }); + + connect(text_input_, &TextInputWidget::stoppedTyping, this, [=]() { + typingRefresher_->stop(); + client_->removeTypingNotification(current_room_); + }); + + connect(typingRefresher_, &QTimer::timeout, this, [=]() { + client_->sendTypingNotification(current_room_); + }); + connect(view_manager_, &TimelineViewManager::updateRoomsLastMessage, room_list_, @@ -600,13 +618,20 @@ ChatPage::updateTypingUsers(const QString &roomid, const QList &user_id { QStringList users; - for (const auto uid : user_ids) + QSettings settings; + QString user_id = settings.value("auth/user_id").toString(); + + for (const auto uid : user_ids) { + if (uid == user_id) + continue; users.append(TimelineViewManager::displayName(uid)); + } users.sort(); - if (current_room_ == roomid) + if (current_room_ == roomid) { typingDisplay_->setUsers(users); + } typingUsers_.insert(roomid, users); } diff --git a/src/MatrixClient.cc b/src/MatrixClient.cc index e1085e82..c6ef501f 100644 --- a/src/MatrixClient.cc +++ b/src/MatrixClient.cc @@ -794,3 +794,53 @@ MatrixClient::leaveRoom(const QString &roomId) emit leftRoom(roomId); }); } + +void +MatrixClient::sendTypingNotification(const QString &roomid, int timeoutInMillis) +{ + QSettings settings; + QString user_id = settings.value("auth/user_id").toString(); + + QUrlQuery query; + query.addQueryItem("access_token", token_); + + QUrl endpoint(server_); + endpoint.setPath(clientApiUrl_ + QString("/rooms/%1/typing/%2").arg(roomid).arg(user_id)); + + endpoint.setQuery(query); + + QString msgType(""); + QJsonObject body; + + body = { { "typing", true }, { "timeout", timeoutInMillis } }; + + QNetworkRequest request(QString(endpoint.toEncoded())); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + put(request, QJsonDocument(body).toJson(QJsonDocument::Compact)); +} + +void +MatrixClient::removeTypingNotification(const QString &roomid) +{ + QSettings settings; + QString user_id = settings.value("auth/user_id").toString(); + + QUrlQuery query; + query.addQueryItem("access_token", token_); + + QUrl endpoint(server_); + endpoint.setPath(clientApiUrl_ + QString("/rooms/%1/typing/%2").arg(roomid).arg(user_id)); + + endpoint.setQuery(query); + + QString msgType(""); + QJsonObject body; + + body = { { "typing", false } }; + + QNetworkRequest request(QString(endpoint.toEncoded())); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + + put(request, QJsonDocument(body).toJson(QJsonDocument::Compact)); +} diff --git a/src/TextInputWidget.cc b/src/TextInputWidget.cc index 0d5e1102..7ebef6b1 100644 --- a/src/TextInputWidget.cc +++ b/src/TextInputWidget.cc @@ -29,15 +29,37 @@ FilteredTextEdit::FilteredTextEdit(QWidget *parent) : QTextEdit(parent) { setAcceptRichText(false); + + typingTimer_ = new QTimer(this); + typingTimer_->setInterval(1000); + typingTimer_->setSingleShot(true); + + connect(typingTimer_, &QTimer::timeout, this, &FilteredTextEdit::stopTyping); } void FilteredTextEdit::keyPressEvent(QKeyEvent *event) { - if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) + if (!typingTimer_->isActive()) { + emit startedTyping(); + } + + typingTimer_->start(); + + if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) { + stopTyping(); + emit enterPressed(); - else + } else { QTextEdit::keyPressEvent(event); + } +} + +void +FilteredTextEdit::stopTyping() +{ + typingTimer_->stop(); + emit stoppedTyping(); } TextInputWidget::TextInputWidget(QWidget *parent) @@ -104,6 +126,10 @@ TextInputWidget::TextInputWidget(QWidget *parent) SIGNAL(emojiSelected(const QString &)), this, SLOT(addSelectedEmoji(const QString &))); + + connect(input_, &FilteredTextEdit::startedTyping, this, &TextInputWidget::startedTyping); + + connect(input_, &FilteredTextEdit::stoppedTyping, this, &TextInputWidget::stoppedTyping); } void @@ -227,3 +253,9 @@ TextInputWidget::hideUploadSpinner() } TextInputWidget::~TextInputWidget() {} + +void +TextInputWidget::stopTyping() +{ + input_->stopTyping(); +} From 886edd03fbc0181d3ba55a85927b0b82cf45ac93 Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Thu, 2 Nov 2017 00:41:13 +0200 Subject: [PATCH 41/64] Add dummy settings menu --- CMakeLists.txt | 4 + include/ChatPage.h | 1 + include/MainWindow.h | 5 + include/SideBarActions.h | 3 + include/UserSettingsPage.h | 80 ++++++++++++++ include/ui/ToggleButton.h | 110 +++++++++++++++++++ src/ChatPage.cc | 2 + src/MainWindow.cc | 28 +++-- src/SideBarActions.cc | 2 + src/UserSettingsPage.cc | 140 ++++++++++++++++++++++++ src/ui/ToggleButton.cc | 212 +++++++++++++++++++++++++++++++++++++ 11 files changed, 581 insertions(+), 6 deletions(-) create mode 100644 include/UserSettingsPage.h create mode 100644 include/ui/ToggleButton.h create mode 100644 src/UserSettingsPage.cc create mode 100644 src/ui/ToggleButton.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index c15093ad..a8c1e347 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -153,6 +153,7 @@ set(SRC_FILES src/RoomMessages.cc src/RoomState.cc src/SideBarActions.cc + src/UserSettingsPage.cc src/Splitter.cc src/Sync.cc src/TextInputWidget.cc @@ -181,6 +182,7 @@ set(SRC_FILES src/ui/RippleOverlay.cc src/ui/OverlayWidget.cc src/ui/TextField.cc + src/ui/ToggleButton.cc src/ui/Theme.cc src/ui/ThemeManager.cc ) @@ -239,6 +241,7 @@ qt5_wrap_cpp(MOC_HEADERS include/RoomInfoListItem.h include/RoomList.h include/SideBarActions.h + include/UserSettingsPage.h include/Splitter.h include/TextInputWidget.h include/TimelineItem.h @@ -262,6 +265,7 @@ qt5_wrap_cpp(MOC_HEADERS include/ui/Ripple.h include/ui/RippleOverlay.h include/ui/TextField.h + include/ui/ToggleButton.h include/ui/Theme.h include/ui/ThemeManager.h ) diff --git a/include/ChatPage.h b/include/ChatPage.h index 849d60e7..f7d2e1a5 100644 --- a/include/ChatPage.h +++ b/include/ChatPage.h @@ -68,6 +68,7 @@ signals: void unreadMessages(int count); void showNotification(const QString &msg); void showLoginPage(const QString &msg); + void showUserSettingsPage(); private slots: void showUnreadMessageNotification(int count); diff --git a/include/MainWindow.h b/include/MainWindow.h index f56592c2..21530d07 100644 --- a/include/MainWindow.h +++ b/include/MainWindow.h @@ -30,6 +30,8 @@ class OverlayModal; class RegisterPage; class SnackBar; class TrayIcon; +class UserSettingsPage; +class UserSettings; class WelcomePage; class MainWindow : public QMainWindow @@ -59,6 +61,7 @@ private slots: // Show the register page in the main window. void showRegisterPage(); + void showUserSettingsPage(); // Show the chat page and start communicating with the given access token. void showChatPage(QString user_id, QString home_server, QString token); @@ -85,6 +88,8 @@ private: // The main chat area. ChatPage *chat_page_; + UserSettingsPage *userSettingsPage_; + QSharedPointer userSettings_; // Used to hide undefined states between page transitions. QSharedPointer progressModal_; diff --git a/include/SideBarActions.h b/include/SideBarActions.h index 7b550578..bf48c4da 100644 --- a/include/SideBarActions.h +++ b/include/SideBarActions.h @@ -14,6 +14,9 @@ public: SideBarActions(QWidget *parent = nullptr); ~SideBarActions(); +signals: + void showSettings(); + protected: void resizeEvent(QResizeEvent *event) override; diff --git a/include/UserSettingsPage.h b/include/UserSettingsPage.h new file mode 100644 index 00000000..9a4c01dd --- /dev/null +++ b/include/UserSettingsPage.h @@ -0,0 +1,80 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include +#include +#include +#include +#include + +class Toggle; + +constexpr int OptionMargin = 6; +constexpr int LayoutSideMargin = 300; + +class UserSettings +{ +public: + UserSettings(); + + void save(); + void load(); + void setTheme(QString theme) { theme_ = theme; } + void setTray(bool state) { isTrayEnabled_ = state; } + + QString theme() const { return !theme_.isEmpty() ? theme_ : "default"; } + bool isTrayEnabled() const { return isTrayEnabled_; } + +private: + QString theme_; + bool isTrayEnabled_; +}; + +class HorizontalLine : public QFrame +{ + Q_OBJECT + +public: + HorizontalLine(QWidget *parent = nullptr); +}; + +class UserSettingsPage : public QWidget +{ + Q_OBJECT + +public: + UserSettingsPage(QSharedPointer settings, QWidget *parent = 0); + +protected: + void showEvent(QShowEvent *event) override; + +signals: + void moveBack(); + +private: + // Layouts + QVBoxLayout *topLayout_; + QHBoxLayout *topBarLayout_; + + // Shared settings object. + QSharedPointer settings_; + + Toggle *trayToggle_; + QComboBox *themeCombo_; +}; diff --git a/include/ui/ToggleButton.h b/include/ui/ToggleButton.h new file mode 100644 index 00000000..14c3450b --- /dev/null +++ b/include/ui/ToggleButton.h @@ -0,0 +1,110 @@ +#pragma once + +#include +#include + +class ToggleTrack; +class ToggleThumb; + +enum class Position +{ + Left, + Right +}; + +class Toggle : public QAbstractButton +{ + Q_OBJECT + + Q_PROPERTY(QColor activeColor WRITE setActiveColor READ activeColor) + Q_PROPERTY(QColor disabledColor WRITE setDisabledColor READ disabledColor) + Q_PROPERTY(QColor inactiveColor WRITE setInactiveColor READ inactiveColor) + Q_PROPERTY(QColor trackColor WRITE setTrackColor READ trackColor) + +public: + Toggle(QWidget *parent = nullptr); + + void setState(bool isEnabled); + + void setActiveColor(const QColor &color); + void setDisabledColor(const QColor &color); + void setInactiveColor(const QColor &color); + void setTrackColor(const QColor &color); + + QColor activeColor() const { return activeColor_; }; + QColor disabledColor() const { return disabledColor_; }; + QColor inactiveColor() const { return inactiveColor_; }; + QColor trackColor() const { return trackColor_.isValid() ? trackColor_ : QColor("#eee"); }; + + QSize sizeHint() const override { return QSize(64, 48); }; + +protected: + void paintEvent(QPaintEvent *event) override; + +private: + void init(); + void setupProperties(); + + ToggleTrack *track_; + ToggleThumb *thumb_; + + QColor disabledColor_; + QColor activeColor_; + QColor inactiveColor_; + QColor trackColor_; +}; + +class ToggleThumb : public QWidget +{ + Q_OBJECT + + Q_PROPERTY(QColor thumbColor WRITE setThumbColor READ thumbColor) + +public: + ToggleThumb(Toggle *parent); + + Position shift() const { return position_; }; + qreal offset() const { return offset_; }; + QColor thumbColor() const { return thumbColor_; }; + + void setShift(Position position); + void setThumbColor(const QColor &color) + { + thumbColor_ = color; + update(); + }; + +protected: + bool eventFilter(QObject *obj, QEvent *event) override; + void paintEvent(QPaintEvent *event) override; + +private: + void updateOffset(); + + Toggle *const toggle_; + QColor thumbColor_; + + Position position_; + qreal offset_; +}; + +class ToggleTrack : public QWidget +{ + Q_OBJECT + + Q_PROPERTY(QColor trackColor WRITE setTrackColor READ trackColor) + +public: + ToggleTrack(Toggle *parent); + + void setTrackColor(const QColor &color); + QColor trackColor() const { return trackColor_; }; + +protected: + bool eventFilter(QObject *obj, QEvent *event) override; + void paintEvent(QPaintEvent *event) override; + +private: + Toggle *const toggle_; + QColor trackColor_; +}; diff --git a/src/ChatPage.cc b/src/ChatPage.cc index d81b64fb..e32b288a 100644 --- a/src/ChatPage.cc +++ b/src/ChatPage.cc @@ -75,6 +75,8 @@ ChatPage::ChatPage(QSharedPointer client, QWidget *parent) sideBarMainLayout_->setMargin(0); sidebarActions_ = new SideBarActions(this); + connect( + sidebarActions_, &SideBarActions::showSettings, this, &ChatPage::showUserSettingsPage); sideBarLayout_->addLayout(sideBarTopLayout_); sideBarLayout_->addLayout(sideBarMainLayout_); diff --git a/src/MainWindow.cc b/src/MainWindow.cc index 92388ae4..5c188903 100644 --- a/src/MainWindow.cc +++ b/src/MainWindow.cc @@ -31,6 +31,7 @@ #include "RegisterPage.h" #include "SnackBar.h" #include "TrayIcon.h" +#include "UserSettingsPage.h" #include "WelcomePage.h" MainWindow *MainWindow::instance_ = nullptr; @@ -54,13 +55,15 @@ MainWindow::MainWindow(QWidget *parent) font.setStyleStrategy(QFont::PreferAntialias); setFont(font); - client_ = QSharedPointer(new MatrixClient("matrix.org")); - trayIcon_ = new TrayIcon(":/logos/nheko-32.png", this); + client_ = QSharedPointer(new MatrixClient("matrix.org")); + userSettings_ = QSharedPointer(new UserSettings); + trayIcon_ = new TrayIcon(":/logos/nheko-32.png", this); - welcome_page_ = new WelcomePage(this); - login_page_ = new LoginPage(client_, this); - register_page_ = new RegisterPage(client_, this); - chat_page_ = new ChatPage(client_, this); + welcome_page_ = new WelcomePage(this); + login_page_ = new LoginPage(client_, this); + register_page_ = new RegisterPage(client_, this); + chat_page_ = new ChatPage(client_, this); + userSettingsPage_ = new UserSettingsPage(userSettings_, this); // Initialize sliding widget manager. pageStack_ = new QStackedWidget(this); @@ -68,6 +71,7 @@ MainWindow::MainWindow(QWidget *parent) pageStack_->addWidget(login_page_); pageStack_->addWidget(register_page_); pageStack_->addWidget(chat_page_); + pageStack_->addWidget(userSettingsPage_); setCentralWidget(pageStack_); @@ -86,12 +90,18 @@ MainWindow::MainWindow(QWidget *parent) showLoginPage(); }); + connect(userSettingsPage_, &UserSettingsPage::moveBack, this, [=]() { + pageStack_->setCurrentWidget(chat_page_); + }); + connect(trayIcon_, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(iconActivated(QSystemTrayIcon::ActivationReason))); connect(chat_page_, SIGNAL(contentLoaded()), this, SLOT(removeOverlayProgressBar())); + connect( + chat_page_, &ChatPage::showUserSettingsPage, this, &MainWindow::showUserSettingsPage); connect(client_.data(), SIGNAL(loginSuccess(QString, QString, QString)), @@ -234,6 +244,12 @@ MainWindow::showRegisterPage() pageStack_->setCurrentWidget(register_page_); } +void +MainWindow::showUserSettingsPage() +{ + pageStack_->setCurrentWidget(userSettingsPage_); +} + void MainWindow::closeEvent(QCloseEvent *event) { diff --git a/src/SideBarActions.cc b/src/SideBarActions.cc index 1484bd00..3898eb22 100644 --- a/src/SideBarActions.cc +++ b/src/SideBarActions.cc @@ -45,6 +45,8 @@ SideBarActions::SideBarActions(QWidget *parent) layout_->addWidget(createRoomBtn_); layout_->addWidget(joinRoomBtn_); layout_->addWidget(settingsBtn_); + + connect(settingsBtn_, &QPushButton::clicked, this, &SideBarActions::showSettings); } SideBarActions::~SideBarActions() {} diff --git a/src/UserSettingsPage.cc b/src/UserSettingsPage.cc new file mode 100644 index 00000000..ff4714f5 --- /dev/null +++ b/src/UserSettingsPage.cc @@ -0,0 +1,140 @@ +/* + * nheko Copyright (C) 2017 Konstantinos Sideris + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#include "Config.h" +#include "FlatButton.h" +#include "UserSettingsPage.h" +#include + +UserSettings::UserSettings() { load(); } + +void +UserSettings::load() +{ + QSettings settings; + isTrayEnabled_ = settings.value("user/tray", true).toBool(); + theme_ = settings.value("user/theme", "default").toString(); +} + +void +UserSettings::save() +{ + QSettings settings; + settings.beginGroup("user"); + settings.setValue("tray", isTrayEnabled_); + settings.setValue("theme", theme()); + settings.endGroup(); +} + +HorizontalLine::HorizontalLine(QWidget *parent) + : QFrame{ parent } +{ + setFrameShape(QFrame::HLine); + setFrameShadow(QFrame::Sunken); +} + +UserSettingsPage::UserSettingsPage(QSharedPointer settings, QWidget *parent) + : QWidget{ parent } + , settings_{ settings } +{ + topLayout_ = new QVBoxLayout(this); + + QIcon icon; + icon.addFile(":/icons/icons/ui/angle-pointing-to-left.png"); + + auto backBtn_ = new FlatButton(this); + backBtn_->setMinimumSize(QSize(24, 24)); + backBtn_->setIcon(icon); + backBtn_->setIconSize(QSize(24, 24)); + + auto heading_ = new QLabel(tr("User Settings")); + heading_->setFont(QFont("Open Sans Bold", 22)); + + topBarLayout_ = new QHBoxLayout; + topBarLayout_->setSpacing(0); + topBarLayout_->setMargin(0); + topBarLayout_->addWidget(backBtn_, 1, Qt::AlignLeft | Qt::AlignVCenter); + topBarLayout_->addWidget(heading_, 0, Qt::AlignBottom); + topBarLayout_->addStretch(1); + + auto trayOptionLayout_ = new QHBoxLayout; + trayOptionLayout_->setContentsMargins(0, OptionMargin, 0, OptionMargin); + auto trayLabel = new QLabel(tr("Minimize to tray"), this); + trayToggle_ = new Toggle(this); + trayToggle_->setActiveColor(QColor("#38A3D8")); + trayToggle_->setInactiveColor(QColor("gray")); + trayLabel->setFont(QFont("Open Sans", 15)); + + trayOptionLayout_->addWidget(trayLabel); + trayOptionLayout_->addWidget(trayToggle_, 0, Qt::AlignBottom | Qt::AlignRight); + + auto themeOptionLayout_ = new QHBoxLayout; + themeOptionLayout_->setContentsMargins(0, OptionMargin, 0, OptionMargin); + auto themeLabel_ = new QLabel(tr("App theme"), this); + themeCombo_ = new QComboBox(this); + themeCombo_->addItem("Default"); + themeCombo_->addItem("System"); + themeLabel_->setFont(QFont("Open Sans", 15)); + + themeOptionLayout_->addWidget(themeLabel_); + themeOptionLayout_->addWidget(themeCombo_, 0, Qt::AlignBottom | Qt::AlignRight); + + auto general_ = new QLabel(tr("GENERAL"), this); + general_->setFont(QFont("Open Sans Bold", 17)); + general_->setStyleSheet("color: #5d6565"); + + auto mainLayout_ = new QVBoxLayout; + mainLayout_->setSpacing(7); + mainLayout_->setContentsMargins( + LayoutSideMargin, LayoutSideMargin / 6, LayoutSideMargin, LayoutSideMargin / 6); + mainLayout_->addWidget(general_, 1, Qt::AlignLeft | Qt::AlignVCenter); + mainLayout_->addWidget(new HorizontalLine(this)); + mainLayout_->addLayout(trayOptionLayout_); + mainLayout_->addWidget(new HorizontalLine(this)); + mainLayout_->addLayout(themeOptionLayout_); + mainLayout_->addWidget(new HorizontalLine(this)); + + topLayout_->addLayout(topBarLayout_); + topLayout_->addLayout(mainLayout_); + topLayout_->addStretch(1); + + connect(themeCombo_, + static_cast(&QComboBox::activated), + [=](const QString &text) { settings_->setTheme(text.toLower()); }); + + connect(trayToggle_, &Toggle::toggled, this, [=](bool isEnabled) { + settings_->setTray(isEnabled); + }); + + connect(backBtn_, &QPushButton::clicked, this, [=]() { + settings_->save(); + emit moveBack(); + }); +} + +void +UserSettingsPage::showEvent(QShowEvent *) +{ + themeCombo_->setCurrentIndex((settings_->theme() == "default" ? 0 : 1)); + trayToggle_->setState(settings_->isTrayEnabled()); +} diff --git a/src/ui/ToggleButton.cc b/src/ui/ToggleButton.cc new file mode 100644 index 00000000..8054bfe7 --- /dev/null +++ b/src/ui/ToggleButton.cc @@ -0,0 +1,212 @@ +#include +#include +#include +#include + +#include "ToggleButton.h" + +void +Toggle::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event); +} + +Toggle::Toggle(QWidget *parent) + : QAbstractButton{ parent } +{ + init(); + + connect(this, &QAbstractButton::toggled, this, &Toggle::setState); +} + +void +Toggle::setState(bool isEnabled) +{ + thumb_->setShift(isEnabled ? Position::Right : Position::Left); + setupProperties(); +} + +void +Toggle::init() +{ + track_ = new ToggleTrack(this); + thumb_ = new ToggleThumb(this); + + setCursor(QCursor(Qt::PointingHandCursor)); + setCheckable(true); + setChecked(false); + setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum); + + setState(false); + setupProperties(); + + QCoreApplication::processEvents(); +} + +void +Toggle::setupProperties() +{ + if (isEnabled()) { + Position position = thumb_->shift(); + + thumb_->setThumbColor(trackColor()); + + if (position == Position::Left) + track_->setTrackColor(activeColor()); + else if (position == Position::Right) + track_->setTrackColor(inactiveColor()); + } + + update(); +} + +void +Toggle::setDisabledColor(const QColor &color) +{ + disabledColor_ = color; + setupProperties(); +} + +void +Toggle::setActiveColor(const QColor &color) +{ + activeColor_ = color; + setupProperties(); +} + +void +Toggle::setInactiveColor(const QColor &color) +{ + inactiveColor_ = color; + setupProperties(); +} + +void +Toggle::setTrackColor(const QColor &color) +{ + trackColor_ = color; + setupProperties(); +} + +ToggleThumb::ToggleThumb(Toggle *parent) + : QWidget{ parent } + , toggle_{ parent } + , position_{ Position::Right } + , offset_{ 0 } +{ + parent->installEventFilter(this); +} + +void +ToggleThumb::setShift(Position position) +{ + if (position_ != position) { + position_ = position; + updateOffset(); + } +} + +bool +ToggleThumb::eventFilter(QObject *obj, QEvent *event) +{ + const QEvent::Type type = event->type(); + + if (QEvent::Resize == type || QEvent::Move == type) { + setGeometry(toggle_->rect().adjusted(8, 8, -8, -8)); + updateOffset(); + } + + return QWidget::eventFilter(obj, event); +} + +void +ToggleThumb::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event) + + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing); + + QBrush brush; + brush.setStyle(Qt::SolidPattern); + brush.setColor(toggle_->isEnabled() ? thumbColor_ : Qt::white); + + painter.setBrush(brush); + painter.setPen(Qt::NoPen); + + int s; + QRectF r; + + s = height() - 10; + r = QRectF(5 + offset_, 5, s, s); + + painter.drawEllipse(r); + + if (!toggle_->isEnabled()) { + brush.setColor(toggle_->disabledColor()); + painter.setBrush(brush); + painter.drawEllipse(r); + } +} + +void +ToggleThumb::updateOffset() +{ + const QSize s(size()); + offset_ = position_ == Position::Left ? static_cast(s.width() - s.height()) : 0; + update(); +} + +ToggleTrack::ToggleTrack(Toggle *parent) + : QWidget{ parent } + , toggle_{ parent } +{ + Q_ASSERT(parent); + + parent->installEventFilter(this); +} + +void +ToggleTrack::setTrackColor(const QColor &color) +{ + trackColor_ = color; + update(); +} + +bool +ToggleTrack::eventFilter(QObject *obj, QEvent *event) +{ + const QEvent::Type type = event->type(); + + if (QEvent::Resize == type || QEvent::Move == type) { + setGeometry(toggle_->rect()); + } + + return QWidget::eventFilter(obj, event); +} + +void +ToggleTrack::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event) + + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing); + + QBrush brush; + if (toggle_->isEnabled()) { + brush.setColor(trackColor_); + painter.setOpacity(0.8); + } else { + brush.setColor(toggle_->disabledColor()); + painter.setOpacity(0.6); + } + + brush.setStyle(Qt::SolidPattern); + painter.setBrush(brush); + painter.setPen(Qt::NoPen); + + const int h = height() / 2; + const QRect r(0, h / 2, width(), h); + painter.drawRoundedRect(r.adjusted(14, 4, -14, -4), h / 2 - 4, h / 2 - 4); +} From ddb23105f1b6b270f4af0653ea246b7bfef1358e Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Thu, 2 Nov 2017 16:02:03 +0200 Subject: [PATCH 42/64] Add issue template --- .github/ISSUE_TEMPLATE.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 00000000..5471c426 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,19 @@ + + +### System: + +- Operating System: +- Qt version: +- C++ compiler: +- Desktop Environment: + +### Actual behavior + +### Expected behavior + +### Steps to reproduce + + +### Debugger traceback From 84741adc16e03bc9f70f113e27c80e2d44357efc Mon Sep 17 00:00:00 2001 From: Jani Mustonen Date: Thu, 2 Nov 2017 22:00:43 +0200 Subject: [PATCH 43/64] Implement a setting for the tray icon (#108) --- include/UserSettingsPage.h | 1 + src/MainWindow.cc | 6 +++++- src/TrayIcon.cc | 3 --- src/UserSettingsPage.cc | 13 +++++++++---- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/include/UserSettingsPage.h b/include/UserSettingsPage.h index 9a4c01dd..6f6da2c5 100644 --- a/include/UserSettingsPage.h +++ b/include/UserSettingsPage.h @@ -66,6 +66,7 @@ protected: signals: void moveBack(); + void trayOptionChanged(bool value); private: // Layouts diff --git a/src/MainWindow.cc b/src/MainWindow.cc index 5c188903..fd44e405 100644 --- a/src/MainWindow.cc +++ b/src/MainWindow.cc @@ -94,6 +94,8 @@ MainWindow::MainWindow(QWidget *parent) pageStack_->setCurrentWidget(chat_page_); }); + connect(userSettingsPage_, SIGNAL(trayOptionChanged(bool)), trayIcon_, SLOT(setVisible(bool))); + connect(trayIcon_, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, @@ -113,6 +115,8 @@ MainWindow::MainWindow(QWidget *parent) QSettings settings; + trayIcon_->setVisible(userSettings_->isTrayEnabled()); + if (hasActiveUser()) { QString token = settings.value("auth/access_token").toString(); QString home_server = settings.value("auth/home_server").toString(); @@ -253,7 +257,7 @@ MainWindow::showUserSettingsPage() void MainWindow::closeEvent(QCloseEvent *event) { - if (isVisible()) { + if (isVisible() && userSettings_->isTrayEnabled()) { event->ignore(); hide(); } diff --git a/src/TrayIcon.cc b/src/TrayIcon.cc index ad644ed9..ac84aaca 100644 --- a/src/TrayIcon.cc +++ b/src/TrayIcon.cc @@ -123,9 +123,6 @@ TrayIcon::TrayIcon(const QString &filename, QWidget *parent) menu->addAction(quitAction_); setContextMenu(menu); - - // We wait a little for the icon to load. - QTimer::singleShot(500, this, [=]() { show(); }); } void diff --git a/src/UserSettingsPage.cc b/src/UserSettingsPage.cc index ff4714f5..d18b76be 100644 --- a/src/UserSettingsPage.cc +++ b/src/UserSettingsPage.cc @@ -32,7 +32,7 @@ void UserSettings::load() { QSettings settings; - isTrayEnabled_ = settings.value("user/tray", true).toBool(); + isTrayEnabled_ = settings.value("user/window/tray", true).toBool(); theme_ = settings.value("user/theme", "default").toString(); } @@ -41,7 +41,11 @@ UserSettings::save() { QSettings settings; settings.beginGroup("user"); + + settings.beginGroup("window"); settings.setValue("tray", isTrayEnabled_); + settings.endGroup(); + settings.setValue("theme", theme()); settings.endGroup(); } @@ -122,8 +126,9 @@ UserSettingsPage::UserSettingsPage(QSharedPointer settings, QWidge static_cast(&QComboBox::activated), [=](const QString &text) { settings_->setTheme(text.toLower()); }); - connect(trayToggle_, &Toggle::toggled, this, [=](bool isEnabled) { - settings_->setTray(isEnabled); + connect(trayToggle_, &Toggle::toggled, this, [=](bool isDisabled) { + settings_->setTray(!isDisabled); + emit trayOptionChanged(!isDisabled); }); connect(backBtn_, &QPushButton::clicked, this, [=]() { @@ -136,5 +141,5 @@ void UserSettingsPage::showEvent(QShowEvent *) { themeCombo_->setCurrentIndex((settings_->theme() == "default" ? 0 : 1)); - trayToggle_->setState(settings_->isTrayEnabled()); + trayToggle_->setState(!settings_->isTrayEnabled()); // Treats true as "off" } From beda0db543208a5ca7ad2b132625b21055a35f95 Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Thu, 2 Nov 2017 22:02:31 +0200 Subject: [PATCH 44/64] Update issue template --- .github/ISSUE_TEMPLATE.md | 1 + src/MainWindow.cc | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 5471c426..1b8d5e07 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -4,6 +4,7 @@ If you want to request a feature or ask a question, feel free to remove all the ### System: +- Nheko commit/version: - Operating System: - Qt version: - C++ compiler: diff --git a/src/MainWindow.cc b/src/MainWindow.cc index fd44e405..04576d44 100644 --- a/src/MainWindow.cc +++ b/src/MainWindow.cc @@ -94,7 +94,8 @@ MainWindow::MainWindow(QWidget *parent) pageStack_->setCurrentWidget(chat_page_); }); - connect(userSettingsPage_, SIGNAL(trayOptionChanged(bool)), trayIcon_, SLOT(setVisible(bool))); + connect( + userSettingsPage_, SIGNAL(trayOptionChanged(bool)), trayIcon_, SLOT(setVisible(bool))); connect(trayIcon_, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), From 13cb0521fa4fd692f66a89c83ef17b7e3ea339bb Mon Sep 17 00:00:00 2001 From: Jani Mustonen Date: Fri, 3 Nov 2017 08:54:17 +0200 Subject: [PATCH 45/64] Improvements to the quick switcher (#109) - Ghetto disambiguation for the quick switcher - Fix the Ctrl+K shortcut - Fix keyboard focus when the quick switcher is closed fixes #114 --- include/MainWindow.h | 1 - include/TextInputWidget.h | 3 +++ src/ChatPage.cc | 9 +++++++-- src/MainWindow.cc | 14 +++++--------- src/QuickSwitcher.cc | 11 ++++++++++- src/TextInputWidget.cc | 6 ++++++ 6 files changed, 31 insertions(+), 13 deletions(-) diff --git a/include/MainWindow.h b/include/MainWindow.h index 21530d07..2d047b51 100644 --- a/include/MainWindow.h +++ b/include/MainWindow.h @@ -47,7 +47,6 @@ public: protected: void closeEvent(QCloseEvent *event); - void keyPressEvent(QKeyEvent *event); private slots: // Handle interaction with the tray icon. diff --git a/include/TextInputWidget.h b/include/TextInputWidget.h index e32ce2ff..32da6ba3 100644 --- a/include/TextInputWidget.h +++ b/include/TextInputWidget.h @@ -79,6 +79,9 @@ signals: void startedTyping(); void stoppedTyping(); +protected: + void focusInEvent(QFocusEvent *event); + private: void showUploadSpinner(); QString parseEmoteCommand(const QString &cmd); diff --git a/src/ChatPage.cc b/src/ChatPage.cc index e32b288a..884e219a 100644 --- a/src/ChatPage.cc +++ b/src/ChatPage.cc @@ -562,6 +562,7 @@ ChatPage::showQuickSwitcher() connect(quickSwitcher_.data(), &QuickSwitcher::closing, this, [=]() { if (!this->quickSwitcherModal_.isNull()) this->quickSwitcherModal_->fadeOut(); + this->text_input_->setFocus(Qt::FocusReason::PopupFocusReason); }); } @@ -575,8 +576,12 @@ ChatPage::showQuickSwitcher() QMap rooms; - for (auto it = state_manager_.constBegin(); it != state_manager_.constEnd(); ++it) - rooms.insert(it.value().getName(), it.key()); + for (auto it = state_manager_.constBegin(); it != state_manager_.constEnd(); ++it) { + QString deambiguator = it.value().canonical_alias.content().alias(); + if (deambiguator == "") + deambiguator = it.key(); + rooms.insert(it.value().getName() + " (" + deambiguator + ")", it.key()); + } quickSwitcher_->setRoomList(rooms); quickSwitcherModal_->fadeIn(); diff --git a/src/MainWindow.cc b/src/MainWindow.cc index 04576d44..9c81ef85 100644 --- a/src/MainWindow.cc +++ b/src/MainWindow.cc @@ -114,6 +114,11 @@ MainWindow::MainWindow(QWidget *parent) QShortcut *quitShortcut = new QShortcut(QKeySequence::Quit, this); connect(quitShortcut, &QShortcut::activated, this, QApplication::quit); + QShortcut *quickSwitchShortcut = new QShortcut(QKeySequence("Ctrl+K"), this); + connect(quickSwitchShortcut, &QShortcut::activated, this, [=]() { + chat_page_->showQuickSwitcher(); + }); + QSettings settings; trayIcon_->setVisible(userSettings_->isTrayEnabled()); @@ -127,15 +132,6 @@ MainWindow::MainWindow(QWidget *parent) } } -void -MainWindow::keyPressEvent(QKeyEvent *e) -{ - if ((e->key() == Qt::Key_K) && (e->modifiers().testFlag(Qt::ControlModifier))) - chat_page_->showQuickSwitcher(); - else - QMainWindow::keyPressEvent(e); -} - void MainWindow::restoreWindowSize() { diff --git a/src/QuickSwitcher.cc b/src/QuickSwitcher.cc index 542eebd9..2be636a4 100644 --- a/src/QuickSwitcher.cc +++ b/src/QuickSwitcher.cc @@ -122,7 +122,16 @@ QuickSwitcher::QuickSwitcher(QWidget *parent) roomSearch_, &RoomSearchInput::hiding, this, [=]() { completer_->popup()->hide(); }); connect(roomSearch_, &QLineEdit::returnPressed, this, [=]() { emit closing(); - emit roomSelected(rooms_[this->roomSearch_->text().trimmed()]); + + QString text(""); + + if (selection_ == -1) { + completer_->setCurrentRow(0); + text = completer_->currentCompletion(); + } else { + text = this->roomSearch_->text().trimmed(); + } + emit roomSelected(rooms_[text]); roomSearch_->clear(); }); diff --git a/src/TextInputWidget.cc b/src/TextInputWidget.cc index 7ebef6b1..e68b1c28 100644 --- a/src/TextInputWidget.cc +++ b/src/TextInputWidget.cc @@ -259,3 +259,9 @@ TextInputWidget::stopTyping() { input_->stopTyping(); } + +void +TextInputWidget::focusInEvent(QFocusEvent *event) +{ + input_->setFocus(event->reason()); +} From 7e03ca43595d1e9f8fa1743d4072d719a2cdfffa Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Fri, 3 Nov 2017 14:25:01 +0200 Subject: [PATCH 46/64] Reset the sender's name when paginating backwards The first message of the timeline would have an avatar and the rest of the messages would use the previous to be configured (whether or not should have an avatar). fixes #63 --- src/TimelineView.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/TimelineView.cc b/src/TimelineView.cc index 354a725c..e5cbbbf6 100644 --- a/src/TimelineView.cc +++ b/src/TimelineView.cc @@ -179,6 +179,10 @@ TimelineView::addBackwardsEvents(const QString &room_id, const RoomMessages &msg isTimelineFinished = false; QList items; + // Reset the sender of the first message in the timeline + // cause we're about to insert a new one. + firstSender_.clear(); + // Parse in reverse order to determine where we should not show sender's // name. auto ii = msgs.chunk().size(); From 8c5a331c73d175b3106967ace19d6baa9464b097 Mon Sep 17 00:00:00 2001 From: Jani Mustonen Date: Sat, 4 Nov 2017 13:28:50 +0200 Subject: [PATCH 47/64] Simple SOCKS proxy support (#110) --- src/main.cc | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/main.cc b/src/main.cc index 2d059788..8bd77254 100644 --- a/src/main.cc +++ b/src/main.cc @@ -19,11 +19,38 @@ #include #include #include +#include #include #include #include "MainWindow.h" +void +setupProxy() +{ + QSettings settings; + + /** + To set up a SOCKS proxy: + [user] + proxy\socks\host=<> + proxy\socks\port=<> + proxy\socks\user=<> + proxy\socks\password=<> + **/ + if (settings.contains("user/proxy/socks/host")) { + QNetworkProxy proxy; + proxy.setType(QNetworkProxy::Socks5Proxy); + proxy.setHostName(settings.value("user/proxy/socks/host").toString()); + proxy.setPort(settings.value("user/proxy/socks/port").toInt()); + if (settings.contains("user/proxy/socks/user")) + proxy.setUser(settings.value("user/proxy/socks/user").toString()); + if (settings.contains("user/proxy/socks/password")) + proxy.setPassword(settings.value("user/proxy/socks/password").toString()); + QNetworkProxy::setApplicationProxy(proxy); + } +} + int main(int argc, char *argv[]) { @@ -62,6 +89,8 @@ main(int argc, char *argv[]) appTranslator.load("nheko_" + lang, ":/translations"); app.installTranslator(&appTranslator); + setupProxy(); + MainWindow w; // Move the MainWindow to the center From 595d11cfa0f4df6a6c677d45f3e62914c068859b Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Sun, 5 Nov 2017 00:19:00 +0200 Subject: [PATCH 48/64] Exclude modifier keys from triggering typing notifications fixes #116 --- src/TextInputWidget.cc | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/TextInputWidget.cc b/src/TextInputWidget.cc index e68b1c28..cad54d96 100644 --- a/src/TextInputWidget.cc +++ b/src/TextInputWidget.cc @@ -40,11 +40,14 @@ FilteredTextEdit::FilteredTextEdit(QWidget *parent) void FilteredTextEdit::keyPressEvent(QKeyEvent *event) { - if (!typingTimer_->isActive()) { - emit startedTyping(); - } + const bool isModifier = (event->modifiers() != Qt::NoModifier); - typingTimer_->start(); + if (!isModifier) { + if (!typingTimer_->isActive()) + emit startedTyping(); + + typingTimer_->start(); + } if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) { stopTyping(); From 7a653b208d693b3b83467d9f0869840996a488a5 Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Sun, 5 Nov 2017 17:56:02 +0200 Subject: [PATCH 49/64] Add build script for AppImage --- .ci/linux/deploy.sh | 18 ++++++++++++++++++ .travis.yml | 20 +++++++++++++++++++- Makefile | 3 +++ 3 files changed, 40 insertions(+), 1 deletion(-) create mode 100755 .ci/linux/deploy.sh diff --git a/.ci/linux/deploy.sh b/.ci/linux/deploy.sh new file mode 100755 index 00000000..8d377e21 --- /dev/null +++ b/.ci/linux/deploy.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +set -e + +mkdir -p appdir +cp build/nheko appdir/ +cp resources/nheko.desktop appdir/ +cp resources/nheko*.png appdir/ + +wget -c "https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage" +chmod a+x linuxdeployqt*.AppImage + +unset QTDIR +unset QT_PLUGIN_PATH +unset LD_LIBRARY_PATH + +./linuxdeployqt*.AppImage ./appdir/*.desktop -bundle-non-qt-libs +./linuxdeployqt*.AppImage ./appdir/*.desktop -appimage diff --git a/.travis.yml b/.travis.yml index b096b78e..e4129080 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,5 +25,23 @@ before_script: - cmake -H. -Bbuild -DCMAKE_BUILD_TYPE=Release script: - make -C build -j2 - - if [ $TRAVIS_OS_NAME == linux ]; then ./.ci/linux/run-tests.sh; fi - if [ $TRAVIS_OS_NAME == osx ]; then make lint; fi + - if [ $TRAVIS_OS_NAME == linux ]; then ./.ci/linux/run-tests.sh; fi + - if [ $TRAVIS_OS_NAME == linux ]; then ./.ci/linux/deploy.sh; fi + +before_deploy: + - git config --global user.email "builds@travis-ci.com" + - git config --global user.name "Travis CI" + - git tag nightly -fa -m "nheko v0.1.0-dev" + - git push -fq https://$TOKEN@github.com/mujx/nheko --tags + +deploy: + skip_cleanup: true + overwrite: true + provider: releases + api_key: + secure: oprXzESukFiXBeF2BXkXUlegsAQc95Ub4kc/OkoNFaYBvqpA+IGpWHmHCx5JPny/OT3Kc2Urpe2JUeGSWDHZ7UCKDjH+NzGP3uN82fHh/HiszG/Srw7+hWEHm1ve+gMK9GS8pr+yUsUrPP0UfVXlPoI4pBWa4zGi2Ndb/SqvjCgIHFLtGyoBo6CydKQ/AyWiXSMKDfJL+Dx4JLIPP4RTCBJy8ZrZ8m/a5Tgy4Ij6+djjrgYCZCEhGxSYw7xDIT/9SV8g9NkrbisqBDrILzAH8Yhe4XMRMXo88OAxV5+Vb9Rw1hrqczez6lpFDbJetv8MjofND+pSoAFwtjaL1wTFK9Ud6w4O9AuHlEQH9cGVdvsxFcosRwJVh58x51JM9ptoktqhx/HHJBTuCHCYYlHwtRwbwqnMYdLzKZG5FnujT8DG+9mcva1fL6tzW/XD505VPMWwXFC/2/pvolgAkTFFXYSALAwZlK3IgoXU8Gok/3B4iHofzQsFf6Yq3BI/88x7tVASUqiYhoKrO50+gb6pNIRCyWgGUiBEVXBp6Ziq3ORQPyQJg7i9HHUGTUu74yvGLHWLwjNQzZP/hxJZK3VlJxzyXntdOxiJc8iOzNrU+rPKBAlAaE6bQDOoniIysEgdD5BXHTLFzPvts4P1n2Ckor5/rNJ+qXR8GU+/y7e1GKU= + file: nheko-x86_64.AppImage + on: + repo: mujx/nheko + tags: false diff --git a/Makefile b/Makefile index b4f3f470..a7565ee5 100644 --- a/Makefile +++ b/Makefile @@ -17,6 +17,9 @@ test: @cmake --build build @cd build && GTEST_COLOR=1 ctest --verbose +linux-appimage: + @./.ci/linux/deploy.sh + app: release-debug $(APP_TEMPLATE) @cp -fp ./build/$(APP_NAME) $(APP_TEMPLATE)/Contents/MacOS @echo "Created '$(APP_NAME).app' in '$(APP_TEMPLATE)'" From 1e166eea1a8ef4dadd2879f92b023742e645e3aa Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Sun, 5 Nov 2017 18:25:49 +0200 Subject: [PATCH 50/64] Remove auto tagging --- .travis.yml | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index e4129080..ae0e4409 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,12 +29,6 @@ script: - if [ $TRAVIS_OS_NAME == linux ]; then ./.ci/linux/run-tests.sh; fi - if [ $TRAVIS_OS_NAME == linux ]; then ./.ci/linux/deploy.sh; fi -before_deploy: - - git config --global user.email "builds@travis-ci.com" - - git config --global user.name "Travis CI" - - git tag nightly -fa -m "nheko v0.1.0-dev" - - git push -fq https://$TOKEN@github.com/mujx/nheko --tags - deploy: skip_cleanup: true overwrite: true @@ -44,4 +38,4 @@ deploy: file: nheko-x86_64.AppImage on: repo: mujx/nheko - tags: false + tags: true From a6d246b43a4b345fb756c976d8228dd206864860 Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Sun, 5 Nov 2017 19:14:52 +0200 Subject: [PATCH 51/64] Add build script for nheko.dmg --- .ci/linux/deploy.sh | 3 +++ .ci/macos/deploy.sh | 12 ++++++++++++ .travis.yml | 3 ++- 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100755 .ci/macos/deploy.sh diff --git a/.ci/linux/deploy.sh b/.ci/linux/deploy.sh index 8d377e21..6a4fb6be 100755 --- a/.ci/linux/deploy.sh +++ b/.ci/linux/deploy.sh @@ -16,3 +16,6 @@ unset LD_LIBRARY_PATH ./linuxdeployqt*.AppImage ./appdir/*.desktop -bundle-non-qt-libs ./linuxdeployqt*.AppImage ./appdir/*.desktop -appimage + +export ARTIFACT=nheko-x86_64.AppImage +chmod +x $ARTIFACT diff --git a/.ci/macos/deploy.sh b/.ci/macos/deploy.sh new file mode 100755 index 00000000..01d99586 --- /dev/null +++ b/.ci/macos/deploy.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +set -e + +make app + +sudo macdeployqt dist/MacOS/Nheko.app -dmg +user=$(id -nu) +sudo chown ${user} dist/MacOS/Nheko.dmg +mv dist/MacOS/Nheko.dmg . + +export ARTIFACT=Nheko.dmg diff --git a/.travis.yml b/.travis.yml index ae0e4409..029ff778 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,6 +26,7 @@ before_script: script: - make -C build -j2 - if [ $TRAVIS_OS_NAME == osx ]; then make lint; fi + - if [ $TRAVIS_OS_NAME == osx ]; then ./.ci/macos/deploy.sh; fi - if [ $TRAVIS_OS_NAME == linux ]; then ./.ci/linux/run-tests.sh; fi - if [ $TRAVIS_OS_NAME == linux ]; then ./.ci/linux/deploy.sh; fi @@ -35,7 +36,7 @@ deploy: provider: releases api_key: secure: oprXzESukFiXBeF2BXkXUlegsAQc95Ub4kc/OkoNFaYBvqpA+IGpWHmHCx5JPny/OT3Kc2Urpe2JUeGSWDHZ7UCKDjH+NzGP3uN82fHh/HiszG/Srw7+hWEHm1ve+gMK9GS8pr+yUsUrPP0UfVXlPoI4pBWa4zGi2Ndb/SqvjCgIHFLtGyoBo6CydKQ/AyWiXSMKDfJL+Dx4JLIPP4RTCBJy8ZrZ8m/a5Tgy4Ij6+djjrgYCZCEhGxSYw7xDIT/9SV8g9NkrbisqBDrILzAH8Yhe4XMRMXo88OAxV5+Vb9Rw1hrqczez6lpFDbJetv8MjofND+pSoAFwtjaL1wTFK9Ud6w4O9AuHlEQH9cGVdvsxFcosRwJVh58x51JM9ptoktqhx/HHJBTuCHCYYlHwtRwbwqnMYdLzKZG5FnujT8DG+9mcva1fL6tzW/XD505VPMWwXFC/2/pvolgAkTFFXYSALAwZlK3IgoXU8Gok/3B4iHofzQsFf6Yq3BI/88x7tVASUqiYhoKrO50+gb6pNIRCyWgGUiBEVXBp6Ziq3ORQPyQJg7i9HHUGTUu74yvGLHWLwjNQzZP/hxJZK3VlJxzyXntdOxiJc8iOzNrU+rPKBAlAaE6bQDOoniIysEgdD5BXHTLFzPvts4P1n2Ckor5/rNJ+qXR8GU+/y7e1GKU= - file: nheko-x86_64.AppImage + file: $ARTIFACT on: repo: mujx/nheko tags: true From 0740169f1c15c2e9f96047f49807e1bbdb133f7a Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Sun, 5 Nov 2017 19:29:35 +0200 Subject: [PATCH 52/64] Don't use make app --- .ci/macos/deploy.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ci/macos/deploy.sh b/.ci/macos/deploy.sh index 01d99586..01fed1b8 100755 --- a/.ci/macos/deploy.sh +++ b/.ci/macos/deploy.sh @@ -2,7 +2,7 @@ set -e -make app +cp -fp ./build/nheko dist/MacOS/Nheko.app/Contents/MacOS sudo macdeployqt dist/MacOS/Nheko.app -dmg user=$(id -nu) From 562df060d3d2eee6a2395146c32d453143dfc910 Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Sun, 5 Nov 2017 20:07:14 +0200 Subject: [PATCH 53/64] Specify artifacts per build --- .ci/linux/deploy.sh | 3 +-- .ci/macos/deploy.sh | 5 +++-- .travis.yml | 29 ++++++++++++++++++++--------- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/.ci/linux/deploy.sh b/.ci/linux/deploy.sh index 6a4fb6be..78245322 100755 --- a/.ci/linux/deploy.sh +++ b/.ci/linux/deploy.sh @@ -17,5 +17,4 @@ unset LD_LIBRARY_PATH ./linuxdeployqt*.AppImage ./appdir/*.desktop -bundle-non-qt-libs ./linuxdeployqt*.AppImage ./appdir/*.desktop -appimage -export ARTIFACT=nheko-x86_64.AppImage -chmod +x $ARTIFACT +chmod +x nheko-x86_64.AppImage diff --git a/.ci/macos/deploy.sh b/.ci/macos/deploy.sh index 01fed1b8..7e551e9d 100755 --- a/.ci/macos/deploy.sh +++ b/.ci/macos/deploy.sh @@ -2,11 +2,12 @@ set -e +# Add Qt binaries to path +PATH=/usr/local/opt/qt/bin/:${PATH} + cp -fp ./build/nheko dist/MacOS/Nheko.app/Contents/MacOS sudo macdeployqt dist/MacOS/Nheko.app -dmg user=$(id -nu) sudo chown ${user} dist/MacOS/Nheko.dmg mv dist/MacOS/Nheko.dmg . - -export ARTIFACT=Nheko.dmg diff --git a/.travis.yml b/.travis.yml index 029ff778..5f2f3027 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,12 +31,23 @@ script: - if [ $TRAVIS_OS_NAME == linux ]; then ./.ci/linux/deploy.sh; fi deploy: - skip_cleanup: true - overwrite: true - provider: releases - api_key: - secure: oprXzESukFiXBeF2BXkXUlegsAQc95Ub4kc/OkoNFaYBvqpA+IGpWHmHCx5JPny/OT3Kc2Urpe2JUeGSWDHZ7UCKDjH+NzGP3uN82fHh/HiszG/Srw7+hWEHm1ve+gMK9GS8pr+yUsUrPP0UfVXlPoI4pBWa4zGi2Ndb/SqvjCgIHFLtGyoBo6CydKQ/AyWiXSMKDfJL+Dx4JLIPP4RTCBJy8ZrZ8m/a5Tgy4Ij6+djjrgYCZCEhGxSYw7xDIT/9SV8g9NkrbisqBDrILzAH8Yhe4XMRMXo88OAxV5+Vb9Rw1hrqczez6lpFDbJetv8MjofND+pSoAFwtjaL1wTFK9Ud6w4O9AuHlEQH9cGVdvsxFcosRwJVh58x51JM9ptoktqhx/HHJBTuCHCYYlHwtRwbwqnMYdLzKZG5FnujT8DG+9mcva1fL6tzW/XD505VPMWwXFC/2/pvolgAkTFFXYSALAwZlK3IgoXU8Gok/3B4iHofzQsFf6Yq3BI/88x7tVASUqiYhoKrO50+gb6pNIRCyWgGUiBEVXBp6Ziq3ORQPyQJg7i9HHUGTUu74yvGLHWLwjNQzZP/hxJZK3VlJxzyXntdOxiJc8iOzNrU+rPKBAlAaE6bQDOoniIysEgdD5BXHTLFzPvts4P1n2Ckor5/rNJ+qXR8GU+/y7e1GKU= - file: $ARTIFACT - on: - repo: mujx/nheko - tags: true + - skip_cleanup: true + overwrite: true + provider: releases + api_key: + secure: oprXzESukFiXBeF2BXkXUlegsAQc95Ub4kc/OkoNFaYBvqpA+IGpWHmHCx5JPny/OT3Kc2Urpe2JUeGSWDHZ7UCKDjH+NzGP3uN82fHh/HiszG/Srw7+hWEHm1ve+gMK9GS8pr+yUsUrPP0UfVXlPoI4pBWa4zGi2Ndb/SqvjCgIHFLtGyoBo6CydKQ/AyWiXSMKDfJL+Dx4JLIPP4RTCBJy8ZrZ8m/a5Tgy4Ij6+djjrgYCZCEhGxSYw7xDIT/9SV8g9NkrbisqBDrILzAH8Yhe4XMRMXo88OAxV5+Vb9Rw1hrqczez6lpFDbJetv8MjofND+pSoAFwtjaL1wTFK9Ud6w4O9AuHlEQH9cGVdvsxFcosRwJVh58x51JM9ptoktqhx/HHJBTuCHCYYlHwtRwbwqnMYdLzKZG5FnujT8DG+9mcva1fL6tzW/XD505VPMWwXFC/2/pvolgAkTFFXYSALAwZlK3IgoXU8Gok/3B4iHofzQsFf6Yq3BI/88x7tVASUqiYhoKrO50+gb6pNIRCyWgGUiBEVXBp6Ziq3ORQPyQJg7i9HHUGTUu74yvGLHWLwjNQzZP/hxJZK3VlJxzyXntdOxiJc8iOzNrU+rPKBAlAaE6bQDOoniIysEgdD5BXHTLFzPvts4P1n2Ckor5/rNJ+qXR8GU+/y7e1GKU= + file: nheko-x86_64.AppImage + on: + condition: $TRAVIS_OS_NAME == linux + repo: mujx/nheko + tags: true + - skip_cleanup: true + overwrite: true + provider: releases + api_key: + secure: oprXzESukFiXBeF2BXkXUlegsAQc95Ub4kc/OkoNFaYBvqpA+IGpWHmHCx5JPny/OT3Kc2Urpe2JUeGSWDHZ7UCKDjH+NzGP3uN82fHh/HiszG/Srw7+hWEHm1ve+gMK9GS8pr+yUsUrPP0UfVXlPoI4pBWa4zGi2Ndb/SqvjCgIHFLtGyoBo6CydKQ/AyWiXSMKDfJL+Dx4JLIPP4RTCBJy8ZrZ8m/a5Tgy4Ij6+djjrgYCZCEhGxSYw7xDIT/9SV8g9NkrbisqBDrILzAH8Yhe4XMRMXo88OAxV5+Vb9Rw1hrqczez6lpFDbJetv8MjofND+pSoAFwtjaL1wTFK9Ud6w4O9AuHlEQH9cGVdvsxFcosRwJVh58x51JM9ptoktqhx/HHJBTuCHCYYlHwtRwbwqnMYdLzKZG5FnujT8DG+9mcva1fL6tzW/XD505VPMWwXFC/2/pvolgAkTFFXYSALAwZlK3IgoXU8Gok/3B4iHofzQsFf6Yq3BI/88x7tVASUqiYhoKrO50+gb6pNIRCyWgGUiBEVXBp6Ziq3ORQPyQJg7i9HHUGTUu74yvGLHWLwjNQzZP/hxJZK3VlJxzyXntdOxiJc8iOzNrU+rPKBAlAaE6bQDOoniIysEgdD5BXHTLFzPvts4P1n2Ckor5/rNJ+qXR8GU+/y7e1GKU= + file: Nheko.dmg + on: + condition: $TRAVIS_OS_NAME == osx + repo: mujx/nheko + tags: true From 2929d4a3a43d5780da37b34feac86c92d08a17f1 Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Sun, 5 Nov 2017 20:11:06 +0200 Subject: [PATCH 54/64] Fix .travis.yml format --- .travis.yml | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5f2f3027..22e28c7f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,23 +31,23 @@ script: - if [ $TRAVIS_OS_NAME == linux ]; then ./.ci/linux/deploy.sh; fi deploy: - - skip_cleanup: true - overwrite: true - provider: releases - api_key: - secure: oprXzESukFiXBeF2BXkXUlegsAQc95Ub4kc/OkoNFaYBvqpA+IGpWHmHCx5JPny/OT3Kc2Urpe2JUeGSWDHZ7UCKDjH+NzGP3uN82fHh/HiszG/Srw7+hWEHm1ve+gMK9GS8pr+yUsUrPP0UfVXlPoI4pBWa4zGi2Ndb/SqvjCgIHFLtGyoBo6CydKQ/AyWiXSMKDfJL+Dx4JLIPP4RTCBJy8ZrZ8m/a5Tgy4Ij6+djjrgYCZCEhGxSYw7xDIT/9SV8g9NkrbisqBDrILzAH8Yhe4XMRMXo88OAxV5+Vb9Rw1hrqczez6lpFDbJetv8MjofND+pSoAFwtjaL1wTFK9Ud6w4O9AuHlEQH9cGVdvsxFcosRwJVh58x51JM9ptoktqhx/HHJBTuCHCYYlHwtRwbwqnMYdLzKZG5FnujT8DG+9mcva1fL6tzW/XD505VPMWwXFC/2/pvolgAkTFFXYSALAwZlK3IgoXU8Gok/3B4iHofzQsFf6Yq3BI/88x7tVASUqiYhoKrO50+gb6pNIRCyWgGUiBEVXBp6Ziq3ORQPyQJg7i9HHUGTUu74yvGLHWLwjNQzZP/hxJZK3VlJxzyXntdOxiJc8iOzNrU+rPKBAlAaE6bQDOoniIysEgdD5BXHTLFzPvts4P1n2Ckor5/rNJ+qXR8GU+/y7e1GKU= - file: nheko-x86_64.AppImage - on: - condition: $TRAVIS_OS_NAME == linux - repo: mujx/nheko - tags: true - - skip_cleanup: true - overwrite: true - provider: releases - api_key: - secure: oprXzESukFiXBeF2BXkXUlegsAQc95Ub4kc/OkoNFaYBvqpA+IGpWHmHCx5JPny/OT3Kc2Urpe2JUeGSWDHZ7UCKDjH+NzGP3uN82fHh/HiszG/Srw7+hWEHm1ve+gMK9GS8pr+yUsUrPP0UfVXlPoI4pBWa4zGi2Ndb/SqvjCgIHFLtGyoBo6CydKQ/AyWiXSMKDfJL+Dx4JLIPP4RTCBJy8ZrZ8m/a5Tgy4Ij6+djjrgYCZCEhGxSYw7xDIT/9SV8g9NkrbisqBDrILzAH8Yhe4XMRMXo88OAxV5+Vb9Rw1hrqczez6lpFDbJetv8MjofND+pSoAFwtjaL1wTFK9Ud6w4O9AuHlEQH9cGVdvsxFcosRwJVh58x51JM9ptoktqhx/HHJBTuCHCYYlHwtRwbwqnMYdLzKZG5FnujT8DG+9mcva1fL6tzW/XD505VPMWwXFC/2/pvolgAkTFFXYSALAwZlK3IgoXU8Gok/3B4iHofzQsFf6Yq3BI/88x7tVASUqiYhoKrO50+gb6pNIRCyWgGUiBEVXBp6Ziq3ORQPyQJg7i9HHUGTUu74yvGLHWLwjNQzZP/hxJZK3VlJxzyXntdOxiJc8iOzNrU+rPKBAlAaE6bQDOoniIysEgdD5BXHTLFzPvts4P1n2Ckor5/rNJ+qXR8GU+/y7e1GKU= - file: Nheko.dmg - on: - condition: $TRAVIS_OS_NAME == osx - repo: mujx/nheko - tags: true + - skip_cleanup: true + overwrite: true + provider: releases + api_key: + secure: oprXzESukFiXBeF2BXkXUlegsAQc95Ub4kc/OkoNFaYBvqpA+IGpWHmHCx5JPny/OT3Kc2Urpe2JUeGSWDHZ7UCKDjH+NzGP3uN82fHh/HiszG/Srw7+hWEHm1ve+gMK9GS8pr+yUsUrPP0UfVXlPoI4pBWa4zGi2Ndb/SqvjCgIHFLtGyoBo6CydKQ/AyWiXSMKDfJL+Dx4JLIPP4RTCBJy8ZrZ8m/a5Tgy4Ij6+djjrgYCZCEhGxSYw7xDIT/9SV8g9NkrbisqBDrILzAH8Yhe4XMRMXo88OAxV5+Vb9Rw1hrqczez6lpFDbJetv8MjofND+pSoAFwtjaL1wTFK9Ud6w4O9AuHlEQH9cGVdvsxFcosRwJVh58x51JM9ptoktqhx/HHJBTuCHCYYlHwtRwbwqnMYdLzKZG5FnujT8DG+9mcva1fL6tzW/XD505VPMWwXFC/2/pvolgAkTFFXYSALAwZlK3IgoXU8Gok/3B4iHofzQsFf6Yq3BI/88x7tVASUqiYhoKrO50+gb6pNIRCyWgGUiBEVXBp6Ziq3ORQPyQJg7i9HHUGTUu74yvGLHWLwjNQzZP/hxJZK3VlJxzyXntdOxiJc8iOzNrU+rPKBAlAaE6bQDOoniIysEgdD5BXHTLFzPvts4P1n2Ckor5/rNJ+qXR8GU+/y7e1GKU= + file: nheko-x86_64.AppImage + on: + condition: $TRAVIS_OS_NAME == linux + repo: mujx/nheko + tags: true + - skip_cleanup: true + overwrite: true + provider: releases + api_key: + secure: oprXzESukFiXBeF2BXkXUlegsAQc95Ub4kc/OkoNFaYBvqpA+IGpWHmHCx5JPny/OT3Kc2Urpe2JUeGSWDHZ7UCKDjH+NzGP3uN82fHh/HiszG/Srw7+hWEHm1ve+gMK9GS8pr+yUsUrPP0UfVXlPoI4pBWa4zGi2Ndb/SqvjCgIHFLtGyoBo6CydKQ/AyWiXSMKDfJL+Dx4JLIPP4RTCBJy8ZrZ8m/a5Tgy4Ij6+djjrgYCZCEhGxSYw7xDIT/9SV8g9NkrbisqBDrILzAH8Yhe4XMRMXo88OAxV5+Vb9Rw1hrqczez6lpFDbJetv8MjofND+pSoAFwtjaL1wTFK9Ud6w4O9AuHlEQH9cGVdvsxFcosRwJVh58x51JM9ptoktqhx/HHJBTuCHCYYlHwtRwbwqnMYdLzKZG5FnujT8DG+9mcva1fL6tzW/XD505VPMWwXFC/2/pvolgAkTFFXYSALAwZlK3IgoXU8Gok/3B4iHofzQsFf6Yq3BI/88x7tVASUqiYhoKrO50+gb6pNIRCyWgGUiBEVXBp6Ziq3ORQPyQJg7i9HHUGTUu74yvGLHWLwjNQzZP/hxJZK3VlJxzyXntdOxiJc8iOzNrU+rPKBAlAaE6bQDOoniIysEgdD5BXHTLFzPvts4P1n2Ckor5/rNJ+qXR8GU+/y7e1GKU= + file: Nheko.dmg + on: + condition: $TRAVIS_OS_NAME == osx + repo: mujx/nheko + tags: true From 4ccb5ed81f1787a3cca12e5ca705fad4e3fa8ca5 Mon Sep 17 00:00:00 2001 From: Benjamin Saunders Date: Sun, 5 Nov 2017 13:01:21 -0800 Subject: [PATCH 55/64] Add input history, enable multi-line input, refactor commands (#119) This also fixes the transmission of mis-typed commands as messages, fixes inability to send messages that start with a command, and does some initial work towards automatically resizing the input field to fit the input message. --- include/TextInputWidget.h | 32 +++++--- src/TextInputWidget.cc | 164 ++++++++++++++++++++++++++------------ 2 files changed, 135 insertions(+), 61 deletions(-) diff --git a/include/TextInputWidget.h b/include/TextInputWidget.h index 32da6ba3..70b1c213 100644 --- a/include/TextInputWidget.h +++ b/include/TextInputWidget.h @@ -17,6 +17,8 @@ #pragma once +#include + #include #include #include @@ -29,26 +31,36 @@ namespace msgs = matrix::events::messages; -static const QString EMOTE_COMMAND("/me "); -static const QString JOIN_COMMAND("/join "); - class FilteredTextEdit : public QTextEdit { Q_OBJECT -private: - QTimer *typingTimer_; - public: explicit FilteredTextEdit(QWidget *parent = nullptr); - void keyPressEvent(QKeyEvent *event); void stopTyping(); + QSize sizeHint() const override; + QSize minimumSizeHint() const override; + + void submit(); + signals: - void enterPressed(); void startedTyping(); void stoppedTyping(); + void message(QString); + void command(QString name, QString args); + +protected: + void keyPressEvent(QKeyEvent *event) override; + +private: + std::deque true_history_, working_history_; + size_t history_index_; + QTimer *typingTimer_; + + void textChanged(); + void afterCompletion(int); }; class TextInputWidget : public QFrame @@ -62,7 +74,6 @@ public: void stopTyping(); public slots: - void onSendButtonClicked(); void openFileSelection(); void hideUploadSpinner(); void focusLineEdit() { input_->setFocus(); }; @@ -84,8 +95,7 @@ protected: private: void showUploadSpinner(); - QString parseEmoteCommand(const QString &cmd); - QString parseJoinCommand(const QString &cmd); + void command(QString name, QString args); QHBoxLayout *topLayout_; FilteredTextEdit *input_; diff --git a/src/TextInputWidget.cc b/src/TextInputWidget.cc index cad54d96..e7c48b5f 100644 --- a/src/TextInputWidget.cc +++ b/src/TextInputWidget.cc @@ -15,6 +15,7 @@ * along with this program. If not, see . */ +#include #include #include #include @@ -25,9 +26,21 @@ #include "Config.h" #include "TextInputWidget.h" +static constexpr size_t INPUT_HISTORY_SIZE = 127; + FilteredTextEdit::FilteredTextEdit(QWidget *parent) - : QTextEdit(parent) + : QTextEdit{ parent } + , history_index_{ 0 } { + connect(document()->documentLayout(), + &QAbstractTextDocumentLayout::documentSizeChanged, + this, + &FilteredTextEdit::updateGeometry); + QSizePolicy policy(QSizePolicy::Expanding, QSizePolicy::Fixed); + policy.setHeightForWidth(true); + setSizePolicy(policy); + working_history_.push_back(""); + connect(this, &QTextEdit::textChanged, this, &FilteredTextEdit::textChanged); setAcceptRichText(false); typingTimer_ = new QTimer(this); @@ -49,12 +62,40 @@ FilteredTextEdit::keyPressEvent(QKeyEvent *event) typingTimer_->start(); } - if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) { - stopTyping(); - - emit enterPressed(); - } else { + switch (event->key()) { + case Qt::Key_Return: + case Qt::Key_Enter: + if (!(event->modifiers() & Qt::ShiftModifier)) { + stopTyping(); + submit(); + } else { + QTextEdit::keyPressEvent(event); + } + break; + case Qt::Key_Up: { + auto initial_cursor = textCursor(); QTextEdit::keyPressEvent(event); + if (textCursor() == initial_cursor && + history_index_ + 1 < working_history_.size()) { + ++history_index_; + setPlainText(working_history_[history_index_]); + moveCursor(QTextCursor::End); + } + break; + } + case Qt::Key_Down: { + auto initial_cursor = textCursor(); + QTextEdit::keyPressEvent(event); + if (textCursor() == initial_cursor && history_index_ > 0) { + --history_index_; + setPlainText(working_history_[history_index_]); + moveCursor(QTextCursor::End); + } + break; + } + default: + QTextEdit::keyPressEvent(event); + break; } } @@ -65,6 +106,65 @@ FilteredTextEdit::stopTyping() emit stoppedTyping(); } +QSize +FilteredTextEdit::sizeHint() const +{ + ensurePolished(); + auto margins = viewportMargins(); + margins += document()->documentMargin(); + QSize size = document()->size().toSize(); + size.rwidth() += margins.left() + margins.right(); + size.rheight() += margins.top() + margins.bottom(); + return size; +} + +QSize +FilteredTextEdit::minimumSizeHint() const +{ + ensurePolished(); + auto margins = viewportMargins(); + margins += document()->documentMargin(); + margins += contentsMargins(); + QSize size(fontMetrics().averageCharWidth() * 10, + fontMetrics().lineSpacing() + margins.top() + margins.bottom()); + return size; +} + +void +FilteredTextEdit::submit() +{ + if (true_history_.size() == INPUT_HISTORY_SIZE) + true_history_.pop_back(); + true_history_.push_front(toPlainText()); + working_history_ = true_history_; + working_history_.push_front(""); + history_index_ = 0; + + QString text = toPlainText(); + if (text.startsWith('/')) { + int command_end = text.indexOf(' '); + if (command_end == -1) + command_end = text.size(); + auto name = text.mid(1, command_end - 1); + auto args = text.mid(command_end + 1); + if (name.isEmpty() || name == "/") { + message(args); + } else { + command(name, args); + } + } else { + message(std::move(text)); + } + + clear(); +} + +void +FilteredTextEdit::textChanged() +{ + working_history_[history_index_] = toPlainText(); +} + TextInputWidget::TextInputWidget(QWidget *parent) : QFrame(parent) { @@ -122,9 +222,10 @@ TextInputWidget::TextInputWidget(QWidget *parent) setLayout(topLayout_); - connect(sendMessageBtn_, SIGNAL(clicked()), this, SLOT(onSendButtonClicked())); + connect(sendMessageBtn_, &FlatButton::clicked, input_, &FilteredTextEdit::submit); connect(sendFileBtn_, SIGNAL(clicked()), this, SLOT(openFileSelection())); - connect(input_, SIGNAL(enterPressed()), sendMessageBtn_, SIGNAL(clicked())); + connect(input_, &FilteredTextEdit::message, this, &TextInputWidget::sendTextMessage); + connect(input_, &FilteredTextEdit::command, this, &TextInputWidget::command); connect(emojiBtn_, SIGNAL(emojiSelected(const QString &)), this, @@ -160,50 +261,13 @@ TextInputWidget::addSelectedEmoji(const QString &emoji) } void -TextInputWidget::onSendButtonClicked() +TextInputWidget::command(QString command, QString args) { - auto msgText = input_->document()->toPlainText().trimmed(); - - if (msgText.isEmpty()) - return; - - if (msgText.startsWith(EMOTE_COMMAND)) { - auto text = parseEmoteCommand(msgText); - - if (!text.isEmpty()) - emit sendEmoteMessage(text); - } else if (msgText.startsWith(JOIN_COMMAND)) { - auto room = parseJoinCommand(msgText); - - if (!room.isEmpty()) - emit sendJoinRoomRequest(room); - } else { - emit sendTextMessage(msgText); + if (command == "me") { + sendEmoteMessage(args); + } else if (command == "join") { + sendJoinRoomRequest(args); } - - input_->clear(); -} - -QString -TextInputWidget::parseJoinCommand(const QString &cmd) -{ - auto room = cmd.right(cmd.size() - JOIN_COMMAND.size()).trimmed(); - - if (!room.isEmpty()) - return room; - - return QString(""); -} - -QString -TextInputWidget::parseEmoteCommand(const QString &cmd) -{ - auto text = cmd.right(cmd.size() - EMOTE_COMMAND.size()).trimmed(); - - if (!text.isEmpty()) - return text; - - return QString(""); } void From 5bd5555a51f52293853e4b4f5dc15dd1c7774538 Mon Sep 17 00:00:00 2001 From: Benjamin Saunders Date: Sun, 5 Nov 2017 13:04:55 -0800 Subject: [PATCH 56/64] Use C++11 braced list style (#121) --- .clang-format | 2 + src/Cache.cc | 10 +- src/EmojiPanel.cc | 10 +- src/EmojiPickButton.cc | 2 +- src/EmojiProvider.cc | 2847 ++++++++++++++++++------------------ src/ImageItem.cc | 10 +- src/ImageOverlayDialog.cc | 4 +- src/Login.cc | 8 +- src/LoginPage.cc | 2 +- src/MainWindow.cc | 4 +- src/MatrixClient.cc | 24 +- src/Register.cc | 2 +- src/RoomInfoListItem.cc | 2 +- src/SideBarActions.cc | 2 +- src/TimelineItem.cc | 41 +- src/TimelineView.cc | 8 +- src/TimelineViewManager.cc | 2 +- src/TopRoomBar.cc | 2 +- src/TypingDisplay.cc | 2 +- src/UserInfoWidget.cc | 6 +- src/UserSettingsPage.cc | 6 +- src/ui/OverlayModal.cc | 4 +- src/ui/ScrollBar.cc | 2 +- src/ui/Theme.cc | 16 +- src/ui/ToggleButton.cc | 14 +- 25 files changed, 1517 insertions(+), 1515 deletions(-) diff --git a/.clang-format b/.clang-format index 6dc5274b..059aee19 100644 --- a/.clang-format +++ b/.clang-format @@ -1,5 +1,6 @@ --- Language: Cpp +Standard: Cpp11 AccessModifierOffset: -8 AlignAfterOpenBracket: Align AlignConsecutiveAssignments: true @@ -10,3 +11,4 @@ IndentCaseLabels: false IndentWidth: 8 KeepEmptyLinesAtTheStartOfBlocks: false PointerAlignment: Right +Cpp11BracedListStyle: true diff --git a/src/Cache.cc b/src/Cache.cc index 99267343..dc2c8a9f 100644 --- a/src/Cache.cc +++ b/src/Cache.cc @@ -31,11 +31,11 @@ static const lmdb::val NEXT_BATCH_KEY("next_batch"); static const lmdb::val transactionID("transaction_id"); Cache::Cache(const QString &userId) - : env_{ nullptr } - , stateDb_{ 0 } - , roomDb_{ 0 } - , isMounted_{ false } - , userId_{ userId } + : env_{nullptr} + , stateDb_{0} + , roomDb_{0} + , isMounted_{false} + , userId_{userId} {} void diff --git a/src/EmojiPanel.cc b/src/EmojiPanel.cc index 71e2526c..3f5f8369 100644 --- a/src/EmojiPanel.cc +++ b/src/EmojiPanel.cc @@ -26,11 +26,11 @@ EmojiPanel::EmojiPanel(QWidget *parent) : QWidget(parent) - , shadowMargin_{ 2 } - , width_{ 370 } - , height_{ 350 } - , animationDuration_{ 100 } - , categoryIconSize_{ 20 } + , shadowMargin_{2} + , width_{370} + , height_{350} + , animationDuration_{100} + , categoryIconSize_{20} { setStyleSheet("QWidget {background: #fff; color: #e8e8e8; border: none;}" "QScrollBar:vertical { background-color: #fff; width: 8px; margin: 0px " diff --git a/src/EmojiPickButton.cc b/src/EmojiPickButton.cc index a63ca0b2..06aa3db9 100644 --- a/src/EmojiPickButton.cc +++ b/src/EmojiPickButton.cc @@ -20,7 +20,7 @@ EmojiPickButton::EmojiPickButton(QWidget *parent) : FlatButton(parent) - , panel_{ nullptr } + , panel_{nullptr} {} void diff --git a/src/EmojiProvider.cc b/src/EmojiProvider.cc index ed380ae0..ec053046 100644 --- a/src/EmojiProvider.cc +++ b/src/EmojiProvider.cc @@ -20,1451 +20,1446 @@ #include "EmojiProvider.h" const QList EmojiProvider::people = { - Emoji{ QString::fromUtf8("\xf0\x9f\x98\x80"), ":grinning:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\x81"), ":grin:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\x82"), ":joy:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xa3"), ":rofl:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\x83"), ":smiley:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\x84"), ":smile:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\x85"), ":sweat_smile:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\x86"), ":laughing:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\x89"), ":wink:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\x8a"), ":blush:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\x8b"), ":yum:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\x8e"), ":sunglasses:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\x8d"), ":heart_eyes:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\x98"), ":kissing_heart:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\x97"), ":kissing:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\x99"), ":kissing_smiling_eyes:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\x9a"), ":kissing_closed_eyes:" }, - Emoji{ QString::fromUtf8("\xe2\x98\xba"), ":relaxed:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x99\x82"), ":slight_smile:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\x97"), ":hugging:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\x94"), ":thinking:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\x90"), ":neutral_face:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\x91"), ":expressionless:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\xb6"), ":no_mouth:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x99\x84"), ":rolling_eyes:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\x8f"), ":smirk:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\xa3"), ":persevere:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\xa5"), ":disappointed_relieved:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\xae"), ":open_mouth:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\x90"), ":zipper_mouth:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\xaf"), ":hushed:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\xaa"), ":sleepy:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\xab"), ":tired_face:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\xb4"), ":sleeping:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\x8c"), ":relieved:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\x93"), ":nerd:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\x9b"), ":stuck_out_tongue:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\x9c"), ":stuck_out_tongue_winking_eye:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\x9d"), ":stuck_out_tongue_closed_eyes:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xa4"), ":drooling_face:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\x92"), ":unamused:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\x93"), ":sweat:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\x94"), ":pensive:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\x95"), ":confused:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x99\x83"), ":upside_down:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\x91"), ":money_mouth:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\xb2"), ":astonished:" }, - Emoji{ QString::fromUtf8("\xe2\x98\xb9"), ":frowning2:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x99\x81"), ":slight_frown:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\x96"), ":confounded:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\x9e"), ":disappointed:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\x9f"), ":worried:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\xa4"), ":triumph:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\xa2"), ":cry:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\xad"), ":sob:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\xa6"), ":frowning:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\xa7"), ":anguished:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\xa8"), ":fearful:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\xa9"), ":weary:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\xac"), ":grimacing:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\xb0"), ":cold_sweat:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\xb1"), ":scream:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\xb3"), ":flushed:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\xb5"), ":dizzy_face:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\xa1"), ":rage:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\xa0"), ":angry:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\x87"), ":innocent:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xa0"), ":cowboy:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xa1"), ":clown:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xa5"), ":lying_face:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\xb7"), ":mask:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\x92"), ":thermometer_face:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\x95"), ":head_bandage:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xa2"), ":nauseated_face:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xa7"), ":sneezing_face:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\x88"), ":smiling_imp:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\xbf"), ":imp:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\xb9"), ":japanese_ogre:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\xba"), ":japanese_goblin:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\x80"), ":skull:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\xbb"), ":ghost:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\xbd"), ":alien:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\x96"), ":robot:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\xa9"), ":poop:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\xba"), ":smiley_cat:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\xb8"), ":smile_cat:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\xb9"), ":joy_cat:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\xbb"), ":heart_eyes_cat:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\xbc"), ":smirk_cat:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\xbd"), ":kissing_cat:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x99\x80"), ":scream_cat:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\xbf"), ":crying_cat_face:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x98\xbe"), ":pouting_cat:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\xa6"), ":boy:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\xa7"), ":girl:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\xa8"), ":man:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\xa9"), ":woman:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\xb4"), ":older_man:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\xb5"), ":older_woman:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\xb6"), ":baby:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\xbc"), ":angel:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\xae"), ":cop:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x95\xb5"), ":spy:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\x82"), ":guardsman:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\xb7"), ":construction_worker:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\xb3"), ":man_with_turban:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\xb1"), ":person_with_blond_hair:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\x85"), ":santa:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xb6"), ":mrs_claus:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\xb8"), ":princess:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xb4"), ":prince:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\xb0"), ":bride_with_veil:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xb5"), ":man_in_tuxedo:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xb0"), ":pregnant_woman:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\xb2"), ":man_with_gua_pi_mao:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x99\x8d"), ":person_frowning:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x99\x8e"), ":person_with_pouting_face:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x99\x85"), ":no_good:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x99\x86"), ":ok_woman:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\x81"), ":information_desk_person:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x99\x8b"), ":raising_hand:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x99\x87"), ":bow:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xa6"), ":face_palm:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xb7"), ":shrug:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\x86"), ":massage:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\x87"), ":haircut:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xb6"), ":walking:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x83"), ":runner:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\x83"), ":dancer:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x95\xba"), ":man_dancing:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\xaf"), ":dancers:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x97\xa3"), ":speaking_head:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\xa4"), ":bust_in_silhouette:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\xa5"), ":busts_in_silhouette:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\xab"), ":couple:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\xac"), ":two_men_holding_hands:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\xad"), ":two_women_holding_hands:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\x8f"), ":couplekiss:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\x91"), ":couple_with_heart:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\xaa"), ":family:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\xaa"), ":muscle:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xb3"), ":selfie:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\x88"), ":point_left:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\x89"), ":point_right:" }, - Emoji{ QString::fromUtf8("\xe2\x98\x9d"), ":point_up:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\x86"), ":point_up_2:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x96\x95"), ":middle_finger:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\x87"), ":point_down:" }, - Emoji{ QString::fromUtf8("\xe2\x9c\x8c"), ":v:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\x9e"), ":fingers_crossed:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x96\x96"), ":vulcan:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\x98"), ":metal:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\x99"), ":call_me:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x96\x90"), ":hand_splayed:" }, - Emoji{ QString::fromUtf8("\xe2\x9c\x8b"), ":raised_hand:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\x8c"), ":ok_hand:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\x8d"), ":thumbsup:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\x8e"), ":thumbsdown:" }, - Emoji{ QString::fromUtf8("\xe2\x9c\x8a"), ":fist:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\x8a"), ":punch:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\x9b"), ":left_facing_fist:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\x9c"), ":right_facing_fist:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\x9a"), ":raised_back_of_hand:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\x8b"), ":wave:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\x8f"), ":clap:" }, - Emoji{ QString::fromUtf8("\xe2\x9c\x8d"), ":writing_hand:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\x90"), ":open_hands:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x99\x8c"), ":raised_hands:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x99\x8f"), ":pray:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\x9d"), ":handshake:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\x85"), ":nail_care:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\x82"), ":ear:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\x83"), ":nose:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\xa3"), ":footprints:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\x80"), ":eyes:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\x81"), ":eye:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\x85"), ":tongue:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\x84"), ":lips:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\x8b"), ":kiss:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\xa4"), ":zzz:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\x93"), ":eyeglasses:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x95\xb6"), ":dark_sunglasses:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\x94"), ":necktie:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\x95"), ":shirt:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\x96"), ":jeans:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\x97"), ":dress:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\x98"), ":kimono:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\x99"), ":bikini:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\x9a"), ":womans_clothes:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\x9b"), ":purse:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\x9c"), ":handbag:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\x9d"), ":pouch:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\x92"), ":school_satchel:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\x9e"), ":mans_shoe:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\x9f"), ":athletic_shoe:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\xa0"), ":high_heel:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\xa1"), ":sandal:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\xa2"), ":boot:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\x91"), ":crown:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x91\x92"), ":womans_hat:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\xa9"), ":tophat:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\x93"), ":mortar_board:" }, - Emoji{ QString::fromUtf8("\xe2\x9b\x91"), ":helmet_with_cross:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\x84"), ":lipstick:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\x8d"), ":ring:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\x82"), ":closed_umbrella:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\xbc"), ":briefcase:" }, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x80"), ":grinning:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x81"), ":grin:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x82"), ":joy:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xa3"), ":rofl:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x83"), ":smiley:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x84"), ":smile:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x85"), ":sweat_smile:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x86"), ":laughing:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x89"), ":wink:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x8a"), ":blush:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x8b"), ":yum:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x8e"), ":sunglasses:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x8d"), ":heart_eyes:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x98"), ":kissing_heart:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x97"), ":kissing:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x99"), ":kissing_smiling_eyes:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x9a"), ":kissing_closed_eyes:"}, + Emoji{QString::fromUtf8("\xe2\x98\xba"), ":relaxed:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x99\x82"), ":slight_smile:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\x97"), ":hugging:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\x94"), ":thinking:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x90"), ":neutral_face:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x91"), ":expressionless:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xb6"), ":no_mouth:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x99\x84"), ":rolling_eyes:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x8f"), ":smirk:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xa3"), ":persevere:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xa5"), ":disappointed_relieved:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xae"), ":open_mouth:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\x90"), ":zipper_mouth:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xaf"), ":hushed:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xaa"), ":sleepy:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xab"), ":tired_face:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xb4"), ":sleeping:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x8c"), ":relieved:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\x93"), ":nerd:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x9b"), ":stuck_out_tongue:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x9c"), ":stuck_out_tongue_winking_eye:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x9d"), ":stuck_out_tongue_closed_eyes:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xa4"), ":drooling_face:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x92"), ":unamused:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x93"), ":sweat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x94"), ":pensive:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x95"), ":confused:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x99\x83"), ":upside_down:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\x91"), ":money_mouth:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xb2"), ":astonished:"}, + Emoji{QString::fromUtf8("\xe2\x98\xb9"), ":frowning2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x99\x81"), ":slight_frown:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x96"), ":confounded:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x9e"), ":disappointed:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x9f"), ":worried:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xa4"), ":triumph:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xa2"), ":cry:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xad"), ":sob:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xa6"), ":frowning:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xa7"), ":anguished:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xa8"), ":fearful:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xa9"), ":weary:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xac"), ":grimacing:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xb0"), ":cold_sweat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xb1"), ":scream:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xb3"), ":flushed:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xb5"), ":dizzy_face:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xa1"), ":rage:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xa0"), ":angry:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x87"), ":innocent:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xa0"), ":cowboy:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xa1"), ":clown:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xa5"), ":lying_face:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xb7"), ":mask:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\x92"), ":thermometer_face:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\x95"), ":head_bandage:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xa2"), ":nauseated_face:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xa7"), ":sneezing_face:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\x88"), ":smiling_imp:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xbf"), ":imp:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xb9"), ":japanese_ogre:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xba"), ":japanese_goblin:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x80"), ":skull:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xbb"), ":ghost:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xbd"), ":alien:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\x96"), ":robot:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xa9"), ":poop:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xba"), ":smiley_cat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xb8"), ":smile_cat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xb9"), ":joy_cat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xbb"), ":heart_eyes_cat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xbc"), ":smirk_cat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xbd"), ":kissing_cat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x99\x80"), ":scream_cat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xbf"), ":crying_cat_face:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x98\xbe"), ":pouting_cat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xa6"), ":boy:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xa7"), ":girl:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xa8"), ":man:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xa9"), ":woman:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xb4"), ":older_man:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xb5"), ":older_woman:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xb6"), ":baby:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xbc"), ":angel:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xae"), ":cop:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\xb5"), ":spy:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x82"), ":guardsman:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xb7"), ":construction_worker:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xb3"), ":man_with_turban:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xb1"), ":person_with_blond_hair:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x85"), ":santa:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb6"), ":mrs_claus:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xb8"), ":princess:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb4"), ":prince:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xb0"), ":bride_with_veil:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb5"), ":man_in_tuxedo:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb0"), ":pregnant_woman:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xb2"), ":man_with_gua_pi_mao:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x99\x8d"), ":person_frowning:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x99\x8e"), ":person_with_pouting_face:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x99\x85"), ":no_good:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x99\x86"), ":ok_woman:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x81"), ":information_desk_person:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x99\x8b"), ":raising_hand:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x99\x87"), ":bow:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xa6"), ":face_palm:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb7"), ":shrug:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x86"), ":massage:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x87"), ":haircut:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb6"), ":walking:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x83"), ":runner:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x83"), ":dancer:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\xba"), ":man_dancing:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xaf"), ":dancers:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x97\xa3"), ":speaking_head:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xa4"), ":bust_in_silhouette:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xa5"), ":busts_in_silhouette:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xab"), ":couple:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xac"), ":two_men_holding_hands:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xad"), ":two_women_holding_hands:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x8f"), ":couplekiss:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x91"), ":couple_with_heart:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xaa"), ":family:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xaa"), ":muscle:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb3"), ":selfie:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x88"), ":point_left:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x89"), ":point_right:"}, + Emoji{QString::fromUtf8("\xe2\x98\x9d"), ":point_up:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x86"), ":point_up_2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x96\x95"), ":middle_finger:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x87"), ":point_down:"}, + Emoji{QString::fromUtf8("\xe2\x9c\x8c"), ":v:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\x9e"), ":fingers_crossed:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x96\x96"), ":vulcan:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\x98"), ":metal:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\x99"), ":call_me:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x96\x90"), ":hand_splayed:"}, + Emoji{QString::fromUtf8("\xe2\x9c\x8b"), ":raised_hand:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x8c"), ":ok_hand:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x8d"), ":thumbsup:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x8e"), ":thumbsdown:"}, + Emoji{QString::fromUtf8("\xe2\x9c\x8a"), ":fist:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x8a"), ":punch:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\x9b"), ":left_facing_fist:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\x9c"), ":right_facing_fist:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\x9a"), ":raised_back_of_hand:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x8b"), ":wave:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x8f"), ":clap:"}, + Emoji{QString::fromUtf8("\xe2\x9c\x8d"), ":writing_hand:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x90"), ":open_hands:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x99\x8c"), ":raised_hands:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x99\x8f"), ":pray:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\x9d"), ":handshake:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x85"), ":nail_care:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x82"), ":ear:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x83"), ":nose:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xa3"), ":footprints:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x80"), ":eyes:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x81"), ":eye:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x85"), ":tongue:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x84"), ":lips:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x8b"), ":kiss:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xa4"), ":zzz:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x93"), ":eyeglasses:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\xb6"), ":dark_sunglasses:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x94"), ":necktie:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x95"), ":shirt:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x96"), ":jeans:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x97"), ":dress:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x98"), ":kimono:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x99"), ":bikini:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x9a"), ":womans_clothes:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x9b"), ":purse:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x9c"), ":handbag:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x9d"), ":pouch:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x92"), ":school_satchel:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x9e"), ":mans_shoe:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x9f"), ":athletic_shoe:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xa0"), ":high_heel:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xa1"), ":sandal:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xa2"), ":boot:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x91"), ":crown:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x92"), ":womans_hat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xa9"), ":tophat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x93"), ":mortar_board:"}, + Emoji{QString::fromUtf8("\xe2\x9b\x91"), ":helmet_with_cross:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x84"), ":lipstick:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x8d"), ":ring:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x82"), ":closed_umbrella:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xbc"), ":briefcase:"}, }; const QList EmojiProvider::nature = { - Emoji{ QString::fromUtf8("\xf0\x9f\x99\x88"), ":see_no_evil:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x99\x89"), ":hear_no_evil:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x99\x8a"), ":speak_no_evil:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\xa6"), ":sweat_drops:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\xa8"), ":dash:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\xb5"), ":monkey_face:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\x92"), ":monkey:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa6\x8d"), ":gorilla:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\xb6"), ":dog:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\x95"), ":dog2:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\xa9"), ":poodle:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\xba"), ":wolf:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa6\x8a"), ":fox:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\xb1"), ":cat:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\x88"), ":cat2:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa6\x81"), ":lion_face:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\xaf"), ":tiger:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\x85"), ":tiger2:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\x86"), ":leopard:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\xb4"), ":horse:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\x8e"), ":racehorse:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa6\x8c"), ":deer:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa6\x84"), ":unicorn:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\xae"), ":cow:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\x82"), ":ox:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\x83"), ":water_buffalo:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\x84"), ":cow2:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\xb7"), ":pig:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\x96"), ":pig2:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\x97"), ":boar:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\xbd"), ":pig_nose:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\x8f"), ":ram:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\x91"), ":sheep:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\x90"), ":goat:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\xaa"), ":dromedary_camel:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\xab"), ":camel:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\x98"), ":elephant:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa6\x8f"), ":rhino:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\xad"), ":mouse:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\x81"), ":mouse2:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\x80"), ":rat:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\xb9"), ":hamster:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\xb0"), ":rabbit:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\x87"), ":rabbit2:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\xbf"), ":chipmunk:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa6\x87"), ":bat:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\xbb"), ":bear:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\xa8"), ":koala:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\xbc"), ":panda_face:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\xbe"), ":feet:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa6\x83"), ":turkey:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\x94"), ":chicken:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\x93"), ":rooster:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\xa3"), ":hatching_chick:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\xa4"), ":baby_chick:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\xa5"), ":hatched_chick:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\xa6"), ":bird:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\xa7"), ":penguin:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x95\x8a"), ":dove:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa6\x85"), ":eagle:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa6\x86"), ":duck:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa6\x89"), ":owl:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\xb8"), ":frog:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\x8a"), ":crocodile:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\xa2"), ":turtle:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa6\x8e"), ":lizard:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\x8d"), ":snake:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\xb2"), ":dragon_face:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\x89"), ":dragon:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\xb3"), ":whale:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\x8b"), ":whale2:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\xac"), ":dolphin:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\x9f"), ":fish:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\xa0"), ":tropical_fish:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\xa1"), ":blowfish:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa6\x88"), ":shark:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\x99"), ":octopus:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\x9a"), ":shell:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa6\x80"), ":crab:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa6\x90"), ":shrimp:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa6\x91"), ":squid:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa6\x8b"), ":butterfly:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\x8c"), ":snail:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\x9b"), ":bug:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\x9c"), ":ant:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\x9d"), ":bee:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x90\x9e"), ":beetle:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x95\xb7"), ":spider:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x95\xb8"), ":spider_web:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa6\x82"), ":scorpion:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\x90"), ":bouquet:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\xb8"), ":cherry_blossom:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\xb5"), ":rosette:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\xb9"), ":rose:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa5\x80"), ":wilted_rose:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\xba"), ":hibiscus:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\xbb"), ":sunflower:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\xbc"), ":blossom:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\xb7"), ":tulip:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\xb1"), ":seedling:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\xb2"), ":evergreen_tree:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\xb3"), ":deciduous_tree:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\xb4"), ":palm_tree:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\xb5"), ":cactus:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\xbe"), ":ear_of_rice:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\xbf"), ":herb:" }, - Emoji{ QString::fromUtf8("\xe2\x98\x98"), ":shamrock:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\x80"), ":four_leaf_clover:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\x81"), ":maple_leaf:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\x82"), ":fallen_leaf:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\x83"), ":leaves:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\x84"), ":mushroom:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\xb0"), ":chestnut:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\x8d"), ":earth_africa:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\x8e"), ":earth_americas:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\x8f"), ":earth_asia:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\x91"), ":new_moon:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\x92"), ":waxing_crescent_moon:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\x93"), ":first_quarter_moon:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\x94"), ":waxing_gibbous_moon:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\x95"), ":full_moon:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\x96"), ":waning_gibbous_moon:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\x97"), ":last_quarter_moon:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\x98"), ":waning_crescent_moon:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\x99"), ":crescent_moon:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\x9a"), ":new_moon_with_face:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\x9b"), ":first_quarter_moon_with_face:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\x9c"), ":last_quarter_moon_with_face:" }, - Emoji{ QString::fromUtf8("\xe2\x98\x80"), ":sunny:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\x9d"), ":full_moon_with_face:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\x9e"), ":sun_with_face:" }, - Emoji{ QString::fromUtf8("\xe2\xad\x90"), ":star:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\x9f"), ":star2:" }, - Emoji{ QString::fromUtf8("\xe2\x98\x81"), ":cloud:" }, - Emoji{ QString::fromUtf8("\xe2\x9b\x85"), ":partly_sunny:" }, - Emoji{ QString::fromUtf8("\xe2\x9b\x88"), ":thunder_cloud_rain:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\xa4"), ":white_sun_small_cloud:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\xa5"), ":white_sun_cloud:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\xa6"), ":white_sun_rain_cloud:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\xa7"), ":cloud_rain:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\xa8"), ":cloud_snow:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\xa9"), ":cloud_lightning:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\xaa"), ":cloud_tornado:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\xab"), ":fog:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\xac"), ":wind_blowing_face:" }, - Emoji{ QString::fromUtf8("\xe2\x98\x82"), ":umbrella2:" }, - Emoji{ QString::fromUtf8("\xe2\x98\x94"), ":umbrella:" }, - Emoji{ QString::fromUtf8("\xe2\x9a\xa1"), ":zap:" }, - Emoji{ QString::fromUtf8("\xe2\x9d\x84"), ":snowflake:" }, - Emoji{ QString::fromUtf8("\xe2\x98\x83"), ":snowman2:" }, - Emoji{ QString::fromUtf8("\xe2\x9b\x84"), ":snowman:" }, - Emoji{ QString::fromUtf8("\xe2\x98\x84"), ":comet:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\xa5"), ":fire:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\xa7"), ":droplet:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\x8a"), ":ocean:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\x83"), ":jack_o_lantern:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\x84"), ":christmas_tree:" }, - Emoji{ QString::fromUtf8("\xe2\x9c\xa8"), ":sparkles:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\x8b"), ":tanabata_tree:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\x8d"), ":bamboo:" }, + Emoji{QString::fromUtf8("\xf0\x9f\x99\x88"), ":see_no_evil:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x99\x89"), ":hear_no_evil:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x99\x8a"), ":speak_no_evil:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xa6"), ":sweat_drops:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xa8"), ":dash:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xb5"), ":monkey_face:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x92"), ":monkey:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa6\x8d"), ":gorilla:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xb6"), ":dog:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x95"), ":dog2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xa9"), ":poodle:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xba"), ":wolf:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa6\x8a"), ":fox:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xb1"), ":cat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x88"), ":cat2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa6\x81"), ":lion_face:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xaf"), ":tiger:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x85"), ":tiger2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x86"), ":leopard:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xb4"), ":horse:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x8e"), ":racehorse:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa6\x8c"), ":deer:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa6\x84"), ":unicorn:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xae"), ":cow:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x82"), ":ox:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x83"), ":water_buffalo:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x84"), ":cow2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xb7"), ":pig:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x96"), ":pig2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x97"), ":boar:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xbd"), ":pig_nose:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x8f"), ":ram:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x91"), ":sheep:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x90"), ":goat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xaa"), ":dromedary_camel:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xab"), ":camel:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x98"), ":elephant:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa6\x8f"), ":rhino:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xad"), ":mouse:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x81"), ":mouse2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x80"), ":rat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xb9"), ":hamster:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xb0"), ":rabbit:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x87"), ":rabbit2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xbf"), ":chipmunk:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa6\x87"), ":bat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xbb"), ":bear:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xa8"), ":koala:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xbc"), ":panda_face:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xbe"), ":feet:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa6\x83"), ":turkey:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x94"), ":chicken:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x93"), ":rooster:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xa3"), ":hatching_chick:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xa4"), ":baby_chick:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xa5"), ":hatched_chick:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xa6"), ":bird:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xa7"), ":penguin:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x8a"), ":dove:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa6\x85"), ":eagle:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa6\x86"), ":duck:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa6\x89"), ":owl:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xb8"), ":frog:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x8a"), ":crocodile:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xa2"), ":turtle:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa6\x8e"), ":lizard:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x8d"), ":snake:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xb2"), ":dragon_face:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x89"), ":dragon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xb3"), ":whale:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x8b"), ":whale2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xac"), ":dolphin:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x9f"), ":fish:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xa0"), ":tropical_fish:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\xa1"), ":blowfish:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa6\x88"), ":shark:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x99"), ":octopus:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x9a"), ":shell:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa6\x80"), ":crab:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa6\x90"), ":shrimp:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa6\x91"), ":squid:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa6\x8b"), ":butterfly:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x8c"), ":snail:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x9b"), ":bug:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x9c"), ":ant:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x9d"), ":bee:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x90\x9e"), ":beetle:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\xb7"), ":spider:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\xb8"), ":spider_web:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa6\x82"), ":scorpion:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x90"), ":bouquet:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xb8"), ":cherry_blossom:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xb5"), ":rosette:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xb9"), ":rose:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x80"), ":wilted_rose:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xba"), ":hibiscus:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xbb"), ":sunflower:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xbc"), ":blossom:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xb7"), ":tulip:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xb1"), ":seedling:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xb2"), ":evergreen_tree:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xb3"), ":deciduous_tree:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xb4"), ":palm_tree:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xb5"), ":cactus:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xbe"), ":ear_of_rice:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xbf"), ":herb:"}, + Emoji{QString::fromUtf8("\xe2\x98\x98"), ":shamrock:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x80"), ":four_leaf_clover:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x81"), ":maple_leaf:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x82"), ":fallen_leaf:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x83"), ":leaves:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x84"), ":mushroom:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xb0"), ":chestnut:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x8d"), ":earth_africa:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x8e"), ":earth_americas:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x8f"), ":earth_asia:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x91"), ":new_moon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x92"), ":waxing_crescent_moon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x93"), ":first_quarter_moon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x94"), ":waxing_gibbous_moon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x95"), ":full_moon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x96"), ":waning_gibbous_moon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x97"), ":last_quarter_moon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x98"), ":waning_crescent_moon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x99"), ":crescent_moon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x9a"), ":new_moon_with_face:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x9b"), ":first_quarter_moon_with_face:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x9c"), ":last_quarter_moon_with_face:"}, + Emoji{QString::fromUtf8("\xe2\x98\x80"), ":sunny:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x9d"), ":full_moon_with_face:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x9e"), ":sun_with_face:"}, + Emoji{QString::fromUtf8("\xe2\xad\x90"), ":star:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x9f"), ":star2:"}, + Emoji{QString::fromUtf8("\xe2\x98\x81"), ":cloud:"}, + Emoji{QString::fromUtf8("\xe2\x9b\x85"), ":partly_sunny:"}, + Emoji{QString::fromUtf8("\xe2\x9b\x88"), ":thunder_cloud_rain:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xa4"), ":white_sun_small_cloud:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xa5"), ":white_sun_cloud:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xa6"), ":white_sun_rain_cloud:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xa7"), ":cloud_rain:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xa8"), ":cloud_snow:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xa9"), ":cloud_lightning:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xaa"), ":cloud_tornado:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xab"), ":fog:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xac"), ":wind_blowing_face:"}, + Emoji{QString::fromUtf8("\xe2\x98\x82"), ":umbrella2:"}, + Emoji{QString::fromUtf8("\xe2\x98\x94"), ":umbrella:"}, + Emoji{QString::fromUtf8("\xe2\x9a\xa1"), ":zap:"}, + Emoji{QString::fromUtf8("\xe2\x9d\x84"), ":snowflake:"}, + Emoji{QString::fromUtf8("\xe2\x98\x83"), ":snowman2:"}, + Emoji{QString::fromUtf8("\xe2\x9b\x84"), ":snowman:"}, + Emoji{QString::fromUtf8("\xe2\x98\x84"), ":comet:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xa5"), ":fire:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xa7"), ":droplet:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x8a"), ":ocean:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x83"), ":jack_o_lantern:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x84"), ":christmas_tree:"}, + Emoji{QString::fromUtf8("\xe2\x9c\xa8"), ":sparkles:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x8b"), ":tanabata_tree:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x8d"), ":bamboo:"}, }; const QList EmojiProvider::food = { - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\x87"), ":grapes:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\x88"), ":melon:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\x89"), ":watermelon:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\x8a"), ":tangerine:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\x8b"), ":lemon:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\x8c"), ":banana:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\x8d"), ":pineapple:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\x8e"), ":apple:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\x8f"), ":green_apple:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\x90"), ":pear:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\x91"), ":peach:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\x92"), ":cherries:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\x93"), ":strawberry:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa5\x9d"), ":kiwi:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\x85"), ":tomato:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa5\x91"), ":avocado:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\x86"), ":eggplant:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa5\x94"), ":potato:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa5\x95"), ":carrot:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\xbd"), ":corn:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\xb6"), ":hot_pepper:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa5\x92"), ":cucumber:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa5\x9c"), ":peanuts:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\x9e"), ":bread:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa5\x90"), ":croissant:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa5\x96"), ":french_bread:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa5\x9e"), ":pancakes:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa7\x80"), ":cheese:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\x96"), ":meat_on_bone:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\x97"), ":poultry_leg:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa5\x93"), ":bacon:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\x94"), ":hamburger:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\x9f"), ":fries:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\x95"), ":pizza:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\xad"), ":hotdog:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\xae"), ":taco:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\xaf"), ":burrito:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa5\x99"), ":stuffed_flatbread:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa5\x9a"), ":egg:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\xb3"), ":cooking:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa5\x98"), ":shallow_pan_of_food:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\xb2"), ":stew:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa5\x97"), ":salad:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\xbf"), ":popcorn:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\xb1"), ":bento:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\x98"), ":rice_cracker:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\x99"), ":rice_ball:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\x9a"), ":rice:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\x9b"), ":curry:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\x9c"), ":ramen:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\x9d"), ":spaghetti:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\xa0"), ":sweet_potato:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\xa2"), ":oden:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\xa3"), ":sushi:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\xa4"), ":fried_shrimp:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\xa5"), ":fish_cake:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\xa1"), ":dango:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\xa6"), ":icecream:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\xa7"), ":shaved_ice:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\xa8"), ":ice_cream:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\xa9"), ":doughnut:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\xaa"), ":cookie:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\x82"), ":birthday:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\xb0"), ":cake:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\xab"), ":chocolate_bar:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\xac"), ":candy:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\xad"), ":lollipop:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\xae"), ":custard:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\xaf"), ":honey_pot:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\xbc"), ":baby_bottle:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa5\x9b"), ":milk:" }, - Emoji{ QString::fromUtf8("\xe2\x98\x95"), ":coffee:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\xb5"), ":tea:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\xb6"), ":sake:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\xbe"), ":champagne:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\xb7"), ":wine_glass:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\xb8"), ":cocktail:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\xb9"), ":tropical_drink:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\xba"), ":beer:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\xbb"), ":beers:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa5\x82"), ":champagne_glass:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa5\x83"), ":tumbler_glass:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\xbd"), ":fork_knife_plate:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8d\xb4"), ":fork_and_knife:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa5\x84"), ":spoon:" }, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x87"), ":grapes:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x88"), ":melon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x89"), ":watermelon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x8a"), ":tangerine:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x8b"), ":lemon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x8c"), ":banana:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x8d"), ":pineapple:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x8e"), ":apple:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x8f"), ":green_apple:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x90"), ":pear:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x91"), ":peach:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x92"), ":cherries:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x93"), ":strawberry:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x9d"), ":kiwi:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x85"), ":tomato:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x91"), ":avocado:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x86"), ":eggplant:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x94"), ":potato:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x95"), ":carrot:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xbd"), ":corn:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xb6"), ":hot_pepper:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x92"), ":cucumber:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x9c"), ":peanuts:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x9e"), ":bread:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x90"), ":croissant:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x96"), ":french_bread:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x9e"), ":pancakes:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa7\x80"), ":cheese:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x96"), ":meat_on_bone:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x97"), ":poultry_leg:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x93"), ":bacon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x94"), ":hamburger:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x9f"), ":fries:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x95"), ":pizza:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xad"), ":hotdog:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xae"), ":taco:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xaf"), ":burrito:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x99"), ":stuffed_flatbread:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x9a"), ":egg:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xb3"), ":cooking:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x98"), ":shallow_pan_of_food:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xb2"), ":stew:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x97"), ":salad:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xbf"), ":popcorn:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xb1"), ":bento:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x98"), ":rice_cracker:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x99"), ":rice_ball:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x9a"), ":rice:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x9b"), ":curry:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x9c"), ":ramen:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\x9d"), ":spaghetti:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xa0"), ":sweet_potato:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xa2"), ":oden:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xa3"), ":sushi:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xa4"), ":fried_shrimp:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xa5"), ":fish_cake:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xa1"), ":dango:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xa6"), ":icecream:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xa7"), ":shaved_ice:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xa8"), ":ice_cream:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xa9"), ":doughnut:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xaa"), ":cookie:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x82"), ":birthday:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xb0"), ":cake:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xab"), ":chocolate_bar:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xac"), ":candy:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xad"), ":lollipop:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xae"), ":custard:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xaf"), ":honey_pot:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xbc"), ":baby_bottle:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x9b"), ":milk:"}, + Emoji{QString::fromUtf8("\xe2\x98\x95"), ":coffee:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xb5"), ":tea:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xb6"), ":sake:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xbe"), ":champagne:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xb7"), ":wine_glass:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xb8"), ":cocktail:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xb9"), ":tropical_drink:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xba"), ":beer:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xbb"), ":beers:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x82"), ":champagne_glass:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x83"), ":tumbler_glass:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xbd"), ":fork_knife_plate:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8d\xb4"), ":fork_and_knife:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x84"), ":spoon:"}, }; const QList EmojiProvider::activity = { - Emoji{ QString::fromUtf8("\xf0\x9f\x91\xbe"), ":space_invader:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x95\xb4"), ":levitate:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xba"), ":fencer:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x87"), ":horse_racing:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x87\xf0\x9f\x8f\xbb"), ":horse_racing_tone1:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x87\xf0\x9f\x8f\xbc"), ":horse_racing_tone2:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x87\xf0\x9f\x8f\xbd"), ":horse_racing_tone3:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x87\xf0\x9f\x8f\xbe"), ":horse_racing_tone4:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x87\xf0\x9f\x8f\xbf"), ":horse_racing_tone5:" }, - Emoji{ QString::fromUtf8("\xe2\x9b\xb7"), ":skier:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x82"), ":snowboarder:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x8c"), ":golfer:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x84"), ":surfer:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x84\xf0\x9f\x8f\xbb"), ":surfer_tone1:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x84\xf0\x9f\x8f\xbc"), ":surfer_tone2:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x84\xf0\x9f\x8f\xbd"), ":surfer_tone3:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x84\xf0\x9f\x8f\xbe"), ":surfer_tone4:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x84\xf0\x9f\x8f\xbf"), ":surfer_tone5:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xa3"), ":rowboat:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xa3\xf0\x9f\x8f\xbb"), ":rowboat_tone1:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xa3\xf0\x9f\x8f\xbc"), ":rowboat_tone2:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xa3\xf0\x9f\x8f\xbd"), ":rowboat_tone3:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xa3\xf0\x9f\x8f\xbe"), ":rowboat_tone4:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xa3\xf0\x9f\x8f\xbf"), ":rowboat_tone5:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x8a"), ":swimmer:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x8a\xf0\x9f\x8f\xbb"), ":swimmer_tone1:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x8a\xf0\x9f\x8f\xbc"), ":swimmer_tone2:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x8a\xf0\x9f\x8f\xbd"), ":swimmer_tone3:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x8a\xf0\x9f\x8f\xbe"), ":swimmer_tone4:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x8a\xf0\x9f\x8f\xbf"), ":swimmer_tone5:" }, - Emoji{ QString::fromUtf8("\xe2\x9b\xb9"), ":basketball_player:" }, - Emoji{ QString::fromUtf8("\xe2\x9b\xb9\xf0\x9f\x8f\xbb"), ":basketball_player_tone1:" }, - Emoji{ QString::fromUtf8("\xe2\x9b\xb9\xf0\x9f\x8f\xbc"), ":basketball_player_tone2:" }, - Emoji{ QString::fromUtf8("\xe2\x9b\xb9\xf0\x9f\x8f\xbd"), ":basketball_player_tone3:" }, - Emoji{ QString::fromUtf8("\xe2\x9b\xb9\xf0\x9f\x8f\xbe"), ":basketball_player_tone4:" }, - Emoji{ QString::fromUtf8("\xe2\x9b\xb9\xf0\x9f\x8f\xbf"), ":basketball_player_tone5:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x8b"), ":lifter:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x8b\xf0\x9f\x8f\xbb"), ":lifter_tone1:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x8b\xf0\x9f\x8f\xbc"), ":lifter_tone2:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x8b\xf0\x9f\x8f\xbd"), ":lifter_tone3:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x8b\xf0\x9f\x8f\xbe"), ":lifter_tone4:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x8b\xf0\x9f\x8f\xbf"), ":lifter_tone5:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xb4"), ":bicyclist:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xb4\xf0\x9f\x8f\xbb"), ":bicyclist_tone1:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xb4\xf0\x9f\x8f\xbc"), ":bicyclist_tone2:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xb4\xf0\x9f\x8f\xbd"), ":bicyclist_tone3:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xb4\xf0\x9f\x8f\xbe"), ":bicyclist_tone4:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xb4\xf0\x9f\x8f\xbf"), ":bicyclist_tone5:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xb5"), ":mountain_bicyclist:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xb5\xf0\x9f\x8f\xbb"), - ":mountain_bicyclist_tone1:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xb5\xf0\x9f\x8f\xbc"), - ":mountain_bicyclist_tone2:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xb5\xf0\x9f\x8f\xbd"), - ":mountain_bicyclist_tone3:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xb5\xf0\x9f\x8f\xbe"), - ":mountain_bicyclist_tone4:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xb5\xf0\x9f\x8f\xbf"), - ":mountain_bicyclist_tone5:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xb8"), ":cartwheel:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xb8\xf0\x9f\x8f\xbb"), ":cartwheel_tone1:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xb8\xf0\x9f\x8f\xbc"), ":cartwheel_tone2:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xb8\xf0\x9f\x8f\xbd"), ":cartwheel_tone3:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xb8\xf0\x9f\x8f\xbe"), ":cartwheel_tone4:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xb8\xf0\x9f\x8f\xbf"), ":cartwheel_tone5:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xbc"), ":wrestlers:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xbc\xf0\x9f\x8f\xbb"), ":wrestlers_tone1:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xbc\xf0\x9f\x8f\xbc"), ":wrestlers_tone2:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xbc\xf0\x9f\x8f\xbd"), ":wrestlers_tone3:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xbc\xf0\x9f\x8f\xbe"), ":wrestlers_tone4:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xbc\xf0\x9f\x8f\xbf"), ":wrestlers_tone5:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xbd"), ":water_polo:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xbd\xf0\x9f\x8f\xbb"), ":water_polo_tone1:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xbd\xf0\x9f\x8f\xbc"), ":water_polo_tone2:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xbd\xf0\x9f\x8f\xbd"), ":water_polo_tone3:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xbd\xf0\x9f\x8f\xbe"), ":water_polo_tone4:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xbd\xf0\x9f\x8f\xbf"), ":water_polo_tone5:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xbe"), ":handball:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xbe\xf0\x9f\x8f\xbb"), ":handball_tone1:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xbe\xf0\x9f\x8f\xbc"), ":handball_tone2:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xbe\xf0\x9f\x8f\xbd"), ":handball_tone3:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xbe\xf0\x9f\x8f\xbe"), ":handball_tone4:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xbe\xf0\x9f\x8f\xbf"), ":handball_tone5:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xb9"), ":juggling:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xb9\xf0\x9f\x8f\xbb"), ":juggling_tone1:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xb9\xf0\x9f\x8f\xbc"), ":juggling_tone2:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xb9\xf0\x9f\x8f\xbd"), ":juggling_tone3:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xb9\xf0\x9f\x8f\xbe"), ":juggling_tone4:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa4\xb9\xf0\x9f\x8f\xbf"), ":juggling_tone5:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\xaa"), ":circus_tent:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\xad"), ":performing_arts:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\xa8"), ":art:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\xb0"), ":slot_machine:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9b\x80"), ":bath:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9b\x80\xf0\x9f\x8f\xbb"), ":bath_tone1:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9b\x80\xf0\x9f\x8f\xbc"), ":bath_tone2:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9b\x80\xf0\x9f\x8f\xbd"), ":bath_tone3:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9b\x80\xf0\x9f\x8f\xbe"), ":bath_tone4:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9b\x80\xf0\x9f\x8f\xbf"), ":bath_tone5:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\x97"), ":reminder_ribbon:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\x9f"), ":tickets:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\xab"), ":ticket:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\x96"), ":military_medal:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x86"), ":trophy:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x85"), ":medal:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa5\x87"), ":first_place:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa5\x88"), ":second_place:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa5\x89"), ":third_place:" }, - Emoji{ QString::fromUtf8("\xe2\x9a\xbd"), ":soccer:" }, - Emoji{ QString::fromUtf8("\xe2\x9a\xbe"), ":baseball:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x80"), ":basketball:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x90"), ":volleyball:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x88"), ":football:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x89"), ":rugby_football:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\xbe"), ":tennis:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\xb1"), ":8ball:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\xb3"), ":bowling:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x8f"), ":cricket:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x91"), ":field_hockey:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x92"), ":hockey:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x93"), ":ping_pong:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\xb8"), ":badminton:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa5\x8a"), ":boxing_glove:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa5\x8b"), ":martial_arts_uniform:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa5\x85"), ":goal:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\xaf"), ":dart:" }, - Emoji{ QString::fromUtf8("\xe2\x9b\xb3"), ":golf:" }, - Emoji{ QString::fromUtf8("\xe2\x9b\xb8"), ":ice_skate:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\xa3"), ":fishing_pole_and_fish:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\xbd"), ":running_shirt_with_sash:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\xbf"), ":ski:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\xae"), ":video_game:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\xb2"), ":game_die:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\xbc"), ":musical_score:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\xa4"), ":microphone:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\xa7"), ":headphones:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\xb7"), ":saxophone:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\xb8"), ":guitar:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\xb9"), ":musical_keyboard:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\xba"), ":trumpet:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\xbb"), ":violin:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\xa5\x81"), ":drum:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\xac"), ":clapper:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\xb9"), ":bow_and_arrow:" }, + Emoji{QString::fromUtf8("\xf0\x9f\x91\xbe"), ":space_invader:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\xb4"), ":levitate:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xba"), ":fencer:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x87"), ":horse_racing:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x87\xf0\x9f\x8f\xbb"), ":horse_racing_tone1:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x87\xf0\x9f\x8f\xbc"), ":horse_racing_tone2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x87\xf0\x9f\x8f\xbd"), ":horse_racing_tone3:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x87\xf0\x9f\x8f\xbe"), ":horse_racing_tone4:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x87\xf0\x9f\x8f\xbf"), ":horse_racing_tone5:"}, + Emoji{QString::fromUtf8("\xe2\x9b\xb7"), ":skier:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x82"), ":snowboarder:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8c"), ":golfer:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x84"), ":surfer:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x84\xf0\x9f\x8f\xbb"), ":surfer_tone1:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x84\xf0\x9f\x8f\xbc"), ":surfer_tone2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x84\xf0\x9f\x8f\xbd"), ":surfer_tone3:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x84\xf0\x9f\x8f\xbe"), ":surfer_tone4:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x84\xf0\x9f\x8f\xbf"), ":surfer_tone5:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa3"), ":rowboat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa3\xf0\x9f\x8f\xbb"), ":rowboat_tone1:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa3\xf0\x9f\x8f\xbc"), ":rowboat_tone2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa3\xf0\x9f\x8f\xbd"), ":rowboat_tone3:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa3\xf0\x9f\x8f\xbe"), ":rowboat_tone4:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa3\xf0\x9f\x8f\xbf"), ":rowboat_tone5:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8a"), ":swimmer:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8a\xf0\x9f\x8f\xbb"), ":swimmer_tone1:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8a\xf0\x9f\x8f\xbc"), ":swimmer_tone2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8a\xf0\x9f\x8f\xbd"), ":swimmer_tone3:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8a\xf0\x9f\x8f\xbe"), ":swimmer_tone4:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8a\xf0\x9f\x8f\xbf"), ":swimmer_tone5:"}, + Emoji{QString::fromUtf8("\xe2\x9b\xb9"), ":basketball_player:"}, + Emoji{QString::fromUtf8("\xe2\x9b\xb9\xf0\x9f\x8f\xbb"), ":basketball_player_tone1:"}, + Emoji{QString::fromUtf8("\xe2\x9b\xb9\xf0\x9f\x8f\xbc"), ":basketball_player_tone2:"}, + Emoji{QString::fromUtf8("\xe2\x9b\xb9\xf0\x9f\x8f\xbd"), ":basketball_player_tone3:"}, + Emoji{QString::fromUtf8("\xe2\x9b\xb9\xf0\x9f\x8f\xbe"), ":basketball_player_tone4:"}, + Emoji{QString::fromUtf8("\xe2\x9b\xb9\xf0\x9f\x8f\xbf"), ":basketball_player_tone5:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8b"), ":lifter:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8b\xf0\x9f\x8f\xbb"), ":lifter_tone1:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8b\xf0\x9f\x8f\xbc"), ":lifter_tone2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8b\xf0\x9f\x8f\xbd"), ":lifter_tone3:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8b\xf0\x9f\x8f\xbe"), ":lifter_tone4:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8b\xf0\x9f\x8f\xbf"), ":lifter_tone5:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb4"), ":bicyclist:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb4\xf0\x9f\x8f\xbb"), ":bicyclist_tone1:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb4\xf0\x9f\x8f\xbc"), ":bicyclist_tone2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb4\xf0\x9f\x8f\xbd"), ":bicyclist_tone3:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb4\xf0\x9f\x8f\xbe"), ":bicyclist_tone4:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb4\xf0\x9f\x8f\xbf"), ":bicyclist_tone5:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb5"), ":mountain_bicyclist:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb5\xf0\x9f\x8f\xbb"), ":mountain_bicyclist_tone1:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb5\xf0\x9f\x8f\xbc"), ":mountain_bicyclist_tone2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb5\xf0\x9f\x8f\xbd"), ":mountain_bicyclist_tone3:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb5\xf0\x9f\x8f\xbe"), ":mountain_bicyclist_tone4:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb5\xf0\x9f\x8f\xbf"), ":mountain_bicyclist_tone5:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb8"), ":cartwheel:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb8\xf0\x9f\x8f\xbb"), ":cartwheel_tone1:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb8\xf0\x9f\x8f\xbc"), ":cartwheel_tone2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb8\xf0\x9f\x8f\xbd"), ":cartwheel_tone3:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb8\xf0\x9f\x8f\xbe"), ":cartwheel_tone4:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb8\xf0\x9f\x8f\xbf"), ":cartwheel_tone5:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbc"), ":wrestlers:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbc\xf0\x9f\x8f\xbb"), ":wrestlers_tone1:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbc\xf0\x9f\x8f\xbc"), ":wrestlers_tone2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbc\xf0\x9f\x8f\xbd"), ":wrestlers_tone3:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbc\xf0\x9f\x8f\xbe"), ":wrestlers_tone4:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbc\xf0\x9f\x8f\xbf"), ":wrestlers_tone5:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbd"), ":water_polo:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbd\xf0\x9f\x8f\xbb"), ":water_polo_tone1:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbd\xf0\x9f\x8f\xbc"), ":water_polo_tone2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbd\xf0\x9f\x8f\xbd"), ":water_polo_tone3:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbd\xf0\x9f\x8f\xbe"), ":water_polo_tone4:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbd\xf0\x9f\x8f\xbf"), ":water_polo_tone5:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbe"), ":handball:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbe\xf0\x9f\x8f\xbb"), ":handball_tone1:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbe\xf0\x9f\x8f\xbc"), ":handball_tone2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbe\xf0\x9f\x8f\xbd"), ":handball_tone3:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbe\xf0\x9f\x8f\xbe"), ":handball_tone4:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xbe\xf0\x9f\x8f\xbf"), ":handball_tone5:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb9"), ":juggling:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb9\xf0\x9f\x8f\xbb"), ":juggling_tone1:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb9\xf0\x9f\x8f\xbc"), ":juggling_tone2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb9\xf0\x9f\x8f\xbd"), ":juggling_tone3:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb9\xf0\x9f\x8f\xbe"), ":juggling_tone4:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa4\xb9\xf0\x9f\x8f\xbf"), ":juggling_tone5:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xaa"), ":circus_tent:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xad"), ":performing_arts:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xa8"), ":art:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xb0"), ":slot_machine:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\x80"), ":bath:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\x80\xf0\x9f\x8f\xbb"), ":bath_tone1:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\x80\xf0\x9f\x8f\xbc"), ":bath_tone2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\x80\xf0\x9f\x8f\xbd"), ":bath_tone3:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\x80\xf0\x9f\x8f\xbe"), ":bath_tone4:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\x80\xf0\x9f\x8f\xbf"), ":bath_tone5:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x97"), ":reminder_ribbon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x9f"), ":tickets:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xab"), ":ticket:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x96"), ":military_medal:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x86"), ":trophy:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x85"), ":medal:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x87"), ":first_place:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x88"), ":second_place:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x89"), ":third_place:"}, + Emoji{QString::fromUtf8("\xe2\x9a\xbd"), ":soccer:"}, + Emoji{QString::fromUtf8("\xe2\x9a\xbe"), ":baseball:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x80"), ":basketball:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x90"), ":volleyball:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x88"), ":football:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x89"), ":rugby_football:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xbe"), ":tennis:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xb1"), ":8ball:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xb3"), ":bowling:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8f"), ":cricket:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x91"), ":field_hockey:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x92"), ":hockey:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x93"), ":ping_pong:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xb8"), ":badminton:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x8a"), ":boxing_glove:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x8b"), ":martial_arts_uniform:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x85"), ":goal:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xaf"), ":dart:"}, + Emoji{QString::fromUtf8("\xe2\x9b\xb3"), ":golf:"}, + Emoji{QString::fromUtf8("\xe2\x9b\xb8"), ":ice_skate:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xa3"), ":fishing_pole_and_fish:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xbd"), ":running_shirt_with_sash:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xbf"), ":ski:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xae"), ":video_game:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xb2"), ":game_die:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xbc"), ":musical_score:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xa4"), ":microphone:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xa7"), ":headphones:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xb7"), ":saxophone:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xb8"), ":guitar:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xb9"), ":musical_keyboard:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xba"), ":trumpet:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xbb"), ":violin:"}, + Emoji{QString::fromUtf8("\xf0\x9f\xa5\x81"), ":drum:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xac"), ":clapper:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xb9"), ":bow_and_arrow:"}, }; const QList EmojiProvider::travel = { - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x8e"), ":race_car:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x8d"), ":motorcycle:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x97\xbe"), ":japan:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x94"), ":mountain_snow:" }, - Emoji{ QString::fromUtf8("\xe2\x9b\xb0"), ":mountain:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\x8b"), ":volcano:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x97\xbb"), ":mount_fuji:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x95"), ":camping:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x96"), ":beach:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x9c"), ":desert:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x9d"), ":island:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x9e"), ":park:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x9f"), ":stadium:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x9b"), ":classical_building:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x97"), ":construction_site:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x98"), ":homes:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x99"), ":cityscape:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x9a"), ":house_abandoned:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\xa0"), ":house:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\xa1"), ":house_with_garden:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\xa2"), ":office:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\xa3"), ":post_office:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\xa4"), ":european_post_office:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\xa5"), ":hospital:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\xa6"), ":bank:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\xa8"), ":hotel:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\xa9"), ":love_hotel:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\xaa"), ":convenience_store:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\xab"), ":school:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\xac"), ":department_store:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\xad"), ":factory:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\xaf"), ":japanese_castle:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\xb0"), ":european_castle:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\x92"), ":wedding:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x97\xbc"), ":tokyo_tower:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x97\xbd"), ":statue_of_liberty:" }, - Emoji{ QString::fromUtf8("\xe2\x9b\xaa"), ":church:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x95\x8c"), ":mosque:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x95\x8d"), ":synagogue:" }, - Emoji{ QString::fromUtf8("\xe2\x9b\xa9"), ":shinto_shrine:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x95\x8b"), ":kaaba:" }, - Emoji{ QString::fromUtf8("\xe2\x9b\xb2"), ":fountain:" }, - Emoji{ QString::fromUtf8("\xe2\x9b\xba"), ":tent:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\x81"), ":foggy:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\x83"), ":night_with_stars:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\x84"), ":sunrise_over_mountains:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\x85"), ":sunrise:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\x86"), ":city_dusk:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\x87"), ":city_sunset:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\x89"), ":bridge_at_night:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\x8c"), ":milky_way:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\xa0"), ":carousel_horse:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\xa1"), ":ferris_wheel:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\xa2"), ":roller_coaster:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\x82"), ":steam_locomotive:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\x83"), ":railway_car:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\x84"), ":bullettrain_side:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\x85"), ":bullettrain_front:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\x86"), ":train2:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\x87"), ":metro:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\x88"), ":light_rail:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\x89"), ":station:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\x8a"), ":tram:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\x9d"), ":monorail:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\x9e"), ":mountain_railway:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\x8b"), ":train:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\x8c"), ":bus:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\x8d"), ":oncoming_bus:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\x8e"), ":trolleybus:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\x90"), ":minibus:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\x91"), ":ambulance:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\x92"), ":fire_engine:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\x93"), ":police_car:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\x94"), ":oncoming_police_car:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\x95"), ":taxi:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\x96"), ":oncoming_taxi:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\x97"), ":red_car:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\x98"), ":oncoming_automobile:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\x99"), ":blue_car:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\x9a"), ":truck:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\x9b"), ":articulated_lorry:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\x9c"), ":tractor:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xb2"), ":bike:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9b\xb4"), ":scooter:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9b\xb5"), ":motor_scooter:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\x8f"), ":busstop:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9b\xa3"), ":motorway:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9b\xa4"), ":railway_track:" }, - Emoji{ QString::fromUtf8("\xe2\x9b\xbd"), ":fuelpump:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xa8"), ":rotating_light:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xa5"), ":traffic_light:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xa6"), ":vertical_traffic_light:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xa7"), ":construction:" }, - Emoji{ QString::fromUtf8("\xe2\x9a\x93"), ":anchor:" }, - Emoji{ QString::fromUtf8("\xe2\x9b\xb5"), ":sailboat:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9b\xb6"), ":canoe:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xa4"), ":speedboat:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9b\xb3"), ":cruise_ship:" }, - Emoji{ QString::fromUtf8("\xe2\x9b\xb4"), ":ferry:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9b\xa5"), ":motorboat:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xa2"), ":ship:" }, - Emoji{ QString::fromUtf8("\xe2\x9c\x88"), ":airplane:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9b\xa9"), ":airplane_small:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9b\xab"), ":airplane_departure:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9b\xac"), ":airplane_arriving:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\xba"), ":seat:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\x81"), ":helicopter:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\x9f"), ":suspension_railway:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xa0"), ":mountain_cableway:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xa1"), ":aerial_tramway:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\x80"), ":rocket:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9b\xb0"), ":satellite_orbital:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\xa0"), ":stars:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\x88"), ":rainbow:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\x86"), ":fireworks:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\x87"), ":sparkler:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\x91"), ":rice_scene:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\x81"), ":checkered_flag:" }, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8e"), ":race_car:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x8d"), ":motorcycle:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x97\xbe"), ":japan:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x94"), ":mountain_snow:"}, + Emoji{QString::fromUtf8("\xe2\x9b\xb0"), ":mountain:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x8b"), ":volcano:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x97\xbb"), ":mount_fuji:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x95"), ":camping:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x96"), ":beach:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x9c"), ":desert:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x9d"), ":island:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x9e"), ":park:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x9f"), ":stadium:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x9b"), ":classical_building:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x97"), ":construction_site:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x98"), ":homes:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x99"), ":cityscape:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x9a"), ":house_abandoned:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xa0"), ":house:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xa1"), ":house_with_garden:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xa2"), ":office:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xa3"), ":post_office:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xa4"), ":european_post_office:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xa5"), ":hospital:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xa6"), ":bank:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xa8"), ":hotel:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xa9"), ":love_hotel:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xaa"), ":convenience_store:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xab"), ":school:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xac"), ":department_store:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xad"), ":factory:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xaf"), ":japanese_castle:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xb0"), ":european_castle:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x92"), ":wedding:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x97\xbc"), ":tokyo_tower:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x97\xbd"), ":statue_of_liberty:"}, + Emoji{QString::fromUtf8("\xe2\x9b\xaa"), ":church:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x8c"), ":mosque:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x8d"), ":synagogue:"}, + Emoji{QString::fromUtf8("\xe2\x9b\xa9"), ":shinto_shrine:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x8b"), ":kaaba:"}, + Emoji{QString::fromUtf8("\xe2\x9b\xb2"), ":fountain:"}, + Emoji{QString::fromUtf8("\xe2\x9b\xba"), ":tent:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x81"), ":foggy:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x83"), ":night_with_stars:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x84"), ":sunrise_over_mountains:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x85"), ":sunrise:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x86"), ":city_dusk:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x87"), ":city_sunset:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x89"), ":bridge_at_night:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x8c"), ":milky_way:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xa0"), ":carousel_horse:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xa1"), ":ferris_wheel:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xa2"), ":roller_coaster:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x82"), ":steam_locomotive:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x83"), ":railway_car:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x84"), ":bullettrain_side:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x85"), ":bullettrain_front:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x86"), ":train2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x87"), ":metro:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x88"), ":light_rail:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x89"), ":station:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x8a"), ":tram:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x9d"), ":monorail:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x9e"), ":mountain_railway:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x8b"), ":train:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x8c"), ":bus:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x8d"), ":oncoming_bus:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x8e"), ":trolleybus:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x90"), ":minibus:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x91"), ":ambulance:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x92"), ":fire_engine:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x93"), ":police_car:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x94"), ":oncoming_police_car:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x95"), ":taxi:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x96"), ":oncoming_taxi:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x97"), ":red_car:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x98"), ":oncoming_automobile:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x99"), ":blue_car:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x9a"), ":truck:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x9b"), ":articulated_lorry:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x9c"), ":tractor:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb2"), ":bike:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\xb4"), ":scooter:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\xb5"), ":motor_scooter:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x8f"), ":busstop:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\xa3"), ":motorway:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\xa4"), ":railway_track:"}, + Emoji{QString::fromUtf8("\xe2\x9b\xbd"), ":fuelpump:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa8"), ":rotating_light:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa5"), ":traffic_light:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa6"), ":vertical_traffic_light:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa7"), ":construction:"}, + Emoji{QString::fromUtf8("\xe2\x9a\x93"), ":anchor:"}, + Emoji{QString::fromUtf8("\xe2\x9b\xb5"), ":sailboat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\xb6"), ":canoe:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa4"), ":speedboat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\xb3"), ":cruise_ship:"}, + Emoji{QString::fromUtf8("\xe2\x9b\xb4"), ":ferry:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\xa5"), ":motorboat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa2"), ":ship:"}, + Emoji{QString::fromUtf8("\xe2\x9c\x88"), ":airplane:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\xa9"), ":airplane_small:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\xab"), ":airplane_departure:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\xac"), ":airplane_arriving:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xba"), ":seat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x81"), ":helicopter:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x9f"), ":suspension_railway:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa0"), ":mountain_cableway:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa1"), ":aerial_tramway:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\x80"), ":rocket:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\xb0"), ":satellite_orbital:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xa0"), ":stars:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x88"), ":rainbow:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x86"), ":fireworks:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x87"), ":sparkler:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x91"), ":rice_scene:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\x81"), ":checkered_flag:"}, }; const QList EmojiProvider::objects = { - Emoji{ QString::fromUtf8("\xe2\x98\xa0"), ":skull_crossbones:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\x8c"), ":love_letter:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\xa3"), ":bomb:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x95\xb3"), ":hole:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9b\x8d"), ":shopping_bags:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\xbf"), ":prayer_beads:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\x8e"), ":gem:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\xaa"), ":knife:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\xba"), ":amphora:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x97\xba"), ":map:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\x88"), ":barber:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x96\xbc"), ":frame_photo:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9b\x8e"), ":bellhop:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xaa"), ":door:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9b\x8c"), ":sleeping_accommodation:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9b\x8f"), ":bed:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9b\x8b"), ":couch:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xbd"), ":toilet:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xbf"), ":shower:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9b\x81"), ":bathtub:" }, - Emoji{ QString::fromUtf8("\xe2\x8c\x9b"), ":hourglass:" }, - Emoji{ QString::fromUtf8("\xe2\x8f\xb3"), ":hourglass_flowing_sand:" }, - Emoji{ QString::fromUtf8("\xe2\x8c\x9a"), ":watch:" }, - Emoji{ QString::fromUtf8("\xe2\x8f\xb0"), ":alarm_clock:" }, - Emoji{ QString::fromUtf8("\xe2\x8f\xb1"), ":stopwatch:" }, - Emoji{ QString::fromUtf8("\xe2\x8f\xb2"), ":timer:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x95\xb0"), ":clock:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\xa1"), ":thermometer:" }, - Emoji{ QString::fromUtf8("\xe2\x9b\xb1"), ":beach_umbrella:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\x88"), ":balloon:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\x89"), ":tada:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\x8a"), ":confetti_ball:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\x8e"), ":dolls:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\x8f"), ":flags:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\x90"), ":wind_chime:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\x80"), ":ribbon:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\x81"), ":gift:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x95\xb9"), ":joystick:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\xaf"), ":postal_horn:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\x99"), ":microphone2:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\x9a"), ":level_slider:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\x9b"), ":control_knobs:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\xbb"), ":radio:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\xb1"), ":iphone:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\xb2"), ":calling:" }, - Emoji{ QString::fromUtf8("\xe2\x98\x8e"), ":telephone:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\x9e"), ":telephone_receiver:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\x9f"), ":pager:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\xa0"), ":fax:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\x8b"), ":battery:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\x8c"), ":electric_plug:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\xbb"), ":computer:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x96\xa5"), ":desktop:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x96\xa8"), ":printer:" }, - Emoji{ QString::fromUtf8("\xe2\x8c\xa8"), ":keyboard:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x96\xb1"), ":mouse_three_button:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x96\xb2"), ":trackball:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\xbd"), ":minidisc:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\xbe"), ":floppy_disk:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\xbf"), ":cd:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\x80"), ":dvd:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\xa5"), ":movie_camera:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\x9e"), ":film_frames:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\xbd"), ":projector:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\xba"), ":tv:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\xb7"), ":camera:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\xb8"), ":camera_with_flash:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\xb9"), ":video_camera:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\xbc"), ":vhs:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\x8d"), ":mag:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\x8e"), ":mag_right:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\xac"), ":microscope:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\xad"), ":telescope:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\xa1"), ":satellite:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x95\xaf"), ":candle:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\xa1"), ":bulb:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\xa6"), ":flashlight:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\xae"), ":izakaya_lantern:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\x94"), ":notebook_with_decorative_cover:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\x95"), ":closed_book:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\x96"), ":book:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\x97"), ":green_book:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\x98"), ":blue_book:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\x99"), ":orange_book:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\x9a"), ":books:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\x93"), ":notebook:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\x92"), ":ledger:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\x83"), ":page_with_curl:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\x9c"), ":scroll:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\x84"), ":page_facing_up:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\xb0"), ":newspaper:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x97\x9e"), ":newspaper2:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\x91"), ":bookmark_tabs:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\x96"), ":bookmark:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\xb7"), ":label:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\xb0"), ":moneybag:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\xb4"), ":yen:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\xb5"), ":dollar:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\xb6"), ":euro:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\xb7"), ":pound:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\xb8"), ":money_with_wings:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\xb3"), ":credit_card:" }, - Emoji{ QString::fromUtf8("\xe2\x9c\x89"), ":envelope:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\xa7"), ":e-mail:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\xa8"), ":incoming_envelope:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\xa9"), ":envelope_with_arrow:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\xa4"), ":outbox_tray:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\xa5"), ":inbox_tray:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\xa6"), ":package:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\xab"), ":mailbox:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\xaa"), ":mailbox_closed:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\xac"), ":mailbox_with_mail:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\xad"), ":mailbox_with_no_mail:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\xae"), ":postbox:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x97\xb3"), ":ballot_box:" }, - Emoji{ QString::fromUtf8("\xe2\x9c\x8f"), ":pencil2:" }, - Emoji{ QString::fromUtf8("\xe2\x9c\x92"), ":black_nib:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x96\x8b"), ":pen_fountain:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x96\x8a"), ":pen_ballpoint:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x96\x8c"), ":paintbrush:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x96\x8d"), ":crayon:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\x9d"), ":pencil:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\x81"), ":file_folder:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\x82"), ":open_file_folder:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x97\x82"), ":dividers:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\x85"), ":date:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\x86"), ":calendar:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x97\x92"), ":notepad_spiral:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x97\x93"), ":calendar_spiral:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\x87"), ":card_index:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\x88"), ":chart_with_upwards_trend:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\x89"), ":chart_with_downwards_trend:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\x8a"), ":bar_chart:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\x8b"), ":clipboard:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\x8c"), ":pushpin:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\x8d"), ":round_pushpin:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\x8e"), ":paperclip:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x96\x87"), ":paperclips:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\x8f"), ":straight_ruler:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\x90"), ":triangular_ruler:" }, - Emoji{ QString::fromUtf8("\xe2\x9c\x82"), ":scissors:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x97\x83"), ":card_box:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x97\x84"), ":file_cabinet:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x97\x91"), ":wastebasket:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\x92"), ":lock:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\x93"), ":unlock:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\x8f"), ":lock_with_ink_pen:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\x90"), ":closed_lock_with_key:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\x91"), ":key:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x97\x9d"), ":key2:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\xa8"), ":hammer:" }, - Emoji{ QString::fromUtf8("\xe2\x9b\x8f"), ":pick:" }, - Emoji{ QString::fromUtf8("\xe2\x9a\x92"), ":hammer_pick:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9b\xa0"), ":tools:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x97\xa1"), ":dagger:" }, - Emoji{ QString::fromUtf8("\xe2\x9a\x94"), ":crossed_swords:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\xab"), ":gun:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9b\xa1"), ":shield:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\xa7"), ":wrench:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\xa9"), ":nut_and_bolt:" }, - Emoji{ QString::fromUtf8("\xe2\x9a\x99"), ":gear:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x97\x9c"), ":compression:" }, - Emoji{ QString::fromUtf8("\xe2\x9a\x97"), ":alembic:" }, - Emoji{ QString::fromUtf8("\xe2\x9a\x96"), ":scales:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\x97"), ":link:" }, - Emoji{ QString::fromUtf8("\xe2\x9b\x93"), ":chains:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\x89"), ":syringe:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\x8a"), ":pill:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xac"), ":smoking:" }, - Emoji{ QString::fromUtf8("\xe2\x9a\xb0"), ":coffin:" }, - Emoji{ QString::fromUtf8("\xe2\x9a\xb1"), ":urn:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x97\xbf"), ":moyai:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9b\xa2"), ":oil:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\xae"), ":crystal_ball:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9b\x92"), ":shopping_cart:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xa9"), ":triangular_flag_on_post:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\x8c"), ":crossed_flags:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\xb4"), ":flag_black:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\xb3"), ":flag_white:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\xb3\xf0\x9f\x8c\x88"), ":rainbow_flag:" }, + Emoji{QString::fromUtf8("\xe2\x98\xa0"), ":skull_crossbones:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x8c"), ":love_letter:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xa3"), ":bomb:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\xb3"), ":hole:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\x8d"), ":shopping_bags:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xbf"), ":prayer_beads:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x8e"), ":gem:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xaa"), ":knife:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xba"), ":amphora:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x97\xba"), ":map:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x88"), ":barber:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x96\xbc"), ":frame_photo:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\x8e"), ":bellhop:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xaa"), ":door:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\x8c"), ":sleeping_accommodation:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\x8f"), ":bed:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\x8b"), ":couch:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xbd"), ":toilet:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xbf"), ":shower:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\x81"), ":bathtub:"}, + Emoji{QString::fromUtf8("\xe2\x8c\x9b"), ":hourglass:"}, + Emoji{QString::fromUtf8("\xe2\x8f\xb3"), ":hourglass_flowing_sand:"}, + Emoji{QString::fromUtf8("\xe2\x8c\x9a"), ":watch:"}, + Emoji{QString::fromUtf8("\xe2\x8f\xb0"), ":alarm_clock:"}, + Emoji{QString::fromUtf8("\xe2\x8f\xb1"), ":stopwatch:"}, + Emoji{QString::fromUtf8("\xe2\x8f\xb2"), ":timer:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\xb0"), ":clock:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\xa1"), ":thermometer:"}, + Emoji{QString::fromUtf8("\xe2\x9b\xb1"), ":beach_umbrella:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x88"), ":balloon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x89"), ":tada:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x8a"), ":confetti_ball:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x8e"), ":dolls:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x8f"), ":flags:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x90"), ":wind_chime:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x80"), ":ribbon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x81"), ":gift:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\xb9"), ":joystick:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xaf"), ":postal_horn:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x99"), ":microphone2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x9a"), ":level_slider:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x9b"), ":control_knobs:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xbb"), ":radio:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xb1"), ":iphone:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xb2"), ":calling:"}, + Emoji{QString::fromUtf8("\xe2\x98\x8e"), ":telephone:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x9e"), ":telephone_receiver:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x9f"), ":pager:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xa0"), ":fax:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x8b"), ":battery:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x8c"), ":electric_plug:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xbb"), ":computer:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x96\xa5"), ":desktop:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x96\xa8"), ":printer:"}, + Emoji{QString::fromUtf8("\xe2\x8c\xa8"), ":keyboard:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x96\xb1"), ":mouse_three_button:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x96\xb2"), ":trackball:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xbd"), ":minidisc:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xbe"), ":floppy_disk:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xbf"), ":cd:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x80"), ":dvd:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xa5"), ":movie_camera:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x9e"), ":film_frames:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xbd"), ":projector:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xba"), ":tv:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xb7"), ":camera:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xb8"), ":camera_with_flash:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xb9"), ":video_camera:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xbc"), ":vhs:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x8d"), ":mag:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x8e"), ":mag_right:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xac"), ":microscope:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xad"), ":telescope:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xa1"), ":satellite:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\xaf"), ":candle:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xa1"), ":bulb:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xa6"), ":flashlight:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xae"), ":izakaya_lantern:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x94"), ":notebook_with_decorative_cover:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x95"), ":closed_book:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x96"), ":book:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x97"), ":green_book:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x98"), ":blue_book:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x99"), ":orange_book:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x9a"), ":books:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x93"), ":notebook:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x92"), ":ledger:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x83"), ":page_with_curl:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x9c"), ":scroll:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x84"), ":page_facing_up:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xb0"), ":newspaper:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x97\x9e"), ":newspaper2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x91"), ":bookmark_tabs:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x96"), ":bookmark:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xb7"), ":label:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xb0"), ":moneybag:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xb4"), ":yen:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xb5"), ":dollar:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xb6"), ":euro:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xb7"), ":pound:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xb8"), ":money_with_wings:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xb3"), ":credit_card:"}, + Emoji{QString::fromUtf8("\xe2\x9c\x89"), ":envelope:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xa7"), ":e-mail:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xa8"), ":incoming_envelope:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xa9"), ":envelope_with_arrow:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xa4"), ":outbox_tray:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xa5"), ":inbox_tray:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xa6"), ":package:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xab"), ":mailbox:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xaa"), ":mailbox_closed:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xac"), ":mailbox_with_mail:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xad"), ":mailbox_with_no_mail:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xae"), ":postbox:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x97\xb3"), ":ballot_box:"}, + Emoji{QString::fromUtf8("\xe2\x9c\x8f"), ":pencil2:"}, + Emoji{QString::fromUtf8("\xe2\x9c\x92"), ":black_nib:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x96\x8b"), ":pen_fountain:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x96\x8a"), ":pen_ballpoint:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x96\x8c"), ":paintbrush:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x96\x8d"), ":crayon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x9d"), ":pencil:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x81"), ":file_folder:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x82"), ":open_file_folder:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x97\x82"), ":dividers:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x85"), ":date:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x86"), ":calendar:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x97\x92"), ":notepad_spiral:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x97\x93"), ":calendar_spiral:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x87"), ":card_index:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x88"), ":chart_with_upwards_trend:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x89"), ":chart_with_downwards_trend:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x8a"), ":bar_chart:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x8b"), ":clipboard:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x8c"), ":pushpin:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x8d"), ":round_pushpin:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x8e"), ":paperclip:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x96\x87"), ":paperclips:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x8f"), ":straight_ruler:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x90"), ":triangular_ruler:"}, + Emoji{QString::fromUtf8("\xe2\x9c\x82"), ":scissors:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x97\x83"), ":card_box:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x97\x84"), ":file_cabinet:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x97\x91"), ":wastebasket:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x92"), ":lock:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x93"), ":unlock:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x8f"), ":lock_with_ink_pen:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x90"), ":closed_lock_with_key:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x91"), ":key:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x97\x9d"), ":key2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xa8"), ":hammer:"}, + Emoji{QString::fromUtf8("\xe2\x9b\x8f"), ":pick:"}, + Emoji{QString::fromUtf8("\xe2\x9a\x92"), ":hammer_pick:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\xa0"), ":tools:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x97\xa1"), ":dagger:"}, + Emoji{QString::fromUtf8("\xe2\x9a\x94"), ":crossed_swords:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xab"), ":gun:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\xa1"), ":shield:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xa7"), ":wrench:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xa9"), ":nut_and_bolt:"}, + Emoji{QString::fromUtf8("\xe2\x9a\x99"), ":gear:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x97\x9c"), ":compression:"}, + Emoji{QString::fromUtf8("\xe2\x9a\x97"), ":alembic:"}, + Emoji{QString::fromUtf8("\xe2\x9a\x96"), ":scales:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x97"), ":link:"}, + Emoji{QString::fromUtf8("\xe2\x9b\x93"), ":chains:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x89"), ":syringe:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x8a"), ":pill:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xac"), ":smoking:"}, + Emoji{QString::fromUtf8("\xe2\x9a\xb0"), ":coffin:"}, + Emoji{QString::fromUtf8("\xe2\x9a\xb1"), ":urn:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x97\xbf"), ":moyai:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\xa2"), ":oil:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xae"), ":crystal_ball:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\x92"), ":shopping_cart:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xa9"), ":triangular_flag_on_post:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\x8c"), ":crossed_flags:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xb4"), ":flag_black:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xb3"), ":flag_white:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xb3\xf0\x9f\x8c\x88"), ":rainbow_flag:"}, }; const QList EmojiProvider::symbols = { - Emoji{ QString::fromUtf8("\xf0\x9f\x91\x81\xf0\x9f\x97\xa8"), ":eye_in_speech_bubble:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\x98"), ":cupid:" }, - Emoji{ QString::fromUtf8("\xe2\x9d\xa4"), ":heart:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\x93"), ":heartbeat:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\x94"), ":broken_heart:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\x95"), ":two_hearts:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\x96"), ":sparkling_heart:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\x97"), ":heartpulse:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\x99"), ":blue_heart:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\x9a"), ":green_heart:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\x9b"), ":yellow_heart:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\x9c"), ":purple_heart:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x96\xa4"), ":black_heart:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\x9d"), ":gift_heart:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\x9e"), ":revolving_hearts:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\x9f"), ":heart_decoration:" }, - Emoji{ QString::fromUtf8("\xe2\x9d\xa3"), ":heart_exclamation:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\xa2"), ":anger:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\xa5"), ":boom:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\xab"), ":dizzy:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\xac"), ":speech_balloon:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x97\xa8"), ":speech_left:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x97\xaf"), ":anger_right:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\xad"), ":thought_balloon:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\xae"), ":white_flower:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\x90"), ":globe_with_meridians:" }, - Emoji{ QString::fromUtf8("\xe2\x99\xa8"), ":hotsprings:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9b\x91"), ":octagonal_sign:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x95\x9b"), ":clock12:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x95\xa7"), ":clock1230:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x95\x90"), ":clock1:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x95\x9c"), ":clock130:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x95\x91"), ":clock2:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x95\x9d"), ":clock230:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x95\x92"), ":clock3:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x95\x9e"), ":clock330:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x95\x93"), ":clock4:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x95\x9f"), ":clock430:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x95\x94"), ":clock5:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x95\xa0"), ":clock530:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x95\x95"), ":clock6:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x95\xa1"), ":clock630:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x95\x96"), ":clock7:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x95\xa2"), ":clock730:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x95\x97"), ":clock8:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x95\xa3"), ":clock830:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x95\x98"), ":clock9:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x95\xa4"), ":clock930:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x95\x99"), ":clock10:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x95\xa5"), ":clock1030:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x95\x9a"), ":clock11:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x95\xa6"), ":clock1130:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8c\x80"), ":cyclone:" }, - Emoji{ QString::fromUtf8("\xe2\x99\xa0"), ":spades:" }, - Emoji{ QString::fromUtf8("\xe2\x99\xa5"), ":hearts:" }, - Emoji{ QString::fromUtf8("\xe2\x99\xa6"), ":diamonds:" }, - Emoji{ QString::fromUtf8("\xe2\x99\xa3"), ":clubs:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x83\x8f"), ":black_joker:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x80\x84"), ":mahjong:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\xb4"), ":flower_playing_cards:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\x87"), ":mute:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\x88"), ":speaker:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\x89"), ":sound:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\x8a"), ":loud_sound:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\xa2"), ":loudspeaker:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\xa3"), ":mega:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\x94"), ":bell:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\x95"), ":no_bell:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\xb5"), ":musical_note:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\xb6"), ":notes:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\xb9"), ":chart:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\xb1"), ":currency_exchange:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\xb2"), ":heavy_dollar_sign:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8f\xa7"), ":atm:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xae"), ":put_litter_in_its_place:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xb0"), ":potable_water:" }, - Emoji{ QString::fromUtf8("\xe2\x99\xbf"), ":wheelchair:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xb9"), ":mens:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xba"), ":womens:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xbb"), ":restroom:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xbc"), ":baby_symbol:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xbe"), ":wc:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9b\x82"), ":passport_control:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9b\x83"), ":customs:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9b\x84"), ":baggage_claim:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9b\x85"), ":left_luggage:" }, - Emoji{ QString::fromUtf8("\xe2\x9a\xa0"), ":warning:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xb8"), ":children_crossing:" }, - Emoji{ QString::fromUtf8("\xe2\x9b\x94"), ":no_entry:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xab"), ":no_entry_sign:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xb3"), ":no_bicycles:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xad"), ":no_smoking:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xaf"), ":do_not_litter:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xb1"), ":non-potable_water:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9a\xb7"), ":no_pedestrians:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\xb5"), ":no_mobile_phones:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\x9e"), ":underage:" }, - Emoji{ QString::fromUtf8("\xe2\x98\xa2"), ":radioactive:" }, - Emoji{ QString::fromUtf8("\xe2\x98\xa3"), ":biohazard:" }, - Emoji{ QString::fromUtf8("\xe2\xac\x86"), ":arrow_up:" }, - Emoji{ QString::fromUtf8("\xe2\x86\x97"), ":arrow_upper_right:" }, - Emoji{ QString::fromUtf8("\xe2\x9e\xa1"), ":arrow_right:" }, - Emoji{ QString::fromUtf8("\xe2\x86\x98"), ":arrow_lower_right:" }, - Emoji{ QString::fromUtf8("\xe2\xac\x87"), ":arrow_down:" }, - Emoji{ QString::fromUtf8("\xe2\x86\x99"), ":arrow_lower_left:" }, - Emoji{ QString::fromUtf8("\xe2\xac\x85"), ":arrow_left:" }, - Emoji{ QString::fromUtf8("\xe2\x86\x96"), ":arrow_upper_left:" }, - Emoji{ QString::fromUtf8("\xe2\x86\x95"), ":arrow_up_down:" }, - Emoji{ QString::fromUtf8("\xe2\x86\x94"), ":left_right_arrow:" }, - Emoji{ QString::fromUtf8("\xe2\x86\xa9"), ":leftwards_arrow_with_hook:" }, - Emoji{ QString::fromUtf8("\xe2\x86\xaa"), ":arrow_right_hook:" }, - Emoji{ QString::fromUtf8("\xe2\xa4\xb4"), ":arrow_heading_up:" }, - Emoji{ QString::fromUtf8("\xe2\xa4\xb5"), ":arrow_heading_down:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\x83"), ":arrows_clockwise:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\x84"), ":arrows_counterclockwise:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\x99"), ":back:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\x9a"), ":end:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\x9b"), ":on:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\x9c"), ":soon:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\x9d"), ":top:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x9b\x90"), ":place_of_worship:" }, - Emoji{ QString::fromUtf8("\xe2\x9a\x9b"), ":atom:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x95\x89"), ":om_symbol:" }, - Emoji{ QString::fromUtf8("\xe2\x9c\xa1"), ":star_of_david:" }, - Emoji{ QString::fromUtf8("\xe2\x98\xb8"), ":wheel_of_dharma:" }, - Emoji{ QString::fromUtf8("\xe2\x98\xaf"), ":yin_yang:" }, - Emoji{ QString::fromUtf8("\xe2\x9c\x9d"), ":cross:" }, - Emoji{ QString::fromUtf8("\xe2\x98\xa6"), ":orthodox_cross:" }, - Emoji{ QString::fromUtf8("\xe2\x98\xaa"), ":star_and_crescent:" }, - Emoji{ QString::fromUtf8("\xe2\x98\xae"), ":peace:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x95\x8e"), ":menorah:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\xaf"), ":six_pointed_star:" }, - Emoji{ QString::fromUtf8("\xe2\x99\x88"), ":aries:" }, - Emoji{ QString::fromUtf8("\xe2\x99\x89"), ":taurus:" }, - Emoji{ QString::fromUtf8("\xe2\x99\x8a"), ":gemini:" }, - Emoji{ QString::fromUtf8("\xe2\x99\x8b"), ":cancer:" }, - Emoji{ QString::fromUtf8("\xe2\x99\x8c"), ":leo:" }, - Emoji{ QString::fromUtf8("\xe2\x99\x8d"), ":virgo:" }, - Emoji{ QString::fromUtf8("\xe2\x99\x8e"), ":libra:" }, - Emoji{ QString::fromUtf8("\xe2\x99\x8f"), ":scorpius:" }, - Emoji{ QString::fromUtf8("\xe2\x99\x90"), ":sagittarius:" }, - Emoji{ QString::fromUtf8("\xe2\x99\x91"), ":capricorn:" }, - Emoji{ QString::fromUtf8("\xe2\x99\x92"), ":aquarius:" }, - Emoji{ QString::fromUtf8("\xe2\x99\x93"), ":pisces:" }, - Emoji{ QString::fromUtf8("\xe2\x9b\x8e"), ":ophiuchus:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\x80"), ":twisted_rightwards_arrows:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\x81"), ":repeat:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\x82"), ":repeat_one:" }, - Emoji{ QString::fromUtf8("\xe2\x96\xb6"), ":arrow_forward:" }, - Emoji{ QString::fromUtf8("\xe2\x8f\xa9"), ":fast_forward:" }, - Emoji{ QString::fromUtf8("\xe2\x8f\xad"), ":track_next:" }, - Emoji{ QString::fromUtf8("\xe2\x8f\xaf"), ":play_pause:" }, - Emoji{ QString::fromUtf8("\xe2\x97\x80"), ":arrow_backward:" }, - Emoji{ QString::fromUtf8("\xe2\x8f\xaa"), ":rewind:" }, - Emoji{ QString::fromUtf8("\xe2\x8f\xae"), ":track_previous:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\xbc"), ":arrow_up_small:" }, - Emoji{ QString::fromUtf8("\xe2\x8f\xab"), ":arrow_double_up:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\xbd"), ":arrow_down_small:" }, - Emoji{ QString::fromUtf8("\xe2\x8f\xac"), ":arrow_double_down:" }, - Emoji{ QString::fromUtf8("\xe2\x8f\xb8"), ":pause_button:" }, - Emoji{ QString::fromUtf8("\xe2\x8f\xb9"), ":stop_button:" }, - Emoji{ QString::fromUtf8("\xe2\x8f\xba"), ":record_button:" }, - Emoji{ QString::fromUtf8("\xe2\x8f\x8f"), ":eject:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x8e\xa6"), ":cinema:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\x85"), ":low_brightness:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\x86"), ":high_brightness:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\xb6"), ":signal_strength:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\xb3"), ":vibration_mode:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\xb4"), ":mobile_phone_off:" }, - Emoji{ QString::fromUtf8("\xe2\x99\xbb"), ":recycle:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x93\x9b"), ":name_badge:" }, - Emoji{ QString::fromUtf8("\xe2\x9a\x9c"), ":fleur-de-lis:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\xb0"), ":beginner:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\xb1"), ":trident:" }, - Emoji{ QString::fromUtf8("\xe2\xad\x95"), ":o:" }, - Emoji{ QString::fromUtf8("\xe2\x9c\x85"), ":white_check_mark:" }, - Emoji{ QString::fromUtf8("\xe2\x98\x91"), ":ballot_box_with_check:" }, - Emoji{ QString::fromUtf8("\xe2\x9c\x94"), ":heavy_check_mark:" }, - Emoji{ QString::fromUtf8("\xe2\x9c\x96"), ":heavy_multiplication_x:" }, - Emoji{ QString::fromUtf8("\xe2\x9d\x8c"), ":x:" }, - Emoji{ QString::fromUtf8("\xe2\x9d\x8e"), ":negative_squared_cross_mark:" }, - Emoji{ QString::fromUtf8("\xe2\x9e\x95"), ":heavy_plus_sign:" }, - Emoji{ QString::fromUtf8("\xe2\x9e\x96"), ":heavy_minus_sign:" }, - Emoji{ QString::fromUtf8("\xe2\x9e\x97"), ":heavy_division_sign:" }, - Emoji{ QString::fromUtf8("\xe2\x9e\xb0"), ":curly_loop:" }, - Emoji{ QString::fromUtf8("\xe2\x9e\xbf"), ":loop:" }, - Emoji{ QString::fromUtf8("\xe3\x80\xbd"), ":part_alternation_mark:" }, - Emoji{ QString::fromUtf8("\xe2\x9c\xb3"), ":eight_spoked_asterisk:" }, - Emoji{ QString::fromUtf8("\xe2\x9c\xb4"), ":eight_pointed_black_star:" }, - Emoji{ QString::fromUtf8("\xe2\x9d\x87"), ":sparkle:" }, - Emoji{ QString::fromUtf8("\xe2\x80\xbc"), ":bangbang:" }, - Emoji{ QString::fromUtf8("\xe2\x81\x89"), ":interrobang:" }, - Emoji{ QString::fromUtf8("\xe2\x9d\x93"), ":question:" }, - Emoji{ QString::fromUtf8("\xe2\x9d\x94"), ":grey_question:" }, - Emoji{ QString::fromUtf8("\xe2\x9d\x95"), ":grey_exclamation:" }, - Emoji{ QString::fromUtf8("\xe2\x9d\x97"), ":exclamation:" }, - Emoji{ QString::fromUtf8("\xe3\x80\xb0"), ":wavy_dash:" }, - Emoji{ QString::fromUtf8("\xc2\xa9"), ":copyright:" }, - Emoji{ QString::fromUtf8("\xc2\xae"), ":registered:" }, - Emoji{ QString::fromUtf8("\xe2\x84\xa2"), ":tm:" }, - Emoji{ QString::fromUtf8("#\xe2\x83\xa3"), ":hash:" }, - Emoji{ QString::fromUtf8("*\xe2\x83\xa3"), ":asterisk:" }, - Emoji{ QString::fromUtf8("0\xe2\x83\xa3"), ":zero:" }, - Emoji{ QString::fromUtf8("1\xe2\x83\xa3"), ":one:" }, - Emoji{ QString::fromUtf8("2\xe2\x83\xa3"), ":two:" }, - Emoji{ QString::fromUtf8("3\xe2\x83\xa3"), ":three:" }, - Emoji{ QString::fromUtf8("4\xe2\x83\xa3"), ":four:" }, - Emoji{ QString::fromUtf8("5\xe2\x83\xa3"), ":five:" }, - Emoji{ QString::fromUtf8("6\xe2\x83\xa3"), ":six:" }, - Emoji{ QString::fromUtf8("7\xe2\x83\xa3"), ":seven:" }, - Emoji{ QString::fromUtf8("8\xe2\x83\xa3"), ":eight:" }, - Emoji{ QString::fromUtf8("9\xe2\x83\xa3"), ":nine:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\x9f"), ":keycap_ten:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\xaf"), ":100:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\xa0"), ":capital_abcd:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\xa1"), ":abcd:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\xa2"), ":1234:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\xa3"), ":symbols:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\xa4"), ":abc:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x85\xb0"), ":a:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x86\x8e"), ":ab:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x85\xb1"), ":b:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x86\x91"), ":cl:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x86\x92"), ":cool:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x86\x93"), ":free:" }, - Emoji{ QString::fromUtf8("\xe2\x84\xb9"), ":information_source:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x86\x94"), ":id:" }, - Emoji{ QString::fromUtf8("\xe2\x93\x82"), ":m:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x86\x95"), ":new:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x86\x96"), ":ng:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x85\xbe"), ":o2:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x86\x97"), ":ok:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x85\xbf"), ":parking:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x86\x98"), ":sos:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x86\x99"), ":up:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x86\x9a"), ":vs:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x88\x81"), ":koko:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x88\x82"), ":sa:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x88\xb7"), ":u6708:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x88\xb6"), ":u6709:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x88\xaf"), ":u6307:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x89\x90"), ":ideograph_advantage:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x88\xb9"), ":u5272:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x88\x9a"), ":u7121:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x88\xb2"), ":u7981:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x89\x91"), ":accept:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x88\xb8"), ":u7533:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x88\xb4"), ":u5408:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x88\xb3"), ":u7a7a:" }, - Emoji{ QString::fromUtf8("\xe3\x8a\x97"), ":congratulations:" }, - Emoji{ QString::fromUtf8("\xe3\x8a\x99"), ":secret:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x88\xba"), ":u55b6:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x88\xb5"), ":u6e80:" }, - Emoji{ QString::fromUtf8("\xe2\x96\xaa"), ":black_small_square:" }, - Emoji{ QString::fromUtf8("\xe2\x96\xab"), ":white_small_square:" }, - Emoji{ QString::fromUtf8("\xe2\x97\xbb"), ":white_medium_square:" }, - Emoji{ QString::fromUtf8("\xe2\x97\xbc"), ":black_medium_square:" }, - Emoji{ QString::fromUtf8("\xe2\x97\xbd"), ":white_medium_small_square:" }, - Emoji{ QString::fromUtf8("\xe2\x97\xbe"), ":black_medium_small_square:" }, - Emoji{ QString::fromUtf8("\xe2\xac\x9b"), ":black_large_square:" }, - Emoji{ QString::fromUtf8("\xe2\xac\x9c"), ":white_large_square:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\xb6"), ":large_orange_diamond:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\xb7"), ":large_blue_diamond:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\xb8"), ":small_orange_diamond:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\xb9"), ":small_blue_diamond:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\xba"), ":small_red_triangle:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\xbb"), ":small_red_triangle_down:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x92\xa0"), ":diamond_shape_with_a_dot_inside:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\x98"), ":radio_button:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\xb2"), ":black_square_button:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\xb3"), ":white_square_button:" }, - Emoji{ QString::fromUtf8("\xe2\x9a\xaa"), ":white_circle:" }, - Emoji{ QString::fromUtf8("\xe2\x9a\xab"), ":black_circle:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\xb4"), ":red_circle:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x94\xb5"), ":blue_circle:" }, + Emoji{QString::fromUtf8("\xf0\x9f\x91\x81\xf0\x9f\x97\xa8"), ":eye_in_speech_bubble:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x98"), ":cupid:"}, + Emoji{QString::fromUtf8("\xe2\x9d\xa4"), ":heart:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x93"), ":heartbeat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x94"), ":broken_heart:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x95"), ":two_hearts:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x96"), ":sparkling_heart:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x97"), ":heartpulse:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x99"), ":blue_heart:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x9a"), ":green_heart:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x9b"), ":yellow_heart:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x9c"), ":purple_heart:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x96\xa4"), ":black_heart:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x9d"), ":gift_heart:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x9e"), ":revolving_hearts:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\x9f"), ":heart_decoration:"}, + Emoji{QString::fromUtf8("\xe2\x9d\xa3"), ":heart_exclamation:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xa2"), ":anger:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xa5"), ":boom:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xab"), ":dizzy:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xac"), ":speech_balloon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x97\xa8"), ":speech_left:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x97\xaf"), ":anger_right:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xad"), ":thought_balloon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xae"), ":white_flower:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x90"), ":globe_with_meridians:"}, + Emoji{QString::fromUtf8("\xe2\x99\xa8"), ":hotsprings:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\x91"), ":octagonal_sign:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x9b"), ":clock12:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\xa7"), ":clock1230:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x90"), ":clock1:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x9c"), ":clock130:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x91"), ":clock2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x9d"), ":clock230:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x92"), ":clock3:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x9e"), ":clock330:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x93"), ":clock4:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x9f"), ":clock430:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x94"), ":clock5:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\xa0"), ":clock530:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x95"), ":clock6:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\xa1"), ":clock630:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x96"), ":clock7:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\xa2"), ":clock730:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x97"), ":clock8:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\xa3"), ":clock830:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x98"), ":clock9:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\xa4"), ":clock930:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x99"), ":clock10:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\xa5"), ":clock1030:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x9a"), ":clock11:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\xa6"), ":clock1130:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8c\x80"), ":cyclone:"}, + Emoji{QString::fromUtf8("\xe2\x99\xa0"), ":spades:"}, + Emoji{QString::fromUtf8("\xe2\x99\xa5"), ":hearts:"}, + Emoji{QString::fromUtf8("\xe2\x99\xa6"), ":diamonds:"}, + Emoji{QString::fromUtf8("\xe2\x99\xa3"), ":clubs:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x83\x8f"), ":black_joker:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x80\x84"), ":mahjong:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xb4"), ":flower_playing_cards:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x87"), ":mute:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x88"), ":speaker:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x89"), ":sound:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x8a"), ":loud_sound:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xa2"), ":loudspeaker:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xa3"), ":mega:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x94"), ":bell:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x95"), ":no_bell:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xb5"), ":musical_note:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xb6"), ":notes:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xb9"), ":chart:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xb1"), ":currency_exchange:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xb2"), ":heavy_dollar_sign:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8f\xa7"), ":atm:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xae"), ":put_litter_in_its_place:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb0"), ":potable_water:"}, + Emoji{QString::fromUtf8("\xe2\x99\xbf"), ":wheelchair:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb9"), ":mens:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xba"), ":womens:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xbb"), ":restroom:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xbc"), ":baby_symbol:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xbe"), ":wc:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\x82"), ":passport_control:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\x83"), ":customs:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\x84"), ":baggage_claim:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\x85"), ":left_luggage:"}, + Emoji{QString::fromUtf8("\xe2\x9a\xa0"), ":warning:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb8"), ":children_crossing:"}, + Emoji{QString::fromUtf8("\xe2\x9b\x94"), ":no_entry:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xab"), ":no_entry_sign:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb3"), ":no_bicycles:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xad"), ":no_smoking:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xaf"), ":do_not_litter:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb1"), ":non-potable_water:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9a\xb7"), ":no_pedestrians:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xb5"), ":no_mobile_phones:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x9e"), ":underage:"}, + Emoji{QString::fromUtf8("\xe2\x98\xa2"), ":radioactive:"}, + Emoji{QString::fromUtf8("\xe2\x98\xa3"), ":biohazard:"}, + Emoji{QString::fromUtf8("\xe2\xac\x86"), ":arrow_up:"}, + Emoji{QString::fromUtf8("\xe2\x86\x97"), ":arrow_upper_right:"}, + Emoji{QString::fromUtf8("\xe2\x9e\xa1"), ":arrow_right:"}, + Emoji{QString::fromUtf8("\xe2\x86\x98"), ":arrow_lower_right:"}, + Emoji{QString::fromUtf8("\xe2\xac\x87"), ":arrow_down:"}, + Emoji{QString::fromUtf8("\xe2\x86\x99"), ":arrow_lower_left:"}, + Emoji{QString::fromUtf8("\xe2\xac\x85"), ":arrow_left:"}, + Emoji{QString::fromUtf8("\xe2\x86\x96"), ":arrow_upper_left:"}, + Emoji{QString::fromUtf8("\xe2\x86\x95"), ":arrow_up_down:"}, + Emoji{QString::fromUtf8("\xe2\x86\x94"), ":left_right_arrow:"}, + Emoji{QString::fromUtf8("\xe2\x86\xa9"), ":leftwards_arrow_with_hook:"}, + Emoji{QString::fromUtf8("\xe2\x86\xaa"), ":arrow_right_hook:"}, + Emoji{QString::fromUtf8("\xe2\xa4\xb4"), ":arrow_heading_up:"}, + Emoji{QString::fromUtf8("\xe2\xa4\xb5"), ":arrow_heading_down:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x83"), ":arrows_clockwise:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x84"), ":arrows_counterclockwise:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x99"), ":back:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x9a"), ":end:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x9b"), ":on:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x9c"), ":soon:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x9d"), ":top:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x9b\x90"), ":place_of_worship:"}, + Emoji{QString::fromUtf8("\xe2\x9a\x9b"), ":atom:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x89"), ":om_symbol:"}, + Emoji{QString::fromUtf8("\xe2\x9c\xa1"), ":star_of_david:"}, + Emoji{QString::fromUtf8("\xe2\x98\xb8"), ":wheel_of_dharma:"}, + Emoji{QString::fromUtf8("\xe2\x98\xaf"), ":yin_yang:"}, + Emoji{QString::fromUtf8("\xe2\x9c\x9d"), ":cross:"}, + Emoji{QString::fromUtf8("\xe2\x98\xa6"), ":orthodox_cross:"}, + Emoji{QString::fromUtf8("\xe2\x98\xaa"), ":star_and_crescent:"}, + Emoji{QString::fromUtf8("\xe2\x98\xae"), ":peace:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x95\x8e"), ":menorah:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xaf"), ":six_pointed_star:"}, + Emoji{QString::fromUtf8("\xe2\x99\x88"), ":aries:"}, + Emoji{QString::fromUtf8("\xe2\x99\x89"), ":taurus:"}, + Emoji{QString::fromUtf8("\xe2\x99\x8a"), ":gemini:"}, + Emoji{QString::fromUtf8("\xe2\x99\x8b"), ":cancer:"}, + Emoji{QString::fromUtf8("\xe2\x99\x8c"), ":leo:"}, + Emoji{QString::fromUtf8("\xe2\x99\x8d"), ":virgo:"}, + Emoji{QString::fromUtf8("\xe2\x99\x8e"), ":libra:"}, + Emoji{QString::fromUtf8("\xe2\x99\x8f"), ":scorpius:"}, + Emoji{QString::fromUtf8("\xe2\x99\x90"), ":sagittarius:"}, + Emoji{QString::fromUtf8("\xe2\x99\x91"), ":capricorn:"}, + Emoji{QString::fromUtf8("\xe2\x99\x92"), ":aquarius:"}, + Emoji{QString::fromUtf8("\xe2\x99\x93"), ":pisces:"}, + Emoji{QString::fromUtf8("\xe2\x9b\x8e"), ":ophiuchus:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x80"), ":twisted_rightwards_arrows:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x81"), ":repeat:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x82"), ":repeat_one:"}, + Emoji{QString::fromUtf8("\xe2\x96\xb6"), ":arrow_forward:"}, + Emoji{QString::fromUtf8("\xe2\x8f\xa9"), ":fast_forward:"}, + Emoji{QString::fromUtf8("\xe2\x8f\xad"), ":track_next:"}, + Emoji{QString::fromUtf8("\xe2\x8f\xaf"), ":play_pause:"}, + Emoji{QString::fromUtf8("\xe2\x97\x80"), ":arrow_backward:"}, + Emoji{QString::fromUtf8("\xe2\x8f\xaa"), ":rewind:"}, + Emoji{QString::fromUtf8("\xe2\x8f\xae"), ":track_previous:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xbc"), ":arrow_up_small:"}, + Emoji{QString::fromUtf8("\xe2\x8f\xab"), ":arrow_double_up:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xbd"), ":arrow_down_small:"}, + Emoji{QString::fromUtf8("\xe2\x8f\xac"), ":arrow_double_down:"}, + Emoji{QString::fromUtf8("\xe2\x8f\xb8"), ":pause_button:"}, + Emoji{QString::fromUtf8("\xe2\x8f\xb9"), ":stop_button:"}, + Emoji{QString::fromUtf8("\xe2\x8f\xba"), ":record_button:"}, + Emoji{QString::fromUtf8("\xe2\x8f\x8f"), ":eject:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x8e\xa6"), ":cinema:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x85"), ":low_brightness:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x86"), ":high_brightness:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xb6"), ":signal_strength:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xb3"), ":vibration_mode:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\xb4"), ":mobile_phone_off:"}, + Emoji{QString::fromUtf8("\xe2\x99\xbb"), ":recycle:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x93\x9b"), ":name_badge:"}, + Emoji{QString::fromUtf8("\xe2\x9a\x9c"), ":fleur-de-lis:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xb0"), ":beginner:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xb1"), ":trident:"}, + Emoji{QString::fromUtf8("\xe2\xad\x95"), ":o:"}, + Emoji{QString::fromUtf8("\xe2\x9c\x85"), ":white_check_mark:"}, + Emoji{QString::fromUtf8("\xe2\x98\x91"), ":ballot_box_with_check:"}, + Emoji{QString::fromUtf8("\xe2\x9c\x94"), ":heavy_check_mark:"}, + Emoji{QString::fromUtf8("\xe2\x9c\x96"), ":heavy_multiplication_x:"}, + Emoji{QString::fromUtf8("\xe2\x9d\x8c"), ":x:"}, + Emoji{QString::fromUtf8("\xe2\x9d\x8e"), ":negative_squared_cross_mark:"}, + Emoji{QString::fromUtf8("\xe2\x9e\x95"), ":heavy_plus_sign:"}, + Emoji{QString::fromUtf8("\xe2\x9e\x96"), ":heavy_minus_sign:"}, + Emoji{QString::fromUtf8("\xe2\x9e\x97"), ":heavy_division_sign:"}, + Emoji{QString::fromUtf8("\xe2\x9e\xb0"), ":curly_loop:"}, + Emoji{QString::fromUtf8("\xe2\x9e\xbf"), ":loop:"}, + Emoji{QString::fromUtf8("\xe3\x80\xbd"), ":part_alternation_mark:"}, + Emoji{QString::fromUtf8("\xe2\x9c\xb3"), ":eight_spoked_asterisk:"}, + Emoji{QString::fromUtf8("\xe2\x9c\xb4"), ":eight_pointed_black_star:"}, + Emoji{QString::fromUtf8("\xe2\x9d\x87"), ":sparkle:"}, + Emoji{QString::fromUtf8("\xe2\x80\xbc"), ":bangbang:"}, + Emoji{QString::fromUtf8("\xe2\x81\x89"), ":interrobang:"}, + Emoji{QString::fromUtf8("\xe2\x9d\x93"), ":question:"}, + Emoji{QString::fromUtf8("\xe2\x9d\x94"), ":grey_question:"}, + Emoji{QString::fromUtf8("\xe2\x9d\x95"), ":grey_exclamation:"}, + Emoji{QString::fromUtf8("\xe2\x9d\x97"), ":exclamation:"}, + Emoji{QString::fromUtf8("\xe3\x80\xb0"), ":wavy_dash:"}, + Emoji{QString::fromUtf8("\xc2\xa9"), ":copyright:"}, + Emoji{QString::fromUtf8("\xc2\xae"), ":registered:"}, + Emoji{QString::fromUtf8("\xe2\x84\xa2"), ":tm:"}, + Emoji{QString::fromUtf8("#\xe2\x83\xa3"), ":hash:"}, + Emoji{QString::fromUtf8("*\xe2\x83\xa3"), ":asterisk:"}, + Emoji{QString::fromUtf8("0\xe2\x83\xa3"), ":zero:"}, + Emoji{QString::fromUtf8("1\xe2\x83\xa3"), ":one:"}, + Emoji{QString::fromUtf8("2\xe2\x83\xa3"), ":two:"}, + Emoji{QString::fromUtf8("3\xe2\x83\xa3"), ":three:"}, + Emoji{QString::fromUtf8("4\xe2\x83\xa3"), ":four:"}, + Emoji{QString::fromUtf8("5\xe2\x83\xa3"), ":five:"}, + Emoji{QString::fromUtf8("6\xe2\x83\xa3"), ":six:"}, + Emoji{QString::fromUtf8("7\xe2\x83\xa3"), ":seven:"}, + Emoji{QString::fromUtf8("8\xe2\x83\xa3"), ":eight:"}, + Emoji{QString::fromUtf8("9\xe2\x83\xa3"), ":nine:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x9f"), ":keycap_ten:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xaf"), ":100:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xa0"), ":capital_abcd:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xa1"), ":abcd:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xa2"), ":1234:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xa3"), ":symbols:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xa4"), ":abc:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x85\xb0"), ":a:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x86\x8e"), ":ab:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x85\xb1"), ":b:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x86\x91"), ":cl:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x86\x92"), ":cool:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x86\x93"), ":free:"}, + Emoji{QString::fromUtf8("\xe2\x84\xb9"), ":information_source:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x86\x94"), ":id:"}, + Emoji{QString::fromUtf8("\xe2\x93\x82"), ":m:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x86\x95"), ":new:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x86\x96"), ":ng:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x85\xbe"), ":o2:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x86\x97"), ":ok:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x85\xbf"), ":parking:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x86\x98"), ":sos:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x86\x99"), ":up:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x86\x9a"), ":vs:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x88\x81"), ":koko:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x88\x82"), ":sa:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x88\xb7"), ":u6708:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x88\xb6"), ":u6709:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x88\xaf"), ":u6307:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x89\x90"), ":ideograph_advantage:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x88\xb9"), ":u5272:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x88\x9a"), ":u7121:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x88\xb2"), ":u7981:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x89\x91"), ":accept:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x88\xb8"), ":u7533:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x88\xb4"), ":u5408:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x88\xb3"), ":u7a7a:"}, + Emoji{QString::fromUtf8("\xe3\x8a\x97"), ":congratulations:"}, + Emoji{QString::fromUtf8("\xe3\x8a\x99"), ":secret:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x88\xba"), ":u55b6:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x88\xb5"), ":u6e80:"}, + Emoji{QString::fromUtf8("\xe2\x96\xaa"), ":black_small_square:"}, + Emoji{QString::fromUtf8("\xe2\x96\xab"), ":white_small_square:"}, + Emoji{QString::fromUtf8("\xe2\x97\xbb"), ":white_medium_square:"}, + Emoji{QString::fromUtf8("\xe2\x97\xbc"), ":black_medium_square:"}, + Emoji{QString::fromUtf8("\xe2\x97\xbd"), ":white_medium_small_square:"}, + Emoji{QString::fromUtf8("\xe2\x97\xbe"), ":black_medium_small_square:"}, + Emoji{QString::fromUtf8("\xe2\xac\x9b"), ":black_large_square:"}, + Emoji{QString::fromUtf8("\xe2\xac\x9c"), ":white_large_square:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xb6"), ":large_orange_diamond:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xb7"), ":large_blue_diamond:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xb8"), ":small_orange_diamond:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xb9"), ":small_blue_diamond:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xba"), ":small_red_triangle:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xbb"), ":small_red_triangle_down:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x92\xa0"), ":diamond_shape_with_a_dot_inside:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\x98"), ":radio_button:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xb2"), ":black_square_button:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xb3"), ":white_square_button:"}, + Emoji{QString::fromUtf8("\xe2\x9a\xaa"), ":white_circle:"}, + Emoji{QString::fromUtf8("\xe2\x9a\xab"), ":black_circle:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xb4"), ":red_circle:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x94\xb5"), ":blue_circle:"}, }; const QList EmojiProvider::flags = { - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xa8"), ":flag_ac:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xa9"), ":flag_ad:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xaa"), ":flag_ae:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xab"), ":flag_af:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xac"), ":flag_ag:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xae"), ":flag_ai:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xb1"), ":flag_al:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xb2"), ":flag_am:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xb4"), ":flag_ao:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xb6"), ":flag_aq:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xb7"), ":flag_ar:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xb8"), ":flag_as:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xb9"), ":flag_at:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xba"), ":flag_au:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xbc"), ":flag_aw:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xbd"), ":flag_ax:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xbf"), ":flag_az:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xa6"), ":flag_ba:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xa7"), ":flag_bb:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xa9"), ":flag_bd:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xaa"), ":flag_be:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xab"), ":flag_bf:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xac"), ":flag_bg:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xad"), ":flag_bh:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xae"), ":flag_bi:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xaf"), ":flag_bj:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xb1"), ":flag_bl:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xb2"), ":flag_bm:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xb3"), ":flag_bn:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xb4"), ":flag_bo:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xb6"), ":flag_bq:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xb7"), ":flag_br:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xb8"), ":flag_bs:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xb9"), ":flag_bt:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xbb"), ":flag_bv:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xbc"), ":flag_bw:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xbe"), ":flag_by:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xbf"), ":flag_bz:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xa6"), ":flag_ca:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xa8"), ":flag_cc:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xa9"), ":flag_cd:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xab"), ":flag_cf:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xac"), ":flag_cg:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xad"), ":flag_ch:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xae"), ":flag_ci:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xb0"), ":flag_ck:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xb1"), ":flag_cl:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xb2"), ":flag_cm:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xb3"), ":flag_cn:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xb4"), ":flag_co:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xb5"), ":flag_cp:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xb7"), ":flag_cr:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xba"), ":flag_cu:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xbb"), ":flag_cv:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xbc"), ":flag_cw:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xbd"), ":flag_cx:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xbe"), ":flag_cy:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xbf"), ":flag_cz:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa9\xf0\x9f\x87\xaa"), ":flag_de:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa9\xf0\x9f\x87\xac"), ":flag_dg:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa9\xf0\x9f\x87\xaf"), ":flag_dj:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa9\xf0\x9f\x87\xb0"), ":flag_dk:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa9\xf0\x9f\x87\xb2"), ":flag_dm:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa9\xf0\x9f\x87\xb4"), ":flag_do:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xa9\xf0\x9f\x87\xbf"), ":flag_dz:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xa6"), ":flag_ea:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xa8"), ":flag_ec:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xaa"), ":flag_ee:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xac"), ":flag_eg:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xad"), ":flag_eh:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xb7"), ":flag_er:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xb8"), ":flag_es:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xb9"), ":flag_et:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xba"), ":flag_eu:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xab\xf0\x9f\x87\xae"), ":flag_fi:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xab\xf0\x9f\x87\xaf"), ":flag_fj:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xab\xf0\x9f\x87\xb0"), ":flag_fk:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xab\xf0\x9f\x87\xb2"), ":flag_fm:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xab\xf0\x9f\x87\xb4"), ":flag_fo:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xab\xf0\x9f\x87\xb7"), ":flag_fr:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xa6"), ":flag_ga:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xa7"), ":flag_gb:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xa9"), ":flag_gd:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xaa"), ":flag_ge:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xab"), ":flag_gf:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xac"), ":flag_gg:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xad"), ":flag_gh:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xae"), ":flag_gi:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xb1"), ":flag_gl:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xb2"), ":flag_gm:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xb3"), ":flag_gn:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xb5"), ":flag_gp:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xb6"), ":flag_gq:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xb7"), ":flag_gr:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xb8"), ":flag_gs:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xb9"), ":flag_gt:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xba"), ":flag_gu:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xbc"), ":flag_gw:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xbe"), ":flag_gy:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xad\xf0\x9f\x87\xb0"), ":flag_hk:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xad\xf0\x9f\x87\xb2"), ":flag_hm:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xad\xf0\x9f\x87\xb3"), ":flag_hn:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xad\xf0\x9f\x87\xb7"), ":flag_hr:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xad\xf0\x9f\x87\xb9"), ":flag_ht:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xad\xf0\x9f\x87\xba"), ":flag_hu:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xa8"), ":flag_ic:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xa9"), ":flag_id:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xaa"), ":flag_ie:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xb1"), ":flag_il:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xb2"), ":flag_im:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xb3"), ":flag_in:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xb4"), ":flag_io:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xb6"), ":flag_iq:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xb7"), ":flag_ir:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xb8"), ":flag_is:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xb9"), ":flag_it:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xaf\xf0\x9f\x87\xaa"), ":flag_je:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xaf\xf0\x9f\x87\xb2"), ":flag_jm:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xaf\xf0\x9f\x87\xb4"), ":flag_jo:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xaf\xf0\x9f\x87\xb5"), ":flag_jp:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xaa"), ":flag_ke:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xac"), ":flag_kg:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xad"), ":flag_kh:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xae"), ":flag_ki:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xb2"), ":flag_km:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xb3"), ":flag_kn:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xb5"), ":flag_kp:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xb7"), ":flag_kr:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xbc"), ":flag_kw:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xbe"), ":flag_ky:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xbf"), ":flag_kz:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xa6"), ":flag_la:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xa7"), ":flag_lb:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xa8"), ":flag_lc:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xae"), ":flag_li:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xb0"), ":flag_lk:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xb7"), ":flag_lr:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xb8"), ":flag_ls:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xb9"), ":flag_lt:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xba"), ":flag_lu:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xbb"), ":flag_lv:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xbe"), ":flag_ly:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xa6"), ":flag_ma:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xa8"), ":flag_mc:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xa9"), ":flag_md:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xaa"), ":flag_me:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xab"), ":flag_mf:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xac"), ":flag_mg:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xad"), ":flag_mh:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb0"), ":flag_mk:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb1"), ":flag_ml:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb2"), ":flag_mm:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb3"), ":flag_mn:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb4"), ":flag_mo:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb5"), ":flag_mp:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb6"), ":flag_mq:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb7"), ":flag_mr:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb8"), ":flag_ms:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb9"), ":flag_mt:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xba"), ":flag_mu:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xbb"), ":flag_mv:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xbc"), ":flag_mw:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xbd"), ":flag_mx:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xbe"), ":flag_my:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xbf"), ":flag_mz:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xa6"), ":flag_na:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xa8"), ":flag_nc:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xaa"), ":flag_ne:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xab"), ":flag_nf:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xac"), ":flag_ng:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xae"), ":flag_ni:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xb1"), ":flag_nl:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xb4"), ":flag_no:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xb5"), ":flag_np:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xb7"), ":flag_nr:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xba"), ":flag_nu:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xbf"), ":flag_nz:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb4\xf0\x9f\x87\xb2"), ":flag_om:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xa6"), ":flag_pa:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xaa"), ":flag_pe:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xab"), ":flag_pf:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xac"), ":flag_pg:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xad"), ":flag_ph:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xb0"), ":flag_pk:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xb1"), ":flag_pl:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xb2"), ":flag_pm:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xb3"), ":flag_pn:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xb7"), ":flag_pr:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xb8"), ":flag_ps:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xb9"), ":flag_pt:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xbc"), ":flag_pw:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xbe"), ":flag_py:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb6\xf0\x9f\x87\xa6"), ":flag_qa:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb7\xf0\x9f\x87\xaa"), ":flag_re:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb7\xf0\x9f\x87\xb4"), ":flag_ro:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb7\xf0\x9f\x87\xb8"), ":flag_rs:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb7\xf0\x9f\x87\xba"), ":flag_ru:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb7\xf0\x9f\x87\xbc"), ":flag_rw:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xa6"), ":flag_sa:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xa7"), ":flag_sb:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xa8"), ":flag_sc:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xa9"), ":flag_sd:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xaa"), ":flag_se:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xac"), ":flag_sg:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xad"), ":flag_sh:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xae"), ":flag_si:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xaf"), ":flag_sj:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xb0"), ":flag_sk:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xb1"), ":flag_sl:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xb2"), ":flag_sm:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xb3"), ":flag_sn:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xb4"), ":flag_so:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xb7"), ":flag_sr:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xb8"), ":flag_ss:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xb9"), ":flag_st:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xbb"), ":flag_sv:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xbd"), ":flag_sx:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xbe"), ":flag_sy:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xbf"), ":flag_sz:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xa6"), ":flag_ta:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xa8"), ":flag_tc:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xa9"), ":flag_td:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xab"), ":flag_tf:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xac"), ":flag_tg:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xad"), ":flag_th:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xaf"), ":flag_tj:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xb0"), ":flag_tk:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xb1"), ":flag_tl:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xb2"), ":flag_tm:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xb3"), ":flag_tn:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xb4"), ":flag_to:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xb7"), ":flag_tr:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xb9"), ":flag_tt:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xbb"), ":flag_tv:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xbc"), ":flag_tw:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xbf"), ":flag_tz:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xba\xf0\x9f\x87\xa6"), ":flag_ua:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xba\xf0\x9f\x87\xac"), ":flag_ug:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xba\xf0\x9f\x87\xb2"), ":flag_um:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xba\xf0\x9f\x87\xb8"), ":flag_us:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xba\xf0\x9f\x87\xbe"), ":flag_uy:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xba\xf0\x9f\x87\xbf"), ":flag_uz:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xbb\xf0\x9f\x87\xa6"), ":flag_va:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xbb\xf0\x9f\x87\xa8"), ":flag_vc:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xbb\xf0\x9f\x87\xaa"), ":flag_ve:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xbb\xf0\x9f\x87\xac"), ":flag_vg:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xbb\xf0\x9f\x87\xae"), ":flag_vi:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xbb\xf0\x9f\x87\xb3"), ":flag_vn:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xbb\xf0\x9f\x87\xba"), ":flag_vu:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xbc\xf0\x9f\x87\xab"), ":flag_wf:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xbc\xf0\x9f\x87\xb8"), ":flag_ws:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xbd\xf0\x9f\x87\xb0"), ":flag_xk:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xbe\xf0\x9f\x87\xaa"), ":flag_ye:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xbe\xf0\x9f\x87\xb9"), ":flag_yt:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xbf\xf0\x9f\x87\xa6"), ":flag_za:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xbf\xf0\x9f\x87\xb2"), ":flag_zm:" }, - Emoji{ QString::fromUtf8("\xf0\x9f\x87\xbf\xf0\x9f\x87\xbc"), ":flag_zw:" }, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xa8"), ":flag_ac:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xa9"), ":flag_ad:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xaa"), ":flag_ae:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xab"), ":flag_af:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xac"), ":flag_ag:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xae"), ":flag_ai:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xb1"), ":flag_al:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xb2"), ":flag_am:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xb4"), ":flag_ao:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xb6"), ":flag_aq:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xb7"), ":flag_ar:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xb8"), ":flag_as:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xb9"), ":flag_at:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xba"), ":flag_au:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xbc"), ":flag_aw:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xbd"), ":flag_ax:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa6\xf0\x9f\x87\xbf"), ":flag_az:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xa6"), ":flag_ba:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xa7"), ":flag_bb:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xa9"), ":flag_bd:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xaa"), ":flag_be:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xab"), ":flag_bf:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xac"), ":flag_bg:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xad"), ":flag_bh:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xae"), ":flag_bi:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xaf"), ":flag_bj:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xb1"), ":flag_bl:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xb2"), ":flag_bm:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xb3"), ":flag_bn:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xb4"), ":flag_bo:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xb6"), ":flag_bq:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xb7"), ":flag_br:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xb8"), ":flag_bs:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xb9"), ":flag_bt:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xbb"), ":flag_bv:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xbc"), ":flag_bw:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xbe"), ":flag_by:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa7\xf0\x9f\x87\xbf"), ":flag_bz:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xa6"), ":flag_ca:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xa8"), ":flag_cc:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xa9"), ":flag_cd:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xab"), ":flag_cf:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xac"), ":flag_cg:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xad"), ":flag_ch:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xae"), ":flag_ci:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xb0"), ":flag_ck:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xb1"), ":flag_cl:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xb2"), ":flag_cm:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xb3"), ":flag_cn:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xb4"), ":flag_co:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xb5"), ":flag_cp:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xb7"), ":flag_cr:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xba"), ":flag_cu:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xbb"), ":flag_cv:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xbc"), ":flag_cw:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xbd"), ":flag_cx:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xbe"), ":flag_cy:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa8\xf0\x9f\x87\xbf"), ":flag_cz:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa9\xf0\x9f\x87\xaa"), ":flag_de:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa9\xf0\x9f\x87\xac"), ":flag_dg:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa9\xf0\x9f\x87\xaf"), ":flag_dj:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa9\xf0\x9f\x87\xb0"), ":flag_dk:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa9\xf0\x9f\x87\xb2"), ":flag_dm:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa9\xf0\x9f\x87\xb4"), ":flag_do:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xa9\xf0\x9f\x87\xbf"), ":flag_dz:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xa6"), ":flag_ea:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xa8"), ":flag_ec:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xaa"), ":flag_ee:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xac"), ":flag_eg:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xad"), ":flag_eh:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xb7"), ":flag_er:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xb8"), ":flag_es:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xb9"), ":flag_et:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xaa\xf0\x9f\x87\xba"), ":flag_eu:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xab\xf0\x9f\x87\xae"), ":flag_fi:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xab\xf0\x9f\x87\xaf"), ":flag_fj:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xab\xf0\x9f\x87\xb0"), ":flag_fk:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xab\xf0\x9f\x87\xb2"), ":flag_fm:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xab\xf0\x9f\x87\xb4"), ":flag_fo:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xab\xf0\x9f\x87\xb7"), ":flag_fr:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xa6"), ":flag_ga:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xa7"), ":flag_gb:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xa9"), ":flag_gd:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xaa"), ":flag_ge:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xab"), ":flag_gf:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xac"), ":flag_gg:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xad"), ":flag_gh:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xae"), ":flag_gi:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xb1"), ":flag_gl:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xb2"), ":flag_gm:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xb3"), ":flag_gn:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xb5"), ":flag_gp:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xb6"), ":flag_gq:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xb7"), ":flag_gr:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xb8"), ":flag_gs:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xb9"), ":flag_gt:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xba"), ":flag_gu:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xbc"), ":flag_gw:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xac\xf0\x9f\x87\xbe"), ":flag_gy:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xad\xf0\x9f\x87\xb0"), ":flag_hk:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xad\xf0\x9f\x87\xb2"), ":flag_hm:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xad\xf0\x9f\x87\xb3"), ":flag_hn:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xad\xf0\x9f\x87\xb7"), ":flag_hr:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xad\xf0\x9f\x87\xb9"), ":flag_ht:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xad\xf0\x9f\x87\xba"), ":flag_hu:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xa8"), ":flag_ic:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xa9"), ":flag_id:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xaa"), ":flag_ie:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xb1"), ":flag_il:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xb2"), ":flag_im:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xb3"), ":flag_in:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xb4"), ":flag_io:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xb6"), ":flag_iq:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xb7"), ":flag_ir:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xb8"), ":flag_is:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xae\xf0\x9f\x87\xb9"), ":flag_it:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xaf\xf0\x9f\x87\xaa"), ":flag_je:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xaf\xf0\x9f\x87\xb2"), ":flag_jm:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xaf\xf0\x9f\x87\xb4"), ":flag_jo:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xaf\xf0\x9f\x87\xb5"), ":flag_jp:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xaa"), ":flag_ke:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xac"), ":flag_kg:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xad"), ":flag_kh:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xae"), ":flag_ki:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xb2"), ":flag_km:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xb3"), ":flag_kn:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xb5"), ":flag_kp:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xb7"), ":flag_kr:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xbc"), ":flag_kw:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xbe"), ":flag_ky:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb0\xf0\x9f\x87\xbf"), ":flag_kz:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xa6"), ":flag_la:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xa7"), ":flag_lb:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xa8"), ":flag_lc:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xae"), ":flag_li:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xb0"), ":flag_lk:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xb7"), ":flag_lr:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xb8"), ":flag_ls:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xb9"), ":flag_lt:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xba"), ":flag_lu:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xbb"), ":flag_lv:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb1\xf0\x9f\x87\xbe"), ":flag_ly:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xa6"), ":flag_ma:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xa8"), ":flag_mc:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xa9"), ":flag_md:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xaa"), ":flag_me:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xab"), ":flag_mf:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xac"), ":flag_mg:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xad"), ":flag_mh:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb0"), ":flag_mk:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb1"), ":flag_ml:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb2"), ":flag_mm:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb3"), ":flag_mn:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb4"), ":flag_mo:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb5"), ":flag_mp:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb6"), ":flag_mq:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb7"), ":flag_mr:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb8"), ":flag_ms:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xb9"), ":flag_mt:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xba"), ":flag_mu:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xbb"), ":flag_mv:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xbc"), ":flag_mw:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xbd"), ":flag_mx:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xbe"), ":flag_my:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb2\xf0\x9f\x87\xbf"), ":flag_mz:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xa6"), ":flag_na:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xa8"), ":flag_nc:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xaa"), ":flag_ne:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xab"), ":flag_nf:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xac"), ":flag_ng:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xae"), ":flag_ni:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xb1"), ":flag_nl:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xb4"), ":flag_no:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xb5"), ":flag_np:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xb7"), ":flag_nr:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xba"), ":flag_nu:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb3\xf0\x9f\x87\xbf"), ":flag_nz:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb4\xf0\x9f\x87\xb2"), ":flag_om:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xa6"), ":flag_pa:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xaa"), ":flag_pe:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xab"), ":flag_pf:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xac"), ":flag_pg:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xad"), ":flag_ph:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xb0"), ":flag_pk:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xb1"), ":flag_pl:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xb2"), ":flag_pm:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xb3"), ":flag_pn:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xb7"), ":flag_pr:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xb8"), ":flag_ps:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xb9"), ":flag_pt:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xbc"), ":flag_pw:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb5\xf0\x9f\x87\xbe"), ":flag_py:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb6\xf0\x9f\x87\xa6"), ":flag_qa:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb7\xf0\x9f\x87\xaa"), ":flag_re:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb7\xf0\x9f\x87\xb4"), ":flag_ro:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb7\xf0\x9f\x87\xb8"), ":flag_rs:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb7\xf0\x9f\x87\xba"), ":flag_ru:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb7\xf0\x9f\x87\xbc"), ":flag_rw:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xa6"), ":flag_sa:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xa7"), ":flag_sb:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xa8"), ":flag_sc:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xa9"), ":flag_sd:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xaa"), ":flag_se:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xac"), ":flag_sg:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xad"), ":flag_sh:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xae"), ":flag_si:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xaf"), ":flag_sj:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xb0"), ":flag_sk:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xb1"), ":flag_sl:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xb2"), ":flag_sm:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xb3"), ":flag_sn:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xb4"), ":flag_so:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xb7"), ":flag_sr:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xb8"), ":flag_ss:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xb9"), ":flag_st:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xbb"), ":flag_sv:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xbd"), ":flag_sx:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xbe"), ":flag_sy:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb8\xf0\x9f\x87\xbf"), ":flag_sz:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xa6"), ":flag_ta:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xa8"), ":flag_tc:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xa9"), ":flag_td:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xab"), ":flag_tf:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xac"), ":flag_tg:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xad"), ":flag_th:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xaf"), ":flag_tj:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xb0"), ":flag_tk:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xb1"), ":flag_tl:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xb2"), ":flag_tm:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xb3"), ":flag_tn:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xb4"), ":flag_to:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xb7"), ":flag_tr:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xb9"), ":flag_tt:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xbb"), ":flag_tv:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xbc"), ":flag_tw:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xb9\xf0\x9f\x87\xbf"), ":flag_tz:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xba\xf0\x9f\x87\xa6"), ":flag_ua:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xba\xf0\x9f\x87\xac"), ":flag_ug:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xba\xf0\x9f\x87\xb2"), ":flag_um:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xba\xf0\x9f\x87\xb8"), ":flag_us:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xba\xf0\x9f\x87\xbe"), ":flag_uy:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xba\xf0\x9f\x87\xbf"), ":flag_uz:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xbb\xf0\x9f\x87\xa6"), ":flag_va:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xbb\xf0\x9f\x87\xa8"), ":flag_vc:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xbb\xf0\x9f\x87\xaa"), ":flag_ve:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xbb\xf0\x9f\x87\xac"), ":flag_vg:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xbb\xf0\x9f\x87\xae"), ":flag_vi:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xbb\xf0\x9f\x87\xb3"), ":flag_vn:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xbb\xf0\x9f\x87\xba"), ":flag_vu:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xbc\xf0\x9f\x87\xab"), ":flag_wf:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xbc\xf0\x9f\x87\xb8"), ":flag_ws:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xbd\xf0\x9f\x87\xb0"), ":flag_xk:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xbe\xf0\x9f\x87\xaa"), ":flag_ye:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xbe\xf0\x9f\x87\xb9"), ":flag_yt:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xbf\xf0\x9f\x87\xa6"), ":flag_za:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xbf\xf0\x9f\x87\xb2"), ":flag_zm:"}, + Emoji{QString::fromUtf8("\xf0\x9f\x87\xbf\xf0\x9f\x87\xbc"), ":flag_zw:"}, }; diff --git a/src/ImageItem.cc b/src/ImageItem.cc index 39fa630f..333fd296 100644 --- a/src/ImageItem.cc +++ b/src/ImageItem.cc @@ -32,8 +32,8 @@ ImageItem::ImageItem(QSharedPointer client, const events::MessageEvent &event, QWidget *parent) : QWidget(parent) - , event_{ event } - , client_{ client } + , event_{event} + , client_{client} { setMouseTracking(true); setCursor(Qt::PointingHandCursor); @@ -66,9 +66,9 @@ ImageItem::ImageItem(QSharedPointer client, const QString &filename, QWidget *parent) : QWidget(parent) - , url_{ url } - , text_{ QFileInfo(filename).fileName() } - , client_{ client } + , url_{url} + , text_{QFileInfo(filename).fileName()} + , client_{client} { setMouseTracking(true); setCursor(Qt::PointingHandCursor); diff --git a/src/ImageOverlayDialog.cc b/src/ImageOverlayDialog.cc index 8d6db45e..7dd4a226 100644 --- a/src/ImageOverlayDialog.cc +++ b/src/ImageOverlayDialog.cc @@ -22,8 +22,8 @@ #include "ImageOverlayDialog.h" ImageOverlayDialog::ImageOverlayDialog(QPixmap image, QWidget *parent) - : QWidget{ parent } - , originalImage_{ image } + : QWidget{parent} + , originalImage_{image} { setMouseTracking(true); setParent(0); diff --git a/src/Login.cc b/src/Login.cc index a9e303f9..ca41d019 100644 --- a/src/Login.cc +++ b/src/Login.cc @@ -39,10 +39,10 @@ LoginRequest::serialize() noexcept #endif QJsonObject body{ - { "type", "m.login.password" }, - { "user", user_ }, - { "password", password_ }, - { "initial_device_display_name", initialDeviceName }, + {"type", "m.login.password"}, + {"user", user_}, + {"password", password_}, + {"initial_device_display_name", initialDeviceName}, }; return QJsonDocument(body).toJson(QJsonDocument::Compact); diff --git a/src/LoginPage.cc b/src/LoginPage.cc index 528b7442..0b65f8bc 100644 --- a/src/LoginPage.cc +++ b/src/LoginPage.cc @@ -28,7 +28,7 @@ LoginPage::LoginPage(QSharedPointer client, QWidget *parent) : QWidget(parent) , inferredServerAddress_() - , client_{ client } + , client_{client} { setStyleSheet("background-color: #fff"); diff --git a/src/MainWindow.cc b/src/MainWindow.cc index 9c81ef85..9653897d 100644 --- a/src/MainWindow.cc +++ b/src/MainWindow.cc @@ -38,8 +38,8 @@ MainWindow *MainWindow::instance_ = nullptr; MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) - , progressModal_{ nullptr } - , spinner_{ nullptr } + , progressModal_{nullptr} + , spinner_{nullptr} { QSizePolicy sizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); setSizePolicy(sizePolicy); diff --git a/src/MatrixClient.cc b/src/MatrixClient.cc index c6ef501f..3876d044 100644 --- a/src/MatrixClient.cc +++ b/src/MatrixClient.cc @@ -38,9 +38,9 @@ MatrixClient::MatrixClient(QString server, QObject *parent) : QNetworkAccessManager(parent) - , clientApiUrl_{ "/_matrix/client/r0" } - , mediaApiUrl_{ "/_matrix/media/r0" } - , server_{ "https://" + server } + , clientApiUrl_{"/_matrix/client/r0"} + , mediaApiUrl_{"/_matrix/media/r0"} + , server_{"https://" + server} { QSettings settings; txn_id_ = settings.value("client/transaction_id", 1).toInt(); @@ -203,10 +203,10 @@ void MatrixClient::sync() noexcept { QJsonObject filter{ - { "room", - QJsonObject{ - { "include_leave", true }, - } }, + {"room", + QJsonObject{ + {"include_leave", true}, + }}, }; QUrlQuery query; @@ -278,13 +278,13 @@ MatrixClient::sendRoomMessage(matrix::events::MessageEventType ty, switch (ty) { case matrix::events::MessageEventType::Text: - body = { { "msgtype", "m.text" }, { "body", msg } }; + body = {{"msgtype", "m.text"}, {"body", msg}}; break; case matrix::events::MessageEventType::Emote: - body = { { "msgtype", "m.emote" }, { "body", msg } }; + body = {{"msgtype", "m.emote"}, {"body", msg}}; break; case matrix::events::MessageEventType::Image: - body = { { "msgtype", "m.image" }, { "body", msg }, { "url", url } }; + body = {{"msgtype", "m.image"}, {"body", msg}, {"url", url}}; break; default: qDebug() << "SendRoomMessage: Unknown message type for" << msg; @@ -812,7 +812,7 @@ MatrixClient::sendTypingNotification(const QString &roomid, int timeoutInMillis) QString msgType(""); QJsonObject body; - body = { { "typing", true }, { "timeout", timeoutInMillis } }; + body = {{"typing", true}, {"timeout", timeoutInMillis}}; QNetworkRequest request(QString(endpoint.toEncoded())); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); @@ -837,7 +837,7 @@ MatrixClient::removeTypingNotification(const QString &roomid) QString msgType(""); QJsonObject body; - body = { { "typing", false } }; + body = {{"typing", false}}; QNetworkRequest request(QString(endpoint.toEncoded())); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); diff --git a/src/Register.cc b/src/Register.cc index 7453c943..84ba82e7 100644 --- a/src/Register.cc +++ b/src/Register.cc @@ -27,7 +27,7 @@ RegisterRequest::RegisterRequest(const QString &username, const QString &passwor QByteArray RegisterRequest::serialize() noexcept { - QJsonObject body{ { "username", user_ }, { "password", password_ } }; + QJsonObject body{{"username", user_}, {"password", password_}}; return QJsonDocument(body).toJson(QJsonDocument::Compact); } diff --git a/src/RoomInfoListItem.cc b/src/RoomInfoListItem.cc index 857189b5..49b24b58 100644 --- a/src/RoomInfoListItem.cc +++ b/src/RoomInfoListItem.cc @@ -33,7 +33,7 @@ RoomInfoListItem::RoomInfoListItem(QSharedPointer settings, : QWidget(parent) , state_(state) , roomId_(room_id) - , roomSettings_{ settings } + , roomSettings_{settings} , isPressed_(false) , maxHeight_(IconSize + 2 * Padding) , unreadMsgCount_(0) diff --git a/src/SideBarActions.cc b/src/SideBarActions.cc index 3898eb22..16579ce3 100644 --- a/src/SideBarActions.cc +++ b/src/SideBarActions.cc @@ -5,7 +5,7 @@ #include "Theme.h" SideBarActions::SideBarActions(QWidget *parent) - : QWidget{ parent } + : QWidget{parent} { setFixedHeight(conf::sidebarActions::height); diff --git a/src/TimelineItem.cc b/src/TimelineItem.cc index ba9e54bd..77bb8b9e 100644 --- a/src/TimelineItem.cc +++ b/src/TimelineItem.cc @@ -85,11 +85,10 @@ TimelineItem::TimelineItem(events::MessageEventType ty, if (ty == events::MessageEventType::Emote) { body = QString("* %1 %2").arg(displayName).arg(body); - descriptionMsg_ = { "", userid, body, descriptiveTime(timestamp) }; + descriptionMsg_ = {"", userid, body, descriptiveTime(timestamp)}; } else { descriptionMsg_ = { - "You: ", userid, body, descriptiveTime(QDateTime::currentDateTime()) - }; + "You: ", userid, body, descriptiveTime(QDateTime::currentDateTime())}; } body = body.toHtmlEscaped(); @@ -114,14 +113,14 @@ TimelineItem::TimelineItem(ImageItem *image, const QString &userid, bool withSender, QWidget *parent) - : QWidget{ parent } + : QWidget{parent} { init(); auto displayName = TimelineViewManager::displayName(userid); auto timestamp = QDateTime::currentDateTime(); - descriptionMsg_ = { "You", userid, " sent an image", descriptiveTime(timestamp) }; + descriptionMsg_ = {"You", userid, " sent an image", descriptiveTime(timestamp)}; generateTimestamp(timestamp); @@ -158,10 +157,10 @@ TimelineItem::TimelineItem(ImageItem *image, auto displayName = TimelineViewManager::displayName(event.sender()); QSettings settings; - descriptionMsg_ = { event.sender() == settings.value("auth/user_id") ? "You" : displayName, - event.sender(), - " sent an image", - descriptiveTime(QDateTime::fromMSecsSinceEpoch(event.timestamp())) }; + descriptionMsg_ = {event.sender() == settings.value("auth/user_id") ? "You" : displayName, + event.sender(), + " sent an image", + descriptiveTime(QDateTime::fromMSecsSinceEpoch(event.timestamp()))}; generateTimestamp(timestamp); @@ -193,10 +192,10 @@ TimelineItem::TimelineItem(const events::MessageEvent &event, : QWidget(parent) { init(); - descriptionMsg_ = { TimelineViewManager::displayName(event.sender()), - event.sender(), - " sent a notification", - descriptiveTime(QDateTime::fromMSecsSinceEpoch(event.timestamp())) }; + descriptionMsg_ = {TimelineViewManager::displayName(event.sender()), + event.sender(), + " sent a notification", + descriptiveTime(QDateTime::fromMSecsSinceEpoch(event.timestamp()))}; auto body = event.content().body().trimmed().toHtmlEscaped(); auto timestamp = QDateTime::fromMSecsSinceEpoch(event.timestamp()); @@ -238,10 +237,10 @@ TimelineItem::TimelineItem(const events::MessageEvent &event, auto displayName = TimelineViewManager::displayName(event.sender()); auto emoteMsg = QString("* %1 %2").arg(displayName).arg(body); - descriptionMsg_ = { "", - event.sender(), - emoteMsg, - descriptiveTime(QDateTime::fromMSecsSinceEpoch(event.timestamp())) }; + descriptionMsg_ = {"", + event.sender(), + emoteMsg, + descriptiveTime(QDateTime::fromMSecsSinceEpoch(event.timestamp()))}; generateTimestamp(timestamp); emoteMsg = emoteMsg.toHtmlEscaped(); @@ -276,10 +275,10 @@ TimelineItem::TimelineItem(const events::MessageEvent &event, auto displayName = TimelineViewManager::displayName(event.sender()); QSettings settings; - descriptionMsg_ = { event.sender() == settings.value("auth/user_id") ? "You" : displayName, - event.sender(), - QString(": %1").arg(body), - descriptiveTime(QDateTime::fromMSecsSinceEpoch(event.timestamp())) }; + descriptionMsg_ = {event.sender() == settings.value("auth/user_id") ? "You" : displayName, + event.sender(), + QString(": %1").arg(body), + descriptiveTime(QDateTime::fromMSecsSinceEpoch(event.timestamp()))}; generateTimestamp(timestamp); diff --git a/src/TimelineView.cc b/src/TimelineView.cc index e5cbbbf6..1ffa731d 100644 --- a/src/TimelineView.cc +++ b/src/TimelineView.cc @@ -49,8 +49,8 @@ TimelineView::TimelineView(const Timeline &timeline, const QString &room_id, QWidget *parent) : QWidget(parent) - , room_id_{ room_id } - , client_{ client } + , room_id_{room_id} + , client_{client} { init(); addEvents(timeline); @@ -60,8 +60,8 @@ TimelineView::TimelineView(QSharedPointer client, const QString &room_id, QWidget *parent) : QWidget(parent) - , room_id_{ room_id } - , client_{ client } + , room_id_{room_id} + , client_{client} { init(); client_->messages(room_id_, ""); diff --git a/src/TimelineViewManager.cc b/src/TimelineViewManager.cc index 01e7d9ab..bf2bee76 100644 --- a/src/TimelineViewManager.cc +++ b/src/TimelineViewManager.cc @@ -194,7 +194,7 @@ QString TimelineViewManager::chooseRandomColor() { std::random_device random_device; - std::mt19937 engine{ random_device() }; + std::mt19937 engine{random_device()}; std::uniform_real_distribution dist(0, 1); float hue = dist(engine); diff --git a/src/TopRoomBar.cc b/src/TopRoomBar.cc index 0361bd47..25745e1a 100644 --- a/src/TopRoomBar.cc +++ b/src/TopRoomBar.cc @@ -30,7 +30,7 @@ TopRoomBar::TopRoomBar(QWidget *parent) : QWidget(parent) - , buttonSize_{ 32 } + , buttonSize_{32} { setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); setMinimumSize(QSize(0, 65)); diff --git a/src/TypingDisplay.cc b/src/TypingDisplay.cc index e3eb9db9..da9c1679 100644 --- a/src/TypingDisplay.cc +++ b/src/TypingDisplay.cc @@ -6,7 +6,7 @@ TypingDisplay::TypingDisplay(QWidget *parent) : QWidget(parent) - , leftPadding_{ 24 } + , leftPadding_{24} { QFont font; font.setPixelSize(conf::typingNotificationFontSize); diff --git a/src/UserInfoWidget.cc b/src/UserInfoWidget.cc index 1fadc8e7..2f7dc2ef 100644 --- a/src/UserInfoWidget.cc +++ b/src/UserInfoWidget.cc @@ -29,9 +29,9 @@ UserInfoWidget::UserInfoWidget(QWidget *parent) : QWidget(parent) , display_name_("User") , user_id_("@user:homeserver.org") - , logoutModal_{ nullptr } - , logoutDialog_{ nullptr } - , logoutButtonSize_{ 20 } + , logoutModal_{nullptr} + , logoutDialog_{nullptr} + , logoutButtonSize_{20} { QSizePolicy sizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); setSizePolicy(sizePolicy); diff --git a/src/UserSettingsPage.cc b/src/UserSettingsPage.cc index d18b76be..fb5f983c 100644 --- a/src/UserSettingsPage.cc +++ b/src/UserSettingsPage.cc @@ -51,15 +51,15 @@ UserSettings::save() } HorizontalLine::HorizontalLine(QWidget *parent) - : QFrame{ parent } + : QFrame{parent} { setFrameShape(QFrame::HLine); setFrameShadow(QFrame::Sunken); } UserSettingsPage::UserSettingsPage(QSharedPointer settings, QWidget *parent) - : QWidget{ parent } - , settings_{ settings } + : QWidget{parent} + , settings_{settings} { topLayout_ = new QVBoxLayout(this); diff --git a/src/ui/OverlayModal.cc b/src/ui/OverlayModal.cc index 05bd7d03..4fb57175 100644 --- a/src/ui/OverlayModal.cc +++ b/src/ui/OverlayModal.cc @@ -22,8 +22,8 @@ OverlayModal::OverlayModal(QWidget *parent, QWidget *content) : OverlayWidget(parent) - , duration_{ 500 } - , color_{ QColor(55, 55, 55) } + , duration_{500} + , color_{QColor(55, 55, 55)} { setAttribute(Qt::WA_TranslucentBackground); diff --git a/src/ui/ScrollBar.cc b/src/ui/ScrollBar.cc index b186be23..24f97461 100644 --- a/src/ui/ScrollBar.cc +++ b/src/ui/ScrollBar.cc @@ -23,7 +23,7 @@ ScrollBar::ScrollBar(QScrollArea *area, QWidget *parent) : QScrollBar(parent) - , area_{ area } + , area_{area} { hideTimer_.setSingleShot(true); diff --git a/src/ui/Theme.cc b/src/ui/Theme.cc index 52d4b883..622428b0 100644 --- a/src/ui/Theme.cc +++ b/src/ui/Theme.cc @@ -54,15 +54,21 @@ void Theme::setColor(const QString &key, ui::Color color) { static const QColor palette[] = { - QColor("#171919"), + QColor("#171919"), - QColor("#EBEBEB"), QColor("#C9C9C9"), QColor("#929292"), + QColor("#EBEBEB"), + QColor("#C9C9C9"), + QColor("#929292"), - QColor("#1C3133"), QColor("#577275"), QColor("#46A451"), + QColor("#1C3133"), + QColor("#577275"), + QColor("#46A451"), - QColor("#5D6565"), QColor("#E22826"), QColor("#81B3A9"), + QColor("#5D6565"), + QColor("#E22826"), + QColor("#81B3A9"), - rgba(0, 0, 0, 0), + rgba(0, 0, 0, 0), }; colors_.insert(key, palette[static_cast(color)]); diff --git a/src/ui/ToggleButton.cc b/src/ui/ToggleButton.cc index 8054bfe7..e7b242b8 100644 --- a/src/ui/ToggleButton.cc +++ b/src/ui/ToggleButton.cc @@ -12,7 +12,7 @@ Toggle::paintEvent(QPaintEvent *event) } Toggle::Toggle(QWidget *parent) - : QAbstractButton{ parent } + : QAbstractButton{parent} { init(); @@ -89,10 +89,10 @@ Toggle::setTrackColor(const QColor &color) } ToggleThumb::ToggleThumb(Toggle *parent) - : QWidget{ parent } - , toggle_{ parent } - , position_{ Position::Right } - , offset_{ 0 } + : QWidget{parent} + , toggle_{parent} + , position_{Position::Right} + , offset_{0} { parent->installEventFilter(this); } @@ -158,8 +158,8 @@ ToggleThumb::updateOffset() } ToggleTrack::ToggleTrack(Toggle *parent) - : QWidget{ parent } - , toggle_{ parent } + : QWidget{parent} + , toggle_{parent} { Q_ASSERT(parent); From 7320c44b1034893de5093d1df34738ba49b7ae55 Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Sun, 5 Nov 2017 23:43:49 +0200 Subject: [PATCH 57/64] Push appveyor builds to github --- appveyor.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index b5625097..c5dca331 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -33,6 +33,16 @@ after_build: - 7z a nheko_win_64.zip .\NhekoRelease\* - C:\nsis\makensis .ci\windows\nheko.nsi +deploy: + provider: GitHub + auth_token: + secure: oprXzESukFiXBeF2BXkXUlegsAQc95Ub4kc/OkoNFaYBvqpA+IGpWHmHCx5JPny/OT3Kc2Urpe2JUeGSWDHZ7UCKDjH+NzGP3uN82fHh/HiszG/Srw7+hWEHm1ve+gMK9GS8pr+yUsUrPP0UfVXlPoI4pBWa4zGi2Ndb/SqvjCgIHFLtGyoBo6CydKQ/AyWiXSMKDfJL+Dx4JLIPP4RTCBJy8ZrZ8m/a5Tgy4Ij6+djjrgYCZCEhGxSYw7xDIT/9SV8g9NkrbisqBDrILzAH8Yhe4XMRMXo88OAxV5+Vb9Rw1hrqczez6lpFDbJetv8MjofND+pSoAFwtjaL1wTFK9Ud6w4O9AuHlEQH9cGVdvsxFcosRwJVh58x51JM9ptoktqhx/HHJBTuCHCYYlHwtRwbwqnMYdLzKZG5FnujT8DG+9mcva1fL6tzW/XD505VPMWwXFC/2/pvolgAkTFFXYSALAwZlK3IgoXU8Gok/3B4iHofzQsFf6Yq3BI/88x7tVASUqiYhoKrO50+gb6pNIRCyWgGUiBEVXBp6Ziq3ORQPyQJg7i9HHUGTUu74yvGLHWLwjNQzZP/hxJZK3VlJxzyXntdOxiJc8iOzNrU+rPKBAlAaE6bQDOoniIysEgdD5BXHTLFzPvts4P1n2Ckor5/rNJ+qXR8GU+/y7e1GKU= + artifact: nheko_setup.exe + force_update: true + on: + branch: master + appveyor_repo_tag: true + artifacts: - path: nheko_win_64.zip - path: NhekoRelease\nheko.exe From 3200a4bc41c70b9500e34577002edf2a64844ab7 Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Sun, 5 Nov 2017 23:48:33 +0200 Subject: [PATCH 58/64] Don't specify branch on appveyor --- appveyor.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index c5dca331..8e45b526 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -40,7 +40,6 @@ deploy: artifact: nheko_setup.exe force_update: true on: - branch: master appveyor_repo_tag: true artifacts: From a57e4003b373229163a433ab4bc5066f17e27230 Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Sun, 5 Nov 2017 23:56:20 +0200 Subject: [PATCH 59/64] Encrypt token for appveyor --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 8e45b526..cbe6c542 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -36,7 +36,7 @@ after_build: deploy: provider: GitHub auth_token: - secure: oprXzESukFiXBeF2BXkXUlegsAQc95Ub4kc/OkoNFaYBvqpA+IGpWHmHCx5JPny/OT3Kc2Urpe2JUeGSWDHZ7UCKDjH+NzGP3uN82fHh/HiszG/Srw7+hWEHm1ve+gMK9GS8pr+yUsUrPP0UfVXlPoI4pBWa4zGi2Ndb/SqvjCgIHFLtGyoBo6CydKQ/AyWiXSMKDfJL+Dx4JLIPP4RTCBJy8ZrZ8m/a5Tgy4Ij6+djjrgYCZCEhGxSYw7xDIT/9SV8g9NkrbisqBDrILzAH8Yhe4XMRMXo88OAxV5+Vb9Rw1hrqczez6lpFDbJetv8MjofND+pSoAFwtjaL1wTFK9Ud6w4O9AuHlEQH9cGVdvsxFcosRwJVh58x51JM9ptoktqhx/HHJBTuCHCYYlHwtRwbwqnMYdLzKZG5FnujT8DG+9mcva1fL6tzW/XD505VPMWwXFC/2/pvolgAkTFFXYSALAwZlK3IgoXU8Gok/3B4iHofzQsFf6Yq3BI/88x7tVASUqiYhoKrO50+gb6pNIRCyWgGUiBEVXBp6Ziq3ORQPyQJg7i9HHUGTUu74yvGLHWLwjNQzZP/hxJZK3VlJxzyXntdOxiJc8iOzNrU+rPKBAlAaE6bQDOoniIysEgdD5BXHTLFzPvts4P1n2Ckor5/rNJ+qXR8GU+/y7e1GKU= + secure: YqB7hcM+4482eSHhtVR7ZA7N7lE78y8BC897/7UDTBQd+NWdWFW/6S+oKDie9TT7 artifact: nheko_setup.exe force_update: true on: From fc86da069840e2428b5b25516bdb1b8c73f94333 Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Mon, 6 Nov 2017 00:03:48 +0200 Subject: [PATCH 60/64] Adjust release name and description --- appveyor.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/appveyor.yml b/appveyor.yml index cbe6c542..4d473858 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -34,11 +34,14 @@ after_build: - C:\nsis\makensis .ci\windows\nheko.nsi deploy: + release: "nheko 0.1.0-dev" + description: "Development builds" provider: GitHub auth_token: secure: YqB7hcM+4482eSHhtVR7ZA7N7lE78y8BC897/7UDTBQd+NWdWFW/6S+oKDie9TT7 artifact: nheko_setup.exe force_update: true + prerelease: true on: appveyor_repo_tag: true From f4b310467e1c34e42088925db963896442e99c95 Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Mon, 6 Nov 2017 00:12:08 +0200 Subject: [PATCH 61/64] Use nightly as the dev release tag --- appveyor.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 4d473858..3bd1dcff 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -34,7 +34,6 @@ after_build: - C:\nsis\makensis .ci\windows\nheko.nsi deploy: - release: "nheko 0.1.0-dev" description: "Development builds" provider: GitHub auth_token: From d0d15f8f58b6bd62aa306d8983f94d469108a7cf Mon Sep 17 00:00:00 2001 From: Benjamin Saunders Date: Sun, 5 Nov 2017 14:25:47 -0800 Subject: [PATCH 62/64] Fix a formatting issue that snuck in between PRs (#123) --- src/TextInputWidget.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/TextInputWidget.cc b/src/TextInputWidget.cc index e7c48b5f..ad053ea3 100644 --- a/src/TextInputWidget.cc +++ b/src/TextInputWidget.cc @@ -29,8 +29,8 @@ static constexpr size_t INPUT_HISTORY_SIZE = 127; FilteredTextEdit::FilteredTextEdit(QWidget *parent) - : QTextEdit{ parent } - , history_index_{ 0 } + : QTextEdit{parent} + , history_index_{0} { connect(document()->documentLayout(), &QAbstractTextDocumentLayout::documentSizeChanged, From e19775443acd91943175b4d1c8b6d1b99b2ee540 Mon Sep 17 00:00:00 2001 From: Benjamin Saunders Date: Sun, 5 Nov 2017 14:29:02 -0800 Subject: [PATCH 63/64] Fix newlines not being displayed (#122) Fixes #64 --- src/TimelineItem.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/TimelineItem.cc b/src/TimelineItem.cc index 77bb8b9e..bd778175 100644 --- a/src/TimelineItem.cc +++ b/src/TimelineItem.cc @@ -92,6 +92,7 @@ TimelineItem::TimelineItem(events::MessageEventType ty, } body = body.toHtmlEscaped(); + body.replace("\n", "
"); body.replace(URL_REGEX, URL_HTML); generateTimestamp(timestamp); @@ -197,7 +198,8 @@ TimelineItem::TimelineItem(const events::MessageEvent &event, " sent a notification", descriptiveTime(QDateTime::fromMSecsSinceEpoch(event.timestamp()))}; - auto body = event.content().body().trimmed().toHtmlEscaped(); + auto body = event.content().body().trimmed().toHtmlEscaped(); + body.replace("\n", "
"); auto timestamp = QDateTime::fromMSecsSinceEpoch(event.timestamp()); generateTimestamp(timestamp); @@ -244,6 +246,7 @@ TimelineItem::TimelineItem(const events::MessageEvent &event, generateTimestamp(timestamp); emoteMsg = emoteMsg.toHtmlEscaped(); + emoteMsg.replace("\n", "
"); emoteMsg.replace(URL_REGEX, URL_HTML); if (with_sender) { @@ -283,6 +286,7 @@ TimelineItem::TimelineItem(const events::MessageEvent &event, generateTimestamp(timestamp); body = body.toHtmlEscaped(); + body.replace("\n", "
"); body.replace(URL_REGEX, URL_HTML); if (with_sender) { From 1a3bacd96eab9fcfe159e902ca4fbccfa31ba61b Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Mon, 6 Nov 2017 00:51:59 +0200 Subject: [PATCH 64/64] Update README [ci skip] --- README.md | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 26356ef6..41ee62a1 100644 --- a/README.md +++ b/README.md @@ -16,10 +16,12 @@ but we are getting close to a more feature complete client. Specifically there is support for: - Joining & leaving rooms - Sending & receiving images and emoji. -- Receiving typing notifications. +- Typing notifications. ### Installation +There are pre-built nigtly releases [here](https://github.com/mujx/nheko/releases/tag/nightly) for Linux ([AppImage](https://appimage.org/)), Mac and Windows. + #### Arch Linux ```bash pacaur -S nheko-git @@ -37,10 +39,6 @@ sudo layman -a matrix sudo emerge -a nheko ``` -#### Windows - -You can find an installer [here](https://ci.appveyor.com/project/mujx/nheko/branch/master/artifacts). - ### Build Requirements - Qt5 (5.7 or greater). Qt 5.7 adds support for color font rendering with @@ -105,15 +103,7 @@ make -C build The `nheko` binary will be located in the `build` directory. -##### MacOS - -You can create an app bundle with `make app`. The output will be located at -`dist/MacOS/Nheko.app` which can be copied to `/Applications/Nheko.app`. - -You can also create a disk image with `make dmg`. The output will be located at -`dist/MacOS/Nheko.dmg` - -##### Nix +#### Nix Download the repo as mentioned above and run