diff --git a/.ci/linux/deploy.sh b/.ci/linux/deploy.sh new file mode 100755 index 00000000..78245322 --- /dev/null +++ b/.ci/linux/deploy.sh @@ -0,0 +1,20 @@ +#!/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 + +chmod +x nheko-x86_64.AppImage diff --git a/.ci/macos/deploy.sh b/.ci/macos/deploy.sh new file mode 100755 index 00000000..7e551e9d --- /dev/null +++ b/.ci/macos/deploy.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +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 . diff --git a/.clang-format b/.clang-format index d5606cdf..059aee19 100644 --- a/.clang-format +++ b/.clang-format @@ -1,13 +1,14 @@ --- Language: Cpp +Standard: Cpp11 AccessModifierOffset: -8 AlignAfterOpenBracket: Align AlignConsecutiveAssignments: true -AllowShortFunctionsOnASingleLine: Empty +AllowShortFunctionsOnASingleLine: true BasedOnStyle: Mozilla -BreakBeforeBraces: Linux ColumnLimit: 100 IndentCaseLabels: false IndentWidth: 8 KeepEmptyLinesAtTheStartOfBlocks: false PointerAlignment: Right +Cpp11BracedListStyle: true diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 00000000..1b8d5e07 --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,20 @@ + + +### System: + +- Nheko commit/version: +- Operating System: +- Qt version: +- C++ compiler: +- Desktop Environment: + +### Actual behavior + +### Expected behavior + +### Steps to reproduce + + +### Debugger traceback diff --git a/.travis.yml b/.travis.yml index b096b78e..22e28c7f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -25,5 +25,29 @@ 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 == 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 + +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 diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d64a9ff..c97d1682 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,6 +56,7 @@ endif() find_package(Qt5Widgets REQUIRED) find_package(Qt5Network REQUIRED) find_package(Qt5LinguistTools REQUIRED) +find_package(Qt5Concurrent REQUIRED) if (APPLE) find_package(Qt5MacExtras REQUIRED) @@ -140,8 +141,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 @@ -150,9 +151,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 @@ -162,34 +160,44 @@ 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/SideBarActions.cc + src/UserSettingsPage.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 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 + src/ui/SnackBar.cc src/ui/RaisedButton.cc src/ui/Ripple.cc src/ui/RippleOverlay.cc src/ui/OverlayWidget.cc src/ui/TextField.cc + src/ui/ToggleButton.cc src/ui/Theme.cc src/ui/ThemeManager.cc ) @@ -234,38 +242,45 @@ 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 - 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/SideBarActions.h + include/UserSettingsPage.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 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 include/ui/RaisedButton.h include/ui/Ripple.h include/ui/RippleOverlay.h include/ui/TextField.h + include/ui/ToggleButton.h include/ui/Theme.h include/ui/ThemeManager.h ) @@ -297,9 +312,6 @@ add_library(matrix_events ${MATRIX_EVENTS} src/Deserializable.cc) target_link_libraries(matrix_events Qt5::Core) if (BUILD_TESTS) - # - # Build tests. - # enable_testing() find_package(GTest REQUIRED) @@ -317,16 +329,13 @@ if (BUILD_TESTS) add_test(MatrixEvents events_test) add_test(MatrixEventCollection event_collection_test) add_test(MatrixMessageEvents message_events) -else() - # - # Build the executable. - # -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() +if(APPVEYOR_BUILD) + set (NHEKO_LIBS matrix_events Qt5::Widgets Qt5::Network Qt5::Concurrent lmdb) +else() + set (NHEKO_LIBS matrix_events Qt5::Widgets Qt5::Network Qt5::Concurrent ${LMDB_LIBRARY}) +endif() set (NHEKO_DEPS ${SRC_FILES} ${UI_HEADERS} ${MOC_HEADERS} ${QRC} ${LANG_QRC} ${QM_SRC} ${META_FILES_TO_INCLUDE}) if(APPLE) diff --git a/Makefile b/Makefile index ba116307..a7565ee5 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: @@ -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)'" diff --git a/README.md b/README.md index b30d8790..41ee62a1 100644 --- a/README.md +++ b/README.md @@ -12,25 +12,33 @@ 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. +- 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 ``` +#### Fedora +```bash +sudo dnf copr enable xvitaly/matrix +sudo dnf install nheko +``` + #### Gentoo Linux ```bash sudo layman -a matrix sudo emerge -a nheko ``` -#### Windows - -You can find a NSIS 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 @@ -95,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 @@ -129,6 +129,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/appveyor.yml b/appveyor.yml index 1b8cb5e7..31a1df7f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -92,6 +92,17 @@ after_build: - set PATH=%BUILD%\tools\bin;%PATH% - binarycreator.exe -f -c installer\config\config.xml -p installer\packages %DIST%-installer.exe +deploy: + 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 + artifacts: - path: nheko_win_64.zip - path: NhekoRelease\nheko.exe diff --git a/include/AvatarProvider.h b/include/AvatarProvider.h index 0a37ffb9..906f2593 100644 --- a/include/AvatarProvider.h +++ b/include/AvatarProvider.h @@ -18,12 +18,17 @@ #pragma once #include -#include #include #include -#include "MatrixClient.h" -#include "TimelineItem.h" +class MatrixClient; +class TimelineItem; + +struct AvatarData +{ + QImage img; + QUrl url; +}; class AvatarProvider : public QObject { @@ -40,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/include/Cache.h b/include/Cache.h index 0f6e5cd2..c16654d0 100644 --- a/include/Cache.h +++ b/include/Cache.h @@ -20,7 +20,7 @@ #include #include -#include "RoomState.h" +class RoomState; class Cache { @@ -33,11 +33,11 @@ public: QString nextBatchToken() const; QMap states(); - inline void deleteData(); - inline void unmount(); - inline QString memberDbName(const QString &roomid); + void deleteData(); + void unmount() { isMounted_ = false; }; void removeRoom(const QString &roomid); + void setup(); private: void setNextBatchToken(lmdb::txn &txn, const QString &token); @@ -52,22 +52,3 @@ private: QString userId_; QString cacheDirectory_; }; - -inline void -Cache::unmount() -{ - isMounted_ = false; -} - -inline QString -Cache::memberDbName(const QString &roomid) -{ - return QString("m.%1").arg(roomid); -} - -inline void -Cache::deleteData() -{ - if (!cacheDirectory_.isEmpty()) - QDir(cacheDirectory_).removeRecursively(); -} diff --git a/include/ChatPage.h b/include/ChatPage.h index 8becc17f..f7d2e1a5 100644 --- a/include/ChatPage.h +++ b/include/ChatPage.h @@ -17,21 +17,37 @@ #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 "Splitter.h" -#include "TextInputWidget.h" -#include "TimelineViewManager.h" -#include "TopRoomBar.h" -#include "UserInfoWidget.h" +#include "MemberEventContent.h" +#include "MessageEvent.h" +#include "StateEvent.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; +class JoinedRoom; +class LeftRoom; + +constexpr int CONSENSUS_TIMEOUT = 1000; +constexpr int SHOW_CONTENT_TIMEOUT = 3000; +constexpr int TYPING_REFRESH_TIMEOUT = 10000; class ChatPage : public QWidget { @@ -43,12 +59,16 @@ public: // Initialize all the components of the UI. void bootstrap(QString userid, QString homeserver, QString token); + void showQuickSwitcher(); signals: void contentLoaded(); void close(); void changeWindowTitle(const QString &msg); void unreadMessages(int count); + void showNotification(const QString &msg); + void showLoginPage(const QString &msg); + void showUserSettingsPage(); private slots: void showUnreadMessageNotification(int count); @@ -59,18 +79,32 @@ 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); -protected: - void keyPressEvent(QKeyEvent *event) override; - private: - void updateDisplayNames(const RoomState &state); + 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 updateUserMetadata(const QJsonArray &events); + void updateUserDisplayName(const Membership &event); + void updateUserAvatarUrl(const Membership &event); void loadStateFromCache(); - void showQuickSwitcher(); + void deleteConfigs(); + void resetUI(); QHBoxLayout *topLayout_; Splitter *splitter; @@ -89,12 +123,15 @@ private: RoomList *room_list_; TimelineViewManager *view_manager_; + SideBarActions *sidebarActions_; TopRoomBar *top_bar_; TextInputWidget *text_input_; + TypingDisplay *typingDisplay_; - QTimer *sync_timer_; - int sync_interval_; + // Safety net if consensus is not possible or too slow. + QTimer *showContentTimer_; + QTimer *consensusTimer_; QString current_room_; QMap room_avatars_; @@ -104,12 +141,20 @@ private: QMap state_manager_; QMap> settingsManager_; - QuickSwitcher *quickSwitcher_ = nullptr; - OverlayModal *quickSwitcherModal_ = nullptr; + // Keeps track of the users currently typing on each room. + QMap> typingUsers_; + QTimer *typingRefresher_; + + QSharedPointer quickSwitcher_; + QSharedPointer quickSwitcherModal_; // Matrix Client API provider. QSharedPointer client_; // 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/Config.h b/include/Config.h index 654eadb6..b5e876c0 100644 --- a/include/Config.h +++ b/include/Config.h @@ -4,70 +4,70 @@ // // Font sizes are in pixels. -namespace conf -{ +namespace conf { // Global settings. -static const int fontSize = 12; -static const int emojiSize = 14; -static const int headerFontSize = 21; +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/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 e17b110b..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 { @@ -39,7 +38,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 +54,3 @@ private: QLabel *category_; }; - -inline void -EmojiCategory::clickIndex(const QModelIndex &index) -{ - emit emojiSelected(index.data(Qt::UserRole).toString()); -} 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 041e25a6..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 @@ -43,5 +44,5 @@ private: // Horizontal distance from panel's bottom right corner. int horizontal_distance_ = 70; - EmojiPanel *panel_; + QSharedPointer panel_; }; diff --git a/include/EmojiProvider.h b/include/EmojiProvider.h index 5b6f7c88..847157fd 100644 --- a/include/EmojiProvider.h +++ b/include/EmojiProvider.h @@ -17,11 +17,10 @@ #pragma once -#include #include -#include -struct Emoji { +struct Emoji +{ // Unicode code. QString unicode; // Keyboard shortcut e.g :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/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/LoginPage.h b/include/LoginPage.h index 5caa3f1e..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 { @@ -43,6 +42,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 +59,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/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 85cd5a70..2d047b51 100644 --- a/include/MainWindow.h +++ b/include/MainWindow.h @@ -19,15 +19,20 @@ #include #include +#include +#include -#include "ChatPage.h" -#include "LoadingIndicator.h" -#include "LoginPage.h" -#include "MatrixClient.h" -#include "OverlayModal.h" -#include "RegisterPage.h" -#include "TrayIcon.h" -#include "WelcomePage.h" +class ChatPage; +class LoadingIndicator; +class LoginPage; +class MatrixClient; +class OverlayModal; +class RegisterPage; +class SnackBar; +class TrayIcon; +class UserSettingsPage; +class UserSettings; +class WelcomePage; class MainWindow : public QMainWindow { @@ -55,6 +60,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); @@ -81,14 +87,19 @@ private: // The main chat area. ChatPage *chat_page_; + UserSettingsPage *userSettingsPage_; + QSharedPointer userSettings_; // Used to hide undefined states between page transitions. - OverlayModal *progress_modal_; - LoadingIndicator *spinner_; + QSharedPointer progressModal_; + QSharedPointer spinner_; // Matrix Client API provider. QSharedPointer client_; // 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 cd023650..d6dd7162 100644 --- a/include/MatrixClient.h +++ b/include/MatrixClient.h @@ -17,12 +17,14 @@ #pragma once -#include +#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 @@ -50,14 +52,16 @@ 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); + void sendTypingNotification(const QString &roomid, int timeoutInMillis = 20000); + void removeTypingNotification(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 +69,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); @@ -91,56 +98,17 @@ 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); 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); 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_; @@ -159,39 +127,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..d640db02 100644 --- a/include/Profile.h +++ b/include/Profile.h @@ -20,29 +20,17 @@ #include #include -#include "Deserializable.h" +class Deserializable; 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..fc4a49e6 100644 --- a/include/Register.h +++ b/include/Register.h @@ -19,7 +19,7 @@ #include -#include "Deserializable.h" +class Deserializable; class RegisterRequest { @@ -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/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 03023038..a137b37f 100644 --- a/include/RoomInfoListItem.h +++ b/include/RoomInfoListItem.h @@ -21,12 +21,14 @@ #include #include -#include "Menu.h" -#include "RippleOverlay.h" -#include "RoomSettings.h" #include "RoomState.h" -struct DescInfo { +class Menu; +class RippleOverlay; +class RoomSettings; + +struct DescInfo +{ QString username; QString userid; QString body; @@ -49,11 +51,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); @@ -96,36 +99,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/RoomList.h b/include/RoomList.h index c2f4255d..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 { @@ -76,8 +79,8 @@ private: OverlayModal *joinRoomModal_; JoinRoomDialog *joinRoomDialog_; - OverlayModal *leaveRoomModal; - LeaveRoomDialog *leaveRoomDialog_; + QSharedPointer leaveRoomModal_; + QSharedPointer leaveRoomDialog_; QMap> rooms_; 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/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/RoomState.h b/include/RoomState.h index a07fcbed..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. // @@ -49,9 +52,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); @@ -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_; @@ -81,21 +85,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/SideBarActions.h b/include/SideBarActions.h new file mode 100644 index 00000000..bf48c4da --- /dev/null +++ b/include/SideBarActions.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include +#include + +#include "FlatButton.h" + +class SideBarActions : public QWidget +{ + Q_OBJECT + +public: + SideBarActions(QWidget *parent = nullptr); + ~SideBarActions(); + +signals: + void showSettings(); + +protected: + void resizeEvent(QResizeEvent *event) override; + +private: + QHBoxLayout *layout_; + + FlatButton *settingsBtn_; + FlatButton *createRoomBtn_; + FlatButton *joinRoomBtn_; +}; diff --git a/include/Sync.h b/include/Sync.h index a9caf473..d59a57dc 100644 --- a/include/Sync.h +++ b/include/Sync.h @@ -18,24 +18,22 @@ #pragma once #include -#include #include -#include #include "Deserializable.h" 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 +49,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,58 +74,29 @@ 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 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; + State state() const { return state_; }; + Timeline timeline() const { return timeline_; }; + QList typingUserIDs() const { return typingUserIDs_; }; void deserialize(const QJsonValue &data) override; private: State state_; Timeline timeline_; - /* Ephemeral ephemeral_; */ + QList typingUserIDs_; /* AccountData account_data_; */ /* UnreadNotifications unread_notifications_; */ }; -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; @@ -184,24 +105,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: @@ -209,38 +118,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 732f4f61..70b1c213 100644 --- a/include/TextInputWidget.h +++ b/include/TextInputWidget.h @@ -17,6 +17,8 @@ #pragma once +#include + #include #include #include @@ -29,17 +31,36 @@ namespace msgs = matrix::events::messages; -static const QString EMOTE_COMMAND("/me "); - class FilteredTextEdit : public QTextEdit { Q_OBJECT + 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 @@ -50,11 +71,12 @@ public: TextInputWidget(QWidget *parent = 0); ~TextInputWidget(); + void stopTyping(); + public slots: - void onSendButtonClicked(); void openFileSelection(); void hideUploadSpinner(); - inline void focusLineEdit(); + void focusLineEdit() { input_->setFocus(); }; private slots: void addSelectedEmoji(const QString &emoji); @@ -63,10 +85,17 @@ signals: void sendTextMessage(QString msg); void sendEmoteMessage(QString msg); void uploadImage(QString filename); + void sendJoinRoomRequest(const QString &room); + + void startedTyping(); + void stoppedTyping(); + +protected: + void focusInEvent(QFocusEvent *event); private: void showUploadSpinner(); - QString parseEmoteCommand(const QString &cmd); + void command(QString name, QString args); QHBoxLayout *topLayout_; FilteredTextEdit *input_; @@ -77,9 +106,3 @@ private: FlatButton *sendMessageBtn_; EmojiPickButton *emojiBtn_; }; - -inline void -TextInputWidget::focusLineEdit() -{ - input_->setFocus(); -} diff --git a/include/TimelineItem.h b/include/TimelineItem.h index 0a0538f9..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; @@ -65,7 +63,7 @@ public: QWidget *parent); void setUserAvatar(const QImage &pixmap); - inline DescInfo descriptionMessage() const; + DescInfo descriptionMessage() const { return descriptionMsg_; }; ~TimelineItem(); @@ -98,9 +96,3 @@ private: QLabel *userName_; QLabel *body_; }; - -inline DescInfo -TimelineItem::descriptionMessage() const -{ - return descriptionMsg_; -} diff --git a/include/TimelineView.h b/include/TimelineView.h index 4b5a2f77..7e44db46 100644 --- a/include/TimelineView.h +++ b/include/TimelineView.h @@ -17,29 +17,31 @@ #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; // 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 +52,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, }; @@ -97,6 +99,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() { return scroll_layout_->count() > 1 || isTimelineFinished; }; + signals: void updateLastTimelineMessage(const QString &user, const DescInfo &info); @@ -116,7 +121,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); @@ -137,12 +142,11 @@ 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; + const int SCROLL_BAR_GAP = 200; QTimer *paginationTimer_; @@ -152,14 +156,12 @@ private: int oldPosition_; int oldHeight_; + FloatingButton *scrollDownBtn_; + + TimelineDirection lastMessageDirection_; + // The events currently rendered. Used for duplicate detection. QMap eventIds_; QList pending_msgs_; QSharedPointer client_; }; - -inline bool -TimelineView::isDuplicate(const QString &event_id) -{ - return eventIds_.contains(event_id); -} diff --git a/include/TimelineViewManager.h b/include/TimelineViewManager.h index 35dcac5a..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 { @@ -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/include/TopRoomBar.h b/include/TopRoomBar.h index 5d8b394e..f1e93d9d 100644 --- a/include/TopRoomBar.h +++ b/include/TopRoomBar.h @@ -18,21 +18,21 @@ #pragma once #include -#include #include #include #include +#include #include #include #include -#include -#include "Avatar.h" -#include "FlatButton.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+)"); @@ -44,10 +44,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); @@ -58,6 +58,7 @@ signals: protected: void paintEvent(QPaintEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; private slots: void closeLeaveRoomDialog(bool leaving); @@ -67,7 +68,7 @@ private: QVBoxLayout *textLayout_; QLabel *nameLabel_; - QLabel *topicLabel_; + Label *topicLabel_; QSharedPointer roomSettings_; @@ -77,45 +78,13 @@ private: FlatButton *settingsBtn_; - OverlayModal *leaveRoomModal; - LeaveRoomDialog *leaveRoomDialog_; + QSharedPointer leaveRoomModal_; + QSharedPointer leaveRoomDialog_; Avatar *avatar_; int buttonSize_; + + 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) -{ - QString elidedText = - QFontMetrics(nameLabel_->font()).elidedText(name, Qt::ElideRight, width() * 0.8); - nameLabel_->setText(elidedText); - update(); -} - -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); - update(); -} 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/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/include/UserInfoWidget.h b/include/UserInfoWidget.h index 25dc2265..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 { @@ -68,8 +66,8 @@ private: QImage avatar_image_; - OverlayModal *logoutModal_; - LogoutDialog *logoutDialog_; + QSharedPointer logoutModal_; + QSharedPointer logoutDialog_; int logoutButtonSize_; }; diff --git a/include/UserSettingsPage.h b/include/UserSettingsPage.h new file mode 100644 index 00000000..6f6da2c5 --- /dev/null +++ b/include/UserSettingsPage.h @@ -0,0 +1,81 @@ +/* + * 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(); + void trayOptionChanged(bool value); + +private: + // Layouts + QVBoxLayout *topLayout_; + QHBoxLayout *topBarLayout_; + + // Shared settings object. + QSharedPointer settings_; + + Toggle *trayToggle_; + QComboBox *themeCombo_; +}; diff --git a/include/Versions.h b/include/Versions.h index 62584eb7..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 { @@ -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/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/events/AliasesEventContent.h b/include/events/AliasesEventContent.h index a60da9e8..7784fad7 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 @@ -34,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 d46f0420..55284aa4 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. */ @@ -38,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 210e4a34..6322c001 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 @@ -40,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 7db2d367..0a47860e 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. @@ -38,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 c6d2da2f..84c21907 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 @@ -67,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 53405087..1c39ae03 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, @@ -37,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; @@ -46,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 738be9d3..4ed9e65f 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, @@ -52,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 5af19218..8b7b1576 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, @@ -55,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_; @@ -65,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 1b105d62..3c4f83e9 100644 --- a/include/events/MessageEvent.h +++ b/include/events/MessageEvent.h @@ -20,15 +20,13 @@ #include "MessageEventContent.h" #include "RoomEvent.h" -namespace matrix -{ -namespace events -{ +namespace matrix { +namespace events { template class MessageEvent : public RoomEvent { public: - inline MsgContent msgContent() const; + MsgContent msgContent() const; void deserialize(const QJsonValue &data) override; @@ -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..aa08c066 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, @@ -65,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 d2cf86da..378f689d 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. */ @@ -37,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 7cfeadf1..63998871 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, @@ -44,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; @@ -70,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 540fafaf..d80951c7 100644 --- a/include/events/RoomEvent.h +++ b/include/events/RoomEvent.h @@ -22,18 +22,16 @@ #include "Event.h" -namespace matrix -{ -namespace events -{ +namespace matrix { +namespace events { 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 75bf1bbb..19342a48 100644 --- a/include/events/StateEvent.h +++ b/include/events/StateEvent.h @@ -21,16 +21,14 @@ #include "RoomEvent.h" -namespace matrix -{ -namespace events -{ +namespace matrix { +namespace events { 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 b6c376e7..67e21208 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. @@ -38,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 41bc2496..8466e0e4 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; @@ -37,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; @@ -47,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/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..2fb2aa83 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; @@ -39,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; @@ -53,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 93a598e2..1e709579 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; @@ -41,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; @@ -51,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 4b523878..27722b37 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; }; @@ -36,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; @@ -47,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/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..79713546 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; @@ -42,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; @@ -52,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/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/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/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/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/include/ui/SnackBar.h b/include/ui/SnackBar.h new file mode 100644 index 00000000..8a35c327 --- /dev/null +++ b/include/ui/SnackBar.h @@ -0,0 +1,81 @@ +#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/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/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/resources/icons/add-file.png b/resources/icons/add-file.png deleted file mode 100644 index 806b166d..00000000 Binary files a/resources/icons/add-file.png and /dev/null differ diff --git a/resources/icons/clip-dark.png b/resources/icons/clip-dark.png deleted file mode 100644 index c3c34fac..00000000 Binary files a/resources/icons/clip-dark.png and /dev/null differ diff --git a/resources/icons/cog.png b/resources/icons/cog.png deleted file mode 100644 index 84311166..00000000 Binary files a/resources/icons/cog.png and /dev/null differ diff --git a/resources/icons/emoji-categories/activity.png b/resources/icons/emoji-categories/activity.png index 7824765d..2d360762 100644 Binary files a/resources/icons/emoji-categories/activity.png and b/resources/icons/emoji-categories/activity.png differ 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 00000000..d8f88711 Binary files /dev/null and b/resources/icons/emoji-categories/activity@2x.png differ diff --git a/resources/icons/emoji-categories/diversity.svg b/resources/icons/emoji-categories/diversity.svg deleted file mode 100755 index 2498950f..00000000 --- a/resources/icons/emoji-categories/diversity.svg +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - diff --git a/resources/icons/emoji-categories/flags.png b/resources/icons/emoji-categories/flags.png index 47f68375..9a52000f 100644 Binary files a/resources/icons/emoji-categories/flags.png and b/resources/icons/emoji-categories/flags.png differ 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 00000000..45350593 Binary files /dev/null and b/resources/icons/emoji-categories/flags@2x.png differ diff --git a/resources/icons/emoji-categories/foods.png b/resources/icons/emoji-categories/foods.png index 2443ece9..15c31069 100644 Binary files a/resources/icons/emoji-categories/foods.png and b/resources/icons/emoji-categories/foods.png differ diff --git a/resources/icons/emoji-categories/foods.svg b/resources/icons/emoji-categories/foods.svg deleted file mode 100644 index 40c97c1c..00000000 --- a/resources/icons/emoji-categories/foods.svg +++ /dev/null @@ -1,25 +0,0 @@ - -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 00000000..bbdd2a3c Binary files /dev/null and b/resources/icons/emoji-categories/foods@2x.png differ diff --git a/resources/icons/emoji-categories/nature.png b/resources/icons/emoji-categories/nature.png index 7a131ba2..eb1786cf 100644 Binary files a/resources/icons/emoji-categories/nature.png and b/resources/icons/emoji-categories/nature.png differ diff --git a/resources/icons/emoji-categories/nature.svg b/resources/icons/emoji-categories/nature.svg deleted file mode 100644 index 4f291283..00000000 --- a/resources/icons/emoji-categories/nature.svg +++ /dev/null @@ -1,36 +0,0 @@ - -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 00000000..81db5c08 Binary files /dev/null and b/resources/icons/emoji-categories/nature@2x.png differ diff --git a/resources/icons/emoji-categories/objects.png b/resources/icons/emoji-categories/objects.png index 7f6a8d13..45c6eb37 100644 Binary files a/resources/icons/emoji-categories/objects.png and b/resources/icons/emoji-categories/objects.png differ diff --git a/resources/icons/emoji-categories/objects.svg b/resources/icons/emoji-categories/objects.svg deleted file mode 100644 index 67c43610..00000000 --- a/resources/icons/emoji-categories/objects.svg +++ /dev/null @@ -1,30 +0,0 @@ - -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 00000000..01fd5cb4 Binary files /dev/null and b/resources/icons/emoji-categories/objects@2x.png differ diff --git a/resources/icons/emoji-categories/people.png b/resources/icons/emoji-categories/people.png index 8c826fa9..710e808a 100644 Binary files a/resources/icons/emoji-categories/people.png and b/resources/icons/emoji-categories/people.png differ 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 00000000..142ba09e Binary files /dev/null and b/resources/icons/emoji-categories/people@2x.png differ diff --git a/resources/icons/emoji-categories/recent.svg b/resources/icons/emoji-categories/recent.svg deleted file mode 100644 index e79b2ebc..00000000 --- a/resources/icons/emoji-categories/recent.svg +++ /dev/null @@ -1,29 +0,0 @@ - -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 69a39754..08184de1 100644 Binary files a/resources/icons/emoji-categories/symbols.png and b/resources/icons/emoji-categories/symbols.png differ 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 00000000..b5e7cc6c Binary files /dev/null and b/resources/icons/emoji-categories/symbols@2x.png differ diff --git a/resources/icons/emoji-categories/travel.png b/resources/icons/emoji-categories/travel.png index ab50f7cb..93da773e 100644 Binary files a/resources/icons/emoji-categories/travel.png and b/resources/icons/emoji-categories/travel.png differ diff --git a/resources/icons/emoji-categories/travel.svg b/resources/icons/emoji-categories/travel.svg deleted file mode 100644 index ca6cb325..00000000 --- a/resources/icons/emoji-categories/travel.svg +++ /dev/null @@ -1,34 +0,0 @@ - -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 00000000..2f72a281 Binary files /dev/null and b/resources/icons/emoji-categories/travel@2x.png differ diff --git a/resources/icons/emoji-categories/unicode9.svg b/resources/icons/emoji-categories/unicode9.svg deleted file mode 100755 index 310c4228..00000000 --- a/resources/icons/emoji-categories/unicode9.svg +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - diff --git a/resources/icons/error.png b/resources/icons/error.png deleted file mode 100644 index 295dbf06..00000000 Binary files a/resources/icons/error.png and /dev/null differ diff --git a/resources/icons/left-angle.png b/resources/icons/left-angle.png deleted file mode 100644 index 71543286..00000000 Binary files a/resources/icons/left-angle.png and /dev/null differ diff --git a/resources/icons/left-chevron.png b/resources/icons/left-chevron.png deleted file mode 100644 index 386395fb..00000000 Binary files a/resources/icons/left-chevron.png and /dev/null differ diff --git a/resources/icons/plus-symbol.png b/resources/icons/plus-symbol.png deleted file mode 100644 index 35aef7d0..00000000 Binary files a/resources/icons/plus-symbol.png and /dev/null differ diff --git a/resources/icons/power-button-off.png b/resources/icons/power-button-off.png deleted file mode 100644 index 2409032f..00000000 Binary files a/resources/icons/power-button-off.png and /dev/null differ diff --git a/resources/icons/search.png b/resources/icons/search.png deleted file mode 100644 index 94e2f4fc..00000000 Binary files a/resources/icons/search.png and /dev/null differ diff --git a/resources/icons/send-button.png b/resources/icons/send-button.png deleted file mode 100644 index d19bf69f..00000000 Binary files a/resources/icons/send-button.png and /dev/null differ diff --git a/resources/icons/share-dark.png b/resources/icons/share-dark.png deleted file mode 100644 index 3e4d42a8..00000000 Binary files a/resources/icons/share-dark.png and /dev/null differ diff --git a/resources/icons/smile.png b/resources/icons/smile.png deleted file mode 100644 index f226a5f3..00000000 Binary files a/resources/icons/smile.png and /dev/null differ diff --git a/resources/icons/ui/add-square-button.png b/resources/icons/ui/add-square-button.png new file mode 100644 index 00000000..a4933c16 Binary files /dev/null and b/resources/icons/ui/add-square-button.png differ 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 00000000..b2c4a44b Binary files /dev/null and b/resources/icons/ui/add-square-button@2x.png differ diff --git a/resources/icons/ui/angle-arrow-down.png b/resources/icons/ui/angle-arrow-down.png new file mode 100644 index 00000000..e40ebca5 Binary files /dev/null and b/resources/icons/ui/angle-arrow-down.png differ diff --git a/resources/icons/ui/angle-arrow-down@2x.png b/resources/icons/ui/angle-arrow-down@2x.png new file mode 100644 index 00000000..ed095bfe Binary files /dev/null and b/resources/icons/ui/angle-arrow-down@2x.png differ diff --git a/resources/icons/ui/angle-pointing-to-left.png b/resources/icons/ui/angle-pointing-to-left.png new file mode 100644 index 00000000..7c5432a6 Binary files /dev/null and b/resources/icons/ui/angle-pointing-to-left.png differ 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 00000000..f2fa93c4 Binary files /dev/null and b/resources/icons/ui/angle-pointing-to-left@2x.png differ 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 00000000..f679d676 Binary files /dev/null and b/resources/icons/ui/cloud-storage-uploading-option.png differ 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 00000000..162bc954 Binary files /dev/null and b/resources/icons/ui/cloud-storage-uploading-option@2x.png differ diff --git a/resources/icons/ui/cursor.png b/resources/icons/ui/cursor.png new file mode 100644 index 00000000..cc7a2477 Binary files /dev/null and b/resources/icons/ui/cursor.png differ diff --git a/resources/icons/ui/cursor@2x.png b/resources/icons/ui/cursor@2x.png new file mode 100644 index 00000000..d02d29fb Binary files /dev/null and b/resources/icons/ui/cursor@2x.png differ diff --git a/resources/icons/ui/paper-clip-outline.png b/resources/icons/ui/paper-clip-outline.png new file mode 100644 index 00000000..a30fd07b Binary files /dev/null and b/resources/icons/ui/paper-clip-outline.png differ diff --git a/resources/icons/ui/paper-clip-outline@2x.png b/resources/icons/ui/paper-clip-outline@2x.png new file mode 100644 index 00000000..6cb3d6a5 Binary files /dev/null and b/resources/icons/ui/paper-clip-outline@2x.png differ diff --git a/resources/icons/ui/plus-black-symbol.png b/resources/icons/ui/plus-black-symbol.png new file mode 100644 index 00000000..4ddfc93f Binary files /dev/null and b/resources/icons/ui/plus-black-symbol.png differ diff --git a/resources/icons/ui/plus-black-symbol@2x.png b/resources/icons/ui/plus-black-symbol@2x.png new file mode 100644 index 00000000..c217b396 Binary files /dev/null and b/resources/icons/ui/plus-black-symbol@2x.png differ diff --git a/resources/icons/ui/power-button-off.png b/resources/icons/ui/power-button-off.png new file mode 100644 index 00000000..7763de0f Binary files /dev/null and b/resources/icons/ui/power-button-off.png differ 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 00000000..a4629446 Binary files /dev/null and b/resources/icons/ui/power-button-off@2x.png differ diff --git a/resources/icons/ui/settings.png b/resources/icons/ui/settings.png new file mode 100644 index 00000000..6e013f75 Binary files /dev/null and b/resources/icons/ui/settings.png differ diff --git a/resources/icons/ui/settings@2x.png b/resources/icons/ui/settings@2x.png new file mode 100644 index 00000000..dae05360 Binary files /dev/null and b/resources/icons/ui/settings@2x.png differ diff --git a/resources/icons/ui/smile.png b/resources/icons/ui/smile.png new file mode 100644 index 00000000..161d46d4 Binary files /dev/null and b/resources/icons/ui/smile.png differ diff --git a/resources/icons/ui/smile@2x.png b/resources/icons/ui/smile@2x.png new file mode 100644 index 00000000..acc439e9 Binary files /dev/null and b/resources/icons/ui/smile@2x.png differ 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 00000000..3ec0165d Binary files /dev/null and b/resources/icons/ui/speech-bubbles-comment-option.png differ diff --git a/resources/icons/ui/speech-bubbles-comment-option@2x.png b/resources/icons/ui/speech-bubbles-comment-option@2x.png new file mode 100644 index 00000000..8a07321d Binary files /dev/null and b/resources/icons/ui/speech-bubbles-comment-option@2x.png differ diff --git a/resources/icons/ui/vertical-ellipsis.png b/resources/icons/ui/vertical-ellipsis.png new file mode 100644 index 00000000..5ce9d78f Binary files /dev/null and b/resources/icons/ui/vertical-ellipsis.png differ diff --git a/resources/icons/ui/vertical-ellipsis@2x.png b/resources/icons/ui/vertical-ellipsis@2x.png new file mode 100644 index 00000000..9af0c042 Binary files /dev/null and b/resources/icons/ui/vertical-ellipsis@2x.png differ diff --git a/resources/icons/user-shape.png b/resources/icons/user-shape.png deleted file mode 100644 index 1b4b46cc..00000000 Binary files a/resources/icons/user-shape.png and /dev/null differ diff --git a/resources/icons/vertical-ellipsis.png b/resources/icons/vertical-ellipsis.png deleted file mode 100644 index 82714451..00000000 Binary files a/resources/icons/vertical-ellipsis.png and /dev/null differ 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! diff --git a/resources/login.png b/resources/login.png new file mode 100644 index 00000000..e65084ef Binary files /dev/null and b/resources/login.png differ diff --git a/resources/login@2x.png b/resources/login@2x.png new file mode 100644 index 00000000..4f89112f Binary files /dev/null and b/resources/login@2x.png differ 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 diff --git a/resources/register.png b/resources/register.png new file mode 100644 index 00000000..e65084ef Binary files /dev/null and b/resources/register.png differ diff --git a/resources/register@2x.png b/resources/register@2x.png new file mode 100644 index 00000000..4f89112f Binary files /dev/null and b/resources/register@2x.png differ diff --git a/resources/res.qrc b/resources/res.qrc index 13a7d309..55962275 100644 --- a/resources/res.qrc +++ b/resources/res.qrc @@ -1,33 +1,60 @@ - 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/ui/angle-arrow-down.png + icons/ui/angle-arrow-down@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 +64,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 00000000..4f89112f Binary files /dev/null and b/resources/splash.png differ diff --git a/resources/splash@2x.png b/resources/splash@2x.png new file mode 100644 index 00000000..3c39b0be Binary files /dev/null and b/resources/splash@2x.png differ diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml deleted file mode 100644 index a25f1013..00000000 --- a/snap/snapcraft.yaml +++ /dev/null @@ -1,23 +0,0 @@ -name: nheko -version: master -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 - build-packages: [gcc] - artifacts: [nheko] - after: [desktop-qt5] diff --git a/src/AvatarProvider.cc b/src/AvatarProvider.cc index c4e5d19e..7e8c9e49 100644 --- a/src/AvatarProvider.cc +++ b/src/AvatarProvider.cc @@ -16,11 +16,12 @@ */ #include "AvatarProvider.h" +#include "MatrixClient.h" +#include "TimelineItem.h" QSharedPointer AvatarProvider::client_; -QMap AvatarProvider::userAvatars_; -QMap AvatarProvider::avatarUrls_; +QMap AvatarProvider::avatars_; QMap> AvatarProvider::toBeResolved_; void @@ -44,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/Cache.cc b/src/Cache.cc index 5ed77086..dc2c8a9f 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; @@ -31,16 +31,26 @@ 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 +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 +58,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 +93,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 @@ -95,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 @@ -156,6 +170,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); @@ -255,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/ChatPage.cc b/src/ChatPage.cc index a6a80e9d..884e219a 100644 --- a/src/ChatPage.cc +++ b/src/ChatPage.cc @@ -15,25 +15,38 @@ * along with this program. If not, see . */ +#include #include #include +#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 "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; namespace events = matrix::events; ChatPage::ChatPage(QSharedPointer client, QWidget *parent) : QWidget(parent) - , sync_interval_(2000) , client_(client) { setStyleSheet("background-color: #fff;"); @@ -61,8 +74,13 @@ ChatPage::ChatPage(QSharedPointer client, QWidget *parent) sideBarMainLayout_->setSpacing(0); sideBarMainLayout_->setMargin(0); + sidebarActions_ = new SideBarActions(this); + connect( + sidebarActions_, &SideBarActions::showSettings, this, &ChatPage::showUserSettingsPage); + sideBarLayout_->addLayout(sideBarTopLayout_); sideBarLayout_->addLayout(sideBarMainLayout_); + sideBarLayout_->addWidget(sidebarActions_); sideBarTopWidget_ = new QWidget(sideBar_); sideBarTopWidget_->setStyleSheet("background-color: #d6dde3; color: #ebebeb;"); @@ -101,22 +119,33 @@ 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(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_); - sync_timer_ = new QTimer(this); - sync_timer_->setSingleShot(true); - connect(sync_timer_, SIGNAL(timeout()), this, SLOT(startSync())); - connect(user_info_widget_, SIGNAL(logout()), client_.data(), SLOT(logout())); connect(client_.data(), SIGNAL(loggedOut()), this, SLOT(logout())); 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, text_input_, &TextInputWidget::stopTyping); + connect(room_list_, &RoomList::roomChanged, this, &ChatPage::changeTopRoomInfo); connect(room_list_, &RoomList::roomChanged, text_input_, &TextInputWidget::focusLineEdit); connect( @@ -136,6 +165,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_, @@ -156,10 +199,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, @@ -177,6 +226,29 @@ ChatPage::ChatPage(QSharedPointer client, QWidget *parent) SIGNAL(initialSyncCompleted(const SyncResponse &)), 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) { + 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, @@ -193,24 +265,62 @@ 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, 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(); + } + }); + AvatarProvider::init(client); } void ChatPage::logout() { - sync_timer_->stop(); + deleteConfigs(); - // Delete all config parameters. + 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(); + + showUnreadMessageNotification(0); +} + +void +ChatPage::deleteConfigs() +{ QSettings settings; settings.beginGroup("auth"); settings.remove(""); @@ -224,21 +334,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 @@ -248,22 +344,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(); -} - -void -ChatPage::startSync() -{ - client_->sync(); + client_->initialSync(); } void @@ -280,101 +377,23 @@ ChatPage::syncFailed(const QString &msg) return; qWarning() << "Sync error:" << msg; - sync_timer_->start(sync_interval_ * 5); -} - -// 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); - } + QTimer::singleShot(SYNC_RETRY_TIMEOUT, this, [=]() { client_->sync(); }); } 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++) { - 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()); - } - - auto leave = response.rooms().leave(); - - for (auto it = leave.constBegin(); it != leave.constEnd(); it++) { - if (state_manager_.contains(it.key())) { - removeRoom(it.key()); - } - } - - 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(); - } - - 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()); - sync_timer_->start(sync_interval_); + client_->setNextBatchToken(response.nextBatch()); + client_->sync(); } void @@ -382,7 +401,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. @@ -396,29 +415,19 @@ 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(); } - try { - cache_->setState(response.nextBatch(), state_manager_); - } catch (const lmdb::error &e) { - qCritical() << "The cache couldn't be initialized: " << e.what(); - cache_->unmount(); - } - - client_->setNextBatchToken(response.nextBatch()); + QtConcurrent::run(cache_.data(), &Cache::setState, response.nextBatch(), state_manager_); // Populate timelines with messages. view_manager_->initialize(response.rooms()); @@ -426,7 +435,8 @@ ChatPage::initialSyncCompleted(const SyncResponse &response) // Initialize room list. room_list_->setInitialRooms(settingsManager_, state_manager_); - sync_timer_->start(sync_interval_); + client_->setNextBatchToken(response.nextBatch()); + client_->sync(); emit contentLoaded(); } @@ -492,19 +502,13 @@ 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(); - 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. @@ -512,9 +516,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); @@ -524,11 +525,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); } } @@ -538,47 +536,52 @@ 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(CONSENSUS_TIMEOUT); - sync_timer_->start(sync_interval_); -} + // Show the content if consensus can't be achieved. + showContentTimer_->start(SHOW_CONTENT_TIMEOUT); -void -ChatPage::keyPressEvent(QKeyEvent *event) -{ - if (event->key() == Qt::Key_K) { - if (event->modifiers() == Qt::ControlModifier) - showQuickSwitcher(); - } + // Start receiving events. + client_->sync(); } 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(); + this->text_input_->setFocus(Qt::FocusReason::PopupFocusReason); }); } - 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)); } 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(); @@ -595,9 +598,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); } } @@ -612,11 +615,216 @@ 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_); } -ChatPage::~ChatPage() +void +ChatPage::updateTypingUsers(const QString &roomid, const QList &user_ids) { - sync_timer_->stop(); + QStringList users; + + 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) { + typingDisplay_->setUsers(users); + } + + 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/Deserializable.cc b/src/Deserializable.cc index 9bef7d68..8bdbfc2c 100644 --- a/src/Deserializable.cc +++ b/src/Deserializable.cc @@ -15,16 +15,11 @@ * along with this program. If not, see . */ -#include -#include -#include - #include "Deserializable.h" DeserializationException::DeserializationException(const std::string &msg) : msg_(msg) -{ -} +{} const char * DeserializationException::what() const noexcept 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/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/EmojiPanel.cc b/src/EmojiPanel.cc index 2730ddb5..3f5f8369 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" @@ -28,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 " @@ -63,43 +61,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 f3e30661..06aa3db9 100644 --- a/src/EmojiPickButton.cc +++ b/src/EmojiPickButton.cc @@ -15,24 +15,23 @@ * along with this program. If not, see . */ -#include - #include "EmojiPickButton.h" +#include "EmojiPanel.h" EmojiPickButton::EmojiPickButton(QWidget *parent) : FlatButton(parent) - , panel_{ nullptr } -{ -} + , panel_{nullptr} +{} void 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/EmojiProvider.cc b/src/EmojiProvider.cc index 11cdffcb..ec053046 100644 --- a/src/EmojiProvider.cc +++ b/src/EmojiProvider.cc @@ -16,1460 +16,1450 @@ */ #include -#include -#include -#include -#include -#include #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 5e95f8b2..333fd296 100644 --- a/src/ImageItem.cc +++ b/src/ImageItem.cc @@ -19,7 +19,6 @@ #include #include #include -#include #include #include @@ -33,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); @@ -67,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 56b5707c..7dd4a226 100644 --- a/src/ImageOverlayDialog.cc +++ b/src/ImageOverlayDialog.cc @@ -16,17 +16,14 @@ */ #include -#include #include #include -#include -#include #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/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 d0dd1ea9..ca41d019 100644 --- a/src/Login.cc +++ b/src/Login.cc @@ -15,20 +15,15 @@ * along with this program. If not, see . */ -#include -#include -#include - -#include "Deserializable.h" #include "Login.h" +#include "Deserializable.h" LoginRequest::LoginRequest() {} LoginRequest::LoginRequest(QString username, QString password) : user_(username) , password_(password) -{ -} +{} QByteArray LoginRequest::serialize() noexcept @@ -44,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 fd7fede3..0b65f8bc 100644 --- a/src/LoginPage.cc +++ b/src/LoginPage.cc @@ -15,16 +15,20 @@ * along with this program. If not, see . */ -#include - -#include "Config.h" -#include "InputValidator.h" #include "LoginPage.h" +#include "Config.h" +#include "FlatButton.h" +#include "InputValidator.h" +#include "LoadingIndicator.h" +#include "MatrixClient.h" +#include "OverlayModal.h" +#include "RaisedButton.h" +#include "TextField.h" LoginPage::LoginPage(QSharedPointer client, QWidget *parent) : QWidget(parent) , inferredServerAddress_() - , client_{ client } + , client_{client} { setStyleSheet("background-color: #fff"); @@ -42,16 +46,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/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 43f4a949..9653897d 100644 --- a/src/MainWindow.cc +++ b/src/MainWindow.cc @@ -15,22 +15,31 @@ * 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 "UserSettingsPage.h" +#include "WelcomePage.h" MainWindow *MainWindow::instance_ = nullptr; MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) - , progress_modal_{ nullptr } - , spinner_{ nullptr } + , progressModal_{nullptr} + , spinner_{nullptr} { QSizePolicy sizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); setSizePolicy(sizePolicy); @@ -46,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); @@ -60,6 +71,7 @@ MainWindow::MainWindow(QWidget *parent) pageStack_->addWidget(login_page_); pageStack_->addWidget(register_page_); pageStack_->addWidget(chat_page_); + pageStack_->addWidget(userSettingsPage_); setCentralWidget(pageStack_); @@ -73,6 +85,17 @@ 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(userSettingsPage_, &UserSettingsPage::moveBack, this, [=]() { + pageStack_->setCurrentWidget(chat_page_); + }); + + connect( + userSettingsPage_, SIGNAL(trayOptionChanged(bool)), trayIcon_, SLOT(setVisible(bool))); connect(trayIcon_, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), @@ -80,6 +103,8 @@ MainWindow::MainWindow(QWidget *parent) 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)), @@ -89,8 +114,15 @@ 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()); + if (hasActiveUser()) { QString token = settings.value("auth/access_token").toString(); QString home_server = settings.value("auth/home_server").toString(); @@ -132,18 +164,23 @@ 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(); + progressModal_.reset(); + spinner_.reset(); + }); - progress_modal_ = nullptr; - spinner_ = nullptr; + // 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); @@ -166,18 +203,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_->setFixedHeight(100); + spinner_->setFixedWidth(100); 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(); @@ -204,10 +245,16 @@ MainWindow::showRegisterPage() pageStack_->setCurrentWidget(register_page_); } +void +MainWindow::showUserSettingsPage() +{ + pageStack_->setCurrentWidget(userSettingsPage_); +} + void MainWindow::closeEvent(QCloseEvent *event) { - if (isVisible()) { + if (isVisible() && userSettings_->isTrayEnabled()) { event->ignore(); hide(); } diff --git a/src/MatrixClient.cc b/src/MatrixClient.cc index bd43efd8..3876d044 100644 --- a/src/MatrixClient.cc +++ b/src/MatrixClient.cc @@ -25,534 +25,45 @@ #include #include #include -#include #include #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) : 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(); - connect(this, SIGNAL(finished(QNetworkReply *)), this, SLOT(onResponse(QNetworkReply *))); + connect(this, + &QNetworkAccessManager::networkAccessibleChanged, + this, + [=](NetworkAccessibility status) { + if (status != NetworkAccessibility::Accessible) + setNetworkAccessible(NetworkAccessibility::Accessible); + }); } void MatrixClient::reset() noexcept { - next_batch_ = ""; - server_ = ""; - token_ = ""; + next_batch_.clear(); + server_.clear(); + token_.clear(); 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) { - qWarning() << 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) { - qWarning() << reply->errorString(); - return; - } - - auto data = reply->readAll(); - QJsonDocument response = QJsonDocument::fromJson(data); - QString 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 { @@ -564,8 +75,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 @@ -582,8 +134,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 @@ -602,23 +166,53 @@ 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 MatrixClient::sync() noexcept { - QJsonObject filter{ { "room", - QJsonObject{ { "include_leave", true }, - { "ephemeral", QJsonObject{ { "limit", 0 } } } } }, - { "presence", QJsonObject{ { "limit", 0 } } } }; + QJsonObject filter{ + {"room", + QJsonObject{ + {"include_leave", true}, + }}, + }; 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()) { @@ -634,9 +228,35 @@ 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)); + 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 @@ -658,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; @@ -674,10 +294,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(); } @@ -685,19 +335,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_); @@ -705,9 +344,37 @@ 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)); + 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 @@ -718,8 +385,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 @@ -740,7 +438,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 @@ -767,8 +487,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 @@ -794,9 +532,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 @@ -804,9 +560,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 @@ -832,12 +606,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_); @@ -846,14 +639,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 @@ -878,10 +692,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 @@ -897,8 +739,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)); + 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 @@ -914,7 +779,68 @@ 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); + }); +} + +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/Profile.cc b/src/Profile.cc index dcd9c7a1..8eaafe07 100644 --- a/src/Profile.cc +++ b/src/Profile.cc @@ -15,11 +15,8 @@ * along with this program. If not, see . */ -#include -#include -#include - #include "Deserializable.h" + #include "Profile.h" void diff --git a/src/QuickSwitcher.cc b/src/QuickSwitcher.cc index 29876f99..2be636a4 100644 --- a/src/QuickSwitcher.cc +++ b/src/QuickSwitcher.cc @@ -16,7 +16,6 @@ */ #include -#include #include #include @@ -24,8 +23,7 @@ RoomSearchInput::RoomSearchInput(QWidget *parent) : TextField(parent) -{ -} +{} bool RoomSearchInput::focusNextPrevChild(bool next) @@ -124,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/Register.cc b/src/Register.cc index 2e2c227c..84ba82e7 100644 --- a/src/Register.cc +++ b/src/Register.cc @@ -15,23 +15,19 @@ * along with this program. If not, see . */ -#include -#include -#include - #include "Deserializable.h" + #include "Register.h" RegisterRequest::RegisterRequest(const QString &username, const QString &password) : user_(username) , password_(password) -{ -} +{} 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/RegisterPage.cc b/src/RegisterPage.cc index e485de7d..304a7dc0 100644 --- a/src/RegisterPage.cc +++ b/src/RegisterPage.cc @@ -15,12 +15,14 @@ * along with this program. If not, see . */ -#include -#include - -#include "Config.h" -#include "InputValidator.h" #include "RegisterPage.h" +#include "Avatar.h" +#include "Config.h" +#include "FlatButton.h" +#include "InputValidator.h" +#include "MatrixClient.h" +#include "RaisedButton.h" +#include "TextField.h" RegisterPage::RegisterPage(QSharedPointer client, QWidget *parent) : QWidget(parent) @@ -38,17 +40,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/RoomInfoListItem.cc b/src/RoomInfoListItem.cc index cd15d839..49b24b58 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, @@ -32,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) @@ -313,4 +314,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/RoomList.cc b/src/RoomList.cc index 0383be6e..c89e4e6e 100644 --- a/src/RoomList.cc +++ b/src/RoomList.cc @@ -16,12 +16,17 @@ */ #include -#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) @@ -136,7 +141,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(); @@ -168,22 +173,29 @@ 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 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(); @@ -220,7 +232,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 { @@ -266,7 +278,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/RoomState.cc b/src/RoomState.cc index de0ed6ea..2b8bcdba 100644 --- a/src/RoomState.cc +++ b/src/RoomState.cc @@ -15,7 +15,6 @@ * along with this program. If not, see . */ -#include #include #include @@ -23,6 +22,9 @@ namespace events = matrix::events; +RoomState::RoomState() {} +RoomState::RoomState(const QJsonArray &events) { updateFromEvents(events); } + void RoomState::resolveName() { diff --git a/src/SideBarActions.cc b/src/SideBarActions.cc new file mode 100644 index 00000000..16579ce3 --- /dev/null +++ b/src/SideBarActions.cc @@ -0,0 +1,66 @@ +#include + +#include "Config.h" +#include "SideBarActions.h" +#include "Theme.h" + +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_); + + connect(settingsBtn_, &QPushButton::clicked, this, &SideBarActions::showSettings); +} + +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/Sync.cc b/src/Sync.cc index 90314352..416fa0c6 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 @@ -88,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()); @@ -113,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 { @@ -168,7 +163,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..ad053ea3 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,19 +26,143 @@ #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); + 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) - emit enterPressed(); - else + const bool isModifier = (event->modifiers() != Qt::NoModifier); + + if (!isModifier) { + if (!typingTimer_->isActive()) + emit startedTyping(); + + typingTimer_->start(); + } + + 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; + } +} + +void +FilteredTextEdit::stopTyping() +{ + typingTimer_->stop(); + 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) @@ -45,51 +170,48 @@ TextInputWidget::TextInputWidget(QWidget *parent) { setFont(QFont("Emoji One")); + setFixedHeight(conf::textInput::height); 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(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(40); - spinner_->setFixedWidth(40); + spinner_->setFixedHeight(32); + spinner_->setFixedWidth(32); spinner_->hide(); QFont font; - font.setPixelSize(conf::fontSize); + font.setPixelSize(conf::textInputFontSize); 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; 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)); @@ -100,13 +222,18 @@ 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, SLOT(addSelectedEmoji(const QString &))); + + connect(input_, &FilteredTextEdit::startedTyping, this, &TextInputWidget::startedTyping); + + connect(input_, &FilteredTextEdit::stoppedTyping, this, &TextInputWidget::stoppedTyping); } void @@ -134,34 +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 { - emit sendTextMessage(msgText); + if (command == "me") { + sendEmoteMessage(args); + } else if (command == "join") { + sendJoinRoomRequest(args); } - - input_->clear(); -} - -QString -TextInputWidget::parseEmoteCommand(const QString &cmd) -{ - auto text = cmd.right(cmd.size() - EMOTE_COMMAND.size()).trimmed(); - - if (!text.isEmpty()) - return text; - - return QString(""); } void @@ -214,3 +320,15 @@ TextInputWidget::hideUploadSpinner() } TextInputWidget::~TextInputWidget() {} + +void +TextInputWidget::stopTyping() +{ + input_->stopTyping(); +} + +void +TextInputWidget::focusInEvent(QFocusEvent *event) +{ + input_->setFocus(event->reason()); +} diff --git a/src/TimelineItem.cc b/src/TimelineItem.cc index 97269584..bd778175 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" @@ -83,14 +85,14 @@ 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(); + body.replace("\n", "
"); body.replace(URL_REGEX, URL_HTML); generateTimestamp(timestamp); @@ -112,14 +114,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); @@ -156,10 +158,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); @@ -191,12 +193,13 @@ 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 body = event.content().body().trimmed().toHtmlEscaped(); + body.replace("\n", "
"); auto timestamp = QDateTime::fromMSecsSinceEpoch(event.timestamp()); generateTimestamp(timestamp); @@ -236,13 +239,14 @@ 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(); + emoteMsg.replace("\n", "
"); emoteMsg.replace(URL_REGEX, URL_HTML); if (with_sender) { @@ -274,14 +278,15 @@ 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); body = body.toHtmlEscaped(); + body.replace("\n", "
"); body.replace(URL_REGEX, URL_HTML); if (with_sender) { @@ -371,17 +376,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; diff --git a/src/TimelineView.cc b/src/TimelineView.cc index 032d1310..1ffa731d 100644 --- a/src/TimelineView.cc +++ b/src/TimelineView.cc @@ -15,21 +15,18 @@ * along with this program. If not, see . */ +#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; @@ -52,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); @@ -63,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_, ""); @@ -82,22 +79,21 @@ 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); - - if (isPaginationScrollPending_) { - isPaginationScrollPending_ = false; - - 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; - - scroll_area_->verticalScrollBar()->setValue(newPosition); + return; } + + 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; + + if (lastMessageDirection_ == TimelineDirection::Top) + scroll_area_->verticalScrollBar()->setValue(newPosition); } void @@ -140,6 +136,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) @@ -173,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(); @@ -195,9 +205,12 @@ 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; + lastMessageDirection_ = TimelineDirection::Top; + + QApplication::processEvents(); + + prev_batch_token_ = msgs.end(); + isPaginationInProgress_ = false; // Exclude the top stretch. if (!msgs.chunk().isEmpty() && scroll_layout_->count() > 1) @@ -349,6 +362,10 @@ TimelineView::addEvents(const Timeline &timeline) } } + lastMessageDirection_ = TimelineDirection::Bottom; + + QApplication::processEvents(); + if (isInitialSync) { prev_batch_token_ = timeline.previousBatch(); isInitialSync = false; @@ -369,6 +386,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); @@ -488,6 +517,10 @@ 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; PendingMessage message(txn_id, body, "", view_item); @@ -506,6 +539,10 @@ 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; PendingMessage message(txn_id, url, "", view_item); @@ -544,7 +581,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 1969ae5b..bf2bee76 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" @@ -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)) { @@ -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); @@ -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; +} diff --git a/src/TopRoomBar.cc b/src/TopRoomBar.cc index f8a7e600..25745e1a 100644 --- a/src/TopRoomBar.cc +++ b/src/TopRoomBar.cc @@ -17,13 +17,20 @@ #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) : QWidget(parent) - , buttonSize_{ 32 } + , buttonSize_{32} { setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); setMinimumSize(QSize(0, 65)); @@ -52,30 +59,31 @@ 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_); 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)); 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); @@ -86,15 +94,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 +134,7 @@ TopRoomBar::TopRoomBar(QWidget *parent) void TopRoomBar::closeLeaveRoomDialog(bool leaving) { - leaveRoomModal->fadeOut(); + leaveRoomModal_->fadeOut(); if (leaving) { emit leaveRoom(); @@ -142,6 +159,9 @@ TopRoomBar::reset() nameLabel_->setText(""); topicLabel_->setText(""); avatar_->setLetter(QChar('?')); + + roomName_.clear(); + roomTopic_.clear(); } void @@ -154,6 +174,33 @@ 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, nameLabel_->width() - perFrameResize); + nameLabel_->setText(elidedText); + + 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 @@ -162,4 +209,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() {} diff --git a/src/TrayIcon.cc b/src/TrayIcon.cc index 1360cc4e..ac84aaca 100644 --- a/src/TrayIcon.cc +++ b/src/TrayIcon.cc @@ -17,6 +17,7 @@ #include #include +#include #include #include "TrayIcon.h" @@ -122,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/TypingDisplay.cc b/src/TypingDisplay.cc new file mode 100644 index 00000000..da9c1679 --- /dev/null +++ b/src/TypingDisplay.cc @@ -0,0 +1,54 @@ +#include +#include + +#include "Config.h" +#include "TypingDisplay.h" + +TypingDisplay::TypingDisplay(QWidget *parent) + : QWidget(parent) + , leftPadding_{24} +{ + 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("#898989")); + + QFont font("Open Sans Bold"); + font.setPixelSize(conf::typingNotificationFontSize); + 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::AlignVCenter, text_); +} diff --git a/src/UserInfoWidget.cc b/src/UserInfoWidget.cc index b65329da..2f7dc2ef 100644 --- a/src/UserInfoWidget.cc +++ b/src/UserInfoWidget.cc @@ -15,21 +15,23 @@ * 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) : QWidget(parent) , display_name_("User") , user_id_("@user:homeserver.org") - , logoutModal_{ nullptr } - , logoutDialog_{ nullptr } - , logoutButtonSize_{ 32 } + , logoutModal_{nullptr} + , logoutDialog_{nullptr} + , logoutButtonSize_{20} { QSizePolicy sizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); setSizePolicy(sizePolicy); @@ -77,15 +79,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_); @@ -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(); diff --git a/src/UserSettingsPage.cc b/src/UserSettingsPage.cc new file mode 100644 index 00000000..fb5f983c --- /dev/null +++ b/src/UserSettingsPage.cc @@ -0,0 +1,145 @@ +/* + * 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/window/tray", true).toBool(); + theme_ = settings.value("user/theme", "default").toString(); +} + +void +UserSettings::save() +{ + QSettings settings; + settings.beginGroup("user"); + + settings.beginGroup("window"); + settings.setValue("tray", isTrayEnabled_); + settings.endGroup(); + + 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 isDisabled) { + settings_->setTray(!isDisabled); + emit trayOptionChanged(!isDisabled); + }); + + connect(backBtn_, &QPushButton::clicked, this, [=]() { + settings_->save(); + emit moveBack(); + }); +} + +void +UserSettingsPage::showEvent(QShowEvent *) +{ + themeCombo_->setCurrentIndex((settings_->theme() == "default" ? 0 : 1)); + trayToggle_->setState(!settings_->isTrayEnabled()); // Treats true as "off" +} 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 5ea145f7..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) @@ -29,11 +30,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.")); 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/main.cc b/src/main.cc index fa89cc60..8bd77254 100644 --- a/src/main.cc +++ b/src/main.cc @@ -19,10 +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[]) { @@ -61,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 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/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); +} 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); +} diff --git a/src/ui/LoadingIndicator.cc b/src/ui/LoadingIndicator.cc index 0fafaf23..f64151ce 100644 --- a/src/ui/LoadingIndicator.cc +++ b/src/ui/LoadingIndicator.cc @@ -1,6 +1,5 @@ #include "LoadingIndicator.h" -#include #include #include @@ -42,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/OverlayModal.cc b/src/ui/OverlayModal.cc index 1da009d4..4fb57175 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 @@ -23,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/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); } 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/SnackBar.cc b/src/ui/SnackBar.cc new file mode 100644 index 00000000..39566e99 --- /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_ -= 1.1; + + 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_); +} 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/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) diff --git a/src/ui/ToggleButton.cc b/src/ui/ToggleButton.cc new file mode 100644 index 00000000..e7b242b8 --- /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); +}