diff options
-rw-r--r-- | resources/qml/PrivacyScreen.qml | 7 | ||||
-rw-r--r-- | resources/qml/Root.qml | 23 | ||||
-rw-r--r-- | resources/qml/components/FlatButton.qml | 4 | ||||
-rw-r--r-- | resources/qml/pages/WelcomePage.qml | 76 | ||||
-rw-r--r-- | resources/res.qrc | 1 | ||||
-rw-r--r-- | src/Cache.cpp | 2 | ||||
-rw-r--r-- | src/ChatPage.cpp | 40 | ||||
-rw-r--r-- | src/ChatPage.h | 4 | ||||
-rw-r--r-- | src/MainWindow.cpp | 347 | ||||
-rw-r--r-- | src/MainWindow.h | 30 | ||||
-rw-r--r-- | src/TrayIcon.cpp | 7 | ||||
-rw-r--r-- | src/TrayIcon.h | 2 | ||||
-rw-r--r-- | src/UserSettingsPage.cpp | 24 | ||||
-rw-r--r-- | src/main.cpp | 9 | ||||
-rw-r--r-- | src/timeline/InputBar.cpp | 6 | ||||
-rw-r--r-- | src/timeline/RoomlistModel.cpp | 5 | ||||
-rw-r--r-- | src/timeline/TimelineModel.cpp | 4 | ||||
-rw-r--r-- | src/timeline/TimelineViewManager.cpp | 204 | ||||
-rw-r--r-- | src/timeline/TimelineViewManager.h | 19 | ||||
-rw-r--r-- | src/ui/NhekoGlobalObject.cpp | 2 | ||||
-rw-r--r-- | src/ui/UIA.cpp | 7 |
21 files changed, 426 insertions, 397 deletions
diff --git a/resources/qml/PrivacyScreen.qml b/resources/qml/PrivacyScreen.qml index e6286bc6..6ad2a557 100644 --- a/resources/qml/PrivacyScreen.qml +++ b/resources/qml/PrivacyScreen.qml @@ -5,6 +5,7 @@ import QtGraphicalEffects 1.0 import QtQuick 2.12 +import QtQuick.Window 2.2 import im.nheko 1.0 Item { @@ -15,7 +16,7 @@ Item { Connections { function onFocusChanged() { - if (TimelineManager.isWindowFocused) { + if (MainWindow.active) { screenSaverTimer.stop(); screenSaver.state = "Invisible"; } else { @@ -25,14 +26,14 @@ Item { } } - target: TimelineManager + target: MainWindow } Timer { id: screenSaverTimer interval: screenTimeout * 1000 - running: true + running: !MainWindow.active onTriggered: { screenSaver.state = "Visible"; } diff --git a/resources/qml/Root.qml b/resources/qml/Root.qml index e4b164e4..004169e1 100644 --- a/resources/qml/Root.qml +++ b/resources/qml/Root.qml @@ -17,10 +17,12 @@ import QtQuick.Window 2.15 import im.nheko 1.0 import im.nheko.EmojiModel 1.0 -Page { +Pane { id: timelineRoot palette: Nheko.colors + background: null + padding: 0 FontMetrics { id: fontMetrics @@ -157,7 +159,6 @@ Page { sequence: "Ctrl+K" onActivated: { var quickSwitch = quickSwitcherComponent.createObject(timelineRoot); - TimelineManager.focusTimeline(); quickSwitch.open(); } } @@ -165,7 +166,6 @@ Page { Shortcut { // Add alternative shortcut, because sometimes Alt+A is stolen by the TextEdit sequences: ["Alt+A", "Ctrl+Shift+A"] - context: Qt.ApplicationShortcut onActivated: Rooms.nextRoomWithActivity() } @@ -366,9 +366,24 @@ Page { id: mainWindow anchors.fill: parent - initialItem: ChatPage { + initialItem: WelcomePage { //anchors.fill: parent } } + Component { + id: chatPage + + ChatPage { + } + } + + Connections { + function onSwitchToChatPage() { + console.log("AAAA"); + mainWindow.replace(chatPage); + } + target: MainWindow + } + } diff --git a/resources/qml/components/FlatButton.qml b/resources/qml/components/FlatButton.qml index 8ca3f104..72184d28 100644 --- a/resources/qml/components/FlatButton.qml +++ b/resources/qml/components/FlatButton.qml @@ -12,7 +12,7 @@ import im.nheko 1.0 Button { id: control - implicitHeight: Math.ceil(control.contentItem.implicitHeight * 1.5) + implicitHeight: Math.ceil(control.contentItem.implicitHeight * 1.70) implicitWidth: Math.ceil(control.contentItem.implicitWidth + control.contentItem.implicitHeight) hoverEnabled: true @@ -42,7 +42,7 @@ Button { background: Rectangle { //height: control.contentItem.implicitHeight * 2 //width: control.contentItem.implicitWidth * 2 - radius: height / 6 + radius: height / 8 color: Qt.lighter(Nheko.colors.dark, control.down ? 1.4 : (control.hovered ? 1.2 : 1)) } diff --git a/resources/qml/pages/WelcomePage.qml b/resources/qml/pages/WelcomePage.qml new file mode 100644 index 00000000..d95b6104 --- /dev/null +++ b/resources/qml/pages/WelcomePage.qml @@ -0,0 +1,76 @@ +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.2 +import QtQuick.Window 2.15 +import im.nheko 1.0 +import "../components/" + +ColumnLayout { + FontMetrics { + id: fontMetrics + } + + Shortcut { + sequence: StandardKey.Quit + onActivated: Qt.quit() + } + + Item { + Layout.fillHeight: true + } + + Image { + Layout.alignment: Qt.AlignHCenter + source: "qrc:/logos/splash.png" + height: 256 + width: 256 + } + + Label { + Layout.margins: Nheko.paddingLarge + Layout.bottomMargin: 0 + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + text: qsTr("Welcome to nheko! The desktop client for the Matrix protocol.") + color: Nheko.colors.text + font.pointSize: fontMetrics.font.pointSize*2 + wrapMode: Text.Wrap + horizontalAlignment: Text.AlignHCenter + } + Label { + Layout.margins: Nheko.paddingLarge + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + text: qsTr("Enjoy your stay!") + color: Nheko.colors.text + font.pointSize: fontMetrics.font.pointSize*1.5 + wrapMode: Text.Wrap + horizontalAlignment: Text.AlignHCenter + } + + RowLayout { + Item { + Layout.fillWidth: true + } + FlatButton { + Layout.margins: Nheko.paddingLarge + Layout.alignment: Qt.AlignHCenter + text: qsTr("REGISTER") + onClicked: { + } + } + FlatButton { + Layout.margins: Nheko.paddingLarge + Layout.alignment: Qt.AlignHCenter + text: qsTr("LOGIN") + onClicked: { + } + } + Item { + Layout.fillWidth: true + } +} + Item { + Layout.fillHeight: true + } +} diff --git a/resources/res.qrc b/resources/res.qrc index a2ee393f..0222619b 100644 --- a/resources/res.qrc +++ b/resources/res.qrc @@ -110,6 +110,7 @@ <file>qml/TypingIndicator.qml</file> <file>qml/NotificationWarning.qml</file> <file>qml/pages/UserSettingsPage.qml</file> + <file>qml/pages/WelcomePage.qml</file> <file>qml/components/AdaptiveLayout.qml</file> <file>qml/components/AdaptiveLayoutElement.qml</file> <file>qml/components/AvatarListTile.qml</file> diff --git a/src/Cache.cpp b/src/Cache.cpp index d1723d98..c15d2f4b 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -325,7 +325,7 @@ static void fatalSecretError() { QMessageBox::critical( - ChatPage::instance(), + nullptr, QCoreApplication::translate("SecretStorage", "Failed to connect to secret storage"), QCoreApplication::translate( "SecretStorage", diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index bfaa6389..fc90e6c7 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -44,8 +44,8 @@ Q_DECLARE_METATYPE(mtx::presence::PresenceState) Q_DECLARE_METATYPE(mtx::secret_storage::AesHmacSha2KeyDescription) Q_DECLARE_METATYPE(SecretsToDecrypt) -ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent) - : QWidget(parent) +ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QObject *parent) + : QObject(parent) , isConnected_(true) , userSettings_{userSettings} , notificationsManager(this) @@ -61,14 +61,8 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent) qRegisterMetaType<mtx::secret_storage::AesHmacSha2KeyDescription>(); qRegisterMetaType<SecretsToDecrypt>(); - topLayout_ = new QHBoxLayout(this); - topLayout_->setSpacing(0); - topLayout_->setContentsMargins(0, 0, 0, 0); - view_manager_ = new TimelineViewManager(callManager_, this); - topLayout_->addWidget(view_manager_->getWidget()); - connect(this, &ChatPage::downloadedSecrets, this, @@ -154,7 +148,7 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent) [this](const QString &roomid, const QString &eventid) { Q_UNUSED(eventid) view_manager_->rooms()->setCurrentRoom(roomid); - activateWindow(); + MainWindow::instance()->requestActivate(); }); connect(¬ificationsManager, &NotificationsManager::sendNotificationReply, @@ -162,15 +156,13 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent) [this](const QString &roomid, const QString &eventid, const QString &body) { view_manager_->rooms()->setCurrentRoom(roomid); view_manager_->queueReply(roomid, eventid, body); - activateWindow(); + MainWindow::instance()->requestActivate(); }); connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this, [this]() { // ensure the qml context is shutdown before we destroy all other singletons // Otherwise Qml will try to access the room list or settings, after they have been // destroyed - topLayout_->removeWidget(view_manager_->getWidget()); - delete view_manager_->getWidget(); }); connect( @@ -201,7 +193,7 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent) // TODO: Replace this once we have proper pushrules support. This is a horrible hack if (prevNotificationCount < notificationCount) { if (userSettings_->hasAlertOnNotification()) - QApplication::alert(this); + MainWindow::instance()->alert(0); } prevNotificationCount = notificationCount; @@ -331,7 +323,7 @@ ChatPage::bootstrap(QString userid, QString homeserver, QString token) } else if (cacheVersion == cache::CacheVersion::Older) { if (!cache::runMigrations()) { QMessageBox::critical( - this, + nullptr, tr("Cache migration failed!"), tr("Migrating the cache to the current version failed. " "This can have different reasons. Please open an " @@ -344,7 +336,7 @@ ChatPage::bootstrap(QString userid, QString homeserver, QString token) return; } else if (cacheVersion == cache::CacheVersion::Newer) { QMessageBox::critical( - this, + nullptr, tr("Incompatible cache version"), tr("The cache on your disk is newer than this version of Nheko " "supports. Please update Nheko or clear your cache.")); @@ -690,7 +682,7 @@ ChatPage::joinRoomVia(const std::string &room_id, if (promptForConfirmation && QMessageBox::Yes != QMessageBox::question( - this, + nullptr, tr("Confirm join"), tr("Do you really want to join %1?").arg(QString::fromStdString(room_id)))) return; @@ -776,7 +768,7 @@ ChatPage::inviteUser(QString userid, QString reason) { auto room = currentRoom(); - if (QMessageBox::question(this, + if (QMessageBox::question(nullptr, tr("Confirm invite"), tr("Do you really want to invite %1 (%2)?") .arg(cache::displayName(room, userid), userid)) != QMessageBox::Yes) @@ -800,7 +792,7 @@ ChatPage::kickUser(QString userid, QString reason) { auto room = currentRoom(); - if (QMessageBox::question(this, + if (QMessageBox::question(nullptr, tr("Confirm kick"), tr("Do you really want to kick %1 (%2)?") .arg(cache::displayName(room, userid), userid)) != QMessageBox::Yes) @@ -825,7 +817,7 @@ ChatPage::banUser(QString userid, QString reason) auto room = currentRoom(); if (QMessageBox::question( - this, + nullptr, tr("Confirm ban"), tr("Do you really want to ban %1 (%2)?").arg(cache::displayName(room, userid), userid)) != QMessageBox::Yes) @@ -849,7 +841,7 @@ ChatPage::unbanUser(QString userid, QString reason) { auto room = currentRoom(); - if (QMessageBox::question(this, + if (QMessageBox::question(nullptr, tr("Confirm unban"), tr("Do you really want to unban %1 (%2)?") .arg(cache::displayName(room, userid), userid)) != QMessageBox::Yes) @@ -1083,7 +1075,7 @@ ChatPage::decryptDownloadedSecrets(mtx::secret_storage::AesHmacSha2KeyDescriptio const SecretsToDecrypt &secrets) { QString text = QInputDialog::getText( - ChatPage::instance(), + nullptr, QCoreApplication::translate("CrossSigningSecrets", "Decrypt secrets"), keyDesc.name.empty() ? QCoreApplication::translate( @@ -1115,7 +1107,7 @@ ChatPage::decryptDownloadedSecrets(mtx::secret_storage::AesHmacSha2KeyDescriptio if (!decryptionKey) { QMessageBox::information( - ChatPage::instance(), + nullptr, QCoreApplication::translate("CrossSigningSecrets", "Decryption failed"), QCoreApplication::translate("CrossSigningSecrets", "Failed to decrypt secrets with the " @@ -1209,7 +1201,7 @@ ChatPage::startChat(QString userid) if (QMessageBox::Yes != QMessageBox::question( - this, + nullptr, tr("Confirm invite"), tr("Do you really want to start a private chat with %1?").arg(userid))) return; @@ -1395,7 +1387,7 @@ ChatPage::handleMatrixUri(const QUrl &uri) bool ChatPage::isRoomActive(const QString &room_id) { - return isActiveWindow() && currentRoom() == room_id; + return MainWindow::instance()->isActive() && currentRoom() == room_id; } QString diff --git a/src/ChatPage.h b/src/ChatPage.h index ae55c923..5e3b509d 100644 --- a/src/ChatPage.h +++ b/src/ChatPage.h @@ -51,12 +51,12 @@ struct Rooms; using SecretsToDecrypt = std::map<std::string, mtx::secret_storage::AesHmacSha2EncryptedData>; -class ChatPage : public QWidget +class ChatPage : public QObject { Q_OBJECT public: - ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent = nullptr); + ChatPage(QSharedPointer<UserSettings> userSettings, QObject *parent = nullptr); // Initialize all the components of the UI. void bootstrap(QString userid, QString homeserver, QString token); diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 5bfce89e..5e7fe6ce 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -13,77 +13,108 @@ #include <mtx/requests.hpp> #include <mtx/responses/login.hpp> +#include "BlurhashProvider.h" #include "Cache.h" #include "Cache_p.h" #include "ChatPage.h" +#include "Clipboard.h" +#include "ColorImageProvider.h" +#include "CombinedImagePackModel.h" +#include "CompletionProxyModel.h" #include "Config.h" +#include "EventAccessors.h" +#include "ImagePackListModel.h" +#include "InviteesModel.h" #include "JdenticonProvider.h" #include "Logging.h" #include "LoginPage.h" #include "MainWindow.h" #include "MatrixClient.h" #include "MemberList.h" +#include "MxcImageProvider.h" +#include "ReadReceiptsModel.h" #include "RegisterPage.h" +#include "RoomDirectoryModel.h" +#include "RoomsModel.h" +#include "SingleImagePackModel.h" #include "TrayIcon.h" #include "UserSettingsPage.h" +#include "UsersModel.h" #include "Utils.h" #include "WelcomePage.h" +#include "emoji/EmojiModel.h" +#include "emoji/Provider.h" +#include "encryption/DeviceVerificationFlow.h" +#include "encryption/SelfVerificationStatus.h" +#include "timeline/DelegateChooser.h" +#include "timeline/TimelineViewManager.h" #include "ui/LoadingIndicator.h" +#include "ui/MxcAnimatedImage.h" +#include "ui/MxcMediaProxy.h" +#include "ui/NhekoCursorShape.h" +#include "ui/NhekoDropArea.h" +#include "ui/NhekoGlobalObject.h" #include "ui/OverlayModal.h" #include "ui/SnackBar.h" +#include "ui/UIA.h" #include "voip/WebRTCSession.h" #include "dialogs/CreateRoom.h" +Q_DECLARE_METATYPE(mtx::events::collections::TimelineEvents) +Q_DECLARE_METATYPE(std::vector<DeviceInfo>) +Q_DECLARE_METATYPE(std::vector<mtx::responses::PublicRoomsChunk>) + MainWindow *MainWindow::instance_ = nullptr; -MainWindow::MainWindow(QWidget *parent) - : QMainWindow(parent) +MainWindow::MainWindow(QWindow *parent) + : QQuickView(parent) , userSettings_{UserSettings::instance()} { instance_ = this; - QMainWindow::setWindowTitle(0); + MainWindow::setWindowTitle(0); setObjectName(QStringLiteral("MainWindow")); + setResizeMode(QQuickView::SizeRootObjectToView); + setMinimumHeight(400); + setMinimumWidth(400); + restoreWindowSize(); - modal_ = new OverlayModal(this); + chat_page_ = new ChatPage(userSettings_, this); + registerQmlTypes(); - restoreWindowSize(); + setColor(Theme::paletteFromTheme(userSettings_->theme()).window().color()); + setSource(QUrl(QStringLiteral("qrc:///qml/Root.qml"))); + // modal_ = new OverlayModal(this); - QFont font; - font.setStyleStrategy(QFont::PreferAntialias); - setFont(font); - trayIcon_ = new TrayIcon(QStringLiteral(":/logos/nheko.svg"), this); + // QFont font; + // font.setStyleStrategy(QFont::PreferAntialias); + // setFont(font); - welcome_page_ = new WelcomePage(this); - login_page_ = new LoginPage(this); - register_page_ = new RegisterPage(this); - chat_page_ = new ChatPage(userSettings_, this); + trayIcon_ = new TrayIcon(QStringLiteral(":/logos/nheko.svg"), this); - // Initialize sliding widget manager. - pageStack_ = new QStackedWidget(this); - pageStack_->addWidget(welcome_page_); - pageStack_->addWidget(login_page_); - pageStack_->addWidget(register_page_); - pageStack_->addWidget(chat_page_); + // welcome_page_ = new WelcomePage(this); + // login_page_ = new LoginPage(this); + // register_page_ = new RegisterPage(this); - setCentralWidget(pageStack_); + //// Initialize sliding widget manager. - connect(welcome_page_, SIGNAL(userLogin()), this, SLOT(showLoginPage())); - connect(welcome_page_, SIGNAL(userRegister()), this, SLOT(showRegisterPage())); + // connect(welcome_page_, SIGNAL(userLogin()), this, SLOT(showLoginPage())); + // connect(welcome_page_, SIGNAL(userRegister()), this, SLOT(showRegisterPage())); - connect(login_page_, SIGNAL(backButtonClicked()), this, SLOT(showWelcomePage())); - connect(login_page_, &LoginPage::loggingIn, this, &MainWindow::showOverlayProgressBar); - connect(register_page_, &RegisterPage::registering, this, &MainWindow::showOverlayProgressBar); - connect(login_page_, &LoginPage::errorOccurred, this, [this]() { removeOverlayProgressBar(); }); - connect( - register_page_, &RegisterPage::errorOccurred, this, [this]() { removeOverlayProgressBar(); }); - connect(register_page_, SIGNAL(backButtonClicked()), this, SLOT(showWelcomePage())); + // connect(login_page_, SIGNAL(backButtonClicked()), this, SLOT(showWelcomePage())); + // connect(login_page_, &LoginPage::loggingIn, this, &MainWindow::showOverlayProgressBar); + // connect(register_page_, &RegisterPage::registering, this, + // &MainWindow::showOverlayProgressBar); connect(login_page_, &LoginPage::errorOccurred, this, + // [this]() { removeOverlayProgressBar(); }); connect( + // register_page_, &RegisterPage::errorOccurred, this, [this]() { removeOverlayProgressBar(); + // }); + // connect(register_page_, SIGNAL(backButtonClicked()), this, SLOT(showWelcomePage())); connect(chat_page_, &ChatPage::closing, this, &MainWindow::showWelcomePage); - connect( - chat_page_, &ChatPage::showOverlayProgressBar, this, &MainWindow::showOverlayProgressBar); + // connect( + // chat_page_, &ChatPage::showOverlayProgressBar, this, &MainWindow::showOverlayProgressBar); connect(chat_page_, &ChatPage::unreadMessages, this, &MainWindow::setWindowTitle); connect(chat_page_, SIGNAL(unreadMessages(int)), trayIcon_, SLOT(setUnreadCount(int))); connect(chat_page_, &ChatPage::showLoginPage, this, [this](const QString &msg) { @@ -101,15 +132,12 @@ MainWindow::MainWindow(QWidget *parent) connect(this, &MainWindow::focusChanged, chat_page_, &ChatPage::chatFocusChanged); - connect(login_page_, &LoginPage::loginOk, this, [this](const mtx::responses::Login &res) { - http::client()->set_user(res.user_id); - showChatPage(); - }); - - connect(register_page_, &RegisterPage::registerOk, this, &MainWindow::showChatPage); + // connect(login_page_, &LoginPage::loginOk, this, [this](const mtx::responses::Login &res) { + // http::client()->set_user(res.user_id); + // showChatPage(); + // }); - QShortcut *quitShortcut = new QShortcut(QKeySequence::Quit, this); - connect(quitShortcut, &QShortcut::activated, this, QApplication::quit); + // connect(register_page_, &RegisterPage::registerOk, this, &MainWindow::showChatPage); trayIcon_->setVisible(userSettings_->tray()); @@ -133,12 +161,168 @@ MainWindow::MainWindow(QWidget *parent) user_id.toStdString()); } + nhlog::ui()->info("User already signed in, showing chat page"); showChatPage(); } }); } void +MainWindow::registerQmlTypes() +{ + qRegisterMetaType<mtx::events::msg::KeyVerificationAccept>(); + qRegisterMetaType<mtx::events::msg::KeyVerificationCancel>(); + qRegisterMetaType<mtx::events::msg::KeyVerificationDone>(); + qRegisterMetaType<mtx::events::msg::KeyVerificationKey>(); + qRegisterMetaType<mtx::events::msg::KeyVerificationMac>(); + qRegisterMetaType<mtx::events::msg::KeyVerificationReady>(); + qRegisterMetaType<mtx::events::msg::KeyVerificationRequest>(); + qRegisterMetaType<mtx::events::msg::KeyVerificationStart>(); + qRegisterMetaType<CombinedImagePackModel *>(); + qRegisterMetaType<mtx::events::collections::TimelineEvents>(); + qRegisterMetaType<std::vector<DeviceInfo>>(); + + qRegisterMetaType<std::vector<mtx::responses::PublicRoomsChunk>>(); + + qmlRegisterUncreatableMetaObject(qml_mtx_events::staticMetaObject, + "im.nheko", + 1, + 0, + "MtxEvent", + QStringLiteral("Can't instantiate enum!")); + qmlRegisterUncreatableMetaObject( + olm::staticMetaObject, "im.nheko", 1, 0, "Olm", QStringLiteral("Can't instantiate enum!")); + qmlRegisterUncreatableMetaObject(crypto::staticMetaObject, + "im.nheko", + 1, + 0, + "Crypto", + QStringLiteral("Can't instantiate enum!")); + qmlRegisterUncreatableMetaObject(verification::staticMetaObject, + "im.nheko", + 1, + 0, + "VerificationStatus", + QStringLiteral("Can't instantiate enum!")); + + qmlRegisterType<DelegateChoice>("im.nheko", 1, 0, "DelegateChoice"); + qmlRegisterType<DelegateChooser>("im.nheko", 1, 0, "DelegateChooser"); + qmlRegisterType<NhekoDropArea>("im.nheko", 1, 0, "NhekoDropArea"); + qmlRegisterType<NhekoCursorShape>("im.nheko", 1, 0, "CursorShape"); + qmlRegisterType<MxcAnimatedImage>("im.nheko", 1, 0, "MxcAnimatedImage"); + qmlRegisterType<MxcMediaProxy>("im.nheko", 1, 0, "MxcMedia"); + qmlRegisterType<RoomDirectoryModel>("im.nheko", 1, 0, "RoomDirectoryModel"); + qmlRegisterUncreatableType<DeviceVerificationFlow>( + "im.nheko", + 1, + 0, + "DeviceVerificationFlow", + QStringLiteral("Can't create verification flow from QML!")); + qmlRegisterUncreatableType<UserProfile>( + "im.nheko", + 1, + 0, + "UserProfileModel", + QStringLiteral("UserProfile needs to be instantiated on the C++ side")); + qmlRegisterUncreatableType<MemberList>( + "im.nheko", + 1, + 0, + "MemberList", + QStringLiteral("MemberList needs to be instantiated on the C++ side")); + qmlRegisterUncreatableType<RoomSettings>( + "im.nheko", + 1, + 0, + "RoomSettingsModel", + QStringLiteral("Room Settings needs to be instantiated on the C++ side")); + qmlRegisterUncreatableType<TimelineModel>( + "im.nheko", 1, 0, "Room", QStringLiteral("Room needs to be instantiated on the C++ side")); + qmlRegisterUncreatableType<ImagePackListModel>( + "im.nheko", + 1, + 0, + "ImagePackListModel", + QStringLiteral("ImagePackListModel needs to be instantiated on the C++ side")); + qmlRegisterUncreatableType<SingleImagePackModel>( + "im.nheko", + 1, + 0, + "SingleImagePackModel", + QStringLiteral("SingleImagePackModel needs to be instantiated on the C++ side")); + qmlRegisterUncreatableType<InviteesModel>( + "im.nheko", + 1, + 0, + "InviteesModel", + QStringLiteral("InviteesModel needs to be instantiated on the C++ side")); + qmlRegisterUncreatableType<ReadReceiptsProxy>( + "im.nheko", + 1, + 0, + "ReadReceiptsProxy", + QStringLiteral("ReadReceiptsProxy needs to be instantiated on the C++ side")); + + qmlRegisterSingletonType<Clipboard>( + "im.nheko", 1, 0, "Clipboard", [](QQmlEngine *, QJSEngine *) -> QObject * { + return new Clipboard(); + }); + qmlRegisterSingletonType<Nheko>( + "im.nheko", 1, 0, "Nheko", [](QQmlEngine *, QJSEngine *) -> QObject * { + return new Nheko(); + }); + qmlRegisterSingletonType<UserSettingsModel>( + "im.nheko", 1, 0, "UserSettingsModel", [](QQmlEngine *, QJSEngine *) -> QObject * { + return new UserSettingsModel(); + }); + + qmlRegisterSingletonInstance("im.nheko", 1, 0, "Settings", userSettings_.data()); + + qRegisterMetaType<mtx::events::collections::TimelineEvents>(); + qRegisterMetaType<std::vector<DeviceInfo>>(); + + qmlRegisterUncreatableType<FilteredCommunitiesModel>( + "im.nheko", + 1, + 0, + "FilteredCommunitiesModel", + QStringLiteral("Use Communities.filtered() to create a FilteredCommunitiesModel")); + + qmlRegisterType<emoji::EmojiModel>("im.nheko.EmojiModel", 1, 0, "EmojiModel"); + qmlRegisterUncreatableType<emoji::Emoji>( + "im.nheko.EmojiModel", 1, 0, "Emoji", QStringLiteral("Used by emoji models")); + qmlRegisterUncreatableMetaObject(emoji::staticMetaObject, + "im.nheko.EmojiModel", + 1, + 0, + "EmojiCategory", + QStringLiteral("Error: Only enums")); + + qmlRegisterType<RoomDirectoryModel>("im.nheko", 1, 0, "RoomDirectoryModel"); + + qmlRegisterSingletonType<SelfVerificationStatus>( + "im.nheko", 1, 0, "SelfVerificationStatus", [](QQmlEngine *, QJSEngine *) -> QObject * { + auto ptr = new SelfVerificationStatus(); + QObject::connect(ChatPage::instance(), + &ChatPage::initializeEmptyViews, + ptr, + &SelfVerificationStatus::invalidate); + return ptr; + }); + qmlRegisterSingletonInstance("im.nheko", 1, 0, "MainWindow", this); + qmlRegisterSingletonInstance("im.nheko", 1, 0, "UIA", UIA::instance()); + qmlRegisterSingletonInstance( + "im.nheko", 1, 0, "CallManager", ChatPage::instance()->callManager()); + + imgProvider = new MxcImageProvider(); + engine()->addImageProvider(QStringLiteral("MxcImage"), imgProvider); + engine()->addImageProvider(QStringLiteral("colorimage"), new ColorImageProvider()); + engine()->addImageProvider(QStringLiteral("blurhash"), new BlurhashProvider()); + if (JdenticonProvider::isAvailable()) + engine()->addImageProvider(QStringLiteral("jdenticon"), new JdenticonProvider()); +} + +void MainWindow::setWindowTitle(int notificationCount) { QString name = QStringLiteral("nheko"); @@ -148,20 +332,23 @@ MainWindow::setWindowTitle(int notificationCount) if (notificationCount > 0) { name.append(QString{QStringLiteral(" (%1)")}.arg(notificationCount)); } - QMainWindow::setWindowTitle(name); + QQuickView::setTitle(name); } bool MainWindow::event(QEvent *event) { auto type = event->type(); - if (type == QEvent::WindowActivate) { + + if (type == QEvent::Close) { + closeEvent(static_cast<QCloseEvent *>(event)); + } else if (type == QEvent::WindowActivate) { emit focusChanged(true); } else if (type == QEvent::WindowDeactivate) { emit focusChanged(false); } - return QMainWindow::event(event); + return QQuickView::event(event); } void @@ -196,19 +383,13 @@ MainWindow::removeOverlayProgressBar() connect(timer, &QTimer::timeout, this, [this, timer]() { timer->deleteLater(); - - if (modal_) - modal_->hide(); - - if (spinner_) - spinner_->stop(); }); // FIXME: Snackbar doesn't work if it's initialized in the constructor. - QTimer::singleShot(0, this, [this]() { - snackBar_ = new SnackBar(this); - connect(chat_page_, &ChatPage::showNotification, snackBar_, &SnackBar::showMessage); - }); + // QTimer::singleShot(0, this, [this]() { + // snackBar_ = new SnackBar(this); + // connect(chat_page_, &ChatPage::showNotification, snackBar_, &SnackBar::showMessage); + //}); timer->start(50); } @@ -229,17 +410,14 @@ MainWindow::showChatPage() showOverlayProgressBar(); - pageStack_->setCurrentWidget(chat_page_); - - pageStack_->removeWidget(welcome_page_); - pageStack_->removeWidget(login_page_); - pageStack_->removeWidget(register_page_); - - login_page_->reset(); + // login_page_->reset(); chat_page_->bootstrap(userid, homeserver, token); connect(cache::client(), &Cache::databaseReady, this, &MainWindow::secretsChanged); connect(cache::client(), &Cache::secretChanged, this, &MainWindow::secretsChanged); + emit reload(); + nhlog::ui()->info("Switching to chat page"); + emit switchToChatPage(); } void @@ -247,7 +425,7 @@ MainWindow::closeEvent(QCloseEvent *event) { if (WebRTCSession::instance().state() != webrtc::State::DISCONNECTED) { if (QMessageBox::question( - this, QStringLiteral("nheko"), QStringLiteral("A call is in progress. Quit?")) != + nullptr, QStringLiteral("nheko"), QStringLiteral("A call is in progress. Quit?")) != QMessageBox::Yes) { event->ignore(); return; @@ -292,20 +470,13 @@ MainWindow::hasActiveUser() void MainWindow::showOverlayProgressBar() { - spinner_ = new LoadingIndicator(this); - spinner_->setFixedHeight(100); - spinner_->setFixedWidth(100); - spinner_->setObjectName(QStringLiteral("ChatPageLoadSpinner")); - spinner_->start(); - - showSolidOverlayModal(spinner_); } void MainWindow::openCreateRoomDialog( std::function<void(const mtx::requests::CreateRoom &request)> callback) { - auto dialog = new dialogs::CreateRoom(this); + auto dialog = new dialogs::CreateRoom(nullptr); connect(dialog, &dialogs::CreateRoom::createRoom, this, @@ -315,50 +486,36 @@ MainWindow::openCreateRoomDialog( } void -MainWindow::showTransparentOverlayModal(QWidget *content, QFlags<Qt::AlignmentFlag> flags) -{ - modal_->setWidget(content); - modal_->setColor(QColor(30, 30, 30, 150)); - modal_->setDismissible(true); - modal_->setContentAlignment(flags); - modal_->raise(); - modal_->show(); -} +MainWindow::showTransparentOverlayModal(QWidget *, QFlags<Qt::AlignmentFlag>) +{} void -MainWindow::showSolidOverlayModal(QWidget *content, QFlags<Qt::AlignmentFlag> flags) +MainWindow::showSolidOverlayModal(QWidget *, QFlags<Qt::AlignmentFlag>) { - modal_->setWidget(content); - modal_->setColor(QColor(30, 30, 30)); - modal_->setDismissible(false); - modal_->setContentAlignment(flags); - modal_->raise(); - modal_->show(); } bool MainWindow::hasActiveDialogs() const { - return modal_ && modal_->isVisible(); + return false; } bool MainWindow::pageSupportsTray() const { - return !welcome_page_->isVisible() && !login_page_->isVisible() && !register_page_->isVisible(); + return false; //! welcome_page_->isVisible() && !login_page_->isVisible() && + //! !register_page_->isVisible(); } void MainWindow::hideOverlay() { - if (modal_) - modal_->hide(); } inline void MainWindow::showDialog(QWidget *dialog) { - utils::centerWidget(dialog, this); + // utils::centerWidget(dialog, this); dialog->raise(); dialog->show(); } @@ -367,23 +524,13 @@ void MainWindow::showWelcomePage() { removeOverlayProgressBar(); - pageStack_->addWidget(welcome_page_); - pageStack_->setCurrentWidget(welcome_page_); } void MainWindow::showLoginPage() { - if (modal_) - modal_->hide(); - - pageStack_->addWidget(login_page_); - pageStack_->setCurrentWidget(login_page_); } void MainWindow::showRegisterPage() -{ - pageStack_->addWidget(register_page_); - pageStack_->setCurrentWidget(register_page_); -} +{} diff --git a/src/MainWindow.h b/src/MainWindow.h index 458eb054..04311e12 100644 --- a/src/MainWindow.h +++ b/src/MainWindow.h @@ -8,7 +8,7 @@ #include <functional> -#include <QMainWindow> +#include <QQuickView> #include <QSharedPointer> #include <QStackedWidget> #include <QSystemTrayIcon> @@ -28,6 +28,7 @@ class OverlayModal; class SnackBar; class TrayIcon; class UserSettings; +class MxcImageProvider; namespace mtx { namespace requests { @@ -42,17 +43,12 @@ class MemberList; class ReCaptcha; } -class MainWindow : public QMainWindow +class MainWindow : public QQuickView { Q_OBJECT - Q_PROPERTY(int x READ x CONSTANT) - Q_PROPERTY(int y READ y CONSTANT) - Q_PROPERTY(int width READ width CONSTANT) - Q_PROPERTY(int height READ height CONSTANT) - public: - explicit MainWindow(QWidget *parent = nullptr); + explicit MainWindow(QWindow *parent = nullptr); static MainWindow *instance() { return instance_; } void saveCurrentWindowSize(); @@ -67,8 +63,10 @@ public: showTransparentOverlayModal(QWidget *content, QFlags<Qt::AlignmentFlag> flags = Qt::AlignTop | Qt::AlignHCenter); + MxcImageProvider *imageProvider() { return imgProvider; } + protected: - void closeEvent(QCloseEvent *event) override; + void closeEvent(QCloseEvent *event); bool event(QEvent *event) override; private slots: @@ -97,6 +95,9 @@ signals: void reload(); void secretsChanged(); + void switchToChatPage(); + void switchToWelcomePage(); + private: void showDialog(QWidget *dialog); bool hasActiveUser(); @@ -106,6 +107,8 @@ private: //! Check if the current page supports the "minimize to tray" functionality. bool pageSupportsTray() const; + void registerQmlTypes(); + static MainWindow *instance_; //! The initial welcome screen. @@ -114,16 +117,11 @@ private: LoginPage *login_page_; //! The register page. RegisterPage *register_page_; - //! A stacked widget that handles the transitions between widgets. - QStackedWidget *pageStack_; //! The main chat area. ChatPage *chat_page_; QSharedPointer<UserSettings> userSettings_; //! Tray icon that shows the unread message count. TrayIcon *trayIcon_; - //! Notifications display. - SnackBar *snackBar_ = nullptr; - //! Overlay modal used to project other widgets. - OverlayModal *modal_ = nullptr; - LoadingIndicator *spinner_ = nullptr; + + MxcImageProvider *imgProvider = nullptr; }; diff --git a/src/TrayIcon.cpp b/src/TrayIcon.cpp index d83156a4..28da9558 100644 --- a/src/TrayIcon.cpp +++ b/src/TrayIcon.cpp @@ -10,6 +10,7 @@ #include <QMenu> #include <QPainter> #include <QTimer> +#include <QWindow> #include "TrayIcon.h" @@ -100,7 +101,7 @@ MsgCountComposedIcon::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State s return result; } -TrayIcon::TrayIcon(const QString &filename, QWidget *parent) +TrayIcon::TrayIcon(const QString &filename, QWindow *parent) : QSystemTrayIcon(parent) { #if defined(Q_OS_MAC) || defined(Q_OS_WIN) @@ -110,13 +111,13 @@ TrayIcon::TrayIcon(const QString &filename, QWidget *parent) setIcon(QIcon(icon_)); #endif - QMenu *menu = new QMenu(parent); + QMenu *menu = new QMenu(); setContextMenu(menu); viewAction_ = new QAction(tr("Show"), this); quitAction_ = new QAction(tr("Quit"), this); - connect(viewAction_, SIGNAL(triggered()), parent, SLOT(show())); + connect(viewAction_, &QAction::triggered, parent, &QWindow::show); connect(quitAction_, &QAction::triggered, this, QApplication::quit); menu->addAction(viewAction_); diff --git a/src/TrayIcon.h b/src/TrayIcon.h index 17bf5eff..554a4a0a 100644 --- a/src/TrayIcon.h +++ b/src/TrayIcon.h @@ -40,7 +40,7 @@ class TrayIcon : public QSystemTrayIcon { Q_OBJECT public: - TrayIcon(const QString &filename, QWidget *parent); + TrayIcon(const QString &filename, QWindow *parent); public slots: void setUnreadCount(int count); diff --git a/src/UserSettingsPage.cpp b/src/UserSettingsPage.cpp index c43733fb..e973fc1f 100644 --- a/src/UserSettingsPage.cpp +++ b/src/UserSettingsPage.cpp @@ -1518,7 +1518,7 @@ UserSettingsModel::setData(const QModelIndex &index, const QVariant &value, int QString homeFolder = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); auto filepath = QFileDialog::getOpenFileName( - MainWindow::instance(), tr("Select a file"), homeFolder, tr("All Files (*)")); + nullptr, tr("Select a file"), homeFolder, tr("All Files (*)")); if (!filepath.isEmpty()) { i->setRingtone(filepath); i->setRingtone(filepath); @@ -1600,11 +1600,11 @@ UserSettingsModel::importSessionKeys() { const QString homeFolder = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); const QString fileName = QFileDialog::getOpenFileName( - MainWindow::instance(), tr("Open Sessions File"), homeFolder, QLatin1String("")); + nullptr, tr("Open Sessions File"), homeFolder, QLatin1String("")); QFile file(fileName); if (!file.open(QIODevice::ReadOnly)) { - QMessageBox::warning(MainWindow::instance(), tr("Error"), file.errorString()); + QMessageBox::warning(nullptr, tr("Error"), file.errorString()); return; } @@ -1612,7 +1612,7 @@ UserSettingsModel::importSessionKeys() auto payload = std::string(bin.data(), bin.size()); bool ok; - auto password = QInputDialog::getText(MainWindow::instance(), + auto password = QInputDialog::getText(nullptr, tr("File Password"), tr("Enter the passphrase to decrypt the file:"), QLineEdit::Password, @@ -1622,8 +1622,7 @@ UserSettingsModel::importSessionKeys() return; if (password.isEmpty()) { - QMessageBox::warning( - MainWindow::instance(), tr("Error"), tr("The password cannot be empty")); + QMessageBox::warning(nullptr, tr("Error"), tr("The password cannot be empty")); return; } @@ -1631,7 +1630,7 @@ UserSettingsModel::importSessionKeys() auto sessions = mtx::crypto::decrypt_exported_sessions(payload, password.toStdString()); cache::importSessionKeys(std::move(sessions)); } catch (const std::exception &e) { - QMessageBox::warning(MainWindow::instance(), tr("Error"), e.what()); + QMessageBox::warning(nullptr, tr("Error"), e.what()); } } void @@ -1639,7 +1638,7 @@ UserSettingsModel::exportSessionKeys() { // Open password dialog. bool ok; - auto password = QInputDialog::getText(MainWindow::instance(), + auto password = QInputDialog::getText(nullptr, tr("File Password"), tr("Enter passphrase to encrypt your session keys:"), QLineEdit::Password, @@ -1649,19 +1648,18 @@ UserSettingsModel::exportSessionKeys() return; if (password.isEmpty()) { - QMessageBox::warning( - MainWindow::instance(), tr("Error"), tr("The password cannot be empty")); + QMessageBox::warning(nullptr, tr("Error"), tr("The password cannot be empty")); return; } // Open file dialog to save the file. const QString homeFolder = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); const QString fileName = QFileDialog::getSaveFileName( - MainWindow::instance(), tr("File to save the exported session keys"), homeFolder); + nullptr, tr("File to save the exported session keys"), homeFolder); QFile file(fileName); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { - QMessageBox::warning(MainWindow::instance(), tr("Error"), file.errorString()); + QMessageBox::warning(nullptr, tr("Error"), file.errorString()); return; } @@ -1679,7 +1677,7 @@ UserSettingsModel::exportSessionKeys() out << prefix << newline << b64 << newline << suffix << newline; file.close(); } catch (const std::exception &e) { - QMessageBox::warning(MainWindow::instance(), tr("Error"), e.what()); + QMessageBox::warning(nullptr, tr("Error"), e.what()); } } void diff --git a/src/main.cpp b/src/main.cpp index 2ae631cf..af8a46a9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -17,6 +17,7 @@ #include <QLibraryInfo> #include <QMessageBox> #include <QPoint> +#include <QQuickView> #include <QScreen> #include <QStandardPaths> #include <QTranslator> @@ -279,6 +280,7 @@ main(int argc, char *argv[]) font.setPointSizeF(settings.lock()->fontSize()); app.setFont(font); + settings.lock()->applyTheme(); if (QLocale().language() == QLocale::C) QLocale::setDefault(QLocale(QLocale::English, QLocale::UnitedKingdom)); @@ -296,15 +298,16 @@ main(int argc, char *argv[]) app.installTranslator(&appTranslator); MainWindow w; + // QQuickView w; // Move the MainWindow to the center - w.move(screenCenter(w.width(), w.height())); + // w.move(screenCenter(w.width(), w.height())); if (!(settings.lock()->startInTray() && settings.lock()->tray())) w.show(); QObject::connect(&app, &QApplication::aboutToQuit, &w, [&w]() { - w.saveCurrentWindowSize(); + // w.saveCurrentWindowSize(); if (http::client() != nullptr) { nhlog::net()->debug("shutting down all I/O threads & open connections"); http::client()->close(true); @@ -314,7 +317,7 @@ main(int argc, char *argv[]) QObject::connect(&app, &SingleApplication::instanceStarted, &w, [&w]() { w.show(); w.raise(); - w.activateWindow(); + w.requestActivate(); }); // It seems like handling the message in a blocking manner is a no-go. I have no idea how to diff --git a/src/timeline/InputBar.cpp b/src/timeline/InputBar.cpp index 5284ce0e..18e224b2 100644 --- a/src/timeline/InputBar.cpp +++ b/src/timeline/InputBar.cpp @@ -266,8 +266,8 @@ void InputBar::openFileSelection() { const QString homeFolder = QStandardPaths::writableLocation(QStandardPaths::HomeLocation); - const auto fileName = QFileDialog::getOpenFileName( - ChatPage::instance(), tr("Select a file"), homeFolder, tr("All Files (*)")); + const auto fileName = + QFileDialog::getOpenFileName(nullptr, tr("Select a file"), homeFolder, tr("All Files (*)")); if (fileName.isEmpty()) return; @@ -659,7 +659,7 @@ InputBar::command(const QString &command, QString args) void InputBar::showPreview(const QMimeData &source, const QString &path, const QStringList &formats) { - auto *previewDialog_ = new dialogs::PreviewUploadOverlay(ChatPage::instance()); + auto *previewDialog_ = new dialogs::PreviewUploadOverlay(nullptr); previewDialog_->setAttribute(Qt::WA_DeleteOnClose); // Force SVG to _not_ be handled as an image, but as raw data diff --git a/src/timeline/RoomlistModel.cpp b/src/timeline/RoomlistModel.cpp index eb453462..aa81f501 100644 --- a/src/timeline/RoomlistModel.cpp +++ b/src/timeline/RoomlistModel.cpp @@ -8,6 +8,7 @@ #include "Cache_p.h" #include "ChatPage.h" #include "Logging.h" +#include "MainWindow.h" #include "MatrixClient.h" #include "MxcImageProvider.h" #include "TimelineModel.h" @@ -275,7 +276,7 @@ RoomlistModel::addRoom(const QString &room_id, bool suppressInsertNotification) connect(newRoom.data(), &TimelineModel::newEncryptedImage, - manager->imageProvider(), + MainWindow::instance()->imageProvider(), &MxcImageProvider::addEncryptionInfo); connect(newRoom.data(), &TimelineModel::forwardToRoom, @@ -509,7 +510,7 @@ RoomlistModel::sync(const mtx::responses::Sync &sync_) // room_model->addEvents(room.timeline); connect(room_model.data(), &TimelineModel::newCallEvent, - manager->callManager(), + ChatPage::instance()->callManager(), &CallManager::syncEvent, Qt::UniqueConnection); diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index 7c9df403..e769fa40 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -1031,7 +1031,7 @@ TimelineModel::setCurrentIndex(int index) if (index != oldIndex) emit currentIndexChanged(index); - if (!ChatPage::instance()->isActiveWindow()) + if (!MainWindow::instance()->isActive()) return; if (!currentId.startsWith('m')) { @@ -1495,7 +1495,7 @@ TimelineModel::saveMedia(const QString &eventId) const const QString openLocation = downloadsFolder + "/" + originalFilename; const QString filename = - QFileDialog::getSaveFileName(manager_->getWidget(), dialogTitle, openLocation, filterString); + QFileDialog::getSaveFileName(nullptr, dialogTitle, openLocation, filterString); if (filename.isEmpty()) return false; diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp index e689e2fa..5d2a4443 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp @@ -8,7 +8,6 @@ #include <QDropEvent> #include <QFileDialog> #include <QMetaType> -#include <QPalette> #include <QQmlContext> #include <QQmlEngine> #include <QStandardPaths> @@ -45,10 +44,6 @@ #include "ui/NhekoGlobalObject.h" #include "ui/UIA.h" -Q_DECLARE_METATYPE(mtx::events::collections::TimelineEvents) -Q_DECLARE_METATYPE(std::vector<DeviceInfo>) -Q_DECLARE_METATYPE(std::vector<mtx::responses::PublicRoomsChunk>) - namespace msgs = mtx::events::msg; namespace { @@ -102,19 +97,6 @@ void TimelineViewManager::updateColorPalette() { userColors.clear(); - - if (ChatPage::instance()->userSettings()->theme() == QLatin1String("light")) { - view->rootContext()->setContextProperty(QStringLiteral("currentActivePalette"), QPalette()); - view->rootContext()->setContextProperty(QStringLiteral("currentInactivePalette"), - QPalette()); - } else if (ChatPage::instance()->userSettings()->theme() == QLatin1String("dark")) { - view->rootContext()->setContextProperty(QStringLiteral("currentActivePalette"), QPalette()); - view->rootContext()->setContextProperty(QStringLiteral("currentInactivePalette"), - QPalette()); - } else { - view->rootContext()->setContextProperty(QStringLiteral("currentActivePalette"), QPalette()); - view->rootContext()->setContextProperty(QStringLiteral("currentInactivePalette"), nullptr); - } } QColor @@ -126,112 +108,15 @@ TimelineViewManager::userColor(QString id, QColor background) return userColors.value(idx); } -TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *parent) +TimelineViewManager::TimelineViewManager(CallManager *, ChatPage *parent) : QObject(parent) - , imgProvider(new MxcImageProvider()) - , colorImgProvider(new ColorImageProvider()) - , blurhashProvider(new BlurhashProvider()) - , jdenticonProvider(new JdenticonProvider()) , rooms_(new RoomlistModel(this)) , communities_(new CommunitiesModel(this)) - , callManager_(callManager) , verificationManager_(new VerificationManager(this)) , presenceEmitter(new PresenceEmitter(this)) { - qRegisterMetaType<mtx::events::msg::KeyVerificationAccept>(); - qRegisterMetaType<mtx::events::msg::KeyVerificationCancel>(); - qRegisterMetaType<mtx::events::msg::KeyVerificationDone>(); - qRegisterMetaType<mtx::events::msg::KeyVerificationKey>(); - qRegisterMetaType<mtx::events::msg::KeyVerificationMac>(); - qRegisterMetaType<mtx::events::msg::KeyVerificationReady>(); - qRegisterMetaType<mtx::events::msg::KeyVerificationRequest>(); - qRegisterMetaType<mtx::events::msg::KeyVerificationStart>(); - qRegisterMetaType<CombinedImagePackModel *>(); - - qRegisterMetaType<std::vector<mtx::responses::PublicRoomsChunk>>(); - - qmlRegisterUncreatableMetaObject(qml_mtx_events::staticMetaObject, - "im.nheko", - 1, - 0, - "MtxEvent", - QStringLiteral("Can't instantiate enum!")); - qmlRegisterUncreatableMetaObject( - olm::staticMetaObject, "im.nheko", 1, 0, "Olm", QStringLiteral("Can't instantiate enum!")); - qmlRegisterUncreatableMetaObject(crypto::staticMetaObject, - "im.nheko", - 1, - 0, - "Crypto", - QStringLiteral("Can't instantiate enum!")); - qmlRegisterUncreatableMetaObject(verification::staticMetaObject, - "im.nheko", - 1, - 0, - "VerificationStatus", - QStringLiteral("Can't instantiate enum!")); - - qmlRegisterType<DelegateChoice>("im.nheko", 1, 0, "DelegateChoice"); - qmlRegisterType<DelegateChooser>("im.nheko", 1, 0, "DelegateChooser"); - qmlRegisterType<NhekoDropArea>("im.nheko", 1, 0, "NhekoDropArea"); - qmlRegisterType<NhekoCursorShape>("im.nheko", 1, 0, "CursorShape"); - qmlRegisterType<MxcAnimatedImage>("im.nheko", 1, 0, "MxcAnimatedImage"); - qmlRegisterType<MxcMediaProxy>("im.nheko", 1, 0, "MxcMedia"); - qmlRegisterUncreatableType<DeviceVerificationFlow>( - "im.nheko", - 1, - 0, - "DeviceVerificationFlow", - QStringLiteral("Can't create verification flow from QML!")); - qmlRegisterUncreatableType<UserProfile>( - "im.nheko", - 1, - 0, - "UserProfileModel", - QStringLiteral("UserProfile needs to be instantiated on the C++ side")); - qmlRegisterUncreatableType<MemberList>( - "im.nheko", - 1, - 0, - "MemberList", - QStringLiteral("MemberList needs to be instantiated on the C++ side")); - qmlRegisterUncreatableType<RoomSettings>( - "im.nheko", - 1, - 0, - "RoomSettingsModel", - QStringLiteral("Room Settings needs to be instantiated on the C++ side")); - qmlRegisterUncreatableType<TimelineModel>( - "im.nheko", 1, 0, "Room", QStringLiteral("Room needs to be instantiated on the C++ side")); - qmlRegisterUncreatableType<ImagePackListModel>( - "im.nheko", - 1, - 0, - "ImagePackListModel", - QStringLiteral("ImagePackListModel needs to be instantiated on the C++ side")); - qmlRegisterUncreatableType<SingleImagePackModel>( - "im.nheko", - 1, - 0, - "SingleImagePackModel", - QStringLiteral("SingleImagePackModel needs to be instantiated on the C++ side")); - qmlRegisterUncreatableType<InviteesModel>( - "im.nheko", - 1, - 0, - "InviteesModel", - QStringLiteral("InviteesModel needs to be instantiated on the C++ side")); - qmlRegisterUncreatableType<ReadReceiptsProxy>( - "im.nheko", - 1, - 0, - "ReadReceiptsProxy", - QStringLiteral("ReadReceiptsProxy needs to be instantiated on the C++ side")); - static auto self = this; - qmlRegisterSingletonInstance("im.nheko", 1, 0, "MainWindow", MainWindow::instance()); qmlRegisterSingletonInstance("im.nheko", 1, 0, "TimelineManager", self); - qmlRegisterSingletonInstance("im.nheko", 1, 0, "UIA", UIA::instance()); qmlRegisterSingletonType<RoomlistModel>( "im.nheko", 1, 0, "Rooms", [](QQmlEngine *, QJSEngine *) -> QObject * { auto ptr = new FilteredRoomlistModel(self->rooms_); @@ -247,79 +132,15 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par return ptr; }); qmlRegisterSingletonInstance("im.nheko", 1, 0, "Communities", self->communities_); - qmlRegisterSingletonInstance( - "im.nheko", 1, 0, "Settings", ChatPage::instance()->userSettings().data()); - qmlRegisterSingletonInstance( - "im.nheko", 1, 0, "CallManager", ChatPage::instance()->callManager()); - qmlRegisterSingletonType<Clipboard>( - "im.nheko", 1, 0, "Clipboard", [](QQmlEngine *, QJSEngine *) -> QObject * { - return new Clipboard(); - }); - qmlRegisterSingletonType<Nheko>( - "im.nheko", 1, 0, "Nheko", [](QQmlEngine *, QJSEngine *) -> QObject * { - return new Nheko(); - }); - qmlRegisterSingletonType<UserSettingsModel>( - "im.nheko", 1, 0, "UserSettingsModel", [](QQmlEngine *, QJSEngine *) -> QObject * { - return new UserSettingsModel(); - }); qmlRegisterSingletonInstance("im.nheko", 1, 0, "VerificationManager", verificationManager_); qmlRegisterSingletonInstance("im.nheko", 1, 0, "Presence", presenceEmitter); - qmlRegisterSingletonType<SelfVerificationStatus>( - "im.nheko", 1, 0, "SelfVerificationStatus", [](QQmlEngine *, QJSEngine *) -> QObject * { - auto ptr = new SelfVerificationStatus(); - QObject::connect(ChatPage::instance(), - &ChatPage::initializeEmptyViews, - ptr, - &SelfVerificationStatus::invalidate); - return ptr; - }); - qRegisterMetaType<mtx::events::collections::TimelineEvents>(); - qRegisterMetaType<std::vector<DeviceInfo>>(); - - qmlRegisterUncreatableType<FilteredCommunitiesModel>( - "im.nheko", - 1, - 0, - "FilteredCommunitiesModel", - QStringLiteral("Use Communities.filtered() to create a FilteredCommunitiesModel")); - - qmlRegisterType<emoji::EmojiModel>("im.nheko.EmojiModel", 1, 0, "EmojiModel"); - qmlRegisterUncreatableType<emoji::Emoji>( - "im.nheko.EmojiModel", 1, 0, "Emoji", QStringLiteral("Used by emoji models")); - qmlRegisterUncreatableMetaObject(emoji::staticMetaObject, - "im.nheko.EmojiModel", - 1, - 0, - "EmojiCategory", - QStringLiteral("Error: Only enums")); - - qmlRegisterType<RoomDirectoryModel>("im.nheko", 1, 0, "RoomDirectoryModel"); - -#ifdef USE_QUICK_VIEW - view = new QQuickView(parent); - container = QWidget::createWindowContainer(view, parent); -#else - view = new QQuickWidget(parent); - container = view; - view->setResizeMode(QQuickWidget::SizeRootObjectToView); - container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - - connect(view, &QQuickWidget::statusChanged, this, [](QQuickWidget::Status status) { - nhlog::ui()->debug("Status changed to {}", status); - }); -#endif - container->setMinimumSize(200, 200); updateColorPalette(); - view->engine()->addImageProvider(QStringLiteral("MxcImage"), imgProvider); - view->engine()->addImageProvider(QStringLiteral("colorimage"), colorImgProvider); - view->engine()->addImageProvider(QStringLiteral("blurhash"), blurhashProvider); - if (JdenticonProvider::isAvailable()) - view->engine()->addImageProvider(QStringLiteral("jdenticon"), jdenticonProvider); - view->setSource(QUrl(QStringLiteral("qrc:///qml/Root.qml"))); - - connect(parent, &ChatPage::themeChanged, this, &TimelineViewManager::updateColorPalette); + + connect(UserSettings::instance().get(), + &UserSettings::themeChanged, + this, + &TimelineViewManager::updateColorPalette); connect(parent, &ChatPage::receivedRoomDeviceVerificationRequest, verificationManager_, @@ -379,7 +200,8 @@ void TimelineViewManager::setVideoCallItem() { WebRTCSession::instance().setVideoItem( - view->rootObject()->findChild<QQuickItem *>(QStringLiteral("videoCallItem"))); + MainWindow::instance()->rootObject()->findChild<QQuickItem *>( + QStringLiteral("videoCallItem"))); } void @@ -401,7 +223,7 @@ TimelineViewManager::showEvent(const QString &room_id, const QString &event_id) if (auto room = rooms_->getRoomById(room_id)) { if (rooms_->currentRoom() != room) { rooms_->setCurrentRoom(room_id); - container->setFocus(); + MainWindow::instance()->requestActivate(); nhlog::ui()->info("Activated room {}", room_id.toStdString()); } @@ -439,7 +261,7 @@ TimelineViewManager::saveMedia(QString mxcUrl) QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); const QString openLocation = downloadsFolder + "/" + mxcUrl.splitRef(u'/').constLast(); - const QString filename = QFileDialog::getSaveFileName(getWidget(), {}, openLocation); + const QString filename = QFileDialog::getSaveFileName(nullptr, {}, openLocation); if (filename.isEmpty()) return; @@ -591,12 +413,6 @@ TimelineViewManager::completerFor(QString completerName, QString roomId) } void -TimelineViewManager::focusTimeline() -{ - getWidget()->setFocus(); -} - -void TimelineViewManager::forwardMessageToRoom(mtx::events::collections::TimelineEvents *e, QString roomId) { diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h index 455702f4..424d78d6 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h @@ -8,8 +8,6 @@ #include <QHash> #include <QQuickItem> #include <QQuickTextDocument> -#include <QQuickView> -#include <QQuickWidget> #include <QWidget> #include <mtx/common.hpp> @@ -48,12 +46,9 @@ class TimelineViewManager : public QObject public: TimelineViewManager(CallManager *callManager, ChatPage *parent = nullptr); - QWidget *getWidget() const { return container; } void sync(const mtx::responses::Sync &sync_); - MxcImageProvider *imageProvider() { return imgProvider; } - CallManager *callManager() { return callManager_; } VerificationManager *verificationManager() { return verificationManager_; } void clearAll() { rooms_->clear(); } @@ -105,7 +100,6 @@ public slots: } void showEvent(const QString &room_id, const QString &event_id); - void focusTimeline(); void updateColorPalette(); void queueReply(const QString &roomid, const QString &repliedToEvent, const QString &replyBody); @@ -122,18 +116,6 @@ public slots: RoomlistModel *rooms() { return rooms_; } private: -#ifdef USE_QUICK_VIEW - QQuickView *view; -#else - QQuickWidget *view; -#endif - QWidget *container; - - MxcImageProvider *imgProvider; - ColorImageProvider *colorImgProvider; - BlurhashProvider *blurhashProvider; - JdenticonProvider *jdenticonProvider; - bool isInitialSync_ = true; bool isWindowFocused_ = false; @@ -141,7 +123,6 @@ private: CommunitiesModel *communities_ = nullptr; // don't move this above the rooms_ - CallManager *callManager_ = nullptr; VerificationManager *verificationManager_ = nullptr; PresenceEmitter *presenceEmitter = nullptr; diff --git a/src/ui/NhekoGlobalObject.cpp b/src/ui/NhekoGlobalObject.cpp index 450cb0d0..3d8d9959 100644 --- a/src/ui/NhekoGlobalObject.cpp +++ b/src/ui/NhekoGlobalObject.cpp @@ -139,5 +139,5 @@ Nheko::openCreateRoomDialog() const void Nheko::reparent(QWindow *win) const { - win->setTransientParent(MainWindow::instance()->windowHandle()); + win->setTransientParent(MainWindow::instance()); } diff --git a/src/ui/UIA.cpp b/src/ui/UIA.cpp index 291d0a9f..9f28ca6a 100644 --- a/src/ui/UIA.cpp +++ b/src/ui/UIA.cpp @@ -13,7 +13,6 @@ #include <mtx/responses/common.hpp> #include "Logging.h" -#include "MainWindow.h" #include "dialogs/FallbackAuth.h" #include "dialogs/ReCaptcha.h" @@ -71,7 +70,7 @@ UIA::genericHandler(QString context) emit phoneNumber(); } else if (current_stage == mtx::user_interactive::auth_types::recaptcha) { auto captchaDialog = - new dialogs::ReCaptcha(QString::fromStdString(u.session), MainWindow::instance()); + new dialogs::ReCaptcha(QString::fromStdString(u.session), nullptr); captchaDialog->setWindowTitle(context); connect( @@ -95,7 +94,7 @@ UIA::genericHandler(QString context) } else if (current_stage == mtx::user_interactive::auth_types::registration_token) { bool ok; QString token = - QInputDialog::getText(MainWindow::instance(), + QInputDialog::getText(nullptr, context, tr("Please enter a valid registration token."), QLineEdit::Normal, @@ -113,7 +112,7 @@ UIA::genericHandler(QString context) // use fallback auto dialog = new dialogs::FallbackAuth(QString::fromStdString(current_stage), QString::fromStdString(u.session), - MainWindow::instance()); + nullptr); dialog->setWindowTitle(context); connect(dialog, &dialogs::FallbackAuth::confirmation, this, [h, u, dialog]() { |