diff --git a/src/Cache.cpp b/src/Cache.cpp
index d1723d98..b55d53a6 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",
@@ -391,6 +391,7 @@ Cache::loadSecrets(std::vector<std::pair<std::string, bool>> toLoad)
&QKeychain::ReadPasswordJob::finished,
this,
[this, name, toLoad, job](QKeychain::Job *) mutable {
+ nhlog::db()->debug("Finished reading '{}'", toLoad.begin()->first);
const QString secret = job->textData();
if (job->error() && job->error() != QKeychain::Error::EntryNotFound) {
nhlog::db()->error("Restoring secret '{}' failed ({}): {}",
@@ -413,6 +414,7 @@ Cache::loadSecrets(std::vector<std::pair<std::string, bool>> toLoad)
// You can't start a job from the finish signal of a job.
QTimer::singleShot(0, this, [this, toLoad] { loadSecrets(toLoad); });
});
+ nhlog::db()->debug("Reading '{}'", name_);
job->start();
}
diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp
index bfaa6389..cdaf7260 100644
--- a/src/ChatPage.cpp
+++ b/src/ChatPage.cpp
@@ -22,7 +22,6 @@
#include "Utils.h"
#include "encryption/DeviceVerificationFlow.h"
#include "encryption/Olm.h"
-#include "ui/OverlayModal.h"
#include "ui/Theme.h"
#include "ui/UserProfile.h"
#include "voip/CallManager.h"
@@ -44,8 +43,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 +60,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 +147,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,17 +155,9 @@ 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(
this,
&ChatPage::initializeViews,
@@ -183,8 +168,6 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
&ChatPage::initializeEmptyViews,
view_manager_,
&TimelineViewManager::initializeRoomlist);
- connect(
- this, &ChatPage::chatFocusChanged, view_manager_, &TimelineViewManager::chatFocusChanged);
connect(this, &ChatPage::syncUI, this, [this](const mtx::responses::Sync &sync) {
view_manager_->sync(sync);
@@ -201,7 +184,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 +314,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 +327,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 +673,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 +759,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)
@@ -787,6 +770,8 @@ ChatPage::inviteUser(QString userid, QString reason)
userid.toStdString(),
[this, userid, room](const mtx::responses::Empty &, mtx::http::RequestErr err) {
if (err) {
+ nhlog::net()->error(
+ "Failed to invite {} to {}: {}", userid.toStdString(), room.toStdString(), *err);
emit showNotification(
tr("Failed to invite %1 to %2: %3")
.arg(userid, room, QString::fromStdString(err->matrix_error.error)));
@@ -800,7 +785,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 +810,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 +834,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)
@@ -1064,8 +1049,6 @@ ChatPage::initiateLogout()
emit loggedOut();
});
-
- emit showOverlayProgressBar();
}
template<typename T>
@@ -1083,7 +1066,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 +1098,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 +1192,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 +1378,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..e4b9e4e8 100644
--- a/src/ChatPage.h
+++ b/src/ChatPage.h
@@ -8,7 +8,6 @@
#include <atomic>
#include <optional>
-#include <stack>
#include <variant>
#include <mtx/common.hpp>
@@ -18,17 +17,15 @@
#include <mtx/events/presence.hpp>
#include <mtx/secret_storage.hpp>
-#include <QHBoxLayout>
#include <QMap>
#include <QPoint>
+#include <QSharedPointer>
#include <QTimer>
-#include <QWidget>
#include "CacheCryptoStructs.h"
#include "CacheStructs.h"
#include "notifications/Manager.h"
-class OverlayModal;
class TimelineViewManager;
class UserSettings;
class NotificationsManager;
@@ -51,12 +48,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);
@@ -112,7 +109,6 @@ signals:
void showNotification(const QString &msg);
void showLoginPage(const QString &msg);
void showUserSettingsPage();
- void showOverlayProgressBar();
void ownProfileOk();
void setUserDisplayName(const QString &name);
@@ -143,7 +139,6 @@ signals:
void retrievedPresence(const QString &statusMsg, mtx::presence::PresenceState state);
void themeChanged();
void decryptSidebarChanged();
- void chatFocusChanged(const bool focused);
//! Signals for device verificaiton
void receivedDeviceVerificationAccept(const mtx::events::msg::KeyVerificationAccept &message);
@@ -201,8 +196,6 @@ private:
template<typename T>
void connectCallMessage();
- QHBoxLayout *topLayout_;
-
TimelineViewManager *view_manager_;
QTimer connectivityTimer_;
diff --git a/src/LoginPage.cpp b/src/LoginPage.cpp
index 4c28f364..6bed446e 100644
--- a/src/LoginPage.cpp
+++ b/src/LoginPage.cpp
@@ -5,11 +5,6 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#include <QDesktopServices>
-#include <QFontMetrics>
-#include <QLabel>
-#include <QPainter>
-#include <QStyleOption>
-#include <QtMath>
#include <mtx/identifiers.hpp>
#include <mtx/requests.hpp>
@@ -18,247 +13,94 @@
#include "Config.h"
#include "Logging.h"
#include "LoginPage.h"
+#include "MainWindow.h"
#include "MatrixClient.h"
#include "SSOHandler.h"
#include "UserSettingsPage.h"
-#include "ui/FlatButton.h"
-#include "ui/LoadingIndicator.h"
-#include "ui/OverlayModal.h"
-#include "ui/RaisedButton.h"
-#include "ui/TextField.h"
Q_DECLARE_METATYPE(LoginPage::LoginMethod)
using namespace mtx::identifiers;
-LoginPage::LoginPage(QWidget *parent)
- : QWidget(parent)
+LoginPage::LoginPage(QObject *parent)
+ : QObject(parent)
, inferredServerAddress_()
{
- qRegisterMetaType<LoginPage::LoginMethod>("LoginPage::LoginMethod");
-
- top_layout_ = new QVBoxLayout();
-
- top_bar_layout_ = new QHBoxLayout();
- top_bar_layout_->setSpacing(0);
- top_bar_layout_->setContentsMargins(0, 0, 0, 0);
-
- back_button_ = new FlatButton(this);
- back_button_->setMinimumSize(QSize(30, 30));
-
- top_bar_layout_->addWidget(back_button_, 0, Qt::AlignLeft | Qt::AlignVCenter);
- top_bar_layout_->addStretch(1);
-
- QIcon icon;
- icon.addFile(QStringLiteral(":/icons/icons/ui/angle-arrow-left.svg"));
-
- back_button_->setIcon(icon);
- back_button_->setIconSize(QSize(32, 32));
-
- QIcon logo;
- logo.addFile(QStringLiteral(":/logos/login.png"));
-
- logo_ = new QLabel(this);
- logo_->setPixmap(logo.pixmap(128));
-
- logo_layout_ = new QHBoxLayout();
- logo_layout_->setContentsMargins(0, 0, 0, 20);
- logo_layout_->addWidget(logo_, 0, Qt::AlignHCenter);
-
- form_wrapper_ = new QHBoxLayout();
- form_widget_ = new QWidget();
- form_widget_->setMinimumSize(QSize(350, 200));
-
- form_layout_ = new QVBoxLayout();
- form_layout_->setSpacing(20);
- form_layout_->setContentsMargins(0, 0, 0, 30);
- form_widget_->setLayout(form_layout_);
-
- form_wrapper_->addStretch(1);
- form_wrapper_->addWidget(form_widget_);
- form_wrapper_->addStretch(1);
-
- matrixid_input_ = new TextField(this);
- matrixid_input_->setLabel(tr("Matrix ID"));
- matrixid_input_->setRegexp(QRegularExpression(QStringLiteral("@.+?:.{3,}")));
- matrixid_input_->setPlaceholderText(tr("e.g @joe:matrix.org"));
- matrixid_input_->setToolTip(
- tr("Your login name. A mxid should start with @ followed by the user id. After the user "
- "id you need to include your server name after a :.\nYou can also put your homeserver "
- "address there, if your server doesn't support .well-known lookup.\nExample: "
- "@user:server.my\nIf Nheko fails to discover your homeserver, it will show you a "
- "field to enter the server manually."));
-
- spinner_ = new LoadingIndicator(this);
- spinner_->setFixedHeight(40);
- spinner_->setFixedWidth(40);
- spinner_->hide();
-
- errorIcon_ = new QLabel(this);
- errorIcon_->setPixmap(QPixmap(QStringLiteral(":/icons/icons/error.png")));
- errorIcon_->hide();
-
- matrixidLayout_ = new QHBoxLayout();
- matrixidLayout_->addWidget(matrixid_input_, 0, Qt::AlignVCenter);
-
- QFont font;
-
- error_matrixid_label_ = new QLabel(this);
- error_matrixid_label_->setFont(font);
- error_matrixid_label_->setWordWrap(true);
-
- password_input_ = new TextField(this);
- password_input_->setLabel(tr("Password"));
- password_input_->setEchoMode(QLineEdit::Password);
- password_input_->setToolTip(tr("Your password."));
-
- deviceName_ = new TextField(this);
- deviceName_->setLabel(tr("Device name"));
- deviceName_->setToolTip(
- tr("A name for this device, which will be shown to others, when verifying your devices. "
- "If none is provided a default is used."));
-
- serverInput_ = new TextField(this);
- serverInput_->setLabel(tr("Homeserver address"));
- serverInput_->setPlaceholderText(tr("server.my:8787"));
- serverInput_->setToolTip(tr("The address that can be used to contact you homeservers "
- "client API.\nExample: https://server.my:8787"));
- serverInput_->hide();
-
- serverLayout_ = new QHBoxLayout();
- serverLayout_->addWidget(serverInput_, 0, Qt::AlignVCenter);
-
- form_layout_->addLayout(matrixidLayout_);
- form_layout_->addWidget(error_matrixid_label_, 0, Qt::AlignHCenter);
- form_layout_->addWidget(password_input_);
- form_layout_->addWidget(deviceName_, Qt::AlignHCenter);
- form_layout_->addLayout(serverLayout_);
-
- error_matrixid_label_->hide();
-
- button_layout_ = new QHBoxLayout();
- button_layout_->setSpacing(20);
- button_layout_->setContentsMargins(0, 0, 0, 30);
-
- login_button_ = new RaisedButton(tr("LOGIN"), this);
- login_button_->setMinimumSize(150, 65);
- login_button_->setFontSize(20);
- login_button_->setCornerRadius(3);
-
- sso_login_button_ = new RaisedButton(tr("SSO LOGIN"), this);
- sso_login_button_->setMinimumSize(150, 65);
- sso_login_button_->setFontSize(20);
- sso_login_button_->setCornerRadius(3);
- sso_login_button_->setVisible(false);
-
- button_layout_->addStretch(1);
- button_layout_->addWidget(login_button_);
- button_layout_->addWidget(sso_login_button_);
- button_layout_->addStretch(1);
-
- error_label_ = new QLabel(this);
- error_label_->setFont(font);
- error_label_->setWordWrap(true);
-
- top_layout_->addLayout(top_bar_layout_);
- top_layout_->addStretch(1);
- top_layout_->addLayout(logo_layout_);
- top_layout_->addLayout(form_wrapper_);
- top_layout_->addStretch(1);
- top_layout_->addLayout(button_layout_);
- top_layout_->addWidget(error_label_, 0, Qt::AlignHCenter);
- top_layout_->addStretch(1);
-
- setLayout(top_layout_);
+ [[maybe_unused]] static auto ignored =
+ qRegisterMetaType<LoginPage::LoginMethod>("LoginPage::LoginMethod");
connect(this, &LoginPage::versionOkCb, this, &LoginPage::versionOk, Qt::QueuedConnection);
connect(this, &LoginPage::versionErrorCb, this, &LoginPage::versionError, Qt::QueuedConnection);
+ connect(
+ this,
+ &LoginPage::loginOk,
+ this,
+ [this](const mtx::responses::Login &res) {
+ loggingIn_ = false;
+ emit loggingInChanged();
- connect(back_button_, SIGNAL(clicked()), this, SLOT(onBackButtonClicked()));
- connect(login_button_, &RaisedButton::clicked, this, [this]() {
- onLoginButtonClicked(passwordSupported ? LoginMethod::Password : LoginMethod::SSO);
- });
- connect(sso_login_button_, &RaisedButton::clicked, this, [this]() {
- onLoginButtonClicked(LoginMethod::SSO);
- });
- connect(this,
- &LoginPage::showErrorMessage,
- this,
- static_cast<void (LoginPage::*)(QLabel *, const QString &)>(&LoginPage::showError),
- Qt::QueuedConnection);
- connect(matrixid_input_, SIGNAL(returnPressed()), login_button_, SLOT(click()));
- connect(password_input_, SIGNAL(returnPressed()), login_button_, SLOT(click()));
- connect(deviceName_, SIGNAL(returnPressed()), login_button_, SLOT(click()));
- connect(serverInput_, SIGNAL(returnPressed()), login_button_, SLOT(click()));
- connect(matrixid_input_, SIGNAL(editingFinished()), this, SLOT(onMatrixIdEntered()));
- connect(serverInput_, SIGNAL(editingFinished()), this, SLOT(onServerAddressEntered()));
+ http::client()->set_user(res.user_id);
+ MainWindow::instance()->showChatPage();
+ },
+ Qt::QueuedConnection);
}
void
LoginPage::showError(const QString &msg)
{
- auto rect = QFontMetrics(font()).boundingRect(msg);
- int width = rect.width();
- int height = rect.height();
- error_label_->setFixedHeight((int)qCeil(width / 200.0) * height);
- error_label_->setText(msg);
+ loggingIn_ = false;
+ emit loggingInChanged();
+
+ error_ = msg;
+ emit errorOccurred();
}
void
-LoginPage::showError(QLabel *label, const QString &msg)
+LoginPage::setHomeserver(QString hs)
{
- auto rect = QFontMetrics(font()).boundingRect(msg);
- int width = rect.width();
- int height = rect.height();
- label->setFixedHeight((int)qCeil(width / 200.0) * height);
- label->setText(msg);
+ if (hs != homeserver_) {
+ homeserver_ = hs;
+ homeserverValid_ = false;
+ emit homeserverChanged();
+ http::client()->set_server(hs.toStdString());
+ checkHomeserverVersion();
+ }
}
void
LoginPage::onMatrixIdEntered()
{
- error_label_->setText(QLatin1String(""));
-
- User user;
+ clearErrors();
- if (!matrixid_input_->isValid()) {
- error_matrixid_label_->show();
- showError(error_matrixid_label_,
- tr("You have entered an invalid Matrix ID e.g @joe:matrix.org"));
- return;
- } else {
- error_matrixid_label_->setText(QLatin1String(""));
- error_matrixid_label_->hide();
- }
+ homeserverValid_ = false;
+ emit homeserverChanged();
+ User user;
try {
- user = parse<User>(matrixid_input_->text().toStdString());
+ user = parse<User>(mxid_.toStdString());
} catch (const std::exception &) {
- showError(error_matrixid_label_,
- tr("You have entered an invalid Matrix ID e.g @joe:matrix.org"));
+ mxidError_ = tr("You have entered an invalid Matrix ID e.g @joe:matrix.org");
+ emit mxidErrorChanged();
return;
}
- QString homeServer = QString::fromStdString(user.hostname());
- if (homeServer != inferredServerAddress_) {
- serverInput_->hide();
- serverLayout_->removeWidget(errorIcon_);
- errorIcon_->hide();
- if (serverInput_->isVisible()) {
- matrixidLayout_->removeWidget(spinner_);
- serverLayout_->addWidget(spinner_, 0, Qt::AlignVCenter | Qt::AlignRight);
- spinner_->start();
- } else {
- serverLayout_->removeWidget(spinner_);
- matrixidLayout_->addWidget(spinner_, 0, Qt::AlignVCenter | Qt::AlignRight);
- spinner_->start();
- }
+ if (user.hostname().empty() || user.localpart().empty()) {
+ mxidError_ = tr("You have entered an invalid Matrix ID e.g @joe:matrix.org");
+ emit mxidErrorChanged();
+ return;
+ } else {
+ nhlog::net()->debug("hostname: {}", user.hostname());
+ }
- inferredServerAddress_ = homeServer;
- serverInput_->setText(homeServer);
+ if (user.hostname() != inferredServerAddress_.toStdString()) {
+ homeserverNeeded_ = false;
+ lookingUpHs_ = true;
+ emit lookingUpHsChanged();
http::client()->set_server(user.hostname());
http::client()->verify_certificates(
!UserSettings::instance()->disableCertificateValidation());
+ homeserver_ = QString::fromStdString(user.hostname());
+ emit homeserverChanged();
http::client()->well_known(
[this](const mtx::responses::WellKnown &res, mtx::http::RequestErr err) {
@@ -286,6 +128,7 @@ LoginPage::onMatrixIdEntered()
nhlog::net()->info("Autodiscovery: Discovered '" + res.homeserver.base_url + "'");
http::client()->set_server(res.homeserver.base_url);
+ emit homeserverChanged();
checkHomeserverVersion();
});
}
@@ -294,6 +137,16 @@ LoginPage::onMatrixIdEntered()
void
LoginPage::checkHomeserverVersion()
{
+ clearErrors();
+
+ try {
+ User user = parse<User>(mxid_.toStdString());
+ } catch (const std::exception &) {
+ mxidError_ = tr("You have entered an invalid Matrix ID e.g @joe:matrix.org");
+ emit mxidErrorChanged();
+ return;
+ }
+
http::client()->versions([this](const mtx::responses::Versions &, mtx::http::RequestErr err) {
if (err) {
if (err->status_code == 404) {
@@ -318,107 +171,78 @@ LoginPage::checkHomeserverVersion()
if (err || flows.flows.empty())
emit versionOkCb(true, false);
- bool ssoSupported_ = false;
- bool passwordSupported_ = false;
+ bool ssoSupported = false;
+ bool passwordSupported = false;
for (const auto &flow : flows.flows) {
if (flow.type == mtx::user_interactive::auth_types::sso) {
- ssoSupported_ = true;
+ ssoSupported = true;
} else if (flow.type == mtx::user_interactive::auth_types::password) {
- passwordSupported_ = true;
+ passwordSupported = true;
}
}
- emit versionOkCb(passwordSupported_, ssoSupported_);
+ emit versionOkCb(passwordSupported, ssoSupported);
});
});
}
void
-LoginPage::onServerAddressEntered()
-{
- error_label_->setText(QLatin1String(""));
- http::client()->verify_certificates(!UserSettings::instance()->disableCertificateValidation());
- http::client()->set_server(serverInput_->text().toStdString());
- checkHomeserverVersion();
-
- serverLayout_->removeWidget(errorIcon_);
- errorIcon_->hide();
- serverLayout_->addWidget(spinner_, 0, Qt::AlignVCenter | Qt::AlignRight);
- spinner_->start();
-}
-
-void
LoginPage::versionError(const QString &error)
{
- showError(error_label_, error);
- serverInput_->show();
+ showError(error);
- spinner_->stop();
- serverLayout_->removeWidget(spinner_);
- serverLayout_->addWidget(errorIcon_, 0, Qt::AlignVCenter | Qt::AlignRight);
- errorIcon_->show();
- matrixidLayout_->removeWidget(spinner_);
+ homeserverNeeded_ = true;
+ lookingUpHs_ = false;
+ homeserverValid_ = false;
+ emit lookingUpHsChanged();
+ emit versionLookedUp();
}
void
-LoginPage::versionOk(bool passwordSupported_, bool ssoSupported_)
+LoginPage::versionOk(bool passwordSupported, bool ssoSupported)
{
- passwordSupported = passwordSupported_;
- ssoSupported = ssoSupported_;
-
- serverLayout_->removeWidget(spinner_);
- matrixidLayout_->removeWidget(spinner_);
- spinner_->stop();
-
- password_input_->setVisible(passwordSupported);
- password_input_->setEnabled(passwordSupported);
- sso_login_button_->setVisible(ssoSupported);
- login_button_->setVisible(passwordSupported);
+ passwordSupported_ = passwordSupported;
+ ssoSupported_ = ssoSupported;
- if (serverInput_->isVisible())
- serverInput_->hide();
+ lookingUpHs_ = false;
+ homeserverValid_ = true;
+ emit homeserverChanged();
+ emit lookingUpHsChanged();
+ emit versionLookedUp();
}
void
-LoginPage::onLoginButtonClicked(LoginMethod loginMethod)
+LoginPage::onLoginButtonClicked(LoginMethod loginMethod,
+ QString userid,
+ QString password,
+ QString deviceName)
{
- error_label_->setText(QLatin1String(""));
- User user;
+ clearErrors();
- if (!matrixid_input_->isValid()) {
- error_matrixid_label_->show();
- showError(error_matrixid_label_,
- tr("You have entered an invalid Matrix ID e.g @joe:matrix.org"));
- return;
- } else {
- error_matrixid_label_->setText(QLatin1String(""));
- error_matrixid_label_->hide();
- }
+ User user;
try {
- user = parse<User>(matrixid_input_->text().toStdString());
+ user = parse<User>(userid.toStdString());
} catch (const std::exception &) {
- showError(error_matrixid_label_,
- tr("You have entered an invalid Matrix ID e.g @joe:matrix.org"));
+ mxidError_ = tr("You have entered an invalid Matrix ID e.g @joe:matrix.org");
+ emit mxidErrorChanged();
return;
}
if (loginMethod == LoginMethod::Password) {
- if (password_input_->text().isEmpty())
- return showError(error_label_, tr("Empty password"));
+ if (password.isEmpty())
+ return showError(tr("Empty password"));
http::client()->login(
user.localpart(),
- password_input_->text().toStdString(),
- deviceName_->text().trimmed().isEmpty() ? initialDeviceName()
- : deviceName_->text().toStdString(),
+ password.toStdString(),
+ deviceName.trimmed().isEmpty() ? initialDeviceName_() : deviceName.toStdString(),
[this](const mtx::responses::Login &res, mtx::http::RequestErr err) {
if (err) {
auto error = err->matrix_error.error;
if (error.empty())
error = err->parse_error;
- showErrorMessage(error_label_, QString::fromStdString(error));
- emit errorOccurred();
+ showError(QString::fromStdString(error));
return;
}
@@ -432,34 +256,33 @@ LoginPage::onLoginButtonClicked(LoginMethod loginMethod)
});
} else {
auto sso = new SSOHandler();
- connect(sso, &SSOHandler::ssoSuccess, this, [this, sso](std::string token) {
- mtx::requests::Login req{};
- req.token = token;
- req.type = mtx::user_interactive::auth_types::token;
- req.device_id = deviceName_->text().trimmed().isEmpty()
- ? initialDeviceName()
- : deviceName_->text().toStdString();
- http::client()->login(
- req, [this](const mtx::responses::Login &res, mtx::http::RequestErr err) {
- if (err) {
- showErrorMessage(error_label_,
- QString::fromStdString(err->matrix_error.error));
- emit errorOccurred();
- return;
- }
+ connect(
+ sso, &SSOHandler::ssoSuccess, this, [this, sso, userid, deviceName](std::string token) {
+ mtx::requests::Login req{};
+ req.token = token;
+ req.type = mtx::user_interactive::auth_types::token;
+ req.device_id =
+ deviceName.trimmed().isEmpty() ? initialDeviceName_() : deviceName.toStdString();
+ http::client()->login(
+ req, [this](const mtx::responses::Login &res, mtx::http::RequestErr err) {
+ if (err) {
+ showError(QString::fromStdString(err->matrix_error.error));
+ emit errorOccurred();
+ return;
+ }
- if (res.well_known) {
- http::client()->set_server(res.well_known->homeserver.base_url);
- nhlog::net()->info("Login requested to user server: " +
- res.well_known->homeserver.base_url);
- }
+ if (res.well_known) {
+ http::client()->set_server(res.well_known->homeserver.base_url);
+ nhlog::net()->info("Login requested to user server: " +
+ res.well_known->homeserver.base_url);
+ }
- emit loginOk(res);
- });
- sso->deleteLater();
- });
+ emit loginOk(res);
+ });
+ sso->deleteLater();
+ });
connect(sso, &SSOHandler::ssoFailed, this, [this, sso]() {
- showErrorMessage(error_label_, tr("SSO login failed"));
+ showError(tr("SSO login failed"));
emit errorOccurred();
sso->deleteLater();
});
@@ -468,37 +291,6 @@ LoginPage::onLoginButtonClicked(LoginMethod loginMethod)
QString::fromStdString(http::client()->login_sso_redirect(sso->url())));
}
- emit loggingIn();
-}
-
-void
-LoginPage::reset()
-{
- matrixid_input_->clear();
- password_input_->clear();
- password_input_->show();
- serverInput_->clear();
-
- spinner_->stop();
- errorIcon_->hide();
- serverLayout_->removeWidget(spinner_);
- serverLayout_->removeWidget(errorIcon_);
- matrixidLayout_->removeWidget(spinner_);
-
- inferredServerAddress_.clear();
-}
-
-void
-LoginPage::onBackButtonClicked()
-{
- emit backButtonClicked();
-}
-
-void
-LoginPage::paintEvent(QPaintEvent *)
-{
- QStyleOption opt;
- opt.initFrom(this);
- QPainter p(this);
- style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
+ loggingIn_ = true;
+ emit loggingInChanged();
}
diff --git a/src/LoginPage.h b/src/LoginPage.h
index fbfd8710..9a1b9653 100644
--- a/src/LoginPage.h
+++ b/src/LoginPage.h
@@ -6,16 +6,7 @@
#pragma once
-#include <QWidget>
-
-class FlatButton;
-class LoadingIndicator;
-class OverlayModal;
-class RaisedButton;
-class TextField;
-class QLabel;
-class QVBoxLayout;
-class QHBoxLayout;
+#include <QObject>
namespace mtx {
namespace responses {
@@ -23,24 +14,77 @@ struct Login;
}
}
-class LoginPage : public QWidget
+class LoginPage : public QObject
{
Q_OBJECT
+ Q_PROPERTY(QString mxid READ mxid WRITE setMxid NOTIFY matrixIdChanged)
+ Q_PROPERTY(QString homeserver READ homeserver WRITE setHomeserver NOTIFY homeserverChanged)
+
+ Q_PROPERTY(QString mxidError READ mxidError NOTIFY mxidErrorChanged)
+ Q_PROPERTY(QString error READ error NOTIFY errorOccurred)
+ Q_PROPERTY(bool lookingUpHs READ lookingUpHs NOTIFY lookingUpHsChanged)
+ Q_PROPERTY(bool homeserverValid READ homeserverValid NOTIFY lookingUpHsChanged)
+ Q_PROPERTY(bool loggingIn READ loggingIn NOTIFY loggingInChanged)
+ Q_PROPERTY(bool passwordSupported READ passwordSupported NOTIFY versionLookedUp)
+ Q_PROPERTY(bool ssoSupported READ ssoSupported NOTIFY versionLookedUp)
+ Q_PROPERTY(bool homeserverNeeded READ homeserverNeeded NOTIFY versionLookedUp)
+
public:
enum class LoginMethod
{
Password,
SSO,
};
+ Q_ENUM(LoginMethod)
+
+ LoginPage(QObject *parent = nullptr);
+
+ Q_INVOKABLE QString initialDeviceName() const
+ {
+ return QString::fromStdString(initialDeviceName_());
+ }
- LoginPage(QWidget *parent = nullptr);
+ bool lookingUpHs() const { return lookingUpHs_; }
+ bool loggingIn() const { return loggingIn_; }
+ bool passwordSupported() const { return passwordSupported_; }
+ bool ssoSupported() const { return ssoSupported_; }
+ bool homeserverNeeded() const { return homeserverNeeded_; }
+ bool homeserverValid() const { return homeserverValid_; }
- void reset();
+ QString homeserver() { return homeserver_; }
+ QString mxid() { return mxid_; }
+
+ QString error() { return error_; }
+ QString mxidError() { return mxidError_; }
+
+ void setHomeserver(QString hs);
+ void setMxid(QString id)
+ {
+ if (id != mxid_) {
+ mxid_ = id;
+ emit matrixIdChanged();
+ onMatrixIdEntered();
+ }
+ }
+
+ static std::string initialDeviceName_()
+ {
+#if defined(Q_OS_MAC)
+ return "Nheko on macOS";
+#elif defined(Q_OS_LINUX)
+ return "Nheko on Linux";
+#elif defined(Q_OS_WIN)
+ return "Nheko on Windows";
+#elif defined(Q_OS_FREEBSD)
+ return "Nheko on FreeBSD";
+#else
+ return "Nheko";
+#endif
+ }
signals:
- void backButtonClicked();
- void loggingIn();
+ void loggingInChanged();
void errorOccurred();
//! Used to trigger the corresponding slot outside of the main thread.
@@ -48,28 +92,26 @@ signals:
void versionOkCb(bool passwordSupported, bool ssoSupported);
void loginOk(const mtx::responses::Login &res);
- void showErrorMessage(QLabel *label, const QString &msg);
-protected:
- void paintEvent(QPaintEvent *event) override;
+ void onServerAddressEntered();
+
+ void matrixIdChanged();
+ void homeserverChanged();
+
+ void mxidErrorChanged();
+ void lookingUpHsChanged();
+ void versionLookedUp();
+ void versionLookupFinished();
public slots:
// Displays errors produced during the login.
void showError(const QString &msg);
- void showError(QLabel *label, const QString &msg);
-
-private slots:
- // Callback for the back button.
- void onBackButtonClicked();
// Callback for the login button.
- void onLoginButtonClicked(LoginMethod loginMethod);
-
- // Callback for probing the server found in the mxid
- void onMatrixIdEntered();
-
- // Callback for probing the manually entered server
- void onServerAddressEntered();
+ void onLoginButtonClicked(LoginMethod loginMethod,
+ QString userid,
+ QString password,
+ QString deviceName);
// Callback for errors produced during server probing
void versionError(const QString &error_message);
@@ -78,48 +120,28 @@ private slots:
private:
void checkHomeserverVersion();
- std::string initialDeviceName()
+ void onMatrixIdEntered();
+ void clearErrors()
{
-#if defined(Q_OS_MAC)
- return "Nheko on macOS";
-#elif defined(Q_OS_LINUX)
- return "Nheko on Linux";
-#elif defined(Q_OS_WIN)
- return "Nheko on Windows";
-#elif defined(Q_OS_FREEBSD)
- return "Nheko on FreeBSD";
-#else
- return "Nheko";
-#endif
+ error_.clear();
+ mxidError_.clear();
+ emit errorOccurred();
+ emit mxidErrorChanged();
}
- QVBoxLayout *top_layout_;
-
- QHBoxLayout *top_bar_layout_;
- QHBoxLayout *logo_layout_;
- QHBoxLayout *button_layout_;
-
- QLabel *logo_;
- QLabel *error_label_;
- QLabel *error_matrixid_label_;
-
- QHBoxLayout *serverLayout_;
- QHBoxLayout *matrixidLayout_;
- LoadingIndicator *spinner_;
- QLabel *errorIcon_;
QString inferredServerAddress_;
- FlatButton *back_button_;
- RaisedButton *login_button_, *sso_login_button_;
+ QString mxid_;
+ QString homeserver_;
+
+ QString mxidError_;
+ QString error_;
- QWidget *form_widget_;
- QHBoxLayout *form_wrapper_;
- QVBoxLayout *form_layout_;
+ bool passwordSupported_ = true;
+ bool ssoSupported_ = false;
- TextField *matrixid_input_;
- TextField *password_input_;
- TextField *deviceName_;
- TextField *serverInput_;
- bool passwordSupported = true;
- bool ssoSupported = false;
+ bool lookingUpHs_ = false;
+ bool loggingIn_ = false;
+ bool homeserverNeeded_ = false;
+ bool homeserverValid_ = false;
};
diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp
index 5bfce89e..83504d86 100644
--- a/src/MainWindow.cpp
+++ b/src/MainWindow.cpp
@@ -5,91 +5,87 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#include <QApplication>
-#include <QLayout>
#include <QMessageBox>
-#include <QPluginLoader>
-#include <QShortcut>
#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 "ui/LoadingIndicator.h"
-#include "ui/OverlayModal.h"
-#include "ui/SnackBar.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/MxcAnimatedImage.h"
+#include "ui/MxcMediaProxy.h"
+#include "ui/NhekoCursorShape.h"
+#include "ui/NhekoDropArea.h"
+#include "ui/NhekoGlobalObject.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"));
-
- modal_ = new OverlayModal(this);
-
+ setResizeMode(QQuickView::SizeRootObjectToView);
+ setMinimumHeight(400);
+ setMinimumWidth(400);
restoreWindowSize();
- QFont font;
- font.setStyleStrategy(QFont::PreferAntialias);
- setFont(font);
-
- trayIcon_ = new TrayIcon(QStringLiteral(":/logos/nheko.svg"), this);
-
- welcome_page_ = new WelcomePage(this);
- login_page_ = new LoginPage(this);
- register_page_ = new RegisterPage(this);
- chat_page_ = new ChatPage(userSettings_, this);
+ chat_page_ = new ChatPage(userSettings_, this);
+ registerQmlTypes();
- // Initialize sliding widget manager.
- pageStack_ = new QStackedWidget(this);
- pageStack_->addWidget(welcome_page_);
- pageStack_->addWidget(login_page_);
- pageStack_->addWidget(register_page_);
- pageStack_->addWidget(chat_page_);
+ setColor(Theme::paletteFromTheme(userSettings_->theme()).window().color());
+ setSource(QUrl(QStringLiteral("qrc:///qml/Root.qml")));
- setCentralWidget(pageStack_);
-
- 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()));
+ trayIcon_ = new TrayIcon(QStringLiteral(":/logos/nheko.svg"), this);
- connect(chat_page_, &ChatPage::closing, this, &MainWindow::showWelcomePage);
- connect(
- chat_page_, &ChatPage::showOverlayProgressBar, this, &MainWindow::showOverlayProgressBar);
+ connect(chat_page_, &ChatPage::closing, this, [this] { switchToLoginPage(""); });
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) {
- login_page_->showError(msg);
- showLoginPage();
- });
+ connect(chat_page_, &ChatPage::showLoginPage, this, &MainWindow::switchToLoginPage);
+ connect(chat_page_, &ChatPage::showNotification, this, &MainWindow::showNotification);
connect(userSettings_.get(), &UserSettings::trayChanged, trayIcon_, &TrayIcon::setVisible);
connect(trayIcon_,
@@ -97,20 +93,6 @@ MainWindow::MainWindow(QWidget *parent)
this,
SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));
- connect(chat_page_, SIGNAL(contentLoaded()), this, SLOT(removeOverlayProgressBar()));
-
- 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);
-
- QShortcut *quitShortcut = new QShortcut(QKeySequence::Quit, this);
- connect(quitShortcut, &QShortcut::activated, this, QApplication::quit);
-
trayIcon_->setVisible(userSettings_->tray());
// load cache on event loop
@@ -133,12 +115,172 @@ 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");
+ qmlRegisterType<LoginPage>("im.nheko", 1, 0, "Login");
+ qmlRegisterType<RegisterPage>("im.nheko", 1, 0, "Registration");
+ 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());
+
+ QObject::connect(engine(), &QQmlEngine::quit, &QGuiApplication::quit);
+}
+
+void
MainWindow::setWindowTitle(int notificationCount)
{
QString name = QStringLiteral("nheko");
@@ -148,20 +290,19 @@ 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) {
- emit focusChanged(true);
- } else if (type == QEvent::WindowDeactivate) {
- emit focusChanged(false);
+
+ if (type == QEvent::Close) {
+ closeEvent(static_cast<QCloseEvent *>(event));
}
- return QMainWindow::event(event);
+ return QQuickView::event(event);
}
void
@@ -189,31 +330,6 @@ MainWindow::saveCurrentWindowSize()
}
void
-MainWindow::removeOverlayProgressBar()
-{
- QTimer *timer = new QTimer(this);
- timer->setSingleShot(true);
-
- 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);
- });
-
- timer->start(50);
-}
-
-void
MainWindow::showChatPage()
{
auto userid = QString::fromStdString(http::client()->user_id().to_string());
@@ -227,19 +343,13 @@ MainWindow::showChatPage()
userSettings_.data()->setDeviceId(device_id);
userSettings_.data()->setHomeserver(homeserver);
- showOverlayProgressBar();
-
- pageStack_->setCurrentWidget(chat_page_);
-
- pageStack_->removeWidget(welcome_page_);
- pageStack_->removeWidget(login_page_);
- pageStack_->removeWidget(register_page_);
-
- 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 +357,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;
@@ -290,22 +400,10 @@ 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,
@@ -314,76 +412,19 @@ MainWindow::openCreateRoomDialog(
showDialog(dialog);
}
-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();
-}
-
-void
-MainWindow::showSolidOverlayModal(QWidget *content, QFlags<Qt::AlignmentFlag> flags)
-{
- 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();
-}
-
bool
MainWindow::pageSupportsTray() const
{
- return !welcome_page_->isVisible() && !login_page_->isVisible() && !register_page_->isVisible();
-}
-
-void
-MainWindow::hideOverlay()
-{
- if (modal_)
- modal_->hide();
+ return !http::client()->access_token().empty();
}
inline void
MainWindow::showDialog(QWidget *dialog)
{
- utils::centerWidget(dialog, this);
+ dialog->setWindowFlags(Qt::WindowType::Dialog | Qt::WindowType::WindowCloseButtonHint |
+ Qt::WindowType::WindowTitleHint);
dialog->raise();
dialog->show();
-}
-
-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_);
+ utils::centerWidget(dialog, this);
+ dialog->window()->windowHandle()->setTransientParent(this);
}
diff --git a/src/MainWindow.h b/src/MainWindow.h
index 458eb054..7bc94328 100644
--- a/src/MainWindow.h
+++ b/src/MainWindow.h
@@ -8,26 +8,21 @@
#include <functional>
-#include <QMainWindow>
+#include <QQuickView>
#include <QSharedPointer>
-#include <QStackedWidget>
#include <QSystemTrayIcon>
#include "UserSettingsPage.h"
-#include "ui/OverlayModal.h"
#include "jdenticoninterface.h"
class ChatPage;
class RegisterPage;
-class LoginPage;
class WelcomePage;
-class LoadingIndicator;
-class OverlayModal;
-class SnackBar;
class TrayIcon;
class UserSettings;
+class MxcImageProvider;
namespace mtx {
namespace requests {
@@ -42,17 +37,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();
@@ -61,69 +51,51 @@ public:
openCreateRoomDialog(std::function<void(const mtx::requests::CreateRoom &request)> callback);
void openJoinRoomDialog(std::function<void(const QString &room_id)> callback);
- void hideOverlay();
- void showSolidOverlayModal(QWidget *content, QFlags<Qt::AlignmentFlag> flags = Qt::AlignCenter);
- void
- showTransparentOverlayModal(QWidget *content,
- QFlags<Qt::AlignmentFlag> flags = Qt::AlignTop | Qt::AlignHCenter);
+ MxcImageProvider *imageProvider() { return imgProvider; }
+
+ //! Show the chat page and start communicating with the given access token.
+ void showChatPage();
protected:
- void closeEvent(QCloseEvent *event) override;
+ void closeEvent(QCloseEvent *event);
bool event(QEvent *event) override;
private slots:
//! Handle interaction with the tray icon.
void iconActivated(QSystemTrayIcon::ActivationReason reason);
- //! Show the welcome page in the main window.
- void showWelcomePage();
-
- //! Show the login page in the main window.
- void showLoginPage();
-
- //! Show the register page in the main window.
- void showRegisterPage();
-
- //! Show the chat page and start communicating with the given access token.
- void showChatPage();
-
- void showOverlayProgressBar();
- void removeOverlayProgressBar();
-
virtual void setWindowTitle(int notificationCount);
signals:
- void focusChanged(const bool focused);
void reload();
void secretsChanged();
+ void showNotification(QString msg);
+
+ void switchToChatPage();
+ void switchToWelcomePage();
+ void switchToLoginPage(QString error);
+
private:
void showDialog(QWidget *dialog);
bool hasActiveUser();
void restoreWindowSize();
- //! Check if there is an open dialog.
- bool hasActiveDialogs() const;
//! Check if the current page supports the "minimize to tray" functionality.
bool pageSupportsTray() const;
+ void registerQmlTypes();
+
static MainWindow *instance_;
//! The initial welcome screen.
WelcomePage *welcome_page_;
- //! The login screen.
- 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/RegisterPage.cpp b/src/RegisterPage.cpp
index d089ac96..5b2ebc78 100644
--- a/src/RegisterPage.cpp
+++ b/src/RegisterPage.cpp
@@ -4,312 +4,83 @@
//
// SPDX-License-Identifier: GPL-3.0-or-later
-#include <QInputDialog>
-#include <QLabel>
-#include <QMetaType>
-#include <QPainter>
-#include <QStyleOption>
-#include <QTimer>
-#include <QtMath>
-
+#include <mtx/responses/common.hpp>
#include <mtx/responses/register.hpp>
#include <mtx/responses/well-known.hpp>
#include <mtxclient/http/client.hpp>
#include "Config.h"
#include "Logging.h"
+#include "LoginPage.h"
#include "MainWindow.h"
#include "MatrixClient.h"
#include "RegisterPage.h"
-#include "ui/FlatButton.h"
-#include "ui/RaisedButton.h"
-#include "ui/TextField.h"
#include "ui/UIA.h"
-#include "dialogs/FallbackAuth.h"
-#include "dialogs/ReCaptcha.h"
-
-Q_DECLARE_METATYPE(mtx::user_interactive::Unauthorized)
-Q_DECLARE_METATYPE(mtx::user_interactive::Auth)
-
-RegisterPage::RegisterPage(QWidget *parent)
- : QWidget(parent)
-{
- qRegisterMetaType<mtx::user_interactive::Unauthorized>();
- qRegisterMetaType<mtx::user_interactive::Auth>();
- top_layout_ = new QVBoxLayout();
-
- back_layout_ = new QHBoxLayout();
- back_layout_->setSpacing(0);
- back_layout_->setContentsMargins(5, 5, -1, -1);
-
- back_button_ = new FlatButton(this);
- back_button_->setMinimumSize(QSize(30, 30));
-
- QIcon icon;
- icon.addFile(QStringLiteral(":/icons/icons/ui/angle-arrow-left.svg"));
-
- back_button_->setIcon(icon);
- back_button_->setIconSize(QSize(32, 32));
-
- back_layout_->addWidget(back_button_, 0, Qt::AlignLeft | Qt::AlignVCenter);
- back_layout_->addStretch(1);
-
- QIcon logo;
- logo.addFile(QStringLiteral(":/logos/register.png"));
-
- logo_ = new QLabel(this);
- logo_->setPixmap(logo.pixmap(128));
-
- logo_layout_ = new QHBoxLayout();
- logo_layout_->setContentsMargins(0, 0, 0, 0);
- logo_layout_->addWidget(logo_, 0, Qt::AlignHCenter);
-
- form_wrapper_ = new QHBoxLayout();
- form_widget_ = new QWidget();
- form_widget_->setMinimumSize(QSize(350, 300));
-
- form_layout_ = new QVBoxLayout();
- form_layout_->setSpacing(20);
- form_layout_->setContentsMargins(0, 0, 0, 40);
- form_widget_->setLayout(form_layout_);
-
- form_wrapper_->addStretch(1);
- form_wrapper_->addWidget(form_widget_);
- form_wrapper_->addStretch(1);
-
- username_input_ = new TextField();
- username_input_->setLabel(tr("Username"));
- username_input_->setRegexp(QRegularExpression(QStringLiteral("[a-z0-9._=/-]+")));
- username_input_->setToolTip(tr("The username must not be empty, and must contain only the "
- "characters a-z, 0-9, ., _, =, -, and /."));
-
- password_input_ = new TextField();
- password_input_->setLabel(tr("Password"));
- password_input_->setRegexp(QRegularExpression(QStringLiteral("^.{8,}$")));
- password_input_->setEchoMode(QLineEdit::Password);
- password_input_->setToolTip(tr("Please choose a secure password. The exact requirements "
- "for password strength may depend on your server."));
-
- password_confirmation_ = new TextField();
- password_confirmation_->setLabel(tr("Password confirmation"));
- password_confirmation_->setEchoMode(QLineEdit::Password);
-
- server_input_ = new TextField();
- server_input_->setLabel(tr("Homeserver"));
- server_input_->setRegexp(QRegularExpression(QStringLiteral(".+")));
- server_input_->setToolTip(
- tr("A server that allows registration. Since matrix is decentralized, you need to first "
- "find a server you can register on or host your own."));
-
- error_username_label_ = new QLabel(this);
- error_username_label_->setWordWrap(true);
- error_username_label_->hide();
-
- error_password_label_ = new QLabel(this);
- error_password_label_->setWordWrap(true);
- error_password_label_->hide();
-
- error_password_confirmation_label_ = new QLabel(this);
- error_password_confirmation_label_->setWordWrap(true);
- error_password_confirmation_label_->hide();
-
- error_server_label_ = new QLabel(this);
- error_server_label_->setWordWrap(true);
- error_server_label_->hide();
-
- form_layout_->addWidget(username_input_, Qt::AlignHCenter);
- form_layout_->addWidget(error_username_label_, Qt::AlignHCenter);
- form_layout_->addWidget(password_input_, Qt::AlignHCenter);
- form_layout_->addWidget(error_password_label_, Qt::AlignHCenter);
- form_layout_->addWidget(password_confirmation_, Qt::AlignHCenter);
- form_layout_->addWidget(error_password_confirmation_label_, Qt::AlignHCenter);
- form_layout_->addWidget(server_input_, Qt::AlignHCenter);
- form_layout_->addWidget(error_server_label_, Qt::AlignHCenter);
-
- button_layout_ = new QHBoxLayout();
- button_layout_->setSpacing(0);
- button_layout_->setContentsMargins(0, 0, 0, 0);
-
- error_label_ = new QLabel(this);
- error_label_->setWordWrap(true);
-
- register_button_ = new RaisedButton(tr("REGISTER"), this);
- register_button_->setMinimumSize(350, 65);
- register_button_->setFontSize(conf::btn::fontSize);
- register_button_->setCornerRadius(conf::btn::cornerRadius);
-
- button_layout_->addStretch(1);
- button_layout_->addWidget(register_button_);
- button_layout_->addStretch(1);
-
- top_layout_->addLayout(back_layout_);
- top_layout_->addLayout(logo_layout_);
- top_layout_->addLayout(form_wrapper_);
- top_layout_->addStretch(1);
- top_layout_->addLayout(button_layout_);
- top_layout_->addWidget(error_label_, 0, Qt::AlignHCenter);
- top_layout_->addStretch(1);
- setLayout(top_layout_);
-
- connect(back_button_, SIGNAL(clicked()), this, SLOT(onBackButtonClicked()));
- connect(register_button_, SIGNAL(clicked()), this, SLOT(onRegisterButtonClicked()));
-
- connect(username_input_, SIGNAL(returnPressed()), register_button_, SLOT(click()));
- connect(username_input_, &TextField::editingFinished, this, &RegisterPage::checkUsername);
- connect(password_input_, SIGNAL(returnPressed()), register_button_, SLOT(click()));
- connect(password_input_, &TextField::editingFinished, this, &RegisterPage::checkPassword);
- connect(password_confirmation_, SIGNAL(returnPressed()), register_button_, SLOT(click()));
- connect(password_confirmation_,
- &TextField::editingFinished,
- this,
- &RegisterPage::checkPasswordConfirmation);
- connect(server_input_, SIGNAL(returnPressed()), register_button_, SLOT(click()));
- connect(server_input_, &TextField::editingFinished, this, &RegisterPage::checkServer);
-
- connect(
- this,
- &RegisterPage::serverError,
- this,
- [this](const QString &msg) {
- server_input_->setValid(false);
- showError(error_server_label_, msg);
- },
- Qt::QueuedConnection);
-
- connect(this, &RegisterPage::wellKnownLookup, this, &RegisterPage::doWellKnownLookup);
- connect(this, &RegisterPage::versionsCheck, this, &RegisterPage::doVersionsCheck);
- connect(this, &RegisterPage::registration, this, &RegisterPage::doRegistration);
-}
-
-void
-RegisterPage::onBackButtonClicked()
+RegisterPage::RegisterPage(QObject *parent)
+ : QObject(parent)
{
- emit backButtonClicked();
+ connect(this, &RegisterPage::registerOk, this, [] { MainWindow::instance()->showChatPage(); });
}
void
-RegisterPage::showError(const QString &msg)
+RegisterPage::setError(QString err)
{
- emit errorOccurred();
- auto rect = QFontMetrics(font()).boundingRect(msg);
- int width = rect.width();
- int height = rect.height();
- error_label_->setFixedHeight(qCeil(width / 200.0) * height);
- error_label_->setText(msg);
+ registrationError_ = err;
+ emit errorChanged();
+ registering_ = false;
+ emit registeringChanged();
}
-
void
-RegisterPage::showError(QLabel *label, const QString &msg)
-{
- emit errorOccurred();
- auto rect = QFontMetrics(font()).boundingRect(msg);
- int width = rect.width();
- int height = rect.height();
- label->setFixedHeight((int)qCeil(width / 200.0) * height);
- label->setText(msg);
- label->show();
-}
-
-bool
-RegisterPage::checkOneField(QLabel *label, const TextField *t_field, const QString &msg)
+RegisterPage::setHsError(QString err)
{
- if (t_field->isValid()) {
- label->hide();
- return true;
- } else {
- showError(label, msg);
- return false;
- }
+ hsError_ = err;
+ emit hsErrorChanged();
+ lookingUpHs_ = false;
+ emit lookingUpHsChanged();
}
-bool
-RegisterPage::checkUsername()
+QString
+RegisterPage::initialDeviceName() const
{
- return checkOneField(error_username_label_,
- username_input_,
- tr("The username must not be empty, and must contain only the "
- "characters a-z, 0-9, ., _, =, -, and /."));
-}
-
-bool
-RegisterPage::checkPassword()
-{
- return checkOneField(
- error_password_label_, password_input_, tr("Password is not long enough (min 8 chars)"));
-}
-
-bool
-RegisterPage::checkPasswordConfirmation()
-{
- if (password_input_->text() == password_confirmation_->text()) {
- error_password_confirmation_label_->hide();
- password_confirmation_->setValid(true);
- return true;
- } else {
- showError(error_password_confirmation_label_, tr("Passwords don't match"));
- password_confirmation_->setValid(false);
- return false;
- }
-}
-
-bool
-RegisterPage::checkServer()
-{
- // This doesn't check that the server is reachable,
- // just that the input is not obviously wrong.
- return checkOneField(error_server_label_, server_input_, tr("Invalid server name"));
+ return QString::fromStdString(LoginPage::initialDeviceName_());
}
void
-RegisterPage::onRegisterButtonClicked()
+RegisterPage::setServer(QString server)
{
- if (checkUsername() && checkPassword() && checkPasswordConfirmation() && checkServer()) {
- auto server = server_input_->text().toStdString();
-
- http::client()->set_server(server);
- http::client()->verify_certificates(
- !UserSettings::instance()->disableCertificateValidation());
+ if (server == lastServer)
+ return;
- // This starts a chain of `emit`s which ends up doing the
- // registration. Signals are used rather than normal function
- // calls so that the dialogs used in UIA work correctly.
- //
- // The sequence of events looks something like this:
- //
- // doKnownLookup
- // v
- // doVersionsCheck
- // v
- // doRegistration -> loops the UIAHandler until complete
+ lastServer = server;
- emit wellKnownLookup();
+ http::client()->set_server(server.toStdString());
+ http::client()->verify_certificates(!UserSettings::instance()->disableCertificateValidation());
- emit registering();
- }
-}
+ hsError_.clear();
+ emit hsErrorChanged();
+ supported_ = false;
+ lookingUpHs_ = true;
+ emit lookingUpHsChanged();
-void
-RegisterPage::doWellKnownLookup()
-{
http::client()->well_known(
[this](const mtx::responses::WellKnown &res, mtx::http::RequestErr err) {
if (err) {
if (err->status_code == 404) {
nhlog::net()->info("Autodiscovery: No .well-known.");
// Check that the homeserver can be reached
- emit versionsCheck();
+ versionsCheck();
return;
}
if (!err->parse_error.empty()) {
- emit serverError(tr("Autodiscovery failed. Received malformed response."));
+ setHsError(tr("Autodiscovery failed. Received malformed response."));
nhlog::net()->error("Autodiscovery failed. Received malformed response.");
+ emit hsErrorChanged();
return;
}
- emit serverError(tr("Autodiscovery failed. Unknown error when "
- "requesting .well-known."));
+ setHsError(tr("Autodiscovery failed. Unknown error when requesting .well-known."));
nhlog::net()->error("Autodiscovery failed. Unknown error when "
"requesting .well-known. {} {}",
err->status_code,
@@ -319,98 +90,140 @@ RegisterPage::doWellKnownLookup()
nhlog::net()->info("Autodiscovery: Discovered '" + res.homeserver.base_url + "'");
http::client()->set_server(res.homeserver.base_url);
+ emit hsErrorChanged();
// Check that the homeserver can be reached
- emit versionsCheck();
+ versionsCheck();
});
}
void
-RegisterPage::doVersionsCheck()
+RegisterPage::versionsCheck()
{
// Make a request to /_matrix/client/versions to check the address
// given is a Matrix homeserver.
http::client()->versions([this](const mtx::responses::Versions &, mtx::http::RequestErr err) {
if (err) {
if (err->status_code == 404) {
- emit serverError(tr("The required endpoints were not found. Possibly "
- "not a Matrix server."));
+ setHsError(
+ tr("The required endpoints were not found. Possibly not a Matrix server."));
+ emit hsErrorChanged();
return;
}
if (!err->parse_error.empty()) {
- emit serverError(tr("Received malformed response. Make sure the homeserver "
- "domain is valid."));
+ setHsError(
+ tr("Received malformed response. Make sure the homeserver domain is valid."));
+ emit hsErrorChanged();
return;
}
- emit serverError(tr("An unknown error occured. Make sure the "
- "homeserver domain is valid."));
+ setHsError(tr("An unknown error occured. Make sure the homeserver domain is valid."));
+ emit hsErrorChanged();
return;
}
- // Attempt registration without an `auth` dict
- emit registration();
+ http::client()->registration(
+ [this](const mtx::responses::Register &, mtx::http::RequestErr e) {
+ nhlog::net()->debug("Registration check: {}", e);
+
+ if (!e) {
+ setHsError(tr("Server does not support querying registration flows!"));
+ emit hsErrorChanged();
+ return;
+ }
+ if (e->status_code != 401) {
+ setHsError(tr("Server does not support registration."));
+ emit hsErrorChanged();
+ return;
+ }
+
+ supported_ = true;
+ lookingUpHs_ = false;
+ emit lookingUpHsChanged();
+ });
});
}
void
-RegisterPage::doRegistration()
+RegisterPage::checkUsername(QString name)
+{
+ usernameAvailable_ = usernameUnavailable_ = false;
+ usernameError_.clear();
+ lookingUpUsername_ = true;
+ emit lookingUpUsernameChanged();
+
+ http::client()->register_username_available(
+ name.toStdString(),
+ [this](const mtx::responses::Available &available, mtx::http::RequestErr e) {
+ if (e) {
+ if (e->matrix_error.errcode == mtx::errors::ErrorCode::M_INVALID_USERNAME) {
+ usernameError_ = tr("Invalid username.");
+ } else if (e->matrix_error.errcode == mtx::errors::ErrorCode::M_USER_IN_USE) {
+ usernameError_ = tr("Name already in use.");
+ } else if (e->matrix_error.errcode == mtx::errors::ErrorCode::M_EXCLUSIVE) {
+ usernameError_ = tr("Part of the reserved namespace.");
+ } else {
+ }
+
+ usernameAvailable_ = false;
+ usernameUnavailable_ = true;
+ } else {
+ usernameAvailable_ = available.available;
+ usernameUnavailable_ = !available.available;
+ }
+ lookingUpUsername_ = false;
+ emit lookingUpUsernameChanged();
+ });
+}
+
+void
+RegisterPage::startRegistration(QString username, QString password, QString devicename)
{
// These inputs should still be alright, but check just in case
- if (checkUsername() && checkPassword() && checkPasswordConfirmation()) {
- auto username = username_input_->text().toStdString();
- auto password = password_input_->text().toStdString();
+ if (!username.isEmpty() && !password.isEmpty() && usernameAvailable_ && supported_) {
+ registrationError_.clear();
+ emit errorChanged();
+ registering_ = true;
+ emit registeringChanged();
+
connect(UIA::instance(), &UIA::error, this, [this](QString msg) {
- showError(msg);
+ setError(msg);
disconnect(UIA::instance(), &UIA::error, this, nullptr);
});
http::client()->registration(
- username,
- password,
+ username.toStdString(),
+ password.toStdString(),
::UIA::instance()->genericHandler(QStringLiteral("Registration")),
- registrationCb());
- }
-}
-
-mtx::http::Callback<mtx::responses::Register>
-RegisterPage::registrationCb()
-{
- // Return a function to be used as the callback when an attempt at
- // registration is made.
- return [this](const mtx::responses::Register &res, mtx::http::RequestErr err) {
- if (!err) {
- http::client()->set_user(res.user_id);
- http::client()->set_access_token(res.access_token);
- emit registerOk();
- disconnect(UIA::instance(), &UIA::error, this, nullptr);
- return;
- }
+ [this](const mtx::responses::Register &res, mtx::http::RequestErr err) {
+ registering_ = false;
+ emit registeringChanged();
- // The server requires registration flows.
- if (err->status_code == 401) {
- if (err->matrix_error.unauthorized.flows.empty()) {
- nhlog::net()->warn("failed to retrieve registration flows: "
- "status_code({}), matrix_error({}) ",
- static_cast<int>(err->status_code),
- err->matrix_error.error);
- showError(QString::fromStdString(err->matrix_error.error));
- }
- return;
- }
+ if (!err) {
+ http::client()->set_user(res.user_id);
+ http::client()->set_access_token(res.access_token);
+ emit registerOk();
+ disconnect(UIA::instance(), &UIA::error, this, nullptr);
+ return;
+ }
- nhlog::net()->error("failed to register: status_code ({}), matrix_error({})",
- static_cast<int>(err->status_code),
- err->matrix_error.error);
+ // The server requires registration flows.
+ if (err->status_code == 401 && err->matrix_error.unauthorized.flows.empty()) {
+ nhlog::net()->warn("failed to retrieve registration flows: "
+ "status_code({}), matrix_error({}) ",
+ static_cast<int>(err->status_code),
+ err->matrix_error.error);
+ setError(QString::fromStdString(err->matrix_error.error));
+ disconnect(UIA::instance(), &UIA::error, this, nullptr);
+ return;
+ }
- showError(QString::fromStdString(err->matrix_error.error));
- };
-}
+ nhlog::net()->error("failed to register: status_code ({}), matrix_error({})",
+ static_cast<int>(err->status_code),
+ err->matrix_error.error);
-void
-RegisterPage::paintEvent(QPaintEvent *)
-{
- QStyleOption opt;
- opt.initFrom(this);
- QPainter p(this);
- style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
+ setError(QString::fromStdString(err->matrix_error.error));
+ disconnect(UIA::instance(), &UIA::error, this, nullptr);
+ },
+ devicename.isEmpty() ? LoginPage::initialDeviceName_() : devicename.toStdString());
+ }
}
diff --git a/src/RegisterPage.h b/src/RegisterPage.h
index f76313c8..67e2a22e 100644
--- a/src/RegisterPage.h
+++ b/src/RegisterPage.h
@@ -6,88 +6,69 @@
#pragma once
-#include <QWidget>
-
-#include <memory>
+#include <QObject>
+#include <QString>
#include <mtx/user_interactive.hpp>
#include <mtxclient/http/client.hpp>
-class FlatButton;
-class RaisedButton;
-class TextField;
-class QLabel;
-class QVBoxLayout;
-class QHBoxLayout;
-
-class RegisterPage : public QWidget
+class RegisterPage : public QObject
{
Q_OBJECT
+ Q_PROPERTY(QString error READ error NOTIFY errorChanged)
+ Q_PROPERTY(QString hsError READ hsError NOTIFY hsErrorChanged)
+ Q_PROPERTY(QString usernameError READ usernameError NOTIFY lookingUpUsernameChanged)
+ Q_PROPERTY(bool registering READ registering NOTIFY registeringChanged)
+ Q_PROPERTY(bool supported READ supported NOTIFY lookingUpHsChanged)
+ Q_PROPERTY(bool lookingUpHs READ lookingUpHs NOTIFY lookingUpHsChanged)
+ Q_PROPERTY(bool lookingUpUsername READ lookingUpUsername NOTIFY lookingUpUsernameChanged)
+ Q_PROPERTY(bool usernameAvailable READ usernameAvailable NOTIFY lookingUpUsernameChanged)
+ Q_PROPERTY(bool usernameUnavailable READ usernameUnavailable NOTIFY lookingUpUsernameChanged)
+
public:
- RegisterPage(QWidget *parent = nullptr);
+ RegisterPage(QObject *parent = nullptr);
-protected:
- void paintEvent(QPaintEvent *event) override;
+ Q_INVOKABLE void setServer(QString server);
+ Q_INVOKABLE void checkUsername(QString name);
+ Q_INVOKABLE void startRegistration(QString username, QString password, QString deviceName);
+ Q_INVOKABLE QString initialDeviceName() const;
-signals:
- void backButtonClicked();
- void errorOccurred();
+ bool registering() const { return registering_; }
+ bool supported() const { return supported_; }
+ bool lookingUpHs() const { return lookingUpHs_; }
+ bool lookingUpUsername() const { return lookingUpUsername_; }
+ bool usernameAvailable() const { return usernameAvailable_; }
+ bool usernameUnavailable() const { return usernameUnavailable_; }
- //! Used to trigger the corresponding slot outside of the main thread.
- void serverError(const QString &err);
+ QString error() const { return registrationError_; }
+ QString usernameError() const { return usernameError_; }
+ QString hsError() const { return hsError_; }
- void wellKnownLookup();
- void versionsCheck();
- void registration();
-
- void registering();
- void registerOk();
-
-private slots:
- void onBackButtonClicked();
- void onRegisterButtonClicked();
-
- // function for showing different errors
- void showError(const QString &msg);
- void showError(QLabel *label, const QString &msg);
+signals:
+ void errorChanged();
+ void hsErrorChanged();
- bool checkOneField(QLabel *label, const TextField *t_field, const QString &msg);
- bool checkUsername();
- bool checkPassword();
- bool checkPasswordConfirmation();
- bool checkServer();
+ void registeringChanged();
+ void lookingUpHsChanged();
+ void lookingUpUsernameChanged();
- void doWellKnownLookup();
- void doVersionsCheck();
- void doRegistration();
- mtx::http::Callback<mtx::responses::Register> registrationCb();
+ void registerOk();
private:
- QVBoxLayout *top_layout_;
-
- QHBoxLayout *back_layout_;
- QHBoxLayout *logo_layout_;
- QHBoxLayout *button_layout_;
+ void versionsCheck();
- QLabel *logo_;
- QLabel *error_label_;
- QLabel *error_username_label_;
- QLabel *error_password_label_;
- QLabel *error_password_confirmation_label_;
- QLabel *error_server_label_;
- QLabel *error_registration_token_label_;
+ void setHsError(QString err);
+ void setError(QString err);
- FlatButton *back_button_;
- RaisedButton *register_button_;
+ QString registrationError_, hsError_, usernameError_;
- QWidget *form_widget_;
- QHBoxLayout *form_wrapper_;
- QVBoxLayout *form_layout_;
+ bool registering_;
+ bool supported_;
+ bool lookingUpHs_;
+ bool lookingUpUsername_;
+ bool usernameAvailable_;
+ bool usernameUnavailable_;
- TextField *username_input_;
- TextField *password_input_;
- TextField *password_confirmation_;
- TextField *server_input_;
- TextField *registration_token_input_;
+ QString lastServer;
};
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..a0aa8f84 100644
--- a/src/UserSettingsPage.cpp
+++ b/src/UserSettingsPage.cpp
@@ -5,25 +5,14 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#include <QApplication>
-#include <QComboBox>
#include <QCoreApplication>
#include <QFileDialog>
-#include <QFontComboBox>
-#include <QFormLayout>
+#include <QFontDatabase>
#include <QInputDialog>
-#include <QLabel>
-#include <QLineEdit>
#include <QMessageBox>
-#include <QPainter>
-#include <QPushButton>
-#include <QResizeEvent>
-#include <QScrollArea>
-#include <QScroller>
-#include <QSpinBox>
#include <QStandardPaths>
#include <QString>
#include <QTextStream>
-#include <QtQml>
#include <mtx/secret_storage.hpp>
#include "Cache.h"
@@ -33,8 +22,7 @@
#include "UserSettingsPage.h"
#include "Utils.h"
#include "encryption/Olm.h"
-#include "ui/FlatButton.h"
-#include "ui/ToggleButton.h"
+#include "ui/Theme.h"
#include "voip/CallDevices.h"
#include "config/nheko.h"
@@ -1518,7 +1506,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 +1588,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 +1600,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 +1610,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 +1618,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 +1626,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 +1636,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 +1665,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/UserSettingsPage.h b/src/UserSettingsPage.h
index a44e0030..e9b8763d 100644
--- a/src/UserSettingsPage.h
+++ b/src/UserSettingsPage.h
@@ -7,12 +7,9 @@
#pragma once
#include <QAbstractListModel>
-#include <QFontDatabase>
-#include <QFrame>
#include <QProcessEnvironment>
#include <QSettings>
#include <QSharedPointer>
-#include <QWidget>
#include "JdenticonProvider.h"
#include <optional>
diff --git a/src/Utils.cpp b/src/Utils.cpp
index a9cfde22..0ac37d8e 100644
--- a/src/Utils.cpp
+++ b/src/Utils.cpp
@@ -17,6 +17,7 @@
#include <QStringBuilder>
#include <QTextBoundaryFinder>
#include <QTextDocument>
+#include <QWindow>
#include <QXmlStreamReader>
#include <array>
@@ -770,20 +771,17 @@ utils::luminance(const QColor &col)
}
void
-utils::centerWidget(QWidget *widget, QWidget *parent)
+utils::centerWidget(QWidget *widget, QWindow *parent)
{
- auto findCenter = [childRect = widget->rect()](QRect hostRect) -> QPoint {
- return QPoint(hostRect.center().x() - (childRect.width() * 0.5),
- hostRect.center().y() - (childRect.height() * 0.5));
- };
-
if (parent) {
- widget->move(parent->window()->frameGeometry().topLeft() +
- parent->window()->rect().center() - widget->rect().center());
+ widget->window()->windowHandle()->setTransientParent(parent);
return;
}
- // Deprecated in 5.13: widget->move(findCenter(QApplication::desktop()->screenGeometry()));
+ auto findCenter = [childRect = widget->rect()](QRect hostRect) -> QPoint {
+ return QPoint(hostRect.center().x() - (childRect.width() * 0.5),
+ hostRect.center().y() - (childRect.height() * 0.5));
+ };
widget->move(findCenter(QGuiApplication::primaryScreen()->geometry()));
}
diff --git a/src/Utils.h b/src/Utils.h
index 87ce1c34..0b6034ac 100644
--- a/src/Utils.h
+++ b/src/Utils.h
@@ -290,7 +290,7 @@ luminance(const QColor &col);
//! Center a widget in relation to another widget.
void
-centerWidget(QWidget *widget, QWidget *parent);
+centerWidget(QWidget *widget, QWindow *parent);
void
restoreCombobox(QComboBox *combo, const QString &value);
diff --git a/src/WelcomePage.cpp b/src/WelcomePage.cpp
deleted file mode 100644
index 5d540f4e..00000000
--- a/src/WelcomePage.cpp
+++ /dev/null
@@ -1,88 +0,0 @@
-// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr>
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-// SPDX-FileCopyrightText: 2022 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#include <QLabel>
-#include <QLayout>
-#include <QPainter>
-#include <QStyleOption>
-
-#include "Config.h"
-#include "WelcomePage.h"
-#include "ui/RaisedButton.h"
-#include "ui/TextLabel.h"
-
-WelcomePage::WelcomePage(QWidget *parent)
- : QWidget(parent)
-{
- auto topLayout_ = new QVBoxLayout(this);
- topLayout_->setSpacing(20);
- topLayout_->setAlignment(Qt::AlignCenter);
-
- QFont headingFont;
- headingFont.setPointSizeF(headingFont.pointSizeF() * 2);
- QFont subTitleFont;
- subTitleFont.setPointSizeF(subTitleFont.pointSizeF() * 1.5);
-
- QIcon icon{QIcon::fromTheme("nheko", QIcon{":/logos/splash.png"})};
-
- auto logo_ = new QLabel(this);
- logo_->setPixmap(icon.pixmap(256));
- logo_->setAlignment(Qt::AlignCenter);
-
- QString heading(tr("Welcome to nheko! The desktop client for the Matrix protocol."));
- QString main(tr("Enjoy your stay!"));
-
- auto intoTxt_ = new TextLabel(heading, this);
- intoTxt_->setFont(headingFont);
- intoTxt_->setAlignment(Qt::AlignCenter);
-
- auto subTitle = new TextLabel(main, this);
- subTitle->setFont(subTitleFont);
- subTitle->setAlignment(Qt::AlignCenter);
-
- topLayout_->addStretch(1);
- topLayout_->addWidget(logo_);
- topLayout_->addWidget(intoTxt_);
- topLayout_->addWidget(subTitle);
-
- auto btnLayout_ = new QHBoxLayout();
- btnLayout_->setSpacing(20);
- btnLayout_->setContentsMargins(0, 20, 0, 20);
-
- const int fontHeight = QFontMetrics{subTitleFont}.height();
- const int buttonHeight = fontHeight * 2.5;
- const int buttonWidth = fontHeight * 8;
-
- auto registerBtn = new RaisedButton(tr("REGISTER"), this);
- registerBtn->setMinimumSize(buttonWidth, buttonHeight);
- registerBtn->setFontSize(subTitleFont.pointSizeF());
- registerBtn->setCornerRadius(conf::btn::cornerRadius);
-
- auto loginBtn = new RaisedButton(tr("LOGIN"), this);
- loginBtn->setMinimumSize(buttonWidth, buttonHeight);
- loginBtn->setFontSize(subTitleFont.pointSizeF());
- loginBtn->setCornerRadius(conf::btn::cornerRadius);
-
- btnLayout_->addStretch(1);
- btnLayout_->addWidget(registerBtn);
- btnLayout_->addWidget(loginBtn);
- btnLayout_->addStretch(1);
-
- topLayout_->addLayout(btnLayout_);
- topLayout_->addStretch(1);
-
- connect(registerBtn, &QPushButton::clicked, this, &WelcomePage::userRegister);
- connect(loginBtn, &QPushButton::clicked, this, &WelcomePage::userLogin);
-}
-
-void
-WelcomePage::paintEvent(QPaintEvent *)
-{
- QStyleOption opt;
- opt.initFrom(this);
- QPainter p(this);
- style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
-}
diff --git a/src/WelcomePage.h b/src/WelcomePage.h
deleted file mode 100644
index 9d5da8ba..00000000
--- a/src/WelcomePage.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-// SPDX-FileCopyrightText: 2022 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#pragma once
-
-#include <QWidget>
-
-class WelcomePage : public QWidget
-{
- Q_OBJECT
-
-public:
- explicit WelcomePage(QWidget *parent = nullptr);
-
-protected:
- void paintEvent(QPaintEvent *) override;
-
-signals:
- // Notify that the user wants to login in.
- void userLogin();
-
- // Notify that the user wants to register.
- void userRegister();
-};
diff --git a/src/main.cpp b/src/main.cpp
index 2ae631cf..24fc8415 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,9 +298,10 @@ 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();
@@ -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..6b380f79 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() != QGuiApplication::focusWindow())
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..0abd102b 100644
--- a/src/timeline/TimelineViewManager.cpp
+++ b/src/timeline/TimelineViewManager.cpp
@@ -5,10 +5,10 @@
#include "TimelineViewManager.h"
+#include <QApplication>
#include <QDropEvent>
#include <QFileDialog>
#include <QMetaType>
-#include <QPalette>
#include <QQmlContext>
#include <QQmlEngine>
#include <QStandardPaths>
@@ -45,10 +45,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 +98,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 +109,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 +133,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_,
@@ -336,6 +158,16 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par
isInitialSync_ = true;
emit initialSyncChanged(true);
});
+ connect(qobject_cast<QApplication *>(QApplication::instance()),
+ &QApplication::focusWindowChanged,
+ this,
+ &TimelineViewManager::focusChanged);
+}
+
+bool
+TimelineViewManager::isWindowFocused() const
+{
+ return MainWindow::instance() == QApplication::focusWindow();
}
void
@@ -379,7 +211,8 @@ void
TimelineViewManager::setVideoCallItem()
{
WebRTCSession::instance().setVideoItem(
- view->rootObject()->findChild<QQuickItem *>(QStringLiteral("videoCallItem")));
+ MainWindow::instance()->rootObject()->findChild<QQuickItem *>(
+ QStringLiteral("videoCallItem")));
}
void
@@ -401,7 +234,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 +272,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 +424,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..13ab5dbb 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>
@@ -43,23 +41,19 @@ class TimelineViewManager : public QObject
Q_PROPERTY(
bool isInitialSync MEMBER isInitialSync_ READ isInitialSync NOTIFY initialSyncChanged)
- Q_PROPERTY(
- bool isWindowFocused MEMBER isWindowFocused_ READ isWindowFocused NOTIFY focusChanged)
+ Q_PROPERTY(bool isWindowFocused READ isWindowFocused NOTIFY focusChanged)
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(); }
Q_INVOKABLE bool isInitialSync() const { return isInitialSync_; }
- bool isWindowFocused() const { return isWindowFocused_; }
+ bool isWindowFocused() const;
Q_INVOKABLE void openImageOverlay(TimelineModel *room, QString mxcUrl, QString eventId);
Q_INVOKABLE void openImagePackSettings(QString roomid);
Q_INVOKABLE void saveMedia(QString mxcUrl);
@@ -98,14 +92,8 @@ public slots:
void updateReadReceipts(const QString &room_id, const std::vector<QString> &event_ids);
void receivedSessionKey(const std::string &room_id, const std::string &session_id);
void initializeRoomlist();
- void chatFocusChanged(bool focused)
- {
- isWindowFocused_ = focused;
- emit focusChanged();
- }
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,26 +110,12 @@ 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;
+ bool isInitialSync_ = true;
RoomlistModel *rooms_ = nullptr;
CommunitiesModel *communities_ = nullptr;
// don't move this above the rooms_
- CallManager *callManager_ = nullptr;
VerificationManager *verificationManager_ = nullptr;
PresenceEmitter *presenceEmitter = nullptr;
diff --git a/src/ui/DropShadow.cpp b/src/ui/DropShadow.cpp
deleted file mode 100644
index 039d6558..00000000
--- a/src/ui/DropShadow.cpp
+++ /dev/null
@@ -1,108 +0,0 @@
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-// SPDX-FileCopyrightText: 2022 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#include "DropShadow.h"
-
-#include <QLinearGradient>
-#include <QPainter>
-
-void
-DropShadow::draw(QPainter &painter,
- qint16 margin,
- qreal radius,
- QColor start,
- QColor end,
- qreal startPosition,
- qreal endPosition0,
- qreal endPosition1,
- qreal width,
- qreal height)
-{
- painter.setPen(Qt::NoPen);
-
- QLinearGradient gradient;
- gradient.setColorAt(startPosition, start);
- gradient.setColorAt(endPosition0, end);
-
- // Right
- QPointF right0(width - margin, height / 2);
- QPointF right1(width, height / 2);
- gradient.setStart(right0);
- gradient.setFinalStop(right1);
- painter.setBrush(QBrush(gradient));
- // Deprecated in 5.13: painter.drawRoundRect(
- // QRectF(QPointF(width - margin * radius, margin), QPointF(width, height -
- // margin)), 0.0, 0.0);
- painter.drawRoundedRect(
- QRectF(QPointF(width - margin * radius, margin), QPointF(width, height - margin)), 0.0, 0.0);
-
- // Left
- QPointF left0(margin, height / 2);
- QPointF left1(0, height / 2);
- gradient.setStart(left0);
- gradient.setFinalStop(left1);
- painter.setBrush(QBrush(gradient));
- painter.drawRoundedRect(
- QRectF(QPointF(margin * radius, margin), QPointF(0, height - margin)), 0.0, 0.0);
-
- // Top
- QPointF top0(width / 2, margin);
- QPointF top1(width / 2, 0);
- gradient.setStart(top0);
- gradient.setFinalStop(top1);
- painter.setBrush(QBrush(gradient));
- painter.drawRoundedRect(QRectF(QPointF(width - margin, 0), QPointF(margin, margin)), 0.0, 0.0);
-
- // Bottom
- QPointF bottom0(width / 2, height - margin);
- QPointF bottom1(width / 2, height);
- gradient.setStart(bottom0);
- gradient.setFinalStop(bottom1);
- painter.setBrush(QBrush(gradient));
- painter.drawRoundedRect(
- QRectF(QPointF(margin, height - margin), QPointF(width - margin, height)), 0.0, 0.0);
-
- // BottomRight
- QPointF bottomright0(width - margin, height - margin);
- QPointF bottomright1(width, height);
- gradient.setStart(bottomright0);
- gradient.setFinalStop(bottomright1);
- gradient.setColorAt(endPosition1, end);
- painter.setBrush(QBrush(gradient));
- painter.drawRoundedRect(QRectF(bottomright0, bottomright1), 0.0, 0.0);
-
- // BottomLeft
- QPointF bottomleft0(margin, height - margin);
- QPointF bottomleft1(0, height);
- gradient.setStart(bottomleft0);
- gradient.setFinalStop(bottomleft1);
- gradient.setColorAt(endPosition1, end);
- painter.setBrush(QBrush(gradient));
- painter.drawRoundedRect(QRectF(bottomleft0, bottomleft1), 0.0, 0.0);
-
- // TopLeft
- QPointF topleft0(margin, margin);
- QPointF topleft1(0, 0);
- gradient.setStart(topleft0);
- gradient.setFinalStop(topleft1);
- gradient.setColorAt(endPosition1, end);
- painter.setBrush(QBrush(gradient));
- painter.drawRoundedRect(QRectF(topleft0, topleft1), 0.0, 0.0);
-
- // TopRight
- QPointF topright0(width - margin, margin);
- QPointF topright1(width, 0);
- gradient.setStart(topright0);
- gradient.setFinalStop(topright1);
- gradient.setColorAt(endPosition1, end);
- painter.setBrush(QBrush(gradient));
- painter.drawRoundedRect(QRectF(topright0, topright1), 0.0, 0.0);
-
- // Widget
- painter.setBrush(QBrush(QColor(0xff, 0xff, 0xff)));
- painter.setRenderHint(QPainter::Antialiasing);
- painter.drawRoundedRect(
- QRectF(QPointF(margin, margin), QPointF(width - margin, height - margin)), radius, radius);
-}
diff --git a/src/ui/DropShadow.h b/src/ui/DropShadow.h
deleted file mode 100644
index 1810a1fe..00000000
--- a/src/ui/DropShadow.h
+++ /dev/null
@@ -1,25 +0,0 @@
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-// SPDX-FileCopyrightText: 2022 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#pragma once
-
-#include <QColor>
-
-class QPainter;
-
-class DropShadow
-{
-public:
- static void draw(QPainter &painter,
- qint16 margin,
- qreal radius,
- QColor start,
- QColor end,
- qreal startPosition,
- qreal endPosition0,
- qreal endPosition1,
- qreal width,
- qreal height);
-};
diff --git a/src/ui/FlatButton.cpp b/src/ui/FlatButton.cpp
deleted file mode 100644
index da322378..00000000
--- a/src/ui/FlatButton.cpp
+++ /dev/null
@@ -1,730 +0,0 @@
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-// SPDX-FileCopyrightText: 2022 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#include <QEventTransition>
-#include <QFontDatabase>
-#include <QIcon>
-#include <QMouseEvent>
-#include <QPaintEvent>
-#include <QPainter>
-#include <QPainterPath>
-#include <QResizeEvent>
-#include <QSignalTransition>
-
-#include "FlatButton.h"
-#include "Ripple.h"
-#include "RippleOverlay.h"
-#include "ThemeManager.h"
-
-// The ampersand is automatically set in QPushButton or QCheckbx
-// by KDEPlatformTheme plugin in Qt5.
-// [https://bugs.kde.org/show_bug.cgi?id=337491]
-//
-// A workaroud is to add
-//
-// [Development]
-// AutoCheckAccelerators=false
-//
-// to ~/.config/kdeglobals
-static QString
-removeKDEAccelerators(QString text)
-{
- return text.remove(QChar('&'));
-}
-
-void
-FlatButton::init()
-{
- ripple_overlay_ = new RippleOverlay(this);
- state_machine_ = new FlatButtonStateMachine(this);
- role_ = ui::Role::Default;
- ripple_style_ = ui::RippleStyle::PositionedRipple;
- icon_placement_ = ui::ButtonIconPlacement::LeftIcon;
- overlay_style_ = ui::OverlayStyle::GrayOverlay;
- bg_mode_ = Qt::TransparentMode;
- fixed_ripple_radius_ = 64;
- corner_radius_ = 3;
- base_opacity_ = 0.13;
- font_size_ = 10; // 10.5;
- use_fixed_ripple_radius_ = false;
-
- setStyle(&ThemeManager::instance());
- setAttribute(Qt::WA_Hover);
- setMouseTracking(true);
- setCursor(QCursor(Qt::PointingHandCursor));
-
- QPainterPath path;
- path.addRoundedRect(rect(), corner_radius_, corner_radius_);
-
- ripple_overlay_->setClipPath(path);
- ripple_overlay_->setClipping(true);
-
- state_machine_->setupProperties();
- state_machine_->startAnimations();
-}
-
-FlatButton::FlatButton(QWidget *parent, ui::ButtonPreset preset)
- : QPushButton(parent)
-{
- init();
- applyPreset(preset);
-}
-
-FlatButton::FlatButton(const QString &text, QWidget *parent, ui::ButtonPreset preset)
- : QPushButton(text, parent)
-{
- init();
- applyPreset(preset);
-}
-
-FlatButton::FlatButton(const QString &text, ui::Role role, QWidget *parent, ui::ButtonPreset preset)
- : QPushButton(text, parent)
-{
- init();
- applyPreset(preset);
- setRole(role);
-}
-
-void
-FlatButton::applyPreset(ui::ButtonPreset preset)
-{
- switch (preset) {
- case ui::ButtonPreset::FlatPreset:
- setOverlayStyle(ui::OverlayStyle::NoOverlay);
- break;
- case ui::ButtonPreset::CheckablePreset:
- setOverlayStyle(ui::OverlayStyle::NoOverlay);
- setCheckable(true);
- break;
- default:
- break;
- }
-}
-
-void
-FlatButton::setRole(ui::Role role)
-{
- role_ = role;
- state_machine_->setupProperties();
-}
-
-ui::Role
-FlatButton::role() const
-{
- return role_;
-}
-
-void
-FlatButton::setForegroundColor(const QColor &color)
-{
- foreground_color_ = color;
- emit foregroundColorChanged();
-}
-
-QColor
-FlatButton::foregroundColor() const
-{
- if (!foreground_color_.isValid()) {
- if (bg_mode_ == Qt::OpaqueMode) {
- return ThemeManager::instance().themeColor(QStringLiteral("BrightWhite"));
- }
-
- switch (role_) {
- case ui::Role::Primary:
- return ThemeManager::instance().themeColor(QStringLiteral("Blue"));
- case ui::Role::Secondary:
- return ThemeManager::instance().themeColor(QStringLiteral("Gray"));
- case ui::Role::Default:
- default:
- return ThemeManager::instance().themeColor(QStringLiteral("Black"));
- }
- }
-
- return foreground_color_;
-}
-
-void
-FlatButton::setBackgroundColor(const QColor &color)
-{
- background_color_ = color;
- emit backgroundColorChanged();
-}
-
-QColor
-FlatButton::backgroundColor() const
-{
- if (!background_color_.isValid()) {
- switch (role_) {
- case ui::Role::Primary:
- return ThemeManager::instance().themeColor(QStringLiteral("Blue"));
- case ui::Role::Secondary:
- return ThemeManager::instance().themeColor(QStringLiteral("Gray"));
- case ui::Role::Default:
- default:
- return ThemeManager::instance().themeColor(QStringLiteral("Black"));
- }
- }
-
- return background_color_;
-}
-
-void
-FlatButton::setOverlayColor(const QColor &color)
-{
- overlay_color_ = color;
- setOverlayStyle(ui::OverlayStyle::TintedOverlay);
- emit overlayColorChanged();
-}
-
-QColor
-FlatButton::overlayColor() const
-{
- if (!overlay_color_.isValid()) {
- return foregroundColor();
- }
-
- return overlay_color_;
-}
-
-void
-FlatButton::setDisabledForegroundColor(const QColor &color)
-{
- disabled_color_ = color;
- emit disabledForegroundColorChanged();
-}
-
-QColor
-FlatButton::disabledForegroundColor() const
-{
- if (!disabled_color_.isValid()) {
- return ThemeManager::instance().themeColor(QStringLiteral("FadedWhite"));
- }
-
- return disabled_color_;
-}
-
-void
-FlatButton::setDisabledBackgroundColor(const QColor &color)
-{
- disabled_background_color_ = color;
- emit disabledBackgroundColorChanged();
-}
-
-QColor
-FlatButton::disabledBackgroundColor() const
-{
- if (!disabled_background_color_.isValid()) {
- return ThemeManager::instance().themeColor(QStringLiteral("FadedWhite"));
- }
-
- return disabled_background_color_;
-}
-
-void
-FlatButton::setFontSize(qreal size)
-{
- font_size_ = size;
-
- QFont f(font());
- f.setPointSizeF(size);
- setFont(f);
-
- emit fontSizeChanged();
-
- update();
-}
-
-qreal
-FlatButton::fontSize() const
-{
- return font_size_;
-}
-
-void
-FlatButton::setOverlayStyle(ui::OverlayStyle style)
-{
- overlay_style_ = style;
- update();
-}
-
-ui::OverlayStyle
-FlatButton::overlayStyle() const
-{
- return overlay_style_;
-}
-
-void
-FlatButton::setRippleStyle(ui::RippleStyle style)
-{
- ripple_style_ = style;
-}
-
-ui::RippleStyle
-FlatButton::rippleStyle() const
-{
- return ripple_style_;
-}
-
-void
-FlatButton::setIconPlacement(ui::ButtonIconPlacement placement)
-{
- icon_placement_ = placement;
- update();
-}
-
-ui::ButtonIconPlacement
-FlatButton::iconPlacement() const
-{
- return icon_placement_;
-}
-
-void
-FlatButton::setCornerRadius(qreal radius)
-{
- corner_radius_ = radius;
- updateClipPath();
- update();
-}
-
-qreal
-FlatButton::cornerRadius() const
-{
- return corner_radius_;
-}
-
-void
-FlatButton::setBackgroundMode(Qt::BGMode mode)
-{
- bg_mode_ = mode;
- state_machine_->setupProperties();
-}
-
-Qt::BGMode
-FlatButton::backgroundMode() const
-{
- return bg_mode_;
-}
-
-void
-FlatButton::setBaseOpacity(qreal opacity)
-{
- base_opacity_ = opacity;
- state_machine_->setupProperties();
-}
-
-qreal
-FlatButton::baseOpacity() const
-{
- return base_opacity_;
-}
-
-void
-FlatButton::setCheckable(bool value)
-{
- state_machine_->updateCheckedStatus();
- state_machine_->setCheckedOverlayProgress(0);
-
- QPushButton::setCheckable(value);
-}
-
-void
-FlatButton::setHasFixedRippleRadius(bool value)
-{
- use_fixed_ripple_radius_ = value;
-}
-
-bool
-FlatButton::hasFixedRippleRadius() const
-{
- return use_fixed_ripple_radius_;
-}
-
-void
-FlatButton::setFixedRippleRadius(qreal radius)
-{
- fixed_ripple_radius_ = radius;
- setHasFixedRippleRadius(true);
-}
-
-QSize
-FlatButton::sizeHint() const
-{
- ensurePolished();
-
- QSize label(fontMetrics().size(Qt::TextSingleLine, removeKDEAccelerators(text())));
-
- int w = 20 + label.width();
- int h = label.height();
-
- if (!icon().isNull()) {
- w += iconSize().width() + FlatButton::IconPadding;
- h = qMax(h, iconSize().height());
- }
-
- return QSize(w, 20 + h);
-}
-
-void
-FlatButton::checkStateSet()
-{
- state_machine_->updateCheckedStatus();
- QPushButton::checkStateSet();
-}
-
-void
-FlatButton::mousePressEvent(QMouseEvent *event)
-{
- if (ui::RippleStyle::NoRipple != ripple_style_) {
- QPoint pos;
- qreal radiusEndValue;
-
- if (ui::RippleStyle::CenteredRipple == ripple_style_) {
- pos = rect().center();
- } else {
- pos = event->pos();
- }
-
- if (use_fixed_ripple_radius_) {
- radiusEndValue = fixed_ripple_radius_;
- } else {
- radiusEndValue = static_cast<qreal>(width()) / 2;
- }
-
- Ripple *ripple = new Ripple(pos);
-
- ripple->setRadiusEndValue(radiusEndValue);
- ripple->setOpacityStartValue(0.35);
- ripple->setColor(foregroundColor());
- ripple->radiusAnimation()->setDuration(250);
- ripple->opacityAnimation()->setDuration(250);
-
- ripple_overlay_->addRipple(ripple);
- }
-
- QPushButton::mousePressEvent(event);
-}
-
-void
-FlatButton::mouseReleaseEvent(QMouseEvent *event)
-{
- QPushButton::mouseReleaseEvent(event);
- state_machine_->updateCheckedStatus();
-}
-
-void
-FlatButton::resizeEvent(QResizeEvent *event)
-{
- QPushButton::resizeEvent(event);
- updateClipPath();
-}
-
-void
-FlatButton::paintEvent(QPaintEvent *event)
-{
- Q_UNUSED(event)
-
- QPainter painter(this);
- painter.setRenderHint(QPainter::Antialiasing);
-
- const qreal cr = corner_radius_;
-
- if (cr > 0) {
- QPainterPath path;
- path.addRoundedRect(rect(), cr, cr);
-
- painter.setClipPath(path);
- painter.setClipping(true);
- }
-
- paintBackground(&painter);
-
- painter.setOpacity(1);
- painter.setClipping(false);
-
- paintForeground(&painter);
-}
-
-void
-FlatButton::paintBackground(QPainter *painter)
-{
- const qreal overlayOpacity = state_machine_->overlayOpacity();
- const qreal checkedProgress = state_machine_->checkedOverlayProgress();
-
- if (Qt::OpaqueMode == bg_mode_) {
- QBrush brush;
- brush.setStyle(Qt::SolidPattern);
-
- if (isEnabled()) {
- brush.setColor(backgroundColor());
- } else {
- brush.setColor(disabledBackgroundColor());
- }
-
- painter->setOpacity(1);
- painter->setBrush(brush);
- painter->setPen(Qt::NoPen);
- painter->drawRect(rect());
- }
-
- QBrush brush;
- brush.setStyle(Qt::SolidPattern);
- painter->setPen(Qt::NoPen);
-
- if (!isEnabled()) {
- return;
- }
-
- if ((ui::OverlayStyle::NoOverlay != overlay_style_) && (overlayOpacity > 0)) {
- if (ui::OverlayStyle::TintedOverlay == overlay_style_) {
- brush.setColor(overlayColor());
- } else {
- brush.setColor(Qt::gray);
- }
-
- painter->setOpacity(overlayOpacity);
- painter->setBrush(brush);
- painter->drawRect(rect());
- }
-
- if (isCheckable() && checkedProgress > 0) {
- const qreal q = Qt::TransparentMode == bg_mode_ ? 0.45 : 0.7;
- brush.setColor(foregroundColor());
- painter->setOpacity(q * checkedProgress);
- painter->setBrush(brush);
- QRect r(rect());
- r.setHeight(static_cast<qreal>(r.height()) * checkedProgress);
- painter->drawRect(r);
- }
-}
-
-#define COLOR_INTERPOLATE(CH) (1 - progress) * source.CH() + progress *dest.CH()
-
-void
-FlatButton::paintForeground(QPainter *painter)
-{
- if (isEnabled()) {
- painter->setPen(foregroundColor());
- const qreal progress = state_machine_->checkedOverlayProgress();
-
- if (isCheckable() && progress > 0) {
- QColor source = foregroundColor();
- QColor dest = Qt::TransparentMode == bg_mode_ ? Qt::white : backgroundColor();
- if (qFuzzyCompare(1, progress)) {
- painter->setPen(dest);
- } else {
- painter->setPen(QColor(COLOR_INTERPOLATE(red),
- COLOR_INTERPOLATE(green),
- COLOR_INTERPOLATE(blue),
- COLOR_INTERPOLATE(alpha)));
- }
- }
- } else {
- painter->setPen(disabledForegroundColor());
- }
-
- if (icon().isNull()) {
- painter->drawText(rect(), Qt::AlignCenter, removeKDEAccelerators(text()));
- return;
- }
-
- QSize textSize(fontMetrics().size(Qt::TextSingleLine, removeKDEAccelerators(text())));
- QSize base(size() - textSize);
-
- const int iw = iconSize().width() + IconPadding;
- QPoint pos((base.width() - iw) / 2, 0);
-
- QRect textGeometry(pos + QPoint(0, base.height() / 2), textSize);
- QRect iconGeometry(pos + QPoint(0, (height() - iconSize().height()) / 2), iconSize());
-
- /* if (ui::LeftIcon == icon_placement_) { */
- /* textGeometry.translate(iw, 0); */
- /* } else { */
- /* iconGeometry.translate(textSize.width() + IconPadding, 0); */
- /* } */
-
- painter->drawText(textGeometry, Qt::AlignCenter, removeKDEAccelerators(text()));
-
- QPixmap pixmap = icon().pixmap(iconSize());
- QPainter icon(&pixmap);
- icon.setCompositionMode(QPainter::CompositionMode_SourceIn);
- icon.fillRect(pixmap.rect(), painter->pen().color());
- painter->drawPixmap(iconGeometry, pixmap);
-}
-
-void
-FlatButton::updateClipPath()
-{
- const qreal radius = corner_radius_;
-
- QPainterPath path;
- path.addRoundedRect(rect(), radius, radius);
- ripple_overlay_->setClipPath(path);
-}
-
-FlatButtonStateMachine::FlatButtonStateMachine(FlatButton *parent)
- : QStateMachine(parent)
- , button_(parent)
- , top_level_state_(new QState(QState::ParallelStates))
- , config_state_(new QState(top_level_state_))
- , checkable_state_(new QState(top_level_state_))
- , checked_state_(new QState(checkable_state_))
- , unchecked_state_(new QState(checkable_state_))
- , neutral_state_(new QState(config_state_))
- , neutral_focused_state_(new QState(config_state_))
- , hovered_state_(new QState(config_state_))
- , hovered_focused_state_(new QState(config_state_))
- , pressed_state_(new QState(config_state_))
- , overlay_opacity_(0)
- , checked_overlay_progress_(parent->isChecked() ? 1 : 0)
- , was_checked_(false)
-{
- Q_ASSERT(parent);
-
- parent->installEventFilter(this);
-
- config_state_->setInitialState(neutral_state_);
- addState(top_level_state_);
- setInitialState(top_level_state_);
-
- checkable_state_->setInitialState(parent->isChecked() ? checked_state_ : unchecked_state_);
- QSignalTransition *transition;
- QPropertyAnimation *animation;
-
- transition = new QSignalTransition(this, SIGNAL(buttonChecked()));
- transition->setTargetState(checked_state_);
- unchecked_state_->addTransition(transition);
-
- animation = new QPropertyAnimation(this, "checkedOverlayProgress", this);
- animation->setDuration(200);
- transition->addAnimation(animation);
-
- transition = new QSignalTransition(this, SIGNAL(buttonUnchecked()));
- transition->setTargetState(unchecked_state_);
- checked_state_->addTransition(transition);
-
- animation = new QPropertyAnimation(this, "checkedOverlayProgress", this);
- animation->setDuration(200);
- transition->addAnimation(animation);
-
- addTransition(button_, QEvent::FocusIn, neutral_state_, neutral_focused_state_);
- addTransition(button_, QEvent::FocusOut, neutral_focused_state_, neutral_state_);
- addTransition(button_, QEvent::Enter, neutral_state_, hovered_state_);
- addTransition(button_, QEvent::Leave, hovered_state_, neutral_state_);
- addTransition(button_, QEvent::Enter, neutral_focused_state_, hovered_focused_state_);
- addTransition(button_, QEvent::Leave, hovered_focused_state_, neutral_focused_state_);
- addTransition(button_, QEvent::FocusIn, hovered_state_, hovered_focused_state_);
- addTransition(button_, QEvent::FocusOut, hovered_focused_state_, hovered_state_);
- addTransition(this, SIGNAL(buttonPressed()), hovered_state_, pressed_state_);
- addTransition(button_, QEvent::Leave, pressed_state_, neutral_focused_state_);
- addTransition(button_, QEvent::FocusOut, pressed_state_, hovered_state_);
-}
-
-void
-FlatButtonStateMachine::setOverlayOpacity(qreal opacity)
-{
- overlay_opacity_ = opacity;
- emit overlayOpacityChanged();
- button_->update();
-}
-
-void
-FlatButtonStateMachine::setCheckedOverlayProgress(qreal opacity)
-{
- checked_overlay_progress_ = opacity;
- emit checkedOverlayProgressChanged();
- button_->update();
-}
-
-void
-FlatButtonStateMachine::startAnimations()
-{
- start();
-}
-
-void
-FlatButtonStateMachine::setupProperties()
-{
- QColor overlayColor;
-
- if (Qt::TransparentMode == button_->backgroundMode()) {
- overlayColor = button_->backgroundColor();
- } else {
- overlayColor = button_->foregroundColor();
- }
-
- const qreal baseOpacity = button_->baseOpacity();
-
- neutral_state_->assignProperty(this, "overlayOpacity", 0);
- neutral_focused_state_->assignProperty(this, "overlayOpacity", 0);
- hovered_state_->assignProperty(this, "overlayOpacity", baseOpacity);
- hovered_focused_state_->assignProperty(this, "overlayOpacity", baseOpacity);
- pressed_state_->assignProperty(this, "overlayOpacity", baseOpacity);
- checked_state_->assignProperty(this, "checkedOverlayProgress", 1);
- unchecked_state_->assignProperty(this, "checkedOverlayProgress", 0);
-
- button_->update();
-}
-
-void
-FlatButtonStateMachine::updateCheckedStatus()
-{
- const bool checked = button_->isChecked();
- if (was_checked_ != checked) {
- was_checked_ = checked;
- if (checked) {
- emit buttonChecked();
- } else {
- emit buttonUnchecked();
- }
- }
-}
-
-bool
-FlatButtonStateMachine::eventFilter(QObject *watched, QEvent *event)
-{
- if (QEvent::FocusIn == event->type()) {
- QFocusEvent *focusEvent = static_cast<QFocusEvent *>(event);
- if (focusEvent && Qt::MouseFocusReason == focusEvent->reason()) {
- emit buttonPressed();
- return true;
- }
- }
-
- return QStateMachine::eventFilter(watched, event);
-}
-
-void
-FlatButtonStateMachine::addTransition(QObject *object,
- const char *signal,
- QState *fromState,
- QState *toState)
-{
- addTransition(new QSignalTransition(object, signal), fromState, toState);
-}
-
-void
-FlatButtonStateMachine::addTransition(QObject *object,
- QEvent::Type eventType,
- QState *fromState,
- QState *toState)
-{
- addTransition(new QEventTransition(object, eventType), fromState, toState);
-}
-
-void
-FlatButtonStateMachine::addTransition(QAbstractTransition *transition,
- QState *fromState,
- QState *toState)
-{
- transition->setTargetState(toState);
-
- QPropertyAnimation *animation;
-
- animation = new QPropertyAnimation(this, "overlayOpacity", this);
- animation->setDuration(150);
- transition->addAnimation(animation);
-
- fromState->addTransition(transition);
-}
diff --git a/src/ui/FlatButton.h b/src/ui/FlatButton.h
deleted file mode 100644
index e6215024..00000000
--- a/src/ui/FlatButton.h
+++ /dev/null
@@ -1,198 +0,0 @@
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-// SPDX-FileCopyrightText: 2022 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#pragma once
-
-#include <QPushButton>
-#include <QStateMachine>
-
-#include "Theme.h"
-
-class RippleOverlay;
-class FlatButton;
-
-class FlatButtonStateMachine : public QStateMachine
-{
- Q_OBJECT
-
- Q_PROPERTY(
- qreal overlayOpacity WRITE setOverlayOpacity READ overlayOpacity NOTIFY overlayOpacityChanged)
- Q_PROPERTY(qreal checkedOverlayProgress WRITE setCheckedOverlayProgress READ
- checkedOverlayProgress NOTIFY checkedOverlayProgressChanged)
-
-public:
- explicit FlatButtonStateMachine(FlatButton *parent);
-
- void setOverlayOpacity(qreal opacity);
- void setCheckedOverlayProgress(qreal opacity);
-
- inline qreal overlayOpacity() const;
- inline qreal checkedOverlayProgress() const;
-
- void startAnimations();
- void setupProperties();
- void updateCheckedStatus();
-
-signals:
- void buttonPressed();
- void buttonChecked();
- void buttonUnchecked();
-
- void overlayOpacityChanged();
- void checkedOverlayProgressChanged();
-
-protected:
- bool eventFilter(QObject *watched, QEvent *event) override;
-
-private:
- void addTransition(QObject *object, const char *signal, QState *fromState, QState *toState);
- void addTransition(QObject *object, QEvent::Type eventType, QState *fromState, QState *toState);
- void addTransition(QAbstractTransition *transition, QState *fromState, QState *toState);
-
- FlatButton *const button_;
-
- QState *const top_level_state_;
- QState *const config_state_;
- QState *const checkable_state_;
- QState *const checked_state_;
- QState *const unchecked_state_;
- QState *const neutral_state_;
- QState *const neutral_focused_state_;
- QState *const hovered_state_;
- QState *const hovered_focused_state_;
- QState *const pressed_state_;
-
- qreal overlay_opacity_;
- qreal checked_overlay_progress_;
-
- bool was_checked_;
-};
-
-inline qreal
-FlatButtonStateMachine::overlayOpacity() const
-{
- return overlay_opacity_;
-}
-
-inline qreal
-FlatButtonStateMachine::checkedOverlayProgress() const
-{
- return checked_overlay_progress_;
-}
-
-class FlatButton : public QPushButton
-{
- Q_OBJECT
-
- Q_PROPERTY(QColor foregroundColor WRITE setForegroundColor READ foregroundColor NOTIFY
- foregroundColorChanged)
- Q_PROPERTY(QColor backgroundColor WRITE setBackgroundColor READ backgroundColor NOTIFY
- backgroundColorChanged)
- Q_PROPERTY(
- QColor overlayColor WRITE setOverlayColor READ overlayColor NOTIFY overlayColorChanged)
- Q_PROPERTY(QColor disabledForegroundColor WRITE setDisabledForegroundColor READ
- disabledForegroundColor NOTIFY disabledForegroundColorChanged)
- Q_PROPERTY(QColor disabledBackgroundColor WRITE setDisabledBackgroundColor READ
- disabledBackgroundColor NOTIFY disabledBackgroundColorChanged)
- Q_PROPERTY(qreal fontSize WRITE setFontSize READ fontSize NOTIFY fontSizeChanged)
-
-public:
- explicit FlatButton(QWidget *parent = nullptr,
- ui::ButtonPreset preset = ui::ButtonPreset::FlatPreset);
- explicit FlatButton(const QString &text,
- QWidget *parent = nullptr,
- ui::ButtonPreset preset = ui::ButtonPreset::FlatPreset);
- FlatButton(const QString &text,
- ui::Role role,
- QWidget *parent = nullptr,
- ui::ButtonPreset preset = ui::ButtonPreset::FlatPreset);
-
- void applyPreset(ui::ButtonPreset preset);
-
- void setBackgroundColor(const QColor &color);
- void setBackgroundMode(Qt::BGMode mode);
- void setBaseOpacity(qreal opacity);
- void setCheckable(bool value);
- void setCornerRadius(qreal radius);
- void setDisabledBackgroundColor(const QColor &color);
- void setDisabledForegroundColor(const QColor &color);
- void setFixedRippleRadius(qreal radius);
- void setFontSize(qreal size);
- void setForegroundColor(const QColor &color);
- void setHasFixedRippleRadius(bool value);
- void setIconPlacement(ui::ButtonIconPlacement placement);
- void setOverlayColor(const QColor &color);
- void setOverlayStyle(ui::OverlayStyle style);
- void setRippleStyle(ui::RippleStyle style);
- void setRole(ui::Role role);
-
- QColor foregroundColor() const;
- QColor backgroundColor() const;
- QColor overlayColor() const;
- QColor disabledForegroundColor() const;
- QColor disabledBackgroundColor() const;
-
- qreal fontSize() const;
- qreal cornerRadius() const;
- qreal baseOpacity() const;
-
- bool hasFixedRippleRadius() const;
-
- ui::Role role() const;
- ui::OverlayStyle overlayStyle() const;
- ui::RippleStyle rippleStyle() const;
- ui::ButtonIconPlacement iconPlacement() const;
-
- Qt::BGMode backgroundMode() const;
-
- QSize sizeHint() const override;
-
-protected:
- int IconPadding = 0;
-
- void checkStateSet() override;
- void mousePressEvent(QMouseEvent *event) override;
- void mouseReleaseEvent(QMouseEvent *event) override;
- void resizeEvent(QResizeEvent *event) override;
- void paintEvent(QPaintEvent *event) override;
-
- virtual void paintBackground(QPainter *painter);
- virtual void paintForeground(QPainter *painter);
- virtual void updateClipPath();
-
- void init();
-
-signals:
- void foregroundColorChanged();
- void backgroundColorChanged();
- void overlayColorChanged();
- void disabledForegroundColorChanged();
- void disabledBackgroundColorChanged();
- void fontSizeChanged();
-
-private:
- RippleOverlay *ripple_overlay_;
- FlatButtonStateMachine *state_machine_;
-
- ui::Role role_;
- ui::RippleStyle ripple_style_;
- ui::ButtonIconPlacement icon_placement_;
- ui::OverlayStyle overlay_style_;
-
- Qt::BGMode bg_mode_;
-
- QColor background_color_;
- QColor foreground_color_;
- QColor overlay_color_;
- QColor disabled_color_;
- QColor disabled_background_color_;
-
- qreal fixed_ripple_radius_;
- qreal corner_radius_;
- qreal base_opacity_;
- qreal font_size_;
-
- bool use_fixed_ripple_radius_;
-};
diff --git a/src/ui/Label.cpp b/src/ui/Label.cpp
deleted file mode 100644
index 40d2cfa4..00000000
--- a/src/ui/Label.cpp
+++ /dev/null
@@ -1,33 +0,0 @@
-// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr>
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-// SPDX-FileCopyrightText: 2022 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#include "Label.h"
-#include <QMouseEvent>
-
-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/Label.h b/src/ui/Label.h
deleted file mode 100644
index 034e3c6c..00000000
--- a/src/ui/Label.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-// SPDX-FileCopyrightText: 2022 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#pragma once
-
-#include <QLabel>
-
-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());
-
-signals:
- void clicked(QMouseEvent *e);
- void pressed(QMouseEvent *e);
- void released(QMouseEvent *e);
-
-protected:
- void mousePressEvent(QMouseEvent *e) override;
- void mouseReleaseEvent(QMouseEvent *e) override;
-
- QPoint pressPosition_;
-};
diff --git a/src/ui/LoadingIndicator.cpp b/src/ui/LoadingIndicator.cpp
deleted file mode 100644
index 151f0750..00000000
--- a/src/ui/LoadingIndicator.cpp
+++ /dev/null
@@ -1,84 +0,0 @@
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-// SPDX-FileCopyrightText: 2022 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#include "LoadingIndicator.h"
-
-#include <QPaintEvent>
-#include <QPainter>
-#include <QTimer>
-
-LoadingIndicator::LoadingIndicator(QWidget *parent)
- : QWidget(parent)
- , interval_(70)
- , angle_(0)
- , color_(Qt::black)
-{
- setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
- setFocusPolicy(Qt::NoFocus);
-
- timer_ = new QTimer(this);
- connect(timer_, SIGNAL(timeout()), this, SLOT(onTimeout()));
-}
-
-void
-LoadingIndicator::paintEvent(QPaintEvent *e)
-{
- Q_UNUSED(e)
-
- if (!timer_->isActive())
- return;
-
- QPainter painter(this);
- painter.setRenderHint(QPainter::Antialiasing);
-
- int width = qMin(this->width(), this->height());
-
- int outerRadius = (width - 4) * 0.5f;
- int innerRadius = outerRadius * 0.78f;
-
- int capsuleRadius = (outerRadius - innerRadius) / 2;
-
- for (int i = 0; i < 8; ++i) {
- QColor color = color_;
-
- color.setAlphaF(1.0f - (i / 8.0f));
-
- painter.setPen(Qt::NoPen);
- painter.setBrush(color);
-
- qreal radius = capsuleRadius * (1.0f - (i / 16.0f));
-
- painter.save();
-
- painter.translate(rect().center());
- painter.rotate(angle_ - i * 45.0f);
-
- QPointF center = QPointF(-capsuleRadius, -innerRadius);
- painter.drawEllipse(center, radius * 2, radius * 2);
-
- painter.restore();
- }
-}
-
-void
-LoadingIndicator::start()
-{
- timer_->start(interval_);
- show();
-}
-
-void
-LoadingIndicator::stop()
-{
- timer_->stop();
- hide();
-}
-
-void
-LoadingIndicator::onTimeout()
-{
- angle_ = (angle_ + 45) % 360;
- repaint();
-}
diff --git a/src/ui/LoadingIndicator.h b/src/ui/LoadingIndicator.h
deleted file mode 100644
index 6d3f2a89..00000000
--- a/src/ui/LoadingIndicator.h
+++ /dev/null
@@ -1,49 +0,0 @@
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-// SPDX-FileCopyrightText: 2022 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#pragma once
-
-#include <QColor>
-#include <QWidget>
-
-class QPainter;
-class QTimer;
-class QPaintEvent;
-class LoadingIndicator : public QWidget
-{
- Q_OBJECT
- Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
-
-public:
- LoadingIndicator(QWidget *parent = nullptr);
-
- void paintEvent(QPaintEvent *e) override;
-
- void start();
- void stop();
-
- QColor color() { return color_; }
- void setColor(QColor color)
- {
- color_ = color;
- emit colorChanged();
- }
-
- int interval() { return interval_; }
- void setInterval(int interval) { interval_ = interval; }
-
-private slots:
- void onTimeout();
-
-signals:
- void colorChanged();
-
-private:
- int interval_;
- int angle_;
-
- QColor color_;
- QTimer *timer_;
-};
diff --git a/src/ui/Menu.h b/src/ui/Menu.h
deleted file mode 100644
index a666229a..00000000
--- a/src/ui/Menu.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-// SPDX-FileCopyrightText: 2022 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#pragma once
-
-#include <QMenu>
-
-#include "Config.h"
-
-class Menu : public QMenu
-{
- Q_OBJECT
-public:
- Menu(QWidget *parent = nullptr)
- : QMenu(parent){};
-
-protected:
- void leaveEvent(QEvent *e) override
- {
- hide();
-
- QMenu::leaveEvent(e);
- }
-};
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/OverlayModal.cpp b/src/ui/OverlayModal.cpp
deleted file mode 100644
index 88334ce8..00000000
--- a/src/ui/OverlayModal.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr>
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-// SPDX-FileCopyrightText: 2022 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#include <QPainter>
-#include <QVBoxLayout>
-
-#include "OverlayModal.h"
-
-OverlayModal::OverlayModal(QWidget *parent)
- : OverlayWidget(parent)
- , color_{QColor(30, 30, 30, 170)}
-{
- layout_ = new QVBoxLayout(this);
- layout_->setSpacing(0);
- layout_->setContentsMargins(10, 40, 10, 20);
- setContentAlignment(Qt::AlignCenter);
-}
-
-void
-OverlayModal::setWidget(QWidget *widget)
-{
- // Delete the previous widget
- if (layout_->count() > 0) {
- QLayoutItem *item;
- while ((item = layout_->takeAt(0)) != nullptr) {
- delete item->widget();
- delete item;
- }
- }
-
- layout_->addWidget(widget);
- content_ = widget;
- content_->setFocus();
-}
-
-void
-OverlayModal::paintEvent(QPaintEvent *event)
-{
- Q_UNUSED(event);
-
- QPainter painter(this);
- painter.fillRect(rect(), color_);
-}
-
-void
-OverlayModal::mousePressEvent(QMouseEvent *e)
-{
- if (isDismissible_ && content_ && !content_->geometry().contains(e->pos()))
- hide();
-}
-
-void
-OverlayModal::keyPressEvent(QKeyEvent *event)
-{
- if (event->key() == Qt::Key_Escape) {
- event->accept();
- hide();
- }
-}
diff --git a/src/ui/OverlayModal.h b/src/ui/OverlayModal.h
deleted file mode 100644
index 5e7ecc20..00000000
--- a/src/ui/OverlayModal.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr>
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-// SPDX-FileCopyrightText: 2022 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#pragma once
-
-#include <QKeyEvent>
-#include <QMouseEvent>
-#include <QPaintEvent>
-#include <QVBoxLayout>
-
-#include "OverlayWidget.h"
-
-class OverlayModal : public OverlayWidget
-{
-public:
- OverlayModal(QWidget *parent);
-
- void setColor(QColor color) { color_ = color; }
- void setDismissible(bool state) { isDismissible_ = state; }
-
- void setContentAlignment(QFlags<Qt::AlignmentFlag> flag) { layout_->setAlignment(flag); }
- void setWidget(QWidget *widget);
-
-protected:
- void paintEvent(QPaintEvent *event) override;
- void keyPressEvent(QKeyEvent *event) override;
- void mousePressEvent(QMouseEvent *event) override;
-
-private:
- QWidget *content_;
- QVBoxLayout *layout_;
-
- QColor color_;
-
- //! Decides whether or not the modal can be removed by clicking into it.
- bool isDismissible_ = true;
-};
diff --git a/src/ui/OverlayWidget.cpp b/src/ui/OverlayWidget.cpp
deleted file mode 100644
index b755a44c..00000000
--- a/src/ui/OverlayWidget.cpp
+++ /dev/null
@@ -1,79 +0,0 @@
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-// SPDX-FileCopyrightText: 2022 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#include "OverlayWidget.h"
-
-#include <QPainter>
-#include <QStyleOption>
-
-OverlayWidget::OverlayWidget(QWidget *parent)
- : QWidget(parent)
-{
- if (parent) {
- parent->installEventFilter(this);
- setGeometry(overlayGeometry());
- raise();
- }
-}
-
-bool
-OverlayWidget::event(QEvent *event)
-{
- if (!parent())
- return QWidget::event(event);
-
- switch (event->type()) {
- case QEvent::ParentChange: {
- parent()->installEventFilter(this);
- setGeometry(overlayGeometry());
- break;
- }
- case QEvent::ParentAboutToChange: {
- parent()->removeEventFilter(this);
- break;
- }
- default:
- break;
- }
-
- return QWidget::event(event);
-}
-
-bool
-OverlayWidget::eventFilter(QObject *obj, QEvent *event)
-{
- switch (event->type()) {
- case QEvent::Move:
- case QEvent::Resize:
- setGeometry(overlayGeometry());
- break;
- default:
- break;
- }
-
- return QWidget::eventFilter(obj, event);
-}
-
-QRect
-OverlayWidget::overlayGeometry() const
-{
- QWidget *widget = parentWidget();
-
- if (!widget)
- return QRect();
-
- return widget->rect();
-}
-
-void
-OverlayWidget::paintEvent(QPaintEvent *event)
-{
- Q_UNUSED(event);
-
- QStyleOption opt;
- opt.initFrom(this);
- QPainter p(this);
- style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
-}
diff --git a/src/ui/OverlayWidget.h b/src/ui/OverlayWidget.h
deleted file mode 100644
index 19ad0cc6..00000000
--- a/src/ui/OverlayWidget.h
+++ /dev/null
@@ -1,26 +0,0 @@
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-// SPDX-FileCopyrightText: 2022 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#pragma once
-
-#include <QEvent>
-#include <QWidget>
-
-class QPainter;
-
-class OverlayWidget : public QWidget
-{
- Q_OBJECT
-
-public:
- explicit OverlayWidget(QWidget *parent = nullptr);
-
-protected:
- bool event(QEvent *event) override;
- bool eventFilter(QObject *obj, QEvent *event) override;
-
- QRect overlayGeometry() const;
- void paintEvent(QPaintEvent *event) override;
-};
diff --git a/src/ui/Painter.h b/src/ui/Painter.h
deleted file mode 100644
index 5a7dae3e..00000000
--- a/src/ui/Painter.h
+++ /dev/null
@@ -1,154 +0,0 @@
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-// SPDX-FileCopyrightText: 2022 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#pragma once
-
-#include <QFontMetrics>
-#include <QPaintDevice>
-#include <QPainter>
-#include <QPainterPath>
-#include <QtGlobal>
-
-class Painter : public QPainter
-{
-public:
- explicit Painter(QPaintDevice *device)
- : QPainter(device)
- {}
-
- void drawTextLeft(int x, int y, const QString &text)
- {
- QFontMetrics m(fontMetrics());
- drawText(x, y + m.ascent(), text);
- }
-
- void drawTextRight(int x, int y, int outerw, const QString &text, int textWidth = -1)
- {
- QFontMetrics m(fontMetrics());
- if (textWidth < 0) {
- textWidth = m.horizontalAdvance(text);
- }
- drawText((outerw - x - textWidth), y + m.ascent(), text);
- }
-
- void drawPixmapLeft(int x, int y, const QPixmap &pix, const QRect &from)
- {
- drawPixmap(QPoint(x, y), pix, from);
- }
-
- void drawPixmapLeft(const QPoint &p, const QPixmap &pix, const QRect &from)
- {
- return drawPixmapLeft(p.x(), p.y(), pix, from);
- }
-
- void drawPixmapLeft(int x, int y, int w, int h, const QPixmap &pix, const QRect &from)
- {
- drawPixmap(QRect(x, y, w, h), pix, from);
- }
-
- void drawPixmapLeft(const QRect &r, const QPixmap &pix, const QRect &from)
- {
- return drawPixmapLeft(r.x(), r.y(), r.width(), r.height(), pix, from);
- }
-
- void drawPixmapLeft(int x, int y, int outerw, const QPixmap &pix)
- {
- Q_UNUSED(outerw);
- drawPixmap(QPoint(x, y), pix);
- }
-
- void drawPixmapLeft(const QPoint &p, int outerw, const QPixmap &pix)
- {
- return drawPixmapLeft(p.x(), p.y(), outerw, pix);
- }
-
- void drawPixmapRight(int x, int y, int outerw, const QPixmap &pix, const QRect &from)
- {
- drawPixmap(QPoint((outerw - x - (from.width() / pix.devicePixelRatio())), y), pix, from);
- }
-
- void drawPixmapRight(const QPoint &p, int outerw, const QPixmap &pix, const QRect &from)
- {
- return drawPixmapRight(p.x(), p.y(), outerw, pix, from);
- }
- void
- drawPixmapRight(int x, int y, int w, int h, int outerw, const QPixmap &pix, const QRect &from)
- {
- drawPixmap(QRect((outerw - x - w), y, w, h), pix, from);
- }
-
- void drawPixmapRight(const QRect &r, int outerw, const QPixmap &pix, const QRect &from)
- {
- return drawPixmapRight(r.x(), r.y(), r.width(), r.height(), outerw, pix, from);
- }
-
- void drawPixmapRight(int x, int y, int outerw, const QPixmap &pix)
- {
- drawPixmap(QPoint((outerw - x - (pix.width() / pix.devicePixelRatio())), y), pix);
- }
-
- void drawPixmapRight(const QPoint &p, int outerw, const QPixmap &pix)
- {
- return drawPixmapRight(p.x(), p.y(), outerw, pix);
- }
-
- void drawAvatar(const QPixmap &pix, int w, int h, int d)
- {
- QPainterPath pp;
- pp.addEllipse((w - d) / 2, (h - d) / 2, d, d);
-
- QRect region((w - d) / 2, (h - d) / 2, d, d);
-
- setClipPath(pp);
- drawPixmap(region, pix);
- }
-
- void drawLetterAvatar(const QString &c,
- const QColor &penColor,
- const QColor &brushColor,
- int w,
- int h,
- int d)
- {
- QRect region((w - d) / 2, (h - d) / 2, d, d);
-
- setPen(Qt::NoPen);
- setBrush(brushColor);
-
- drawEllipse(region.center(), d / 2, d / 2);
-
- setBrush(Qt::NoBrush);
- drawEllipse(region.center(), d / 2, d / 2);
-
- setPen(penColor);
- drawText(region.translated(0, -1), Qt::AlignCenter, c);
- }
-};
-
-class PainterHighQualityEnabler
-{
-public:
- PainterHighQualityEnabler(Painter &p)
- : _painter(p)
- {
- hints_ =
- QPainter::Antialiasing | QPainter::SmoothPixmapTransform | QPainter::TextAntialiasing;
-
- _painter.setRenderHints(hints_);
- }
-
- ~PainterHighQualityEnabler()
- {
- if (hints_)
- _painter.setRenderHints(hints_, false);
- }
-
- PainterHighQualityEnabler(const PainterHighQualityEnabler &other) = delete;
- PainterHighQualityEnabler &operator=(const PainterHighQualityEnabler &other) = delete;
-
-private:
- Painter &_painter;
- QPainter::RenderHints hints_ = {};
-};
diff --git a/src/ui/RaisedButton.cpp b/src/ui/RaisedButton.cpp
deleted file mode 100644
index 491ab573..00000000
--- a/src/ui/RaisedButton.cpp
+++ /dev/null
@@ -1,92 +0,0 @@
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-// SPDX-FileCopyrightText: 2022 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#include <QEventTransition>
-#include <QPropertyAnimation>
-
-#include "RaisedButton.h"
-
-void
-RaisedButton::init()
-{
- shadow_state_machine_ = new QStateMachine(this);
- normal_state_ = new QState;
- pressed_state_ = new QState;
- effect_ = new QGraphicsDropShadowEffect;
-
- effect_->setBlurRadius(7);
- effect_->setOffset(QPointF(0, 2));
- effect_->setColor(QColor(0, 0, 0, 75));
-
- setBackgroundMode(Qt::OpaqueMode);
- setMinimumHeight(42);
- setGraphicsEffect(effect_);
- setBaseOpacity(0.3);
-
- shadow_state_machine_->addState(normal_state_);
- shadow_state_machine_->addState(pressed_state_);
-
- normal_state_->assignProperty(effect_, "offset", QPointF(0, 2));
- normal_state_->assignProperty(effect_, "blurRadius", 7);
-
- pressed_state_->assignProperty(effect_, "offset", QPointF(0, 5));
- pressed_state_->assignProperty(effect_, "blurRadius", 29);
-
- QAbstractTransition *transition;
-
- transition = new QEventTransition(this, QEvent::MouseButtonPress);
- transition->setTargetState(pressed_state_);
- normal_state_->addTransition(transition);
-
- transition = new QEventTransition(this, QEvent::MouseButtonDblClick);
- transition->setTargetState(pressed_state_);
- normal_state_->addTransition(transition);
-
- transition = new QEventTransition(this, QEvent::MouseButtonRelease);
- transition->setTargetState(normal_state_);
- pressed_state_->addTransition(transition);
-
- QPropertyAnimation *animation;
-
- animation = new QPropertyAnimation(effect_, "offset", this);
- animation->setDuration(100);
- shadow_state_machine_->addDefaultAnimation(animation);
-
- animation = new QPropertyAnimation(effect_, "blurRadius", this);
- animation->setDuration(100);
- shadow_state_machine_->addDefaultAnimation(animation);
-
- shadow_state_machine_->setInitialState(normal_state_);
- shadow_state_machine_->start();
-}
-
-RaisedButton::RaisedButton(QWidget *parent)
- : FlatButton(parent)
-{
- init();
-}
-
-RaisedButton::RaisedButton(const QString &text, QWidget *parent)
- : FlatButton(parent)
-{
- init();
- setText(text);
-}
-
-bool
-RaisedButton::event(QEvent *event)
-{
- if (QEvent::EnabledChange == event->type()) {
- if (isEnabled()) {
- shadow_state_machine_->start();
- effect_->setEnabled(true);
- } else {
- shadow_state_machine_->stop();
- effect_->setEnabled(false);
- }
- }
-
- return FlatButton::event(event);
-}
diff --git a/src/ui/RaisedButton.h b/src/ui/RaisedButton.h
deleted file mode 100644
index 7464c207..00000000
--- a/src/ui/RaisedButton.h
+++ /dev/null
@@ -1,32 +0,0 @@
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-// SPDX-FileCopyrightText: 2022 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#pragma once
-
-#include <QGraphicsDropShadowEffect>
-#include <QState>
-#include <QStateMachine>
-
-#include "FlatButton.h"
-
-class RaisedButton : public FlatButton
-{
- Q_OBJECT
-
-public:
- explicit RaisedButton(QWidget *parent = nullptr);
- explicit RaisedButton(const QString &text, QWidget *parent = nullptr);
-
-protected:
- bool event(QEvent *event) override;
-
-private:
- void init();
-
- QStateMachine *shadow_state_machine_;
- QState *normal_state_;
- QState *pressed_state_;
- QGraphicsDropShadowEffect *effect_;
-};
diff --git a/src/ui/Ripple.cpp b/src/ui/Ripple.cpp
deleted file mode 100644
index 84c0b394..00000000
--- a/src/ui/Ripple.cpp
+++ /dev/null
@@ -1,116 +0,0 @@
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-// SPDX-FileCopyrightText: 2022 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#include "Ripple.h"
-#include "RippleOverlay.h"
-
-Ripple::Ripple(const QPoint ¢er, QObject *parent)
- : QParallelAnimationGroup(parent)
- , overlay_(nullptr)
- , radius_anim_(animate("radius"))
- , opacity_anim_(animate("opacity"))
- , radius_(0)
- , opacity_(0)
- , center_(center)
-{
- init();
-}
-
-Ripple::Ripple(const QPoint ¢er, RippleOverlay *overlay, QObject *parent)
- : QParallelAnimationGroup(parent)
- , overlay_(overlay)
- , radius_anim_(animate("radius"))
- , opacity_anim_(animate("opacity"))
- , radius_(0)
- , opacity_(0)
- , center_(center)
-{
- init();
-}
-
-void
-Ripple::setRadius(qreal radius)
-{
- Q_ASSERT(overlay_);
-
- if (radius_ == radius)
- return;
-
- radius_ = radius;
- overlay_->update();
-
- emit radiusChanged();
-}
-
-void
-Ripple::setOpacity(qreal opacity)
-{
- Q_ASSERT(overlay_);
-
- if (opacity_ == opacity)
- return;
-
- opacity_ = opacity;
- overlay_->update();
-
- emit opacityChanged();
-}
-
-void
-Ripple::setColor(const QColor &color)
-{
- if (brush_.color() == color)
- return;
-
- brush_.setColor(color);
-
- if (overlay_)
- overlay_->update();
-}
-
-void
-Ripple::setBrush(const QBrush &brush)
-{
- brush_ = brush;
-
- if (overlay_)
- overlay_->update();
-}
-
-void
-Ripple::destroy()
-{
- Q_ASSERT(overlay_);
-
- overlay_->removeRipple(this);
-}
-
-QPropertyAnimation *
-Ripple::animate(const QByteArray &property, const QEasingCurve &easing, int duration)
-{
- QPropertyAnimation *animation = new QPropertyAnimation;
- animation->setTargetObject(this);
- animation->setPropertyName(property);
- animation->setEasingCurve(easing);
- animation->setDuration(duration);
-
- addAnimation(animation);
-
- return animation;
-}
-
-void
-Ripple::init()
-{
- setOpacityStartValue(0.5);
- setOpacityEndValue(0);
- setRadiusStartValue(0);
- setRadiusEndValue(300);
-
- brush_.setColor(Qt::black);
- brush_.setStyle(Qt::SolidPattern);
-
- connect(this, SIGNAL(finished()), this, SLOT(destroy()));
-}
diff --git a/src/ui/Ripple.h b/src/ui/Ripple.h
deleted file mode 100644
index 43d291cb..00000000
--- a/src/ui/Ripple.h
+++ /dev/null
@@ -1,154 +0,0 @@
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-// SPDX-FileCopyrightText: 2022 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#pragma once
-
-#include <QBrush>
-#include <QEasingCurve>
-#include <QParallelAnimationGroup>
-#include <QPoint>
-#include <QPropertyAnimation>
-
-class RippleOverlay;
-
-class Ripple : public QParallelAnimationGroup
-{
- Q_OBJECT
-
- Q_PROPERTY(qreal radius WRITE setRadius READ radius NOTIFY radiusChanged)
- Q_PROPERTY(qreal opacity WRITE setOpacity READ opacity NOTIFY opacityChanged)
-
-public:
- explicit Ripple(const QPoint ¢er, QObject *parent = nullptr);
- Ripple(const QPoint ¢er, RippleOverlay *overlay, QObject *parent = nullptr);
-
- inline void setOverlay(RippleOverlay *overlay);
-
- void setRadius(qreal radius);
- void setOpacity(qreal opacity);
- void setColor(const QColor &color);
- void setBrush(const QBrush &brush);
-
- inline qreal radius() const;
- inline qreal opacity() const;
- inline QColor color() const;
- inline QBrush brush() const;
- inline QPoint center() const;
-
- inline QPropertyAnimation *radiusAnimation() const;
- inline QPropertyAnimation *opacityAnimation() const;
-
- inline void setOpacityStartValue(qreal value);
- inline void setOpacityEndValue(qreal value);
- inline void setRadiusStartValue(qreal value);
- inline void setRadiusEndValue(qreal value);
- inline void setDuration(int msecs);
-
-protected slots:
- void destroy();
-
-signals:
- void radiusChanged();
- void opacityChanged();
-
-private:
- Q_DISABLE_COPY(Ripple)
-
- QPropertyAnimation *animate(const QByteArray &property,
- const QEasingCurve &easing = QEasingCurve::OutQuad,
- int duration = 800);
-
- void init();
-
- RippleOverlay *overlay_;
-
- QPropertyAnimation *const radius_anim_;
- QPropertyAnimation *const opacity_anim_;
-
- qreal radius_;
- qreal opacity_;
-
- QPoint center_;
- QBrush brush_;
-};
-
-inline void
-Ripple::setOverlay(RippleOverlay *overlay)
-{
- overlay_ = overlay;
-}
-
-inline qreal
-Ripple::radius() const
-{
- return radius_;
-}
-
-inline qreal
-Ripple::opacity() const
-{
- return opacity_;
-}
-
-inline QColor
-Ripple::color() const
-{
- return brush_.color();
-}
-
-inline QBrush
-Ripple::brush() const
-{
- return brush_;
-}
-
-inline QPoint
-Ripple::center() const
-{
- return center_;
-}
-
-inline QPropertyAnimation *
-Ripple::radiusAnimation() const
-{
- return radius_anim_;
-}
-
-inline QPropertyAnimation *
-Ripple::opacityAnimation() const
-{
- return opacity_anim_;
-}
-
-inline void
-Ripple::setOpacityStartValue(qreal value)
-{
- opacity_anim_->setStartValue(value);
-}
-
-inline void
-Ripple::setOpacityEndValue(qreal value)
-{
- opacity_anim_->setEndValue(value);
-}
-
-inline void
-Ripple::setRadiusStartValue(qreal value)
-{
- radius_anim_->setStartValue(value);
-}
-
-inline void
-Ripple::setRadiusEndValue(qreal value)
-{
- radius_anim_->setEndValue(value);
-}
-
-inline void
-Ripple::setDuration(int msecs)
-{
- radius_anim_->setDuration(msecs);
- opacity_anim_->setDuration(msecs);
-}
diff --git a/src/ui/RippleOverlay.cpp b/src/ui/RippleOverlay.cpp
deleted file mode 100644
index 97ad8662..00000000
--- a/src/ui/RippleOverlay.cpp
+++ /dev/null
@@ -1,67 +0,0 @@
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-// SPDX-FileCopyrightText: 2022 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#include <QPainter>
-
-#include "Ripple.h"
-#include "RippleOverlay.h"
-
-RippleOverlay::RippleOverlay(QWidget *parent)
- : OverlayWidget(parent)
- , use_clip_(false)
-{
- setAttribute(Qt::WA_TransparentForMouseEvents);
- setAttribute(Qt::WA_NoSystemBackground);
-}
-
-void
-RippleOverlay::addRipple(Ripple *ripple)
-{
- ripple->setOverlay(this);
- ripples_.push_back(ripple);
- ripple->start();
-}
-
-void
-RippleOverlay::addRipple(const QPoint &position, qreal radius)
-{
- Ripple *ripple = new Ripple(position);
- ripple->setRadiusEndValue(radius);
- addRipple(ripple);
-}
-
-void
-RippleOverlay::removeRipple(Ripple *ripple)
-{
- if (ripples_.removeOne(ripple))
- delete ripple;
-}
-
-void
-RippleOverlay::paintEvent(QPaintEvent *event)
-{
- Q_UNUSED(event)
-
- QPainter painter(this);
- painter.setRenderHint(QPainter::Antialiasing);
- painter.setPen(Qt::NoPen);
-
- if (use_clip_)
- painter.setClipPath(clip_path_);
-
- for (auto it = ripples_.constBegin(); it != ripples_.constEnd(); ++it)
- paintRipple(&painter, *it);
-}
-
-void
-RippleOverlay::paintRipple(QPainter *painter, Ripple *ripple)
-{
- const qreal radius = ripple->radius();
- const QPointF center = ripple->center();
-
- painter->setOpacity(ripple->opacity());
- painter->setBrush(ripple->brush());
- painter->drawEllipse(center, radius, radius);
-}
diff --git a/src/ui/RippleOverlay.h b/src/ui/RippleOverlay.h
deleted file mode 100644
index f70d00a7..00000000
--- a/src/ui/RippleOverlay.h
+++ /dev/null
@@ -1,62 +0,0 @@
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-// SPDX-FileCopyrightText: 2022 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#pragma once
-
-#include <QPainterPath>
-
-#include "OverlayWidget.h"
-
-class Ripple;
-
-class RippleOverlay : public OverlayWidget
-{
- Q_OBJECT
-
-public:
- explicit RippleOverlay(QWidget *parent = nullptr);
-
- void addRipple(Ripple *ripple);
- void addRipple(const QPoint &position, qreal radius = 300);
-
- void removeRipple(Ripple *ripple);
-
- inline void setClipping(bool enable);
- inline bool hasClipping() const;
-
- inline void setClipPath(const QPainterPath &path);
-
-protected:
- void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE;
-
-private:
- Q_DISABLE_COPY(RippleOverlay)
-
- void paintRipple(QPainter *painter, Ripple *ripple);
-
- QList<Ripple *> ripples_;
- QPainterPath clip_path_;
- bool use_clip_;
-};
-
-inline void
-RippleOverlay::setClipping(bool enable)
-{
- use_clip_ = enable;
- update();
-}
-
-inline bool
-RippleOverlay::hasClipping() const
-{
- return use_clip_;
-}
-
-inline void
-RippleOverlay::setClipPath(const QPainterPath &path)
-{
- clip_path_ = path;
- update();
-}
diff --git a/src/ui/SnackBar.cpp b/src/ui/SnackBar.cpp
deleted file mode 100644
index 50c3d3f9..00000000
--- a/src/ui/SnackBar.cpp
+++ /dev/null
@@ -1,136 +0,0 @@
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-// SPDX-FileCopyrightText: 2022 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#include <QPainter>
-
-#include "SnackBar.h"
-
-constexpr int STARTING_OFFSET = 1;
-constexpr int BOX_PADDING = 10;
-constexpr double MIN_WIDTH = 400.0;
-constexpr double MIN_WIDTH_PERCENTAGE = 0.3;
-
-SnackBar::SnackBar(QWidget *parent)
- : OverlayWidget(parent)
- , offset_anim(this, "offset", this)
-{
- QFont font;
- font.setPointSizeF(font.pointSizeF() * 1.2);
- font.setWeight(QFont::Weight::Thin);
- setFont(font);
-
- boxHeight_ = QFontMetrics(font).height() * 2;
- offset_ = STARTING_OFFSET;
- position_ = SnackBarPosition::Top;
-
- hideTimer_.setSingleShot(true);
-
- offset_anim.setStartValue(1.0);
- offset_anim.setEndValue(0.0);
- offset_anim.setDuration(100);
- offset_anim.setEasingCurve(QEasingCurve::OutCubic);
-
- connect(this, &SnackBar::offsetChanged, this, [this]() mutable { repaint(); });
- connect(
- &offset_anim, &QPropertyAnimation::finished, this, [this]() { hideTimer_.start(10000); });
-
- connect(&hideTimer_, SIGNAL(timeout()), this, SLOT(hideMessage()));
-
- hide();
-}
-
-void
-SnackBar::start()
-{
- if (messages_.empty())
- return;
-
- show();
- raise();
-
- offset_anim.start();
-}
-
-void
-SnackBar::hideMessage()
-{
- stopTimers();
- hide();
-
- if (!messages_.empty())
- // Moving on to the next message.
- messages_.pop_front();
-
- // Resetting the starting position of the widget.
- offset_ = STARTING_OFFSET;
-
- if (!messages_.empty())
- start();
-}
-
-void
-SnackBar::stopTimers()
-{
- hideTimer_.stop();
-}
-
-void
-SnackBar::showMessage(const QString &msg)
-{
- messages_.push_back(msg);
-
- // There is already an active message.
- if (isVisible())
- return;
-
- start();
-}
-
-void
-SnackBar::mousePressEvent(QMouseEvent *)
-{
- hideMessage();
-}
-
-void
-SnackBar::paintEvent(QPaintEvent *event)
-{
- Q_UNUSED(event)
-
- if (messages_.empty())
- return;
-
- auto message_ = messages_.front();
-
- QPainter p(this);
- p.setRenderHint(QPainter::Antialiasing);
-
- QBrush brush;
- brush.setStyle(Qt::SolidPattern);
- brush.setColor(bgColor_);
- p.setBrush(brush);
-
- QRect r(0, 0, std::max(MIN_WIDTH, width() * MIN_WIDTH_PERCENTAGE), 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(-BOX_PADDING, -BOX_PADDING, BOX_PADDING, BOX_PADDING);
-
- const qreal s = 1 - offset_;
-
- if (position_ == SnackBarPosition::Bottom)
- p.translate((width() - (r.width() - 2 * BOX_PADDING)) / 2,
- height() - BOX_PADDING - s * (r.height()));
- else
- p.translate((width() - (r.width() - 2 * BOX_PADDING)) / 2,
- s * (r.height()) - 2 * BOX_PADDING);
-
- br.moveCenter(r.center());
- p.drawRoundedRect(r.adjusted(0, 0, 0, 4), 4, 4);
- p.setPen(textColor_);
- p.drawText(br, Qt::AlignHCenter | Qt::AlignTop | Qt::TextWordWrap, message_);
-}
diff --git a/src/ui/SnackBar.h b/src/ui/SnackBar.h
deleted file mode 100644
index caac68aa..00000000
--- a/src/ui/SnackBar.h
+++ /dev/null
@@ -1,98 +0,0 @@
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-// SPDX-FileCopyrightText: 2022 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#pragma once
-
-#include <QCoreApplication>
-#include <QPaintEvent>
-#include <QPropertyAnimation>
-#include <QTimer>
-#include <deque>
-
-#include "OverlayWidget.h"
-
-enum class SnackBarPosition
-{
- Bottom,
- Top,
-};
-
-class SnackBar : public OverlayWidget
-{
- Q_OBJECT
-
- Q_PROPERTY(
- QColor bgColor READ backgroundColor WRITE setBackgroundColor NOTIFY backgroundColorChanged)
- Q_PROPERTY(QColor textColor READ textColor WRITE setTextColor NOTIFY textColorChanged)
- Q_PROPERTY(double offset READ offset WRITE setOffset NOTIFY offsetChanged)
-
-public:
- explicit SnackBar(QWidget *parent);
-
- QColor backgroundColor() const { return bgColor_; }
- void setBackgroundColor(const QColor &color)
- {
- bgColor_ = color;
- update();
- emit backgroundColorChanged();
- }
-
- QColor textColor() const { return textColor_; }
- void setTextColor(const QColor &color)
- {
- textColor_ = color;
- update();
- emit textColorChanged();
- }
- void setPosition(SnackBarPosition pos)
- {
- position_ = pos;
- update();
- }
-
- double offset() { return offset_; }
- void setOffset(double offset)
- {
- if (offset != offset_) {
- offset_ = offset;
- emit offsetChanged();
- }
- }
-
-public slots:
- void showMessage(const QString &msg);
-
-signals:
- void offsetChanged();
- void backgroundColorChanged();
- void textColorChanged();
-
-protected:
- void paintEvent(QPaintEvent *event) override;
- void mousePressEvent(QMouseEvent *event) override;
-
-private slots:
- void hideMessage();
-
-private:
- void stopTimers();
- void start();
-
- QColor bgColor_;
- QColor textColor_;
-
- qreal bgOpacity_;
- qreal offset_;
-
- std::deque<QString> messages_;
-
- QTimer hideTimer_;
-
- double boxHeight_;
-
- QPropertyAnimation offset_anim;
-
- SnackBarPosition position_;
-};
diff --git a/src/ui/TextLabel.cpp b/src/ui/TextLabel.cpp
deleted file mode 100644
index 246d9d6b..00000000
--- a/src/ui/TextLabel.cpp
+++ /dev/null
@@ -1,123 +0,0 @@
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-// SPDX-FileCopyrightText: 2022 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#include "ui/TextLabel.h"
-
-#include <QAbstractTextDocumentLayout>
-#include <QDesktopServices>
-#include <QEvent>
-#include <QWheelEvent>
-
-#include "Utils.h"
-
-bool
-ContextMenuFilter::eventFilter(QObject *obj, QEvent *event)
-{
- if (event->type() == QEvent::MouseButtonPress) {
- emit contextMenuIsOpening();
- return true;
- }
-
- return QObject::eventFilter(obj, event);
-}
-
-TextLabel::TextLabel(QWidget *parent)
- : TextLabel(QString(), parent)
-{}
-
-TextLabel::TextLabel(const QString &text, QWidget *parent)
- : QTextBrowser(parent)
-{
- document()->setDefaultStyleSheet(QStringLiteral("a {color: %1; }").arg(utils::linkColor()));
-
- setText(text);
- setOpenExternalLinks(true);
-
- // Make it look and feel like an ordinary label.
- setReadOnly(true);
- setFrameStyle(QFrame::NoFrame);
- QPalette pal = palette();
- pal.setColor(QPalette::Base, Qt::transparent);
- setPalette(pal);
-
- // Wrap anywhere but prefer words, adjust minimum height on the fly.
- setLineWrapMode(QTextEdit::WidgetWidth);
- setWordWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
- connect(document()->documentLayout(),
- &QAbstractTextDocumentLayout::documentSizeChanged,
- this,
- &TextLabel::adjustHeight);
- document()->setDocumentMargin(0);
-
- setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
- setFixedHeight(0);
-
- connect(this, &TextLabel::linkActivated, this, &TextLabel::handleLinkActivation);
-
- auto filter = new ContextMenuFilter(this);
- installEventFilter(filter);
- connect(filter, &ContextMenuFilter::contextMenuIsOpening, this, [this]() {
- contextMenuRequested_ = true;
- });
-}
-
-void
-TextLabel::focusOutEvent(QFocusEvent *e)
-{
- QTextBrowser::focusOutEvent(e);
-
- // We keep the selection available for the context menu.
- if (contextMenuRequested_) {
- contextMenuRequested_ = false;
- return;
- }
-
- QTextCursor cursor = textCursor();
- cursor.clearSelection();
- setTextCursor(cursor);
-}
-
-void
-TextLabel::mousePressEvent(QMouseEvent *e)
-{
- link_ = (e->button() & Qt::LeftButton) ? anchorAt(e->pos()) : QString();
- QTextBrowser::mousePressEvent(e);
-}
-
-void
-TextLabel::mouseReleaseEvent(QMouseEvent *e)
-{
- if (e->button() & Qt::LeftButton && !link_.isEmpty() && anchorAt(e->pos()) == link_) {
- emit linkActivated(link_);
- return;
- }
-
- QTextBrowser::mouseReleaseEvent(e);
-}
-
-void
-TextLabel::wheelEvent(QWheelEvent *event)
-{
- event->ignore();
-}
-
-void
-TextLabel::handleLinkActivation(const QUrl &url)
-{
- auto parts = url.toString().split('/');
- auto defaultHandler = [](const QUrl &url) { QDesktopServices::openUrl(url); };
-
- if (url.host() != QLatin1String("matrix.to") || parts.isEmpty())
- return defaultHandler(url);
-
- try {
- using namespace mtx::identifiers;
- parse<User>(parts.last().toStdString());
- } catch (const std::exception &) {
- return defaultHandler(url);
- }
-
- emit userProfileTriggered(parts.last());
-}
diff --git a/src/ui/TextLabel.h b/src/ui/TextLabel.h
deleted file mode 100644
index 2c6f4aa3..00000000
--- a/src/ui/TextLabel.h
+++ /dev/null
@@ -1,60 +0,0 @@
-// SPDX-FileCopyrightText: 2021 Nheko Contributors
-// SPDX-FileCopyrightText: 2022 Nheko Contributors
-//
-// SPDX-License-Identifier: GPL-3.0-or-later
-
-#pragma once
-
-#include <QSize>
-#include <QString>
-#include <QTextBrowser>
-#include <QUrl>
-
-class QMouseEvent;
-class QFocusEvent;
-class QWheelEvent;
-
-class ContextMenuFilter : public QObject
-{
- Q_OBJECT
-
-public:
- explicit ContextMenuFilter(QWidget *parent)
- : QObject(parent)
- {}
-
-signals:
- void contextMenuIsOpening();
-
-protected:
- bool eventFilter(QObject *obj, QEvent *event) override;
-};
-
-class TextLabel : public QTextBrowser
-{
- Q_OBJECT
-
-public:
- TextLabel(const QString &text, QWidget *parent = nullptr);
- TextLabel(QWidget *parent = nullptr);
-
- void wheelEvent(QWheelEvent *event) override;
- void clearLinks() { link_.clear(); }
-
-protected:
- void mousePressEvent(QMouseEvent *e) override;
- void mouseReleaseEvent(QMouseEvent *e) override;
- void focusOutEvent(QFocusEvent *e) override;
-
-private slots:
- void adjustHeight(const QSizeF &size) { setFixedHeight(size.height()); }
- void handleLinkActivation(const QUrl &link);
-
-signals:
- void userProfileTriggered(const QString &user_id);
- void linkActivated(const QUrl &link);
-
-private:
- QString link_;
- bool contextMenuRequested_ = false;
-};
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]() {
|