Init rewritten avatar.

This commit is contained in:
Black Hat 2018-11-04 19:14:02 +08:00
parent c19f49a898
commit 5a46810016
24 changed files with 167 additions and 101 deletions

View File

@ -35,7 +35,7 @@ RowLayout {
round: false round: false
visible: avatarVisible visible: avatarVisible
hint: author.displayName hint: author.displayName
image: author.avatar source: author.paintable
} }
Rectangle { Rectangle {
@ -142,7 +142,7 @@ RowLayout {
height: parent.height height: parent.height
hint: modelData.displayName hint: modelData.displayName
image: modelData.avatar source: modelData.paintable
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent

View File

@ -27,7 +27,7 @@ Column {
height: parent.height height: parent.height
hint: user.displayName hint: user.displayName
image: user.avatar source: user.paintable
} }
ColumnLayout { ColumnLayout {

View File

@ -33,7 +33,7 @@ Drawer {
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
hint: room ? room.displayName : "No name" hint: room ? room.displayName : "No name"
image: spectralController.safeImage(room ? room.avatar : null) source: room ? room.paintable : null
} }
Label { Label {
@ -131,7 +131,7 @@ Drawer {
Layout.preferredWidth: height Layout.preferredWidth: height
Layout.fillHeight: true Layout.fillHeight: true
image: avatar source: paintable
hint: name hint: name
} }

View File

@ -6,7 +6,7 @@ import QtQuick.Controls.Material 2.2
import Spectral 0.1 import Spectral 0.1
Rectangle { Rectangle {
property alias image: headerImage.image property alias paintable: headerImage.source
property alias topic: headerTopicLabel.text property alias topic: headerTopicLabel.text
signal clicked() signal clicked()
@ -33,6 +33,7 @@ Rectangle {
id: headerImage id: headerImage
source: currentRoom.paintable
hint: currentRoom ? currentRoom.displayName : "No name" hint: currentRoom ? currentRoom.displayName : "No name"
} }

View File

@ -66,9 +66,8 @@ Rectangle {
Layout.preferredWidth: height Layout.preferredWidth: height
Layout.fillHeight: true Layout.fillHeight: true
source: paintable
hint: name || "No Name" hint: name || "No Name"
image: avatar
} }
ColumnLayout { ColumnLayout {

View File

@ -1,7 +1,7 @@
import QtQuick 2.9 import QtQuick 2.9
RoomPanelForm { RoomPanelForm {
roomHeader.image: spectralController.safeImage(currentRoom ? currentRoom.avatar : null) roomHeader.paintable: currentRoom ? currentRoom.paintable : null
roomHeader.topic: currentRoom ? (currentRoom.topic).replace(/(\r\n\t|\n|\r\t)/gm,"") : "" roomHeader.topic: currentRoom ? (currentRoom.topic).replace(/(\r\n\t|\n|\r\t)/gm,"") : ""
roomHeader.onClicked: roomDrawer.open() roomHeader.onClicked: roomDrawer.open()

View File

@ -272,7 +272,7 @@ Item {
Layout.preferredWidth: height Layout.preferredWidth: height
Layout.fillHeight: true Layout.fillHeight: true
image: modelData.avatar source: modelData.paintable
hint: modelData.displayName hint: modelData.displayName
} }

View File

@ -82,7 +82,7 @@ Rectangle {
width: parent.height width: parent.height
height: parent.height height: parent.height
visible: !isEmoji visible: !isEmoji
image: spectralController.safeImage(modelData.avatar) source: modelData.paintable
} }
Label { Label {
height: parent.height height: parent.height

View File

@ -153,7 +153,7 @@ ApplicationWindow {
anchors.margins: 12 anchors.margins: 12
hint: user.displayName hint: user.displayName
image: user.avatar source: user.paintable
} }
highlightColor: spectralController.color(user.id) highlightColor: spectralController.color(user.id)

View File

@ -98,7 +98,8 @@ HEADERS += \
src/accountlistmodel.h \ src/accountlistmodel.h \
src/spectraluser.h \ src/spectraluser.h \
src/notifications/manager.h \ src/notifications/manager.h \
src/utils.h src/utils.h \
src/paintable.h
SOURCES += src/main.cpp \ SOURCES += src/main.cpp \
src/controller.cpp \ src/controller.cpp \
@ -111,7 +112,8 @@ SOURCES += src/main.cpp \
src/imageitem.cpp \ src/imageitem.cpp \
src/accountlistmodel.cpp \ src/accountlistmodel.cpp \
src/spectraluser.cpp \ src/spectraluser.cpp \
src/utils.cpp src/utils.cpp \
src/paintable.cpp
unix:!mac { unix:!mac {
SOURCES += src/notifications/managerlinux.cpp SOURCES += src/notifications/managerlinux.cpp

View File

@ -220,11 +220,6 @@ void Controller::playAudio(QUrl localFile) {
connect(player, &QMediaPlayer::stateChanged, [=] { player->deleteLater(); }); connect(player, &QMediaPlayer::stateChanged, [=] { player->deleteLater(); });
} }
QImage Controller::safeImage(QImage image) {
if (image.isNull()) return QImage();
return image;
}
QColor Controller::color(QString userId) { QColor Controller::color(QString userId) {
return QColor(SettingsGroup("UI/Color").value(userId, "#498882").toString()); return QColor(SettingsGroup("UI/Color").value(userId, "#498882").toString());
} }

View File

@ -84,8 +84,6 @@ class Controller : public QObject {
const QString& roomName, const QString& senderName, const QString& roomName, const QString& senderName,
const QString& text, const QImage& icon, const QString& text, const QImage& icon,
const QUrl& iconPath); const QUrl& iconPath);
static QImage safeImage(QImage image);
}; };
#endif // CONTROLLER_H #endif // CONTROLLER_H

View File

@ -11,7 +11,37 @@ void ImageItem::paint(QPainter *painter) {
painter->setRenderHint(QPainter::Antialiasing, true); painter->setRenderHint(QPainter::Antialiasing, true);
if (m_image.isNull()) { if (!m_paintable) {
paintHint(painter, bounding_rect);
return;
}
QImage image = m_paintable->image(int(bounding_rect.width()),
int(bounding_rect.height()));
if (image.isNull()) {
paintHint(painter, bounding_rect);
return;
}
QImage scaled = image.scaled(
int(bounding_rect.width()) + 1, int(bounding_rect.height()) + 1,
Qt::KeepAspectRatioByExpanding, Qt::FastTransformation);
QPointF center = bounding_rect.center() - scaled.rect().center();
if (m_round) {
QPainterPath clip;
clip.addEllipse(
0, 0, bounding_rect.width(),
bounding_rect.height()); // this is the shape we want to clip to
painter->setClipPath(clip);
}
painter->drawImage(center, scaled);
}
void ImageItem::paintHint(QPainter *painter, QRectF bounding_rect) {
painter->setPen(Qt::NoPen); painter->setPen(Qt::NoPen);
if (m_color.isEmpty()) if (m_color.isEmpty())
painter->setBrush(QColor(stringtoColor(m_hint))); painter->setBrush(QColor(stringtoColor(m_hint)));
@ -33,29 +63,16 @@ void ImageItem::paint(QPainter *painter) {
painter->drawText( painter->drawText(
QRect(0, 0, int(bounding_rect.width()), int(bounding_rect.height())), QRect(0, 0, int(bounding_rect.width()), int(bounding_rect.height())),
Qt::AlignCenter, m_hint.at(0).toUpper()); Qt::AlignCenter, m_hint.at(0).toUpper());
return;
}
QImage scaled = m_image.scaled(
int(bounding_rect.width()) + 1, int(bounding_rect.height()) + 1,
Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation);
QPointF center = bounding_rect.center() - scaled.rect().center();
if (m_round) {
QPainterPath clip;
clip.addEllipse(
0, 0, bounding_rect.width(),
bounding_rect.height()); // this is the shape we want to clip to
painter->setClipPath(clip);
}
painter->drawImage(center, scaled);
} }
void ImageItem::setImage(const QImage &image) { void ImageItem::setPaintable(Paintable *paintable) {
m_image = image; if (!paintable) return;
emit imageChanged(); disconnect(m_paintable);
m_paintable = paintable;
connect(m_paintable, &Paintable::paintableChanged, this, [=] {
this->update();
});
emit paintableChanged();
update(); update();
} }

View File

@ -7,21 +7,24 @@
#include <QQuickItem> #include <QQuickItem>
#include <QQuickPaintedItem> #include <QQuickPaintedItem>
#include "paintable.h"
class ImageItem : public QQuickPaintedItem { class ImageItem : public QQuickPaintedItem {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QImage image READ image WRITE setImage NOTIFY imageChanged) Q_PROPERTY(Paintable* source READ paintable WRITE setPaintable NOTIFY
paintableChanged)
Q_PROPERTY(QString hint READ hint WRITE setHint NOTIFY hintChanged) Q_PROPERTY(QString hint READ hint WRITE setHint NOTIFY hintChanged)
Q_PROPERTY(QString defaultColor READ defaultColor WRITE setDefaultColor NOTIFY Q_PROPERTY(QString defaultColor READ defaultColor WRITE setDefaultColor NOTIFY
defaultColorChanged) defaultColorChanged)
Q_PROPERTY(bool round READ round WRITE setRound NOTIFY roundChanged) Q_PROPERTY(bool round READ round WRITE setRound NOTIFY roundChanged)
public: public:
ImageItem(QQuickItem *parent = nullptr); ImageItem(QQuickItem* parent = nullptr);
void paint(QPainter *painter); void paint(QPainter* painter);
QImage image() const { return m_image; } Paintable* paintable() { return m_paintable; }
void setImage(const QImage &image); void setPaintable(Paintable* paintable);
QString hint() { return m_hint; } QString hint() { return m_hint; }
void setHint(QString hint); void setHint(QString hint);
@ -33,18 +36,19 @@ class ImageItem : public QQuickPaintedItem {
void setRound(bool value); void setRound(bool value);
signals: signals:
void imageChanged(); void paintableChanged();
void hintChanged(); void hintChanged();
void defaultColorChanged(); void defaultColorChanged();
void roundChanged(); void roundChanged();
private: private:
QImage m_image; Paintable* m_paintable = nullptr;
QString m_hint = "H"; QString m_hint = "H";
QString m_color; QString m_color;
bool m_round = true; bool m_round = true;
QString stringtoColor(QString string); QString stringtoColor(QString string);
void paintHint(QPainter* painter, QRectF bounding_rect);
}; };
#endif // IMAGEITEM_H #endif // IMAGEITEM_H

3
src/paintable.cpp Normal file
View File

@ -0,0 +1,3 @@
#include "paintable.h"
Paintable::Paintable(QObject *parent) : QObject(parent) {}

19
src/paintable.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef PAINTABLE_H
#define PAINTABLE_H
#include <QImage>
#include <QObject>
class Paintable : public QObject {
Q_OBJECT
public:
Paintable(QObject* parent = nullptr);
virtual QImage image(int) = 0;
virtual QImage image(int, int) = 0;
signals:
void paintableChanged();
};
#endif // PAINTABLE_H

View File

@ -73,21 +73,23 @@ void RoomListModel::connectRoomSignals(SpectralRoom* room) {
connect(room, &Room::tagsChanged, this, [=] { refresh(room); }); connect(room, &Room::tagsChanged, this, [=] { refresh(room); });
connect(room, &Room::joinStateChanged, this, [=] { refresh(room); }); connect(room, &Room::joinStateChanged, this, [=] { refresh(room); });
connect(room, &Room::avatarChanged, this, connect(room, &Room::avatarChanged, this,
[=] { refresh(room, {AvatarRole}); }); [=] { refresh(room, {PaintableRole}); });
connect(room, &Room::addedMessages, this, connect(room, &Room::addedMessages, this,
[=] { refresh(room, {LastEventRole}); }); [=] { refresh(room, {LastEventRole}); });
connect( connect(room, &Room::aboutToAddNewMessages, this,
room, &Room::aboutToAddNewMessages, this,
[=](QMatrixClient::RoomEventsRange eventsRange) { [=](QMatrixClient::RoomEventsRange eventsRange) {
RoomEvent* event = (eventsRange.end() - 1)->get(); RoomEvent* event = (eventsRange.end() - 1)->get();
User* sender = room->user(event->senderId()); User* sender = room->user(event->senderId());
if (sender == room->localUser()) return; if (sender == room->localUser()) return;
QUrl _url = room->avatarUrl(); QUrl _url = room->avatarUrl();
emit newMessage( emit newMessage(
room->id(), event->id(), room->displayName(), sender->displayname(), room->id(), event->id(), room->displayName(),
utils::eventToString(*event), room->avatar(128), sender->displayname(), utils::eventToString(*event),
QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + room->avatar(128),
"/avatar/" + _url.authority() + '_' + _url.fileName() + ".png")); QUrl::fromLocalFile(QStandardPaths::writableLocation(
QStandardPaths::CacheLocation) +
"/avatar/" + _url.authority() + '_' +
_url.fileName() + ".png"));
}); });
} }
@ -152,10 +154,7 @@ QVariant RoomListModel::data(const QModelIndex& index, int role) const {
} }
SpectralRoom* room = m_rooms.at(index.row()); SpectralRoom* room = m_rooms.at(index.row());
if (role == NameRole) return room->displayName(); if (role == NameRole) return room->displayName();
if (role == AvatarRole) { if (role == PaintableRole) return QVariant::fromValue(room->paintable());
if (!room->avatarUrl().isEmpty()) return room->avatar(64, 64);
return QImage();
}
if (role == TopicRole) return room->topic(); if (role == TopicRole) return room->topic();
if (role == CategoryRole) { if (role == CategoryRole) {
if (room->joinState() == JoinState::Invite) return RoomType::Invited; if (room->joinState() == JoinState::Invite) return RoomType::Invited;
@ -195,7 +194,7 @@ void RoomListModel::unreadMessagesChanged(SpectralRoom* room) {
QHash<int, QByteArray> RoomListModel::roleNames() const { QHash<int, QByteArray> RoomListModel::roleNames() const {
QHash<int, QByteArray> roles; QHash<int, QByteArray> roles;
roles[NameRole] = "name"; roles[NameRole] = "name";
roles[AvatarRole] = "avatar"; roles[PaintableRole] = "paintable";
roles[TopicRole] = "topic"; roles[TopicRole] = "topic";
roles[CategoryRole] = "category"; roles[CategoryRole] = "category";
roles[UnreadCountRole] = "unreadCount"; roles[UnreadCountRole] = "unreadCount";

View File

@ -31,7 +31,7 @@ class RoomListModel : public QAbstractListModel {
public: public:
enum EventRoles { enum EventRoles {
NameRole = Qt::UserRole + 1, NameRole = Qt::UserRole + 1,
AvatarRole, PaintableRole,
TopicRole, TopicRole,
CategoryRole, CategoryRole,
UnreadCountRole, UnreadCountRole,

View File

@ -17,8 +17,6 @@
SpectralRoom::SpectralRoom(Connection* connection, QString roomId, SpectralRoom::SpectralRoom(Connection* connection, QString roomId,
JoinState joinState) JoinState joinState)
: Room(connection, std::move(roomId), joinState) { : Room(connection, std::move(roomId), joinState) {
connect(this, &Room::avatarChanged, this,
&SpectralRoom::inheritedAvatarChanged);
connect(this, &SpectralRoom::notificationCountChanged, this, connect(this, &SpectralRoom::notificationCountChanged, this,
&SpectralRoom::countChanged); &SpectralRoom::countChanged);
connect(this, &SpectralRoom::highlightCountChanged, this, connect(this, &SpectralRoom::highlightCountChanged, this,

View File

@ -1,6 +1,7 @@
#ifndef SpectralRoom_H #ifndef SpectralRoom_H
#define SpectralRoom_H #define SpectralRoom_H
#include "paintable.h"
#include "room.h" #include "room.h"
#include "spectraluser.h" #include "spectraluser.h"
@ -9,9 +10,29 @@
using namespace QMatrixClient; using namespace QMatrixClient;
class RoomPaintable : public Paintable {
Q_OBJECT
public:
RoomPaintable(Room* parent) : Paintable(parent), m_room(parent) {
connect(m_room, &Room::avatarChanged, [=] { emit paintableChanged(); });
}
QImage image(int dimension) override {
if (!m_room) return QImage();
return m_room->avatar(dimension);
}
QImage image(int width, int height) override {
if (!m_room) return QImage();
return m_room->avatar(width, height);
}
private:
Room* m_room;
};
class SpectralRoom : public Room { class SpectralRoom : public Room {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QImage avatar READ getAvatar NOTIFY inheritedAvatarChanged) Q_PROPERTY(Paintable* paintable READ paintable CONSTANT)
Q_PROPERTY(bool hasUsersTyping READ hasUsersTyping NOTIFY typingChanged) Q_PROPERTY(bool hasUsersTyping READ hasUsersTyping NOTIFY typingChanged)
Q_PROPERTY(QString usersTyping READ getUsersTyping NOTIFY typingChanged) Q_PROPERTY(QString usersTyping READ getUsersTyping NOTIFY typingChanged)
Q_PROPERTY(QString cachedInput READ cachedInput WRITE setCachedInput NOTIFY Q_PROPERTY(QString cachedInput READ cachedInput WRITE setCachedInput NOTIFY
@ -26,7 +47,7 @@ class SpectralRoom : public Room {
explicit SpectralRoom(Connection* connection, QString roomId, explicit SpectralRoom(Connection* connection, QString roomId,
JoinState joinState = {}); JoinState joinState = {});
QImage getAvatar() { return avatar(128); } Paintable* paintable() { return new RoomPaintable(this); }
const QString& cachedInput() const { return m_cachedInput; } const QString& cachedInput() const { return m_cachedInput; }
void setCachedInput(const QString& input) { void setCachedInput(const QString& input) {
@ -100,7 +121,6 @@ class SpectralRoom : public Room {
signals: signals:
void cachedInputChanged(); void cachedInputChanged();
void busyChanged(); void busyChanged();
void inheritedAvatarChanged(); // https://bugreports.qt.io/browse/QTBUG-7684
void hasFileUploadingChanged(); void hasFileUploadingChanged();
void fileUploadingProgressChanged(); void fileUploadingProgressChanged();

View File

@ -1,7 +1 @@
#include "spectraluser.h" #include "spectraluser.h"
SpectralUser::SpectralUser(QString userId, Connection* connection)
: User(userId, connection) {
connect(this, &User::avatarChanged, this,
&SpectralUser::inheritedAvatarChanged);
}

View File

@ -1,6 +1,7 @@
#ifndef SpectralUser_H #ifndef SpectralUser_H
#define SpectralUser_H #define SpectralUser_H
#include "paintable.h"
#include "room.h" #include "room.h"
#include "user.h" #include "user.h"
@ -8,18 +9,34 @@
using namespace QMatrixClient; using namespace QMatrixClient;
class UserPaintable : public Paintable {
Q_OBJECT
public:
UserPaintable(User* parent) : Paintable(parent), m_user(parent) {
connect(m_user, &User::avatarChanged, [=] { emit paintableChanged(); });
}
QImage image(int dimension) override {
if (!m_user) return QImage();
return m_user->avatar(dimension);
}
QImage image(int width, int height) override {
if (!m_user) return QImage();
return m_user->avatar(width, height);
}
private:
User* m_user;
};
class SpectralUser : public User { class SpectralUser : public User {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QImage avatar READ getAvatar NOTIFY inheritedAvatarChanged) Q_PROPERTY(Paintable* paintable READ paintable CONSTANT)
public: public:
SpectralUser(QString userId, Connection* connection); SpectralUser(QString userId, Connection* connection)
: User(userId, connection) {}
QImage getAvatar() { return avatar(128); } Paintable* paintable() { return new UserPaintable(this); }
signals:
void inheritedAvatarChanged(
User* user,
const Room* roomContext); // https://bugreports.qt.io/browse/QTBUG-7684
}; };
#endif // SpectralUser_H #endif // SpectralUser_H

View File

@ -8,6 +8,8 @@
#include <room.h> #include <room.h>
#include <user.h> #include <user.h>
#include "spectraluser.h"
UserListModel::UserListModel(QObject* parent) UserListModel::UserListModel(QObject* parent)
: QAbstractListModel(parent), m_currentRoom(nullptr) {} : QAbstractListModel(parent), m_currentRoom(nullptr) {}
@ -70,10 +72,8 @@ QVariant UserListModel::data(const QModelIndex& index, int role) const {
if (role == UserIDRole) { if (role == UserIDRole) {
return user->id(); return user->id();
} }
if (role == AvatarRole) { if (role == PaintableRole) {
if (!user->avatarUrl(m_currentRoom).isEmpty()) return QVariant::fromValue((static_cast<SpectralUser*>(user))->paintable());
return user->avatar(64, m_currentRoom);
return QImage();
} }
return QVariant(); return QVariant();
@ -115,7 +115,7 @@ void UserListModel::refresh(QMatrixClient::User* user, QVector<int> roles) {
void UserListModel::avatarChanged(QMatrixClient::User* user, void UserListModel::avatarChanged(QMatrixClient::User* user,
const QMatrixClient::Room* context) { const QMatrixClient::Room* context) {
if (context == m_currentRoom) refresh(user, {AvatarRole}); if (context == m_currentRoom) refresh(user, {PaintableRole});
} }
int UserListModel::findUserPos(User* user) const { int UserListModel::findUserPos(User* user) const {
@ -130,6 +130,6 @@ QHash<int, QByteArray> UserListModel::roleNames() const {
QHash<int, QByteArray> roles; QHash<int, QByteArray> roles;
roles[NameRole] = "name"; roles[NameRole] = "name";
roles[UserIDRole] = "userId"; roles[UserIDRole] = "userId";
roles[AvatarRole] = "avatar"; roles[PaintableRole] = "paintable";
return roles; return roles;
} }

View File

@ -17,7 +17,7 @@ class UserListModel : public QAbstractListModel {
Q_PROPERTY( Q_PROPERTY(
QMatrixClient::Room* room READ room WRITE setRoom NOTIFY roomChanged) QMatrixClient::Room* room READ room WRITE setRoom NOTIFY roomChanged)
public: public:
enum EventRoles { NameRole = Qt::UserRole + 1, UserIDRole, AvatarRole }; enum EventRoles { NameRole = Qt::UserRole + 1, UserIDRole, PaintableRole };
using User = QMatrixClient::User; using User = QMatrixClient::User;