mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-22 03:00:46 +03:00
Implement avatars in qml timeline
This commit is contained in:
parent
aae295cb02
commit
ebeb1eb772
12 changed files with 190 additions and 8 deletions
|
@ -231,6 +231,7 @@ set(SRC_FILES
|
|||
src/Logging.cpp
|
||||
src/MainWindow.cpp
|
||||
src/MatrixClient.cpp
|
||||
src/MxcImageProvider.cpp
|
||||
src/QuickSwitcher.cpp
|
||||
src/Olm.cpp
|
||||
src/RegisterPage.cpp
|
||||
|
|
45
resources/qml/Avatar.qml
Normal file
45
resources/qml/Avatar.qml
Normal file
|
@ -0,0 +1,45 @@
|
|||
import QtQuick 2.6
|
||||
import QtGraphicalEffects 1.0
|
||||
import Qt.labs.settings 1.0
|
||||
|
||||
Rectangle {
|
||||
id: avatar
|
||||
width: 48
|
||||
height: 48
|
||||
radius: settings.avatar_circles ? height/2 : 3
|
||||
|
||||
Settings {
|
||||
id: settings
|
||||
category: "user"
|
||||
property bool avatar_circles: true
|
||||
}
|
||||
|
||||
property alias url: img.source
|
||||
property string displayName
|
||||
|
||||
Text {
|
||||
anchors.fill: parent
|
||||
text: String.fromCodePoint(displayName.codePointAt(0))
|
||||
color: colors.text
|
||||
font.pixelSize: avatar.height/2
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
Image {
|
||||
id: img
|
||||
anchors.fill: parent
|
||||
asynchronous: true
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: OpacityMask {
|
||||
maskSource: Rectangle {
|
||||
anchors.fill: parent
|
||||
width: avatar.width
|
||||
height: avatar.height
|
||||
radius: settings.avatar_circles ? height/2 : 3
|
||||
}
|
||||
}
|
||||
}
|
||||
color: colors.dark
|
||||
}
|
|
@ -181,10 +181,11 @@ Rectangle {
|
|||
Row {
|
||||
height: userName.height
|
||||
spacing: 4
|
||||
Rectangle {
|
||||
Avatar {
|
||||
width: 48
|
||||
height: 48
|
||||
color: "green"
|
||||
url: chat.model.avatarUrl(section.split(" ")[0]).replace("mxc://", "image://MxcImage/")
|
||||
displayName: chat.model.displayName(section.split(" ")[0])
|
||||
}
|
||||
|
||||
Text {
|
||||
|
|
|
@ -116,6 +116,7 @@
|
|||
</qresource>
|
||||
<qresource prefix="/">
|
||||
<file>qml/TimelineView.qml</file>
|
||||
<file>qml/Avatar.qml</file>
|
||||
<file>qml/delegates/TextMessage.qml</file>
|
||||
<file>qml/delegates/NoticeMessage.qml</file>
|
||||
</qresource>
|
||||
|
|
79
src/MxcImageProvider.cpp
Normal file
79
src/MxcImageProvider.cpp
Normal file
|
@ -0,0 +1,79 @@
|
|||
#include "MxcImageProvider.h"
|
||||
|
||||
#include "Cache.h"
|
||||
|
||||
void
|
||||
MxcImageResponse::run()
|
||||
{
|
||||
if (m_requestedSize.isValid()) {
|
||||
QString fileName = QString("%1_%2x%3")
|
||||
.arg(m_id)
|
||||
.arg(m_requestedSize.width())
|
||||
.arg(m_requestedSize.height());
|
||||
|
||||
auto data = cache::client()->image(fileName);
|
||||
if (!data.isNull() && m_image.loadFromData(data)) {
|
||||
m_image = m_image.scaled(m_requestedSize, Qt::KeepAspectRatio);
|
||||
m_image.setText("mxc url", "mxc://" + m_id);
|
||||
emit finished();
|
||||
return;
|
||||
}
|
||||
|
||||
mtx::http::ThumbOpts opts;
|
||||
opts.mxc_url = "mxc://" + m_id.toStdString();
|
||||
opts.width = m_requestedSize.width() > 0 ? m_requestedSize.width() : -1;
|
||||
opts.height = m_requestedSize.height() > 0 ? m_requestedSize.height() : -1;
|
||||
opts.method = "scale";
|
||||
http::client()->get_thumbnail(
|
||||
opts, [this, fileName](const std::string &res, mtx::http::RequestErr err) {
|
||||
if (err) {
|
||||
nhlog::net()->error("Failed to download image {}",
|
||||
m_id.toStdString());
|
||||
m_error = "Failed download";
|
||||
emit finished();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto data = QByteArray(res.data(), res.size());
|
||||
cache::client()->saveImage(fileName, data);
|
||||
m_image.loadFromData(data);
|
||||
m_image = m_image.scaled(m_requestedSize, Qt::KeepAspectRatio);
|
||||
m_image.setText("mxc url", "mxc://" + m_id);
|
||||
|
||||
emit finished();
|
||||
});
|
||||
} else {
|
||||
auto data = cache::client()->image(m_id);
|
||||
if (!data.isNull() && m_image.loadFromData(data)) {
|
||||
m_image.setText("mxc url", "mxc://" + m_id);
|
||||
emit finished();
|
||||
return;
|
||||
}
|
||||
|
||||
http::client()->download(
|
||||
"mxc://" + m_id.toStdString(),
|
||||
[this](const std::string &res,
|
||||
const std::string &,
|
||||
const std::string &originalFilename,
|
||||
mtx::http::RequestErr err) {
|
||||
if (err) {
|
||||
nhlog::net()->error("Failed to download image {}",
|
||||
m_id.toStdString());
|
||||
m_error = "Failed download";
|
||||
emit finished();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto data = QByteArray(res.data(), res.size());
|
||||
m_image.loadFromData(data);
|
||||
m_image.setText("original filename",
|
||||
QString::fromStdString(originalFilename));
|
||||
m_image.setText("mxc url", "mxc://" + m_id);
|
||||
cache::client()->saveImage(m_id, data);
|
||||
|
||||
emit finished();
|
||||
});
|
||||
}
|
||||
}
|
48
src/MxcImageProvider.h
Normal file
48
src/MxcImageProvider.h
Normal file
|
@ -0,0 +1,48 @@
|
|||
#pragma once
|
||||
|
||||
#include <QQuickAsyncImageProvider>
|
||||
#include <QQuickImageResponse>
|
||||
|
||||
#include <QImage>
|
||||
#include <QThreadPool>
|
||||
|
||||
class MxcImageResponse
|
||||
: public QQuickImageResponse
|
||||
, public QRunnable
|
||||
{
|
||||
public:
|
||||
MxcImageResponse(const QString &id, const QSize &requestedSize)
|
||||
: m_id(id)
|
||||
, m_requestedSize(requestedSize)
|
||||
{
|
||||
setAutoDelete(false);
|
||||
}
|
||||
|
||||
QQuickTextureFactory *textureFactory() const override
|
||||
{
|
||||
return QQuickTextureFactory::textureFactoryForImage(m_image);
|
||||
}
|
||||
QString errorString() const override { return m_error; }
|
||||
|
||||
void run() override;
|
||||
|
||||
QString m_id, m_error;
|
||||
QSize m_requestedSize;
|
||||
QImage m_image;
|
||||
};
|
||||
|
||||
class MxcImageProvider : public QQuickAsyncImageProvider
|
||||
{
|
||||
public:
|
||||
QQuickImageResponse *requestImageResponse(const QString &id,
|
||||
const QSize &requestedSize) override
|
||||
{
|
||||
MxcImageResponse *response = new MxcImageResponse(id, requestedSize);
|
||||
pool.start(response);
|
||||
return response;
|
||||
}
|
||||
|
||||
private:
|
||||
QThreadPool pool;
|
||||
};
|
||||
|
|
@ -142,7 +142,7 @@ RoomInfoListItem::resizeEvent(QResizeEvent *)
|
|||
void
|
||||
RoomInfoListItem::paintEvent(QPaintEvent *event)
|
||||
{
|
||||
bool rounded = QSettings().value("user/avatar/circles", true).toBool();
|
||||
bool rounded = QSettings().value("user/avatar_circles", true).toBool();
|
||||
|
||||
Q_UNUSED(event);
|
||||
|
||||
|
|
|
@ -53,7 +53,7 @@ UserSettings::load()
|
|||
isReadReceiptsEnabled_ = settings.value("user/read_receipts", true).toBool();
|
||||
theme_ = settings.value("user/theme", defaultTheme_).toString();
|
||||
font_ = settings.value("user/font_family", "default").toString();
|
||||
avatarCircles_ = settings.value("user/avatar/circles", true).toBool();
|
||||
avatarCircles_ = settings.value("user/avatar_circles", true).toBool();
|
||||
emojiFont_ = settings.value("user/emoji_font_family", "default").toString();
|
||||
baseFontSize_ = settings.value("user/font_size", QFont().pointSizeF()).toDouble();
|
||||
|
||||
|
@ -119,9 +119,7 @@ UserSettings::save()
|
|||
settings.setValue("start_in_tray", isStartInTrayEnabled_);
|
||||
settings.endGroup();
|
||||
|
||||
settings.beginGroup("avatar");
|
||||
settings.setValue("circles", avatarCircles_);
|
||||
settings.endGroup();
|
||||
settings.setValue("avatar_circles", avatarCircles_);
|
||||
|
||||
settings.setValue("font_size", baseFontSize_);
|
||||
settings.setValue("typing_notifications", isTypingNotificationsEnabled_);
|
||||
|
|
|
@ -325,6 +325,12 @@ TimelineModel::displayName(QString id) const
|
|||
return Cache::displayName(room_id_, id);
|
||||
}
|
||||
|
||||
QString
|
||||
TimelineModel::avatarUrl(QString id) const
|
||||
{
|
||||
return Cache::avatarUrl(room_id_, id);
|
||||
}
|
||||
|
||||
QString
|
||||
TimelineModel::formatDateSeparator(QDate date) const
|
||||
{
|
||||
|
|
|
@ -90,6 +90,7 @@ public:
|
|||
|
||||
Q_INVOKABLE QColor userColor(QString id, QColor background);
|
||||
Q_INVOKABLE QString displayName(QString id) const;
|
||||
Q_INVOKABLE QString avatarUrl(QString id) const;
|
||||
Q_INVOKABLE QString formatDateSeparator(QDate date) const;
|
||||
Q_INVOKABLE QString escapeEmoji(QString str) const;
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <QQmlContext>
|
||||
|
||||
#include "Logging.h"
|
||||
#include "MxcImageProvider.h"
|
||||
|
||||
TimelineViewManager::TimelineViewManager(QWidget *parent)
|
||||
{
|
||||
|
@ -18,6 +19,7 @@ TimelineViewManager::TimelineViewManager(QWidget *parent)
|
|||
container = QWidget::createWindowContainer(view, parent);
|
||||
container->setMinimumSize(200, 200);
|
||||
view->rootContext()->setContextProperty("timelineManager", this);
|
||||
view->engine()->addImageProvider("MxcImage", new MxcImageProvider());
|
||||
view->setSource(QUrl("qrc:///qml/TimelineView.qml"));
|
||||
}
|
||||
|
||||
|
|
|
@ -101,7 +101,7 @@ Avatar::setIcon(const QIcon &icon)
|
|||
void
|
||||
Avatar::paintEvent(QPaintEvent *)
|
||||
{
|
||||
bool rounded = QSettings().value("user/avatar/circles", true).toBool();
|
||||
bool rounded = QSettings().value("user/avatar_circles", true).toBool();
|
||||
|
||||
QPainter painter(this);
|
||||
painter.setRenderHint(QPainter::Antialiasing);
|
||||
|
|
Loading…
Reference in a new issue