diff --git a/imports/Spectral/Component/Timeline/MessageDelegate.qml b/imports/Spectral/Component/Timeline/MessageDelegate.qml index c12e04f..da54a4a 100644 --- a/imports/Spectral/Component/Timeline/MessageDelegate.qml +++ b/imports/Spectral/Component/Timeline/MessageDelegate.qml @@ -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 diff --git a/imports/Spectral/Page/SettingAccountDelegate.qml b/imports/Spectral/Page/SettingAccountDelegate.qml index 42d52d3..62441a3 100644 --- a/imports/Spectral/Page/SettingAccountDelegate.qml +++ b/imports/Spectral/Page/SettingAccountDelegate.qml @@ -27,7 +27,7 @@ Column { height: parent.height hint: user.displayName - image: user.avatar + source: user.paintable } ColumnLayout { diff --git a/imports/Spectral/Panel/RoomDrawer.qml b/imports/Spectral/Panel/RoomDrawer.qml index 7c83cf8..6ba25b5 100644 --- a/imports/Spectral/Panel/RoomDrawer.qml +++ b/imports/Spectral/Panel/RoomDrawer.qml @@ -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 } diff --git a/imports/Spectral/Panel/RoomHeader.qml b/imports/Spectral/Panel/RoomHeader.qml index 0be4291..a0a13b1 100644 --- a/imports/Spectral/Panel/RoomHeader.qml +++ b/imports/Spectral/Panel/RoomHeader.qml @@ -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" } diff --git a/imports/Spectral/Panel/RoomListDelegate.qml b/imports/Spectral/Panel/RoomListDelegate.qml index 371d7aa..8268e5d 100644 --- a/imports/Spectral/Panel/RoomListDelegate.qml +++ b/imports/Spectral/Panel/RoomListDelegate.qml @@ -66,9 +66,8 @@ Rectangle { Layout.preferredWidth: height Layout.fillHeight: true + source: paintable hint: name || "No Name" - - image: avatar } ColumnLayout { diff --git a/imports/Spectral/Panel/RoomPanel.qml b/imports/Spectral/Panel/RoomPanel.qml index 0ecac76..658d4d3 100644 --- a/imports/Spectral/Panel/RoomPanel.qml +++ b/imports/Spectral/Panel/RoomPanel.qml @@ -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() diff --git a/imports/Spectral/Panel/RoomPanelForm.ui.qml b/imports/Spectral/Panel/RoomPanelForm.ui.qml index e1d7bf3..1bf0f19 100644 --- a/imports/Spectral/Panel/RoomPanelForm.ui.qml +++ b/imports/Spectral/Panel/RoomPanelForm.ui.qml @@ -272,7 +272,7 @@ Item { Layout.preferredWidth: height Layout.fillHeight: true - image: modelData.avatar + source: modelData.paintable hint: modelData.displayName } diff --git a/imports/Spectral/Panel/RoomPanelInput.qml b/imports/Spectral/Panel/RoomPanelInput.qml index 43fd239..2ff6ab3 100644 --- a/imports/Spectral/Panel/RoomPanelInput.qml +++ b/imports/Spectral/Panel/RoomPanelInput.qml @@ -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 diff --git a/qml/main.qml b/qml/main.qml index 0475737..216b04d 100644 --- a/qml/main.qml +++ b/qml/main.qml @@ -153,7 +153,7 @@ ApplicationWindow { anchors.margins: 12 hint: user.displayName - image: user.avatar + source: user.paintable } highlightColor: spectralController.color(user.id) diff --git a/spectral.pro b/spectral.pro index 9828ea8..8c86282 100644 --- a/spectral.pro +++ b/spectral.pro @@ -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 diff --git a/src/controller.cpp b/src/controller.cpp index 287786e..bbacf75 100644 --- a/src/controller.cpp +++ b/src/controller.cpp @@ -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()); } diff --git a/src/controller.h b/src/controller.h index 43b889c..0234fb4 100644 --- a/src/controller.h +++ b/src/controller.h @@ -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 diff --git a/src/imageitem.cpp b/src/imageitem.cpp index 6b9a533..3ef079a 100644 --- a/src/imageitem.cpp +++ b/src/imageitem.cpp @@ -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(); } diff --git a/src/imageitem.h b/src/imageitem.h index 6558ec7..32ab9bc 100644 --- a/src/imageitem.h +++ b/src/imageitem.h @@ -7,21 +7,24 @@ #include #include +#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 diff --git a/src/paintable.cpp b/src/paintable.cpp new file mode 100644 index 0000000..a44b826 --- /dev/null +++ b/src/paintable.cpp @@ -0,0 +1,3 @@ +#include "paintable.h" + +Paintable::Paintable(QObject *parent) : QObject(parent) {} diff --git a/src/paintable.h b/src/paintable.h new file mode 100644 index 0000000..f09d778 --- /dev/null +++ b/src/paintable.h @@ -0,0 +1,19 @@ +#ifndef PAINTABLE_H +#define PAINTABLE_H + +#include +#include + +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 diff --git a/src/roomlistmodel.cpp b/src/roomlistmodel.cpp index 2304795..293ecc9 100644 --- a/src/roomlistmodel.cpp +++ b/src/roomlistmodel.cpp @@ -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 RoomListModel::roleNames() const { QHash roles; roles[NameRole] = "name"; - roles[AvatarRole] = "avatar"; + roles[PaintableRole] = "paintable"; roles[TopicRole] = "topic"; roles[CategoryRole] = "category"; roles[UnreadCountRole] = "unreadCount"; diff --git a/src/roomlistmodel.h b/src/roomlistmodel.h index 40b0f04..c42e956 100644 --- a/src/roomlistmodel.h +++ b/src/roomlistmodel.h @@ -31,7 +31,7 @@ class RoomListModel : public QAbstractListModel { public: enum EventRoles { NameRole = Qt::UserRole + 1, - AvatarRole, + PaintableRole, TopicRole, CategoryRole, UnreadCountRole, diff --git a/src/spectralroom.cpp b/src/spectralroom.cpp index e151577..64ff036 100644 --- a/src/spectralroom.cpp +++ b/src/spectralroom.cpp @@ -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, diff --git a/src/spectralroom.h b/src/spectralroom.h index d3a4a9b..6490634 100644 --- a/src/spectralroom.h +++ b/src/spectralroom.h @@ -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(); diff --git a/src/spectraluser.cpp b/src/spectraluser.cpp index 2b131d8..cf0e67d 100644 --- a/src/spectraluser.cpp +++ b/src/spectraluser.cpp @@ -1,7 +1 @@ #include "spectraluser.h" - -SpectralUser::SpectralUser(QString userId, Connection* connection) - : User(userId, connection) { - connect(this, &User::avatarChanged, this, - &SpectralUser::inheritedAvatarChanged); -} diff --git a/src/spectraluser.h b/src/spectraluser.h index 1cb7871..fbe217c 100644 --- a/src/spectraluser.h +++ b/src/spectraluser.h @@ -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 diff --git a/src/userlistmodel.cpp b/src/userlistmodel.cpp index 8902d40..4a41459 100644 --- a/src/userlistmodel.cpp +++ b/src/userlistmodel.cpp @@ -8,6 +8,8 @@ #include #include +#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(user))->paintable()); } return QVariant(); @@ -115,7 +115,7 @@ void UserListModel::refresh(QMatrixClient::User* user, QVector 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 UserListModel::roleNames() const { QHash roles; roles[NameRole] = "name"; roles[UserIDRole] = "userId"; - roles[AvatarRole] = "avatar"; + roles[PaintableRole] = "paintable"; return roles; } diff --git a/src/userlistmodel.h b/src/userlistmodel.h index fd01a5e..88146ad 100644 --- a/src/userlistmodel.h +++ b/src/userlistmodel.h @@ -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;