Init rewritten avatar.

square-messages
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
visible: avatarVisible
hint: author.displayName
image: author.avatar
source: author.paintable
}
Rectangle {
@ -142,7 +142,7 @@ RowLayout {
height: parent.height
hint: modelData.displayName
image: modelData.avatar
source: modelData.paintable
MouseArea {
anchors.fill: parent

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
import QtQuick 2.9
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.onClicked: roomDrawer.open()

View File

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

View File

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

View File

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

View File

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

View File

@ -220,11 +220,6 @@ void Controller::playAudio(QUrl localFile) {
connect(player, &QMediaPlayer::stateChanged, [=] { player->deleteLater(); });
}
QImage Controller::safeImage(QImage image) {
if (image.isNull()) return QImage();
return image;
}
QColor Controller::color(QString userId) {
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& text, const QImage& icon,
const QUrl& iconPath);
static QImage safeImage(QImage image);
};
#endif // CONTROLLER_H

View File

@ -11,34 +11,22 @@ void ImageItem::paint(QPainter *painter) {
painter->setRenderHint(QPainter::Antialiasing, true);
if (m_image.isNull()) {
painter->setPen(Qt::NoPen);
if (m_color.isEmpty())
painter->setBrush(QColor(stringtoColor(m_hint)));
else
painter->setBrush(QColor(m_color));
if (m_round)
painter->drawEllipse(0, 0, int(bounding_rect.width()),
int(bounding_rect.height()));
else
painter->drawRect(0, 0, int(bounding_rect.width()),
int(bounding_rect.height()));
painter->setPen(QPen(Qt::white, 2));
QFont font;
font.setStyleHint(QFont::SansSerif);
font.setPixelSize(int(bounding_rect.width() / 2));
font.setBold(true);
painter->setFont(font);
painter->drawText(
QRect(0, 0, int(bounding_rect.width()), int(bounding_rect.height())),
Qt::AlignCenter, m_hint.at(0).toUpper());
if (!m_paintable) {
paintHint(painter, bounding_rect);
return;
}
QImage scaled = m_image.scaled(
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::SmoothTransformation);
Qt::KeepAspectRatioByExpanding, Qt::FastTransformation);
QPointF center = bounding_rect.center() - scaled.rect().center();
@ -53,9 +41,38 @@ void ImageItem::paint(QPainter *painter) {
painter->drawImage(center, scaled);
}
void ImageItem::setImage(const QImage &image) {
m_image = image;
emit imageChanged();
void ImageItem::paintHint(QPainter *painter, QRectF bounding_rect) {
painter->setPen(Qt::NoPen);
if (m_color.isEmpty())
painter->setBrush(QColor(stringtoColor(m_hint)));
else
painter->setBrush(QColor(m_color));
if (m_round)
painter->drawEllipse(0, 0, int(bounding_rect.width()),
int(bounding_rect.height()));
else
painter->drawRect(0, 0, int(bounding_rect.width()),
int(bounding_rect.height()));
painter->setPen(QPen(Qt::white, 2));
QFont font;
font.setStyleHint(QFont::SansSerif);
font.setPixelSize(int(bounding_rect.width() / 2));
font.setBold(true);
painter->setFont(font);
painter->drawText(
QRect(0, 0, int(bounding_rect.width()), int(bounding_rect.height())),
Qt::AlignCenter, m_hint.at(0).toUpper());
}
void ImageItem::setPaintable(Paintable *paintable) {
if (!paintable) return;
disconnect(m_paintable);
m_paintable = paintable;
connect(m_paintable, &Paintable::paintableChanged, this, [=] {
this->update();
});
emit paintableChanged();
update();
}

View File

@ -7,21 +7,24 @@
#include <QQuickItem>
#include <QQuickPaintedItem>
#include "paintable.h"
class ImageItem : public QQuickPaintedItem {
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 defaultColor READ defaultColor WRITE setDefaultColor NOTIFY
defaultColorChanged)
Q_PROPERTY(bool round READ round WRITE setRound NOTIFY roundChanged)
public:
ImageItem(QQuickItem *parent = nullptr);
ImageItem(QQuickItem* parent = nullptr);
void paint(QPainter *painter);
void paint(QPainter* painter);
QImage image() const { return m_image; }
void setImage(const QImage &image);
Paintable* paintable() { return m_paintable; }
void setPaintable(Paintable* paintable);
QString hint() { return m_hint; }
void setHint(QString hint);
@ -33,18 +36,19 @@ class ImageItem : public QQuickPaintedItem {
void setRound(bool value);
signals:
void imageChanged();
void paintableChanged();
void hintChanged();
void defaultColorChanged();
void roundChanged();
private:
QImage m_image;
Paintable* m_paintable = nullptr;
QString m_hint = "H";
QString m_color;
bool m_round = true;
QString stringtoColor(QString string);
void paintHint(QPainter* painter, QRectF bounding_rect);
};
#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,22 +73,24 @@ void RoomListModel::connectRoomSignals(SpectralRoom* room) {
connect(room, &Room::tagsChanged, this, [=] { refresh(room); });
connect(room, &Room::joinStateChanged, this, [=] { refresh(room); });
connect(room, &Room::avatarChanged, this,
[=] { refresh(room, {AvatarRole}); });
[=] { refresh(room, {PaintableRole}); });
connect(room, &Room::addedMessages, this,
[=] { refresh(room, {LastEventRole}); });
connect(
room, &Room::aboutToAddNewMessages, this,
[=](QMatrixClient::RoomEventsRange eventsRange) {
RoomEvent* event = (eventsRange.end() - 1)->get();
User* sender = room->user(event->senderId());
if (sender == room->localUser()) return;
QUrl _url = room->avatarUrl();
emit newMessage(
room->id(), event->id(), room->displayName(), sender->displayname(),
utils::eventToString(*event), room->avatar(128),
QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) +
"/avatar/" + _url.authority() + '_' + _url.fileName() + ".png"));
});
connect(room, &Room::aboutToAddNewMessages, this,
[=](QMatrixClient::RoomEventsRange eventsRange) {
RoomEvent* event = (eventsRange.end() - 1)->get();
User* sender = room->user(event->senderId());
if (sender == room->localUser()) return;
QUrl _url = room->avatarUrl();
emit newMessage(
room->id(), event->id(), room->displayName(),
sender->displayname(), utils::eventToString(*event),
room->avatar(128),
QUrl::fromLocalFile(QStandardPaths::writableLocation(
QStandardPaths::CacheLocation) +
"/avatar/" + _url.authority() + '_' +
_url.fileName() + ".png"));
});
}
void RoomListModel::updateRoom(Room* room, Room* prev) {
@ -152,10 +154,7 @@ QVariant RoomListModel::data(const QModelIndex& index, int role) const {
}
SpectralRoom* room = m_rooms.at(index.row());
if (role == NameRole) return room->displayName();
if (role == AvatarRole) {
if (!room->avatarUrl().isEmpty()) return room->avatar(64, 64);
return QImage();
}
if (role == PaintableRole) return QVariant::fromValue(room->paintable());
if (role == TopicRole) return room->topic();
if (role == CategoryRole) {
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> roles;
roles[NameRole] = "name";
roles[AvatarRole] = "avatar";
roles[PaintableRole] = "paintable";
roles[TopicRole] = "topic";
roles[CategoryRole] = "category";
roles[UnreadCountRole] = "unreadCount";

View File

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

View File

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

View File

@ -1,6 +1,7 @@
#ifndef SpectralRoom_H
#define SpectralRoom_H
#include "paintable.h"
#include "room.h"
#include "spectraluser.h"
@ -9,9 +10,29 @@
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 {
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(QString usersTyping READ getUsersTyping NOTIFY typingChanged)
Q_PROPERTY(QString cachedInput READ cachedInput WRITE setCachedInput NOTIFY
@ -26,7 +47,7 @@ class SpectralRoom : public Room {
explicit SpectralRoom(Connection* connection, QString roomId,
JoinState joinState = {});
QImage getAvatar() { return avatar(128); }
Paintable* paintable() { return new RoomPaintable(this); }
const QString& cachedInput() const { return m_cachedInput; }
void setCachedInput(const QString& input) {
@ -100,7 +121,6 @@ class SpectralRoom : public Room {
signals:
void cachedInputChanged();
void busyChanged();
void inheritedAvatarChanged(); // https://bugreports.qt.io/browse/QTBUG-7684
void hasFileUploadingChanged();
void fileUploadingProgressChanged();

View File

@ -1,7 +1 @@
#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
#define SpectralUser_H
#include "paintable.h"
#include "room.h"
#include "user.h"
@ -8,18 +9,34 @@
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 {
Q_OBJECT
Q_PROPERTY(QImage avatar READ getAvatar NOTIFY inheritedAvatarChanged)
Q_PROPERTY(Paintable* paintable READ paintable CONSTANT)
public:
SpectralUser(QString userId, Connection* connection);
SpectralUser(QString userId, Connection* connection)
: User(userId, connection) {}
QImage getAvatar() { return avatar(128); }
signals:
void inheritedAvatarChanged(
User* user,
const Room* roomContext); // https://bugreports.qt.io/browse/QTBUG-7684
Paintable* paintable() { return new UserPaintable(this); }
};
#endif // SpectralUser_H

View File

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

View File

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