diff --git a/CMakeLists.txt b/CMakeLists.txt
index 20ef5cab..1b6c08b7 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -345,6 +345,7 @@ set(SRC_FILES
src/DeviceVerificationFlow.cpp
src/EventAccessors.cpp
src/InviteesModel.cpp
+ src/JdenticonProvider.cpp
src/Logging.cpp
src/LoginPage.cpp
src/MainWindow.cpp
@@ -557,6 +558,7 @@ qt5_wrap_cpp(MOC_HEADERS
src/DeviceVerificationFlow.h
src/ImagePackListModel.h
src/InviteesModel.h
+ src/JdenticonProvider.h
src/LoginPage.h
src/MainWindow.h
src/MemberList.h
diff --git a/resources/qml/Avatar.qml b/resources/qml/Avatar.qml
index ab067eee..7bbdeba3 100644
--- a/resources/qml/Avatar.qml
+++ b/resources/qml/Avatar.qml
@@ -35,11 +35,18 @@ Rectangle {
font.pixelSize: avatar.height / 2
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
- visible: img.status != Image.Ready
+ visible: img.status != Image.Ready && !Settings.useIdenticon
color: Nheko.colors.text
}
Image {
+ id: identicon
+ anchors.fill: parent
+ visible: img.status != Image.Ready && Settings.useIdenticon
+ source: "image://jdenticon/" + userid
+ }
+
+ Image {
id: img
anchors.fill: parent
diff --git a/src/JdenticonProvider.cpp b/src/JdenticonProvider.cpp
new file mode 100644
index 00000000..4be972dc
--- /dev/null
+++ b/src/JdenticonProvider.cpp
@@ -0,0 +1,68 @@
+#include "JdenticonProvider.h"
+
+#include <QApplication>
+#include <QDir>
+#include <QPainter>
+#include <QPluginLoader>
+#include <QSvgRenderer>
+
+#include <mtxclient/crypto/client.hpp>
+
+#include "Cache.h"
+#include "Logging.h"
+#include "MatrixClient.h"
+#include "Utils.h"
+#include "jdenticoninterface.h"
+
+JdenticonResponse::JdenticonResponse(const QString &key, const QSize &requestedSize)
+ : m_key(key)
+ , m_requestedSize(requestedSize.isValid() ? requestedSize : QSize(100, 100))
+ , m_pixmap{m_requestedSize}
+ , jdenticonInterface_{Jdenticon::getJdenticonInterface()}
+{
+ setAutoDelete(false);
+}
+
+void
+JdenticonResponse::run()
+{
+ m_pixmap.fill(Qt::transparent);
+ QPainter painter{&m_pixmap};
+ QSvgRenderer renderer{
+ jdenticonInterface_->generate(m_key, m_requestedSize.width()).toUtf8()};
+ // m_image = QImage::fromData(jdenticonInterface_->generate(m_key,
+ // size->width()).toUtf8());
+ renderer.render(&painter);
+
+ emit finished();
+}
+
+namespace Jdenticon {
+JdenticonInterface *
+getJdenticonInterface()
+{
+ static JdenticonInterface *interface = nullptr;
+
+ if (interface == nullptr) {
+ QDir pluginsDir(qApp->applicationDirPath());
+
+ bool plugins = pluginsDir.cd("plugins");
+ if (plugins) {
+ for (QString fileName : pluginsDir.entryList(QDir::Files)) {
+ QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName));
+ QObject *plugin = pluginLoader.instance();
+ if (plugin) {
+ interface = qobject_cast<JdenticonInterface *>(plugin);
+ if (interface) {
+ nhlog::ui()->info("Loaded jdenticon plugin.");
+ break;
+ }
+ }
+ }
+ } else
+ nhlog::ui()->info("jdenticon plugin not found.");
+ }
+
+ return interface;
+}
+}
diff --git a/src/JdenticonProvider.h b/src/JdenticonProvider.h
new file mode 100644
index 00000000..053842bb
--- /dev/null
+++ b/src/JdenticonProvider.h
@@ -0,0 +1,59 @@
+#pragma once
+
+#include <QImage>
+#include <QQuickAsyncImageProvider>
+#include <QQuickImageResponse>
+#include <QThreadPool>
+
+#include <mtx/common.hpp>
+
+#include <boost/optional.hpp>
+
+#include "jdenticoninterface.h"
+
+namespace Jdenticon {
+JdenticonInterface *
+getJdenticonInterface();
+}
+
+class JdenticonResponse
+ : public QQuickImageResponse
+ , public QRunnable
+{
+public:
+ JdenticonResponse(const QString &key, const QSize &requestedSize);
+
+ QQuickTextureFactory *textureFactory() const override
+ {
+ return QQuickTextureFactory::textureFactoryForImage(m_pixmap.toImage());
+ }
+
+ void run() override;
+
+ QString m_key;
+ QSize m_requestedSize;
+ QPixmap m_pixmap;
+ JdenticonInterface *jdenticonInterface_ = nullptr;
+};
+
+class JdenticonProvider
+ : public QObject
+ , public QQuickAsyncImageProvider
+{
+ Q_OBJECT
+
+public:
+ static bool isAvailable() { return Jdenticon::getJdenticonInterface() != nullptr; }
+
+public slots:
+ QQuickImageResponse *requestImageResponse(const QString &key,
+ const QSize &requestedSize) override
+ {
+ JdenticonResponse *response = new JdenticonResponse(key, requestedSize);
+ pool.start(response);
+ return response;
+ }
+
+private:
+ QThreadPool pool;
+};
diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp
index 7eadc6df..b423304f 100644
--- a/src/MainWindow.cpp
+++ b/src/MainWindow.cpp
@@ -16,6 +16,7 @@
#include "Cache_p.h"
#include "ChatPage.h"
#include "Config.h"
+#include "JdenticonProvider.h"
#include "Logging.h"
#include "LoginPage.h"
#include "MainWindow.h"
@@ -152,10 +153,6 @@ MainWindow::MainWindow(QWidget *parent)
showChatPage();
}
});
-
- if (loadJdenticonPlugin()) {
- nhlog::ui()->info("loaded jdenticon.");
- }
}
void
@@ -428,29 +425,6 @@ MainWindow::showDialog(QWidget *dialog)
dialog->show();
}
-bool
-MainWindow::loadJdenticonPlugin()
-{
- QDir pluginsDir(qApp->applicationDirPath());
-
- bool plugins = pluginsDir.cd("plugins");
- if (plugins) {
- foreach (QString fileName, pluginsDir.entryList(QDir::Files)) {
- QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName));
- QObject *plugin = pluginLoader.instance();
- if (plugin) {
- jdenticonInteface_ = qobject_cast<JdenticonInterface *>(plugin);
- if (jdenticonInteface_) {
- nhlog::ui()->info("Found jdenticon plugin.");
- return true;
- }
- }
- }
- }
-
- nhlog::ui()->info("jdenticon plugin not found.");
- return false;
-}
void
MainWindow::showWelcomePage()
{
diff --git a/src/MainWindow.h b/src/MainWindow.h
index d423af9f..18bcfe3d 100644
--- a/src/MainWindow.h
+++ b/src/MainWindow.h
@@ -137,6 +137,4 @@ private:
//! Overlay modal used to project other widgets.
OverlayModal *modal_ = nullptr;
LoadingIndicator *spinner_ = nullptr;
-
- JdenticonInterface *jdenticonInteface_ = nullptr;
};
diff --git a/src/UserSettingsPage.cpp b/src/UserSettingsPage.cpp
index af32344c..bfe5232b 100644
--- a/src/UserSettingsPage.cpp
+++ b/src/UserSettingsPage.cpp
@@ -86,6 +86,7 @@ UserSettings::load(std::optional<QString> profile)
theme_ = settings.value("user/theme", defaultTheme_).toString();
font_ = settings.value("user/font_family", "default").toString();
avatarCircles_ = settings.value("user/avatar_circles", true).toBool();
+ useIdenticon_ = settings.value("user/use_identicon", true).toBool();
decryptSidebar_ = settings.value("user/decrypt_sidebar", true).toBool();
privacyScreen_ = settings.value("user/privacy_screen", false).toBool();
privacyScreenTimeout_ = settings.value("user/privacy_screen_timeout", 0).toInt();
@@ -596,6 +597,15 @@ UserSettings::setDisableCertificateValidation(bool disabled)
disableCertificateValidation_ = disabled;
http::client()->verify_certificates(!disabled);
emit disableCertificateValidationChanged(disabled);
+}
+
+void
+UserSettings::setUseIdenticon(bool state)
+{
+ if (state == useIdenticon_)
+ return;
+ useIdenticon_ = state;
+ emit useIdenticonChanged(useIdenticon_);
save();
}
@@ -674,6 +684,7 @@ UserSettings::save()
settings.setValue("screen_share_hide_cursor", screenShareHideCursor_);
settings.setValue("use_stun_server", useStunServer_);
settings.setValue("currentProfile", profile_);
+ settings.setValue("use_identicon", useIdenticon_);
settings.endGroup(); // user
@@ -746,6 +757,7 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge
trayToggle_ = new Toggle{this};
startInTrayToggle_ = new Toggle{this};
avatarCircles_ = new Toggle{this};
+ useIdenticon_ = new Toggle{this};
decryptSidebar_ = new Toggle(this);
privacyScreen_ = new Toggle{this};
onlyShareKeysWithVerifiedUsers_ = new Toggle(this);
@@ -779,6 +791,7 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge
trayToggle_->setChecked(settings_->tray());
startInTrayToggle_->setChecked(settings_->startInTray());
avatarCircles_->setChecked(settings_->avatarCircles());
+ useIdenticon_->setChecked(settings_->useIdenticon());
decryptSidebar_->setChecked(settings_->decryptSidebar());
privacyScreen_->setChecked(settings_->privacyScreen());
onlyShareKeysWithVerifiedUsers_->setChecked(settings_->onlyShareKeysWithVerifiedUsers());
@@ -941,6 +954,12 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge
boxWrap(tr("Circular Avatars"),
avatarCircles_,
tr("Change the appearance of user avatars in chats.\nOFF - square, ON - Circle."));
+ if (JdenticonProvider::isAvailable())
+ boxWrap(
+ tr("Use identicons"),
+ useIdenticon_,
+ tr(
+ "Display an identicon instead of a letter when a user has not set an avatar."));
boxWrap(tr("Group's sidebar"),
groupViewToggle_,
tr("Show a column containing groups and tags next to the room list."));
@@ -1263,6 +1282,10 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge
settings_->setAvatarCircles(enabled);
});
+ connect(useIdenticon_, &Toggle::toggled, this, [this](bool enabled) {
+ settings_->setUseIdenticon(enabled);
+ });
+
connect(markdown_, &Toggle::toggled, this, [this](bool enabled) {
settings_->setMarkdown(enabled);
});
diff --git a/src/UserSettingsPage.h b/src/UserSettingsPage.h
index 93b53211..bcd9439b 100644
--- a/src/UserSettingsPage.h
+++ b/src/UserSettingsPage.h
@@ -12,6 +12,7 @@
#include <QSharedPointer>
#include <QWidget>
+#include "JdenticonProvider.h"
#include <optional>
class Toggle;
@@ -105,6 +106,8 @@ class UserSettings : public QObject
Q_PROPERTY(QString homeserver READ homeserver WRITE setHomeserver NOTIFY homeserverChanged)
Q_PROPERTY(bool disableCertificateValidation READ disableCertificateValidation WRITE
setDisableCertificateValidation NOTIFY disableCertificateValidationChanged)
+ Q_PROPERTY(
+ bool useIdenticon READ useIdenticon WRITE setUseIdenticon NOTIFY useIdenticonChanged)
UserSettings();
@@ -172,6 +175,7 @@ public:
void setHomeserver(QString homeserver);
void setDisableCertificateValidation(bool disabled);
void setHiddenTags(QStringList hiddenTags);
+ void setUseIdenticon(bool state);
QString theme() const { return !theme_.isEmpty() ? theme_ : defaultTheme_; }
bool messageHoverHighlight() const { return messageHoverHighlight_; }
@@ -230,6 +234,7 @@ public:
QString homeserver() const { return homeserver_; }
bool disableCertificateValidation() const { return disableCertificateValidation_; }
QStringList hiddenTags() const { return hiddenTags_; }
+ bool useIdenticon() const { return useIdenticon_ && JdenticonProvider::isAvailable(); }
signals:
void groupViewStateChanged(bool state);
@@ -277,6 +282,7 @@ signals:
void deviceIdChanged(QString deviceId);
void homeserverChanged(QString homeserver);
void disableCertificateValidationChanged(bool disabled);
+ void useIdenticonChanged(bool state);
private:
// Default to system theme if QT_QPA_PLATFORMTHEME var is set.
@@ -330,6 +336,7 @@ private:
QString deviceId_;
QString homeserver_;
QStringList hiddenTags_;
+ bool useIdenticon_;
QSettings settings;
@@ -391,6 +398,7 @@ private:
Toggle *desktopNotifications_;
Toggle *alertOnNotification_;
Toggle *avatarCircles_;
+ Toggle *useIdenticon_;
Toggle *useStunServer_;
Toggle *decryptSidebar_;
Toggle *privacyScreen_;
diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp
index 681cbe09..ea231b03 100644
--- a/src/timeline/TimelineViewManager.cpp
+++ b/src/timeline/TimelineViewManager.cpp
@@ -141,6 +141,7 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par
, imgProvider(new MxcImageProvider())
, colorImgProvider(new ColorImageProvider())
, blurhashProvider(new BlurhashProvider())
+ , jdenticonProvider(new JdenticonProvider())
, callManager_(callManager)
, rooms_(new RoomlistModel(this))
, communities_(new CommunitiesModel(this))
@@ -310,6 +311,8 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par
view->engine()->addImageProvider("MxcImage", imgProvider);
view->engine()->addImageProvider("colorimage", colorImgProvider);
view->engine()->addImageProvider("blurhash", blurhashProvider);
+ if (JdenticonProvider::isAvailable())
+ view->engine()->addImageProvider("jdenticon", jdenticonProvider);
view->setSource(QUrl("qrc:///qml/Root.qml"));
connect(parent, &ChatPage::themeChanged, this, &TimelineViewManager::updateColorPalette);
diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h
index 4dd5e996..8991de55 100644
--- a/src/timeline/TimelineViewManager.h
+++ b/src/timeline/TimelineViewManager.h
@@ -18,6 +18,7 @@
#include "Cache.h"
#include "CallManager.h"
+#include "JdenticonProvider.h"
#include "Logging.h"
#include "TimelineModel.h"
#include "Utils.h"
@@ -141,6 +142,7 @@ private:
MxcImageProvider *imgProvider;
ColorImageProvider *colorImgProvider;
BlurhashProvider *blurhashProvider;
+ JdenticonProvider *jdenticonProvider;
CallManager *callManager_ = nullptr;
diff --git a/src/ui/Theme.h b/src/ui/Theme.h
index b5bcd4dd..254fbadf 100644
--- a/src/ui/Theme.h
+++ b/src/ui/Theme.h
@@ -11,7 +11,8 @@ namespace ui {
enum class AvatarType
{
Image,
- Letter
+ Letter,
+ Jdenticon
};
// Default font size.
|