mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-25 20:48:52 +03:00
Merge pull request #655 from LorenDB/qml-all-the-things
QML all the things, part 2: Read receipts dialog
This commit is contained in:
commit
5b5a89b522
21 changed files with 375 additions and 502 deletions
|
@ -286,7 +286,6 @@ set(SRC_FILES
|
||||||
src/dialogs/Logout.cpp
|
src/dialogs/Logout.cpp
|
||||||
src/dialogs/PreviewUploadOverlay.cpp
|
src/dialogs/PreviewUploadOverlay.cpp
|
||||||
src/dialogs/ReCaptcha.cpp
|
src/dialogs/ReCaptcha.cpp
|
||||||
src/dialogs/ReadReceipts.cpp
|
|
||||||
|
|
||||||
# Emoji
|
# Emoji
|
||||||
src/emoji/EmojiModel.cpp
|
src/emoji/EmojiModel.cpp
|
||||||
|
@ -305,7 +304,6 @@ set(SRC_FILES
|
||||||
src/timeline/RoomlistModel.cpp
|
src/timeline/RoomlistModel.cpp
|
||||||
|
|
||||||
# UI components
|
# UI components
|
||||||
src/ui/Avatar.cpp
|
|
||||||
src/ui/Badge.cpp
|
src/ui/Badge.cpp
|
||||||
src/ui/DropShadow.cpp
|
src/ui/DropShadow.cpp
|
||||||
src/ui/FlatButton.cpp
|
src/ui/FlatButton.cpp
|
||||||
|
@ -352,6 +350,7 @@ set(SRC_FILES
|
||||||
src/MemberList.cpp
|
src/MemberList.cpp
|
||||||
src/MxcImageProvider.cpp
|
src/MxcImageProvider.cpp
|
||||||
src/Olm.cpp
|
src/Olm.cpp
|
||||||
|
src/ReadReceiptsModel.cpp
|
||||||
src/RegisterPage.cpp
|
src/RegisterPage.cpp
|
||||||
src/SSOHandler.cpp
|
src/SSOHandler.cpp
|
||||||
src/CombinedImagePackModel.cpp
|
src/CombinedImagePackModel.cpp
|
||||||
|
@ -499,7 +498,6 @@ qt5_wrap_cpp(MOC_HEADERS
|
||||||
src/dialogs/PreviewUploadOverlay.h
|
src/dialogs/PreviewUploadOverlay.h
|
||||||
src/dialogs/RawMessage.h
|
src/dialogs/RawMessage.h
|
||||||
src/dialogs/ReCaptcha.h
|
src/dialogs/ReCaptcha.h
|
||||||
src/dialogs/ReadReceipts.h
|
|
||||||
|
|
||||||
# Emoji
|
# Emoji
|
||||||
src/emoji/EmojiModel.h
|
src/emoji/EmojiModel.h
|
||||||
|
@ -517,7 +515,6 @@ qt5_wrap_cpp(MOC_HEADERS
|
||||||
src/timeline/RoomlistModel.h
|
src/timeline/RoomlistModel.h
|
||||||
|
|
||||||
# UI components
|
# UI components
|
||||||
src/ui/Avatar.h
|
|
||||||
src/ui/Badge.h
|
src/ui/Badge.h
|
||||||
src/ui/FlatButton.h
|
src/ui/FlatButton.h
|
||||||
src/ui/FloatingButton.h
|
src/ui/FloatingButton.h
|
||||||
|
@ -558,6 +555,7 @@ qt5_wrap_cpp(MOC_HEADERS
|
||||||
src/MainWindow.h
|
src/MainWindow.h
|
||||||
src/MemberList.h
|
src/MemberList.h
|
||||||
src/MxcImageProvider.h
|
src/MxcImageProvider.h
|
||||||
|
src/ReadReceiptsModel.h
|
||||||
src/RegisterPage.h
|
src/RegisterPage.h
|
||||||
src/SSOHandler.h
|
src/SSOHandler.h
|
||||||
src/CombinedImagePackModel.h
|
src/CombinedImagePackModel.h
|
||||||
|
|
|
@ -580,7 +580,7 @@ ScrollView {
|
||||||
|
|
||||||
Platform.MenuItem {
|
Platform.MenuItem {
|
||||||
text: qsTr("Read receip&ts")
|
text: qsTr("Read receip&ts")
|
||||||
onTriggered: room.readReceiptsAction(messageContextMenu.eventId)
|
onTriggered: room.showReadReceipts(messageContextMenu.eventId)
|
||||||
}
|
}
|
||||||
|
|
||||||
Platform.MenuItem {
|
Platform.MenuItem {
|
||||||
|
|
131
resources/qml/ReadReceipts.qml
Normal file
131
resources/qml/ReadReceipts.qml
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
|
import im.nheko 1.0
|
||||||
|
|
||||||
|
ApplicationWindow {
|
||||||
|
id: readReceiptsRoot
|
||||||
|
|
||||||
|
property ReadReceiptsProxy readReceipts
|
||||||
|
property Room room
|
||||||
|
|
||||||
|
x: MainWindow.x + (MainWindow.width / 2) - (width / 2)
|
||||||
|
y: MainWindow.y + (MainWindow.height / 2) - (height / 2)
|
||||||
|
height: 380
|
||||||
|
width: 340
|
||||||
|
minimumHeight: 380
|
||||||
|
minimumWidth: headerTitle.width + 2 * Nheko.paddingMedium
|
||||||
|
palette: Nheko.colors
|
||||||
|
color: Nheko.colors.window
|
||||||
|
flags: Qt.Dialog
|
||||||
|
|
||||||
|
Shortcut {
|
||||||
|
sequence: StandardKey.Cancel
|
||||||
|
onActivated: readReceiptsRoot.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Nheko.paddingMedium
|
||||||
|
spacing: Nheko.paddingMedium
|
||||||
|
|
||||||
|
Label {
|
||||||
|
id: headerTitle
|
||||||
|
|
||||||
|
color: Nheko.colors.text
|
||||||
|
Layout.alignment: Qt.AlignCenter
|
||||||
|
text: qsTr("Read receipts")
|
||||||
|
font.pointSize: fontMetrics.font.pointSize * 1.5
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrollView {
|
||||||
|
palette: Nheko.colors
|
||||||
|
padding: Nheko.paddingMedium
|
||||||
|
ScrollBar.horizontal.visible: false
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.minimumHeight: 200
|
||||||
|
Layout.fillWidth: true
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: readReceiptsList
|
||||||
|
|
||||||
|
clip: true
|
||||||
|
spacing: Nheko.paddingMedium
|
||||||
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
|
model: readReceipts
|
||||||
|
|
||||||
|
delegate: RowLayout {
|
||||||
|
spacing: Nheko.paddingMedium
|
||||||
|
|
||||||
|
Avatar {
|
||||||
|
width: Nheko.avatarSize
|
||||||
|
height: Nheko.avatarSize
|
||||||
|
userid: model.mxid
|
||||||
|
url: model.avatarUrl.replace("mxc://", "image://MxcImage/")
|
||||||
|
displayName: model.displayName
|
||||||
|
onClicked: room.openUserProfile(model.mxid)
|
||||||
|
ToolTip.visible: avatarHover.hovered
|
||||||
|
ToolTip.text: model.mxid
|
||||||
|
|
||||||
|
HoverHandler {
|
||||||
|
id: avatarHover
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
spacing: Nheko.paddingSmall
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: model.displayName
|
||||||
|
color: TimelineManager.userColor(model ? model.mxid : "", Nheko.colors.window)
|
||||||
|
font.pointSize: fontMetrics.font.pointSize
|
||||||
|
ToolTip.visible: displayNameHover.hovered
|
||||||
|
ToolTip.text: model.mxid
|
||||||
|
|
||||||
|
TapHandler {
|
||||||
|
onSingleTapped: room.openUserProfile(userId)
|
||||||
|
}
|
||||||
|
|
||||||
|
CursorShape {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
}
|
||||||
|
|
||||||
|
HoverHandler {
|
||||||
|
id: displayNameHover
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Label {
|
||||||
|
text: model.timestamp
|
||||||
|
color: Nheko.colors.buttonText
|
||||||
|
font.pointSize: fontMetrics.font.pointSize * 0.9
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
footer: DialogButtonBox {
|
||||||
|
standardButtons: DialogButtonBox.Ok
|
||||||
|
onAccepted: readReceiptsRoot.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -96,6 +96,14 @@ Page {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: readReceiptsDialog
|
||||||
|
|
||||||
|
ReadReceipts {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
Shortcut {
|
Shortcut {
|
||||||
sequence: "Ctrl+K"
|
sequence: "Ctrl+K"
|
||||||
onActivated: {
|
onActivated: {
|
||||||
|
|
|
@ -34,7 +34,7 @@ ImageButton {
|
||||||
}
|
}
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (status == MtxEvent.Read)
|
if (status == MtxEvent.Read)
|
||||||
room.readReceiptsAction(eventId);
|
room.showReadReceipts(eventId);
|
||||||
|
|
||||||
}
|
}
|
||||||
image: {
|
image: {
|
||||||
|
|
|
@ -249,4 +249,16 @@ Item {
|
||||||
roomid: room ? room.roomId : ""
|
roomid: room ? room.roomId : ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
function onOpenReadReceiptsDialog(rr) {
|
||||||
|
var dialog = readReceiptsDialog.createObject(timelineRoot, {
|
||||||
|
"readReceipts": rr,
|
||||||
|
"room": room
|
||||||
|
});
|
||||||
|
dialog.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
target: room
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,7 +112,6 @@
|
||||||
</qresource>
|
</qresource>
|
||||||
<qresource prefix="/">
|
<qresource prefix="/">
|
||||||
<file>qtquickcontrols2.conf</file>
|
<file>qtquickcontrols2.conf</file>
|
||||||
|
|
||||||
<file>qml/Root.qml</file>
|
<file>qml/Root.qml</file>
|
||||||
<file>qml/ChatPage.qml</file>
|
<file>qml/ChatPage.qml</file>
|
||||||
<file>qml/CommunitiesList.qml</file>
|
<file>qml/CommunitiesList.qml</file>
|
||||||
|
@ -177,6 +176,7 @@
|
||||||
<file>qml/components/FlatButton.qml</file>
|
<file>qml/components/FlatButton.qml</file>
|
||||||
<file>qml/RoomMembers.qml</file>
|
<file>qml/RoomMembers.qml</file>
|
||||||
<file>qml/InviteDialog.qml</file>
|
<file>qml/InviteDialog.qml</file>
|
||||||
|
<file>qml/ReadReceipts.qml</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
<qresource prefix="/media">
|
<qresource prefix="/media">
|
||||||
<file>media/ring.ogg</file>
|
<file>media/ring.ogg</file>
|
||||||
|
|
|
@ -31,7 +31,6 @@
|
||||||
|
|
||||||
#include "notifications/Manager.h"
|
#include "notifications/Manager.h"
|
||||||
|
|
||||||
#include "dialogs/ReadReceipts.h"
|
|
||||||
#include "timeline/TimelineViewManager.h"
|
#include "timeline/TimelineViewManager.h"
|
||||||
|
|
||||||
#include "blurhash.hpp"
|
#include "blurhash.hpp"
|
||||||
|
|
|
@ -36,7 +36,6 @@
|
||||||
#include "dialogs/JoinRoom.h"
|
#include "dialogs/JoinRoom.h"
|
||||||
#include "dialogs/LeaveRoom.h"
|
#include "dialogs/LeaveRoom.h"
|
||||||
#include "dialogs/Logout.h"
|
#include "dialogs/Logout.h"
|
||||||
#include "dialogs/ReadReceipts.h"
|
|
||||||
|
|
||||||
MainWindow *MainWindow::instance_ = nullptr;
|
MainWindow *MainWindow::instance_ = nullptr;
|
||||||
|
|
||||||
|
@ -398,27 +397,6 @@ MainWindow::openLogoutDialog()
|
||||||
showDialog(dialog);
|
showDialog(dialog);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
MainWindow::openReadReceiptsDialog(const QString &event_id)
|
|
||||||
{
|
|
||||||
auto dialog = new dialogs::ReadReceipts(this);
|
|
||||||
|
|
||||||
const auto room_id = chat_page_->currentRoom();
|
|
||||||
|
|
||||||
try {
|
|
||||||
dialog->addUsers(cache::readReceipts(event_id, room_id));
|
|
||||||
} catch (const lmdb::error &) {
|
|
||||||
nhlog::db()->warn("failed to retrieve read receipts for {} {}",
|
|
||||||
event_id.toStdString(),
|
|
||||||
chat_page_->currentRoom().toStdString());
|
|
||||||
dialog->deleteLater();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
showDialog(dialog);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
MainWindow::hasActiveDialogs() const
|
MainWindow::hasActiveDialogs() const
|
||||||
{
|
{
|
||||||
|
|
|
@ -65,7 +65,6 @@ public:
|
||||||
std::function<void(const mtx::requests::CreateRoom &request)> callback);
|
std::function<void(const mtx::requests::CreateRoom &request)> callback);
|
||||||
void openJoinRoomDialog(std::function<void(const QString &room_id)> callback);
|
void openJoinRoomDialog(std::function<void(const QString &room_id)> callback);
|
||||||
void openLogoutDialog();
|
void openLogoutDialog();
|
||||||
void openReadReceiptsDialog(const QString &event_id);
|
|
||||||
|
|
||||||
void hideOverlay();
|
void hideOverlay();
|
||||||
void showSolidOverlayModal(QWidget *content,
|
void showSolidOverlayModal(QWidget *content,
|
||||||
|
|
|
@ -2,16 +2,6 @@
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
#include <QAbstractSlider>
|
|
||||||
#include <QLabel>
|
|
||||||
#include <QListWidgetItem>
|
|
||||||
#include <QPainter>
|
|
||||||
#include <QPushButton>
|
|
||||||
#include <QScrollBar>
|
|
||||||
#include <QShortcut>
|
|
||||||
#include <QStyleOption>
|
|
||||||
#include <QVBoxLayout>
|
|
||||||
|
|
||||||
#include "MemberList.h"
|
#include "MemberList.h"
|
||||||
|
|
||||||
#include "Cache.h"
|
#include "Cache.h"
|
||||||
|
@ -20,7 +10,6 @@
|
||||||
#include "Logging.h"
|
#include "Logging.h"
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
#include "timeline/TimelineViewManager.h"
|
#include "timeline/TimelineViewManager.h"
|
||||||
#include "ui/Avatar.h"
|
|
||||||
|
|
||||||
MemberList::MemberList(const QString &room_id, QObject *parent)
|
MemberList::MemberList(const QString &room_id, QObject *parent)
|
||||||
: QAbstractListModel{parent}
|
: QAbstractListModel{parent}
|
||||||
|
|
|
@ -4,9 +4,10 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "CacheStructs.h"
|
|
||||||
#include <QAbstractListModel>
|
#include <QAbstractListModel>
|
||||||
|
|
||||||
|
#include "CacheStructs.h"
|
||||||
|
|
||||||
class MemberList : public QAbstractListModel
|
class MemberList : public QAbstractListModel
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
131
src/ReadReceiptsModel.cpp
Normal file
131
src/ReadReceiptsModel.cpp
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#include "ReadReceiptsModel.h"
|
||||||
|
|
||||||
|
#include <QLocale>
|
||||||
|
|
||||||
|
#include "Cache.h"
|
||||||
|
#include "Cache_p.h"
|
||||||
|
#include "Logging.h"
|
||||||
|
#include "Utils.h"
|
||||||
|
|
||||||
|
ReadReceiptsModel::ReadReceiptsModel(QString event_id, QString room_id, QObject *parent)
|
||||||
|
: QAbstractListModel{parent}
|
||||||
|
, event_id_{event_id}
|
||||||
|
, room_id_{room_id}
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
addUsers(cache::readReceipts(event_id_, room_id_));
|
||||||
|
} catch (const lmdb::error &) {
|
||||||
|
nhlog::db()->warn("failed to retrieve read receipts for {} {}",
|
||||||
|
event_id_.toStdString(),
|
||||||
|
room_id_.toStdString());
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
connect(cache::client(), &Cache::newReadReceipts, this, &ReadReceiptsModel::update);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ReadReceiptsModel::update()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
addUsers(cache::readReceipts(event_id_, room_id_));
|
||||||
|
} catch (const lmdb::error &) {
|
||||||
|
nhlog::db()->warn("failed to retrieve read receipts for {} {}",
|
||||||
|
event_id_.toStdString(),
|
||||||
|
room_id_.toStdString());
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<int, QByteArray>
|
||||||
|
ReadReceiptsModel::roleNames() const
|
||||||
|
{
|
||||||
|
// Note: RawTimestamp is purposely not included here
|
||||||
|
return {
|
||||||
|
{Mxid, "mxid"},
|
||||||
|
{DisplayName, "displayName"},
|
||||||
|
{AvatarUrl, "avatarUrl"},
|
||||||
|
{Timestamp, "timestamp"},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant
|
||||||
|
ReadReceiptsModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if (!index.isValid() || index.row() >= (int)readReceipts_.size() || index.row() < 0)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
switch (role) {
|
||||||
|
case Mxid:
|
||||||
|
return readReceipts_[index.row()].first;
|
||||||
|
case DisplayName:
|
||||||
|
return cache::displayName(room_id_, readReceipts_[index.row()].first);
|
||||||
|
case AvatarUrl:
|
||||||
|
return cache::avatarUrl(room_id_, readReceipts_[index.row()].first);
|
||||||
|
case Timestamp:
|
||||||
|
return dateFormat(readReceipts_[index.row()].second);
|
||||||
|
case RawTimestamp:
|
||||||
|
return readReceipts_[index.row()].second;
|
||||||
|
default:
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ReadReceiptsModel::addUsers(
|
||||||
|
const std::multimap<uint64_t, std::string, std::greater<uint64_t>> &users)
|
||||||
|
{
|
||||||
|
auto newReceipts = users.size() - readReceipts_.size();
|
||||||
|
|
||||||
|
if (newReceipts > 0) {
|
||||||
|
beginInsertRows(
|
||||||
|
QModelIndex{}, readReceipts_.size(), readReceipts_.size() + newReceipts - 1);
|
||||||
|
|
||||||
|
for (const auto &user : users) {
|
||||||
|
QPair<QString, QDateTime> item = {
|
||||||
|
QString::fromStdString(user.second),
|
||||||
|
QDateTime::fromMSecsSinceEpoch(user.first)};
|
||||||
|
if (!readReceipts_.contains(item))
|
||||||
|
readReceipts_.push_back(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
endInsertRows();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString
|
||||||
|
ReadReceiptsModel::dateFormat(const QDateTime &then) const
|
||||||
|
{
|
||||||
|
auto now = QDateTime::currentDateTime();
|
||||||
|
auto days = then.daysTo(now);
|
||||||
|
|
||||||
|
if (days == 0)
|
||||||
|
return QLocale::system().toString(then.time(), QLocale::ShortFormat);
|
||||||
|
else if (days < 2)
|
||||||
|
return tr("Yesterday, %1")
|
||||||
|
.arg(QLocale::system().toString(then.time(), QLocale::ShortFormat));
|
||||||
|
else if (days < 7)
|
||||||
|
//: %1 is the name of the current day, %2 is the time the read receipt was read. The
|
||||||
|
//: result may look like this: Monday, 7:15
|
||||||
|
return QString("%1, %2")
|
||||||
|
.arg(then.toString("dddd"))
|
||||||
|
.arg(QLocale::system().toString(then.time(), QLocale::ShortFormat));
|
||||||
|
|
||||||
|
return QLocale::system().toString(then.time(), QLocale::ShortFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadReceiptsProxy::ReadReceiptsProxy(QString event_id, QString room_id, QObject *parent)
|
||||||
|
: QSortFilterProxyModel{parent}
|
||||||
|
, model_{event_id, room_id, this}
|
||||||
|
{
|
||||||
|
setSourceModel(&model_);
|
||||||
|
setSortRole(ReadReceiptsModel::RawTimestamp);
|
||||||
|
sort(0, Qt::DescendingOrder);
|
||||||
|
setDynamicSortFilter(true);
|
||||||
|
}
|
73
src/ReadReceiptsModel.h
Normal file
73
src/ReadReceiptsModel.h
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
#ifndef READRECEIPTSMODEL_H
|
||||||
|
#define READRECEIPTSMODEL_H
|
||||||
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
|
#include <QDateTime>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QSortFilterProxyModel>
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
class ReadReceiptsModel : public QAbstractListModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum Roles
|
||||||
|
{
|
||||||
|
Mxid,
|
||||||
|
DisplayName,
|
||||||
|
AvatarUrl,
|
||||||
|
Timestamp,
|
||||||
|
RawTimestamp,
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit ReadReceiptsModel(QString event_id, QString room_id, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
QString eventId() const { return event_id_; }
|
||||||
|
QString roomId() const { return room_id_; }
|
||||||
|
|
||||||
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
int rowCount(const QModelIndex &parent) const override
|
||||||
|
{
|
||||||
|
Q_UNUSED(parent)
|
||||||
|
return readReceipts_.size();
|
||||||
|
}
|
||||||
|
QVariant data(const QModelIndex &index, int role) const override;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void addUsers(const std::multimap<uint64_t, std::string, std::greater<uint64_t>> &users);
|
||||||
|
void update();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString dateFormat(const QDateTime &then) const;
|
||||||
|
|
||||||
|
QString event_id_;
|
||||||
|
QString room_id_;
|
||||||
|
QVector<QPair<QString, QDateTime>> readReceipts_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ReadReceiptsProxy : public QSortFilterProxyModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(QString eventId READ eventId CONSTANT)
|
||||||
|
Q_PROPERTY(QString roomId READ roomId CONSTANT)
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ReadReceiptsProxy(QString event_id, QString room_id, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
QString eventId() const { return event_id_; }
|
||||||
|
QString roomId() const { return room_id_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
QString event_id_;
|
||||||
|
QString room_id_;
|
||||||
|
|
||||||
|
ReadReceiptsModel model_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // READRECEIPTSMODEL_H
|
|
@ -1,179 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#include <QDebug>
|
|
||||||
#include <QIcon>
|
|
||||||
#include <QLabel>
|
|
||||||
#include <QListWidgetItem>
|
|
||||||
#include <QPainter>
|
|
||||||
#include <QPushButton>
|
|
||||||
#include <QShortcut>
|
|
||||||
#include <QStyleOption>
|
|
||||||
#include <QTimer>
|
|
||||||
#include <QVBoxLayout>
|
|
||||||
|
|
||||||
#include "dialogs/ReadReceipts.h"
|
|
||||||
|
|
||||||
#include "AvatarProvider.h"
|
|
||||||
#include "Cache.h"
|
|
||||||
#include "ChatPage.h"
|
|
||||||
#include "Config.h"
|
|
||||||
#include "Utils.h"
|
|
||||||
#include "ui/Avatar.h"
|
|
||||||
|
|
||||||
using namespace dialogs;
|
|
||||||
|
|
||||||
ReceiptItem::ReceiptItem(QWidget *parent,
|
|
||||||
const QString &user_id,
|
|
||||||
uint64_t timestamp,
|
|
||||||
const QString &room_id)
|
|
||||||
: QWidget(parent)
|
|
||||||
{
|
|
||||||
topLayout_ = new QHBoxLayout(this);
|
|
||||||
topLayout_->setMargin(0);
|
|
||||||
|
|
||||||
textLayout_ = new QVBoxLayout;
|
|
||||||
textLayout_->setMargin(0);
|
|
||||||
textLayout_->setSpacing(conf::modals::TEXT_SPACING);
|
|
||||||
|
|
||||||
QFont nameFont;
|
|
||||||
nameFont.setPointSizeF(nameFont.pointSizeF() * 1.1);
|
|
||||||
|
|
||||||
auto displayName = cache::displayName(room_id, user_id);
|
|
||||||
|
|
||||||
avatar_ = new Avatar(this, 44);
|
|
||||||
avatar_->setLetter(utils::firstChar(displayName));
|
|
||||||
|
|
||||||
// If it's a matrix id we use the second letter.
|
|
||||||
if (displayName.size() > 1 && displayName.at(0) == '@')
|
|
||||||
avatar_->setLetter(QChar(displayName.at(1)));
|
|
||||||
|
|
||||||
userName_ = new QLabel(displayName, this);
|
|
||||||
userName_->setFont(nameFont);
|
|
||||||
|
|
||||||
timestamp_ = new QLabel(dateFormat(QDateTime::fromMSecsSinceEpoch(timestamp)), this);
|
|
||||||
|
|
||||||
textLayout_->addWidget(userName_);
|
|
||||||
textLayout_->addWidget(timestamp_);
|
|
||||||
|
|
||||||
topLayout_->addWidget(avatar_);
|
|
||||||
topLayout_->addLayout(textLayout_, 1);
|
|
||||||
|
|
||||||
avatar_->setImage(ChatPage::instance()->currentRoom(), user_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ReceiptItem::paintEvent(QPaintEvent *)
|
|
||||||
{
|
|
||||||
QStyleOption opt;
|
|
||||||
opt.init(this);
|
|
||||||
QPainter p(this);
|
|
||||||
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString
|
|
||||||
ReceiptItem::dateFormat(const QDateTime &then) const
|
|
||||||
{
|
|
||||||
auto now = QDateTime::currentDateTime();
|
|
||||||
auto days = then.daysTo(now);
|
|
||||||
|
|
||||||
if (days == 0)
|
|
||||||
return tr("Today %1")
|
|
||||||
.arg(QLocale::system().toString(then.time(), QLocale::ShortFormat));
|
|
||||||
else if (days < 2)
|
|
||||||
return tr("Yesterday %1")
|
|
||||||
.arg(QLocale::system().toString(then.time(), QLocale::ShortFormat));
|
|
||||||
else if (days < 7)
|
|
||||||
return QString("%1 %2")
|
|
||||||
.arg(then.toString("dddd"))
|
|
||||||
.arg(QLocale::system().toString(then.time(), QLocale::ShortFormat));
|
|
||||||
|
|
||||||
return QLocale::system().toString(then.time(), QLocale::ShortFormat);
|
|
||||||
}
|
|
||||||
|
|
||||||
ReadReceipts::ReadReceipts(QWidget *parent)
|
|
||||||
: QFrame(parent)
|
|
||||||
{
|
|
||||||
setAutoFillBackground(true);
|
|
||||||
setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
|
|
||||||
setWindowModality(Qt::WindowModal);
|
|
||||||
setAttribute(Qt::WA_DeleteOnClose, true);
|
|
||||||
|
|
||||||
auto layout = new QVBoxLayout(this);
|
|
||||||
layout->setSpacing(conf::modals::WIDGET_SPACING);
|
|
||||||
layout->setMargin(conf::modals::WIDGET_MARGIN);
|
|
||||||
|
|
||||||
userList_ = new QListWidget;
|
|
||||||
userList_->setFrameStyle(QFrame::NoFrame);
|
|
||||||
userList_->setSelectionMode(QAbstractItemView::NoSelection);
|
|
||||||
userList_->setSpacing(conf::modals::TEXT_SPACING);
|
|
||||||
|
|
||||||
QFont largeFont;
|
|
||||||
largeFont.setPointSizeF(largeFont.pointSizeF() * 1.5);
|
|
||||||
|
|
||||||
setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum);
|
|
||||||
setMinimumHeight(userList_->sizeHint().height() * 2);
|
|
||||||
setMinimumWidth(std::max(userList_->sizeHint().width() + 4 * conf::modals::WIDGET_MARGIN,
|
|
||||||
QFontMetrics(largeFont).averageCharWidth() * 30 -
|
|
||||||
2 * conf::modals::WIDGET_MARGIN));
|
|
||||||
|
|
||||||
QFont font;
|
|
||||||
font.setPointSizeF(font.pointSizeF() * conf::modals::LABEL_MEDIUM_SIZE_RATIO);
|
|
||||||
|
|
||||||
topLabel_ = new QLabel(tr("Read receipts"), this);
|
|
||||||
topLabel_->setAlignment(Qt::AlignCenter);
|
|
||||||
topLabel_->setFont(font);
|
|
||||||
|
|
||||||
auto okBtn = new QPushButton(tr("Close"), this);
|
|
||||||
|
|
||||||
auto buttonLayout = new QHBoxLayout();
|
|
||||||
buttonLayout->setSpacing(15);
|
|
||||||
buttonLayout->addStretch(1);
|
|
||||||
buttonLayout->addWidget(okBtn);
|
|
||||||
|
|
||||||
layout->addWidget(topLabel_);
|
|
||||||
layout->addWidget(userList_);
|
|
||||||
layout->addLayout(buttonLayout);
|
|
||||||
|
|
||||||
auto closeShortcut = new QShortcut(QKeySequence(QKeySequence::Cancel), this);
|
|
||||||
connect(closeShortcut, &QShortcut::activated, this, &ReadReceipts::close);
|
|
||||||
connect(okBtn, &QPushButton::clicked, this, &ReadReceipts::close);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ReadReceipts::addUsers(const std::multimap<uint64_t, std::string, std::greater<uint64_t>> &receipts)
|
|
||||||
{
|
|
||||||
// We want to remove any previous items that have been set.
|
|
||||||
userList_->clear();
|
|
||||||
|
|
||||||
for (const auto &receipt : receipts) {
|
|
||||||
auto user = new ReceiptItem(this,
|
|
||||||
QString::fromStdString(receipt.second),
|
|
||||||
receipt.first,
|
|
||||||
ChatPage::instance()->currentRoom());
|
|
||||||
auto item = new QListWidgetItem(userList_);
|
|
||||||
|
|
||||||
item->setSizeHint(user->minimumSizeHint());
|
|
||||||
item->setFlags(Qt::NoItemFlags);
|
|
||||||
item->setTextAlignment(Qt::AlignCenter);
|
|
||||||
|
|
||||||
userList_->setItemWidget(item, user);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ReadReceipts::paintEvent(QPaintEvent *)
|
|
||||||
{
|
|
||||||
QStyleOption opt;
|
|
||||||
opt.init(this);
|
|
||||||
QPainter p(this);
|
|
||||||
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ReadReceipts::hideEvent(QHideEvent *event)
|
|
||||||
{
|
|
||||||
userList_->clear();
|
|
||||||
QFrame::hideEvent(event);
|
|
||||||
}
|
|
|
@ -1,61 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QDateTime>
|
|
||||||
#include <QFrame>
|
|
||||||
|
|
||||||
class Avatar;
|
|
||||||
class QLabel;
|
|
||||||
class QListWidget;
|
|
||||||
class QHBoxLayout;
|
|
||||||
class QVBoxLayout;
|
|
||||||
|
|
||||||
namespace dialogs {
|
|
||||||
|
|
||||||
class ReceiptItem : public QWidget
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
ReceiptItem(QWidget *parent,
|
|
||||||
const QString &user_id,
|
|
||||||
uint64_t timestamp,
|
|
||||||
const QString &room_id);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void paintEvent(QPaintEvent *) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
QString dateFormat(const QDateTime &then) const;
|
|
||||||
|
|
||||||
QHBoxLayout *topLayout_;
|
|
||||||
QVBoxLayout *textLayout_;
|
|
||||||
|
|
||||||
Avatar *avatar_;
|
|
||||||
|
|
||||||
QLabel *userName_;
|
|
||||||
QLabel *timestamp_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class ReadReceipts : public QFrame
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
explicit ReadReceipts(QWidget *parent = nullptr);
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void addUsers(const std::multimap<uint64_t, std::string, std::greater<uint64_t>> &users);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void paintEvent(QPaintEvent *event) override;
|
|
||||||
void hideEvent(QHideEvent *event) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
QLabel *topLabel_;
|
|
||||||
|
|
||||||
QListWidget *userList_;
|
|
||||||
};
|
|
||||||
} // dialogs
|
|
|
@ -28,6 +28,7 @@
|
||||||
#include "MemberList.h"
|
#include "MemberList.h"
|
||||||
#include "MxcImageProvider.h"
|
#include "MxcImageProvider.h"
|
||||||
#include "Olm.h"
|
#include "Olm.h"
|
||||||
|
#include "ReadReceiptsModel.h"
|
||||||
#include "TimelineViewManager.h"
|
#include "TimelineViewManager.h"
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
#include "dialogs/RawMessage.h"
|
#include "dialogs/RawMessage.h"
|
||||||
|
@ -1089,9 +1090,9 @@ TimelineModel::relatedInfo(QString id)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
TimelineModel::readReceiptsAction(QString id) const
|
TimelineModel::showReadReceipts(QString id)
|
||||||
{
|
{
|
||||||
MainWindow::instance()->openReadReceiptsDialog(id);
|
emit openReadReceiptsDialog(new ReadReceiptsProxy{id, roomId(), this});
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "InviteesModel.h"
|
#include "InviteesModel.h"
|
||||||
#include "MemberList.h"
|
#include "MemberList.h"
|
||||||
#include "Permissions.h"
|
#include "Permissions.h"
|
||||||
|
#include "ReadReceiptsModel.h"
|
||||||
#include "ui/RoomSettings.h"
|
#include "ui/RoomSettings.h"
|
||||||
#include "ui/UserProfile.h"
|
#include "ui/UserProfile.h"
|
||||||
|
|
||||||
|
@ -241,7 +242,7 @@ public:
|
||||||
Q_INVOKABLE void openUserProfile(QString userid);
|
Q_INVOKABLE void openUserProfile(QString userid);
|
||||||
Q_INVOKABLE void editAction(QString id);
|
Q_INVOKABLE void editAction(QString id);
|
||||||
Q_INVOKABLE void replyAction(QString id);
|
Q_INVOKABLE void replyAction(QString id);
|
||||||
Q_INVOKABLE void readReceiptsAction(QString id) const;
|
Q_INVOKABLE void showReadReceipts(QString id);
|
||||||
Q_INVOKABLE void redactEvent(QString id);
|
Q_INVOKABLE void redactEvent(QString id);
|
||||||
Q_INVOKABLE int idToIndex(QString id) const;
|
Q_INVOKABLE int idToIndex(QString id) const;
|
||||||
Q_INVOKABLE QString indexToId(int index) const;
|
Q_INVOKABLE QString indexToId(int index) const;
|
||||||
|
@ -348,6 +349,7 @@ signals:
|
||||||
void typingUsersChanged(std::vector<QString> users);
|
void typingUsersChanged(std::vector<QString> users);
|
||||||
void replyChanged(QString reply);
|
void replyChanged(QString reply);
|
||||||
void editChanged(QString reply);
|
void editChanged(QString reply);
|
||||||
|
void openReadReceiptsDialog(ReadReceiptsProxy *rr);
|
||||||
void paginationInProgressChanged(const bool);
|
void paginationInProgressChanged(const bool);
|
||||||
void newCallEvent(const mtx::events::collections::TimelineEvents &event);
|
void newCallEvent(const mtx::events::collections::TimelineEvents &event);
|
||||||
void scrollToIndex(int index);
|
void scrollToIndex(int index);
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include "MainWindow.h"
|
#include "MainWindow.h"
|
||||||
#include "MatrixClient.h"
|
#include "MatrixClient.h"
|
||||||
#include "MxcImageProvider.h"
|
#include "MxcImageProvider.h"
|
||||||
|
#include "ReadReceiptsModel.h"
|
||||||
#include "RoomsModel.h"
|
#include "RoomsModel.h"
|
||||||
#include "SingleImagePackModel.h"
|
#include "SingleImagePackModel.h"
|
||||||
#include "UserSettingsPage.h"
|
#include "UserSettingsPage.h"
|
||||||
|
@ -205,6 +206,12 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par
|
||||||
0,
|
0,
|
||||||
"InviteesModel",
|
"InviteesModel",
|
||||||
"InviteesModel needs to be instantiated on the C++ side");
|
"InviteesModel needs to be instantiated on the C++ side");
|
||||||
|
qmlRegisterUncreatableType<ReadReceiptsProxy>(
|
||||||
|
"im.nheko",
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
"ReadReceiptsProxy",
|
||||||
|
"ReadReceiptsProxy needs to be instantiated on the C++ side");
|
||||||
|
|
||||||
static auto self = this;
|
static auto self = this;
|
||||||
qmlRegisterSingletonType<MainWindow>(
|
qmlRegisterSingletonType<MainWindow>(
|
||||||
|
|
|
@ -1,168 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#include <QPainter>
|
|
||||||
#include <QPainterPath>
|
|
||||||
#include <QSettings>
|
|
||||||
|
|
||||||
#include "AvatarProvider.h"
|
|
||||||
#include "Utils.h"
|
|
||||||
#include "ui/Avatar.h"
|
|
||||||
|
|
||||||
Avatar::Avatar(QWidget *parent, int size)
|
|
||||||
: QWidget(parent)
|
|
||||||
, size_(size)
|
|
||||||
{
|
|
||||||
type_ = ui::AvatarType::Letter;
|
|
||||||
letter_ = "A";
|
|
||||||
|
|
||||||
QFont _font(font());
|
|
||||||
_font.setPointSizeF(ui::FontSize);
|
|
||||||
setFont(_font);
|
|
||||||
|
|
||||||
QSizePolicy policy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
|
|
||||||
setSizePolicy(policy);
|
|
||||||
}
|
|
||||||
|
|
||||||
QColor
|
|
||||||
Avatar::textColor() const
|
|
||||||
{
|
|
||||||
if (!text_color_.isValid())
|
|
||||||
return QColor("black");
|
|
||||||
|
|
||||||
return text_color_;
|
|
||||||
}
|
|
||||||
|
|
||||||
QColor
|
|
||||||
Avatar::backgroundColor() const
|
|
||||||
{
|
|
||||||
if (!text_color_.isValid())
|
|
||||||
return QColor("white");
|
|
||||||
|
|
||||||
return background_color_;
|
|
||||||
}
|
|
||||||
|
|
||||||
QSize
|
|
||||||
Avatar::sizeHint() const
|
|
||||||
{
|
|
||||||
return QSize(size_ + 2, size_ + 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Avatar::setTextColor(const QColor &color)
|
|
||||||
{
|
|
||||||
text_color_ = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Avatar::setBackgroundColor(const QColor &color)
|
|
||||||
{
|
|
||||||
background_color_ = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Avatar::setLetter(const QString &letter)
|
|
||||||
{
|
|
||||||
letter_ = letter;
|
|
||||||
type_ = ui::AvatarType::Letter;
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Avatar::setImage(const QString &avatar_url)
|
|
||||||
{
|
|
||||||
avatar_url_ = avatar_url;
|
|
||||||
AvatarProvider::resolve(avatar_url,
|
|
||||||
static_cast<int>(size_ * pixmap_.devicePixelRatio()),
|
|
||||||
this,
|
|
||||||
[this, requestedRatio = pixmap_.devicePixelRatio()](QPixmap pm) {
|
|
||||||
if (pm.isNull())
|
|
||||||
return;
|
|
||||||
type_ = ui::AvatarType::Image;
|
|
||||||
pixmap_ = pm;
|
|
||||||
pixmap_.setDevicePixelRatio(requestedRatio);
|
|
||||||
update();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Avatar::setImage(const QString &room, const QString &user)
|
|
||||||
{
|
|
||||||
room_ = room;
|
|
||||||
user_ = user;
|
|
||||||
AvatarProvider::resolve(room,
|
|
||||||
user,
|
|
||||||
static_cast<int>(size_ * pixmap_.devicePixelRatio()),
|
|
||||||
this,
|
|
||||||
[this, requestedRatio = pixmap_.devicePixelRatio()](QPixmap pm) {
|
|
||||||
if (pm.isNull())
|
|
||||||
return;
|
|
||||||
type_ = ui::AvatarType::Image;
|
|
||||||
pixmap_ = pm;
|
|
||||||
pixmap_.setDevicePixelRatio(requestedRatio);
|
|
||||||
update();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Avatar::setDevicePixelRatio(double ratio)
|
|
||||||
{
|
|
||||||
if (type_ == ui::AvatarType::Image && abs(pixmap_.devicePixelRatio() - ratio) > 0.01) {
|
|
||||||
pixmap_ = pixmap_.scaled(QSize(size_, size_) * ratio);
|
|
||||||
pixmap_.setDevicePixelRatio(ratio);
|
|
||||||
|
|
||||||
if (!avatar_url_.isEmpty())
|
|
||||||
setImage(avatar_url_);
|
|
||||||
else
|
|
||||||
setImage(room_, user_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
Avatar::paintEvent(QPaintEvent *)
|
|
||||||
{
|
|
||||||
bool rounded = QSettings().value(QStringLiteral("user/avatar_circles"), true).toBool();
|
|
||||||
|
|
||||||
QPainter painter(this);
|
|
||||||
|
|
||||||
painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform |
|
|
||||||
QPainter::TextAntialiasing);
|
|
||||||
|
|
||||||
QRectF r = rect();
|
|
||||||
const int hs = size_ / 2;
|
|
||||||
|
|
||||||
if (type_ != ui::AvatarType::Image) {
|
|
||||||
QBrush brush;
|
|
||||||
brush.setStyle(Qt::SolidPattern);
|
|
||||||
brush.setColor(backgroundColor());
|
|
||||||
|
|
||||||
painter.setPen(Qt::NoPen);
|
|
||||||
painter.setBrush(brush);
|
|
||||||
rounded ? painter.drawEllipse(r) : painter.drawRoundedRect(r, 3, 3);
|
|
||||||
} else if (painter.isActive()) {
|
|
||||||
setDevicePixelRatio(painter.device()->devicePixelRatioF());
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (type_) {
|
|
||||||
case ui::AvatarType::Image: {
|
|
||||||
QPainterPath ppath;
|
|
||||||
|
|
||||||
rounded ? ppath.addEllipse(width() / 2 - hs, height() / 2 - hs, size_, size_)
|
|
||||||
: ppath.addRoundedRect(r, 3, 3);
|
|
||||||
|
|
||||||
painter.setClipPath(ppath);
|
|
||||||
painter.drawPixmap(QRect(width() / 2 - hs, height() / 2 - hs, size_, size_),
|
|
||||||
pixmap_);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case ui::AvatarType::Letter: {
|
|
||||||
painter.setPen(textColor());
|
|
||||||
painter.setBrush(Qt::NoBrush);
|
|
||||||
painter.drawText(r.translated(0, -1), Qt::AlignCenter, letter_);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QImage>
|
|
||||||
#include <QPixmap>
|
|
||||||
#include <QWidget>
|
|
||||||
|
|
||||||
#include "Theme.h"
|
|
||||||
|
|
||||||
class Avatar : public QWidget
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
Q_PROPERTY(QColor textColor WRITE setTextColor READ textColor)
|
|
||||||
Q_PROPERTY(QColor backgroundColor WRITE setBackgroundColor READ backgroundColor)
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit Avatar(QWidget *parent = nullptr, int size = ui::AvatarSize);
|
|
||||||
|
|
||||||
void setBackgroundColor(const QColor &color);
|
|
||||||
void setImage(const QString &avatar_url);
|
|
||||||
void setImage(const QString &room, const QString &user);
|
|
||||||
void setLetter(const QString &letter);
|
|
||||||
void setTextColor(const QColor &color);
|
|
||||||
void setDevicePixelRatio(double ratio);
|
|
||||||
|
|
||||||
QColor backgroundColor() const;
|
|
||||||
QColor textColor() const;
|
|
||||||
|
|
||||||
QSize sizeHint() const override;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void paintEvent(QPaintEvent *event) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void init();
|
|
||||||
|
|
||||||
ui::AvatarType type_;
|
|
||||||
QString letter_;
|
|
||||||
QString avatar_url_, room_, user_;
|
|
||||||
QColor background_color_;
|
|
||||||
QColor text_color_;
|
|
||||||
QPixmap pixmap_;
|
|
||||||
int size_;
|
|
||||||
};
|
|
Loading…
Reference in a new issue