forked from mirror/nheko
Communities (#195)
parent
81a706bf20
commit
312df6f3bb
@ -0,0 +1,42 @@ |
||||
#pragma once |
||||
|
||||
#include <QScrollArea> |
||||
#include <QSharedPointer> |
||||
#include <QVBoxLayout> |
||||
#include <QWidget> |
||||
|
||||
#include "CommunitiesListItem.h" |
||||
#include "Community.h" |
||||
#include "MatrixClient.h" |
||||
#include "ui/Theme.h" |
||||
|
||||
class CommunitiesList : public QWidget |
||||
{ |
||||
Q_OBJECT |
||||
|
||||
public: |
||||
CommunitiesList(QSharedPointer<MatrixClient> client, QWidget *parent = nullptr); |
||||
~CommunitiesList(); |
||||
|
||||
void setCommunities(const QMap<QString, QSharedPointer<Community>> &communities); |
||||
void clear(); |
||||
|
||||
void addCommunity(QSharedPointer<Community> community, const QString &community_id); |
||||
void removeCommunity(const QString &community_id); |
||||
signals: |
||||
void communityChanged(const QString &community_id); |
||||
|
||||
public slots: |
||||
void updateCommunityAvatar(const QString &community_id, const QPixmap &img); |
||||
void highlightSelectedCommunity(const QString &community_id); |
||||
|
||||
private: |
||||
QVBoxLayout *topLayout_; |
||||
QVBoxLayout *contentsLayout_; |
||||
QWidget *scrollAreaContents_; |
||||
QScrollArea *scrollArea_; |
||||
|
||||
QMap<QString, QSharedPointer<CommunitiesListItem>> communities_; |
||||
|
||||
QSharedPointer<MatrixClient> client_; |
||||
}; |
@ -0,0 +1,98 @@ |
||||
#pragma once |
||||
|
||||
#include <QDebug> |
||||
#include <QMouseEvent> |
||||
#include <QPainter> |
||||
#include <QSharedPointer> |
||||
#include <QWidget> |
||||
|
||||
#include "Community.h" |
||||
#include "Menu.h" |
||||
#include "ui/Theme.h" |
||||
|
||||
class CommunitiesListItem : public QWidget |
||||
{ |
||||
Q_OBJECT |
||||
Q_PROPERTY(QColor highlightedBackgroundColor READ highlightedBackgroundColor WRITE |
||||
setHighlightedBackgroundColor) |
||||
Q_PROPERTY( |
||||
QColor hoverBackgroundColor READ hoverBackgroundColor WRITE setHoverBackgroundColor) |
||||
Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor) |
||||
|
||||
public: |
||||
CommunitiesListItem(QSharedPointer<Community> community, |
||||
QString community_id, |
||||
QWidget *parent = nullptr); |
||||
|
||||
~CommunitiesListItem(); |
||||
|
||||
void setCommunity(QSharedPointer<Community> community); |
||||
|
||||
inline bool isPressed() const; |
||||
inline void setAvatar(const QImage &avatar_image); |
||||
|
||||
QColor highlightedBackgroundColor() const { return highlightedBackgroundColor_; } |
||||
QColor hoverBackgroundColor() const { return hoverBackgroundColor_; } |
||||
QColor backgroundColor() const { return backgroundColor_; } |
||||
|
||||
void setHighlightedBackgroundColor(QColor &color) { highlightedBackgroundColor_ = color; } |
||||
void setHoverBackgroundColor(QColor &color) { hoverBackgroundColor_ = color; } |
||||
void setBackgroundColor(QColor &color) { backgroundColor_ = color; } |
||||
|
||||
QColor highlightedBackgroundColor_; |
||||
QColor hoverBackgroundColor_; |
||||
QColor backgroundColor_; |
||||
|
||||
signals: |
||||
void clicked(const QString &community_id); |
||||
|
||||
public slots: |
||||
void setPressedState(bool state); |
||||
|
||||
protected: |
||||
void mousePressEvent(QMouseEvent *event) override; |
||||
void paintEvent(QPaintEvent *event) override; |
||||
void contextMenuEvent(QContextMenuEvent *event) override; |
||||
|
||||
private: |
||||
const int IconSize = 55; |
||||
|
||||
QSharedPointer<Community> community_; |
||||
QString communityId_; |
||||
QString communityName_; |
||||
QString communityShortDescription; |
||||
|
||||
QPixmap communityAvatar_; |
||||
|
||||
Menu *menu_; |
||||
bool isPressed_ = false; |
||||
}; |
||||
|
||||
inline bool |
||||
CommunitiesListItem::isPressed() const |
||||
{ |
||||
return isPressed_; |
||||
} |
||||
|
||||
inline void |
||||
CommunitiesListItem::setAvatar(const QImage &avatar_image) |
||||
{ |
||||
communityAvatar_ = QPixmap::fromImage( |
||||
avatar_image.scaled(IconSize, IconSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); |
||||
update(); |
||||
} |
||||
|
||||
class WorldCommunityListItem : public CommunitiesListItem |
||||
{ |
||||
Q_OBJECT |
||||
public: |
||||
WorldCommunityListItem(QWidget *parent = nullptr); |
||||
~WorldCommunityListItem(); |
||||
|
||||
protected: |
||||
void mousePressEvent(QMouseEvent *event) override; |
||||
void paintEvent(QPaintEvent *event) override; |
||||
|
||||
private: |
||||
const int IconSize = 55; |
||||
}; |
@ -0,0 +1,62 @@ |
||||
#pragma once |
||||
|
||||
#include <QJsonObject> |
||||
#include <QObject> |
||||
#include <QString> |
||||
#include <QUrl> |
||||
|
||||
class Community : public QObject |
||||
{ |
||||
Q_OBJECT |
||||
|
||||
public: |
||||
void parseProfile(const QJsonObject &profile); |
||||
void parseRooms(const QJsonObject &rooms); |
||||
|
||||
inline QUrl getAvatar() const; |
||||
inline QString getName() const; |
||||
inline QString getShortDescription() const; |
||||
inline QString getLongDescription() const; |
||||
inline const QList<QString> getRoomList() const; |
||||
|
||||
signals: |
||||
void roomsChanged(QList<QString> &rooms); |
||||
|
||||
private: |
||||
QUrl avatar_; |
||||
QString name_; |
||||
QString short_description_; |
||||
QString long_description_; |
||||
|
||||
QList<QString> rooms_; |
||||
}; |
||||
|
||||
inline QUrl |
||||
Community::getAvatar() const |
||||
{ |
||||
return avatar_; |
||||
} |
||||
|
||||
inline QString |
||||
Community::getName() const |
||||
{ |
||||
return name_; |
||||
} |
||||
|
||||
inline QString |
||||
Community::getShortDescription() const |
||||
{ |
||||
return short_description_; |
||||
} |
||||
|
||||
inline QString |
||||
Community::getLongDescription() const |
||||
{ |
||||
return long_description_; |
||||
} |
||||
|
||||
inline const QList<QString> |
||||
Community::getRoomList() const |
||||
{ |
||||
return rooms_; |
||||
} |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 3.9 KiB |
@ -0,0 +1,150 @@ |
||||
#include "CommunitiesList.h" |
||||
|
||||
#include <QLabel> |
||||
|
||||
CommunitiesList::CommunitiesList(QSharedPointer<MatrixClient> client, QWidget *parent) |
||||
: QWidget(parent) |
||||
, client_(client) |
||||
{ |
||||
QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); |
||||
sizePolicy.setHorizontalStretch(0); |
||||
sizePolicy.setVerticalStretch(1); |
||||
setSizePolicy(sizePolicy); |
||||
|
||||
setStyleSheet("border-style: none;"); |
||||
|
||||
topLayout_ = new QVBoxLayout(this); |
||||
topLayout_->setSpacing(0); |
||||
topLayout_->setMargin(0); |
||||
|
||||
setFixedWidth(ui::sidebar::CommunitiesSidebarSize); |
||||
|
||||
scrollArea_ = new QScrollArea(this); |
||||
scrollArea_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); |
||||
scrollArea_->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); |
||||
scrollArea_->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents); |
||||
scrollArea_->setWidgetResizable(true); |
||||
scrollArea_->setAlignment(Qt::AlignLeading | Qt::AlignTop | Qt::AlignVCenter); |
||||
|
||||
scrollAreaContents_ = new QWidget(); |
||||
|
||||
contentsLayout_ = new QVBoxLayout(scrollAreaContents_); |
||||
contentsLayout_->setSpacing(0); |
||||
contentsLayout_->setMargin(0); |
||||
|
||||
WorldCommunityListItem *world_list_item = new WorldCommunityListItem(); |
||||
contentsLayout_->addWidget(world_list_item); |
||||
communities_.insert("world", QSharedPointer<CommunitiesListItem>(world_list_item)); |
||||
connect(world_list_item, |
||||
&WorldCommunityListItem::clicked, |
||||
this, |
||||
&CommunitiesList::highlightSelectedCommunity); |
||||
contentsLayout_->addStretch(1); |
||||
|
||||
scrollArea_->setWidget(scrollAreaContents_); |
||||
topLayout_->addWidget(scrollArea_); |
||||
|
||||
connect(client_.data(), |
||||
&MatrixClient::communityProfileRetrieved, |
||||
this, |
||||
[=](QString communityId, QJsonObject profile) { |
||||
client_->fetchCommunityAvatar(communityId, |
||||
QUrl(profile["avatar_url"].toString())); |
||||
}); |
||||
connect(client_.data(), |
||||
SIGNAL(communityAvatarRetrieved(const QString &, const QPixmap &)), |
||||
this, |
||||
SLOT(updateCommunityAvatar(const QString &, const QPixmap &))); |
||||
} |
||||
|
||||
CommunitiesList::~CommunitiesList() {} |
||||
|
||||
void |
||||
CommunitiesList::setCommunities(const QMap<QString, QSharedPointer<Community>> &communities) |
||||
{ |
||||
communities_.clear(); |
||||
|
||||
// TODO: still not sure how to handle the "world" special-case
|
||||
WorldCommunityListItem *world_list_item = new WorldCommunityListItem(); |
||||
communities_.insert("world", QSharedPointer<CommunitiesListItem>(world_list_item)); |
||||
connect(world_list_item, |
||||
&WorldCommunityListItem::clicked, |
||||
this, |
||||
&CommunitiesList::highlightSelectedCommunity); |
||||
contentsLayout_->insertWidget(0, world_list_item); |
||||
|
||||
for (auto it = communities.constBegin(); it != communities.constEnd(); it++) { |
||||
const auto community_id = it.key(); |
||||
const auto community = it.value(); |
||||
|
||||
addCommunity(community, community_id); |
||||
|
||||
client_->fetchCommunityProfile(community_id); |
||||
client_->fetchCommunityRooms(community_id); |
||||
} |
||||
|
||||
world_list_item->setPressedState(true); |
||||
emit communityChanged("world"); |
||||
} |
||||
|
||||
void |
||||
CommunitiesList::clear() |
||||
{ |
||||
communities_.clear(); |
||||
} |
||||
|
||||
void |
||||
CommunitiesList::addCommunity(QSharedPointer<Community> community, const QString &community_id) |
||||
{ |
||||
CommunitiesListItem *list_item = |
||||
new CommunitiesListItem(community, community_id, scrollArea_); |
||||
|
||||
communities_.insert(community_id, QSharedPointer<CommunitiesListItem>(list_item)); |
||||
|
||||
client_->fetchCommunityAvatar(community_id, community->getAvatar()); |
||||
|
||||
contentsLayout_->insertWidget(contentsLayout_->count() - 1, list_item); |
||||
|
||||
connect(list_item, |
||||
&CommunitiesListItem::clicked, |
||||
this, |
||||
&CommunitiesList::highlightSelectedCommunity); |
||||
} |
||||
|
||||
void |
||||
CommunitiesList::removeCommunity(const QString &community_id) |
||||
{ |
||||
communities_.remove(community_id); |
||||
} |
||||
|
||||
void |
||||
CommunitiesList::updateCommunityAvatar(const QString &community_id, const QPixmap &img) |
||||
{ |
||||
if (!communities_.contains(community_id)) { |
||||
qWarning() << "Avatar update on nonexistent community" << community_id; |
||||
return; |
||||
} |
||||
|
||||
communities_.value(community_id)->setAvatar(img.toImage()); |
||||
} |
||||
|
||||
void |
||||
CommunitiesList::highlightSelectedCommunity(const QString &community_id) |
||||
{ |
||||
emit communityChanged(community_id); |
||||
|
||||
if (!communities_.contains(community_id)) { |
||||
qDebug() << "CommunitiesList: clicked unknown community"; |
||||
return; |
||||
} |
||||
|
||||
for (auto it = communities_.constBegin(); it != communities_.constEnd(); it++) { |
||||
if (it.key() != community_id) { |
||||
it.value()->setPressedState(false); |
||||
} else { |
||||
it.value()->setPressedState(true); |
||||
scrollArea_->ensureWidgetVisible( |
||||
qobject_cast<QWidget *>(it.value().data())); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,200 @@ |
||||
#include "CommunitiesListItem.h" |
||||
|
||||
CommunitiesListItem::CommunitiesListItem(QSharedPointer<Community> community, |
||||
QString community_id, |
||||
QWidget *parent) |
||||
: QWidget(parent) |
||||
, community_(community) |
||||
, communityId_(community_id) |
||||
{ |
||||
// menu_ = new Menu(this);
|
||||
setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); |
||||
setFixedHeight(ui::sidebar::CommunitiesSidebarSize); |
||||
setFixedWidth(ui::sidebar::CommunitiesSidebarSize); |
||||
} |
||||
|
||||
CommunitiesListItem::~CommunitiesListItem() {} |
||||
|
||||
void |
||||
CommunitiesListItem::setCommunity(QSharedPointer<Community> community) |
||||
{ |
||||
community_ = community; |
||||
} |
||||
|
||||
void |
||||
CommunitiesListItem::setPressedState(bool state) |
||||
{ |
||||
if (isPressed_ != state) { |
||||
isPressed_ = state; |
||||
update(); |
||||
} |
||||
} |
||||
|
||||
void |
||||
CommunitiesListItem::mousePressEvent(QMouseEvent *event) |
||||
{ |
||||
if (event->buttons() == Qt::RightButton) { |
||||
QWidget::mousePressEvent(event); |
||||
return; |
||||
} |
||||
|
||||
emit clicked(communityId_); |
||||
|
||||
setPressedState(true); |
||||
} |
||||
|
||||
void |
||||
CommunitiesListItem::paintEvent(QPaintEvent *event) |
||||
{ |
||||
Q_UNUSED(event); |
||||
|
||||
QPainter p(this); |
||||
p.setRenderHint(QPainter::TextAntialiasing); |
||||
p.setRenderHint(QPainter::SmoothPixmapTransform); |
||||
p.setRenderHint(QPainter::Antialiasing); |
||||
|
||||
if (isPressed_) |
||||
p.fillRect(rect(), highlightedBackgroundColor_); |
||||
else if (underMouse()) |
||||
p.fillRect(rect(), hoverBackgroundColor_); |
||||
else |
||||
p.fillRect(rect(), backgroundColor_); |
||||
|
||||
QFont font; |
||||
font.setPixelSize(conf::fontSize); |
||||
|
||||
p.setPen(QColor("#333")); |
||||
|
||||
QRect avatarRegion((width() - IconSize) / 2, (height() - IconSize) / 2, IconSize, IconSize); |
||||
|
||||
font.setBold(false); |
||||
p.setPen(Qt::NoPen); |
||||
|
||||
// We using the first letter of room's name.
|
||||
if (communityAvatar_.isNull()) { |
||||
QBrush brush; |
||||
brush.setStyle(Qt::SolidPattern); |
||||
brush.setColor("#eee"); |
||||
|
||||
p.setPen(Qt::NoPen); |
||||
p.setBrush(brush); |
||||
|
||||
p.drawEllipse(avatarRegion.center(), IconSize / 2, IconSize / 2); |
||||
|
||||
font.setPixelSize(conf::roomlist::fonts::bubble); |
||||
p.setFont(font); |
||||
p.setPen(QColor("#000")); |
||||
p.setBrush(Qt::NoBrush); |
||||
p.drawText( |
||||
avatarRegion.translated(0, -1), Qt::AlignCenter, QChar(community_->getName()[0])); |
||||
} else { |
||||
p.save(); |
||||
|
||||
QPainterPath path; |
||||
path.addEllipse( |
||||
(width() - IconSize) / 2, (height() - IconSize) / 2, IconSize, IconSize); |
||||
p.setClipPath(path); |
||||
|
||||
p.drawPixmap(avatarRegion, communityAvatar_); |
||||
p.restore(); |
||||
} |
||||
|
||||
// TODO: Discord-style community ping counts?
|
||||
/*if (unreadMsgCount_ > 0) {
|
||||
QColor textColor("white"); |
||||
QColor backgroundColor("#38A3D8"); |
||||
|
||||
QBrush brush; |
||||
brush.setStyle(Qt::SolidPattern); |
||||
brush.setColor(backgroundColor); |
||||
|
||||
if (isPressed_) |
||||
brush.setColor(textColor); |
||||
|
||||
QFont unreadCountFont; |
||||
unreadCountFont.setPixelSize(conf::roomlist::fonts::badge); |
||||
unreadCountFont.setBold(true); |
||||
|
||||
p.setBrush(brush); |
||||
p.setPen(Qt::NoPen); |
||||
p.setFont(unreadCountFont); |
||||
|
||||
int diameter = 20; |
||||
|
||||
QRectF r( |
||||
width() - diameter - 5, height() - diameter - 5, diameter, diameter); |
||||
|
||||
p.setPen(Qt::NoPen); |
||||
p.drawEllipse(r); |
||||
|
||||
p.setPen(QPen(textColor)); |
||||
|
||||
if (isPressed_) |
||||
p.setPen(QPen(backgroundColor)); |
||||
|
||||
p.setBrush(Qt::NoBrush); |
||||
p.drawText( |
||||
r.translated(0, -0.5), Qt::AlignCenter, QString::number(unreadMsgCount_)); |
||||
}*/ |
||||
} |
||||
|
||||
void |
||||
CommunitiesListItem::contextMenuEvent(QContextMenuEvent *event) |
||||
{ |
||||
Q_UNUSED(event); |
||||
|
||||
// menu_->popup(event->globalPos());
|
||||
} |
||||
|
||||
WorldCommunityListItem::WorldCommunityListItem(QWidget *parent) |
||||
: CommunitiesListItem(QSharedPointer<Community>(), "", parent) |
||||
{} |
||||
|
||||
WorldCommunityListItem::~WorldCommunityListItem() {} |
||||
|
||||
void |
||||
WorldCommunityListItem::mousePressEvent(QMouseEvent *event) |
||||
{ |
||||
if (event->buttons() == Qt::RightButton) { |
||||
QWidget::mousePressEvent(event); |
||||
return; |
||||
} |
||||
|
||||
emit CommunitiesListItem::clicked("world"); |
||||
|
||||
setPressedState(true); |
||||
} |
||||
|
||||
void |
||||
WorldCommunityListItem::paintEvent(QPaintEvent *event) |
||||
{ |
||||
Q_UNUSED(event); |
||||
|
||||
static QPixmap worldIcon(":/icons/icons/ui/world.png"); |
||||
|
||||
QPainter p(this); |
||||
p.setRenderHint(QPainter::SmoothPixmapTransform); |
||||
p.setRenderHint(QPainter::Antialiasing); |
||||
|
||||
if (isPressed()) |
||||
p.fillRect(rect(), highlightedBackgroundColor_); |
||||
else if (underMouse()) |
||||
p.fillRect(rect(), hoverBackgroundColor_); |
||||
else |
||||
p.fillRect(rect(), backgroundColor_); |
||||
|
||||
QBrush brush; |
||||
brush.setStyle(Qt::SolidPattern); |
||||
brush.setColor("#FFFFFF"); |
||||
|
||||
p.setPen(Qt::NoPen); |
||||
p.setBrush(brush); |
||||
|
||||
QRect avatarRegion((width() - IconSize) / 2, (height() - IconSize) / 2, IconSize, IconSize); |
||||
p.drawEllipse(avatarRegion.center(), IconSize / 2, IconSize / 2); |
||||
QPainterPath path; |
||||
path.addEllipse((width() - IconSize) / 2, (height() - IconSize) / 2, IconSize, IconSize); |
||||
p.setClipPath(path); |
||||
|
||||
p.drawPixmap(avatarRegion, worldIcon); |
||||
} |
@ -0,0 +1,44 @@ |
||||
#include "include/Community.h" |
||||
|
||||
#include <QJsonArray> |
||||
#include <QJsonValue> |
||||
|
||||
void |
||||
Community::parseProfile(const QJsonObject &profile) |
||||
{ |
||||
if (profile["name"].type() == QJsonValue::Type::String) { |
||||
name_ = profile["name"].toString(); |
||||
} else { |
||||
name_ = "Unnamed Community"; // TODO: what is correct here?
|
||||
} |
||||
|
||||
if (profile["avatar_url"].type() == QJsonValue::Type::String) { |
||||
avatar_ = QUrl(profile["avatar_url"].toString()); |
||||
} else { |
||||
avatar_ = QUrl(); |
||||
} |
||||
|
||||
if (profile["short_description"].type() == QJsonValue::Type::String) { |
||||
short_description_ = profile["short_description"].toString(); |
||||
} else { |
||||
short_description_ = ""; |
||||
} |
||||
|
||||
if (profile["long_description"].type() == QJsonValue::Type::String) { |
||||
long_description_ = profile["long_description"].toString(); |
||||
} else { |
||||
long_description_ = ""; |
||||
} |
||||
} |
||||
|
||||
void |
||||
Community::parseRooms(const QJsonObject &rooms) |
||||
{ |
||||
rooms_.clear(); |
||||
|
||||
for (auto i = 0; i < rooms["chunk"].toArray().size(); i++) { |
||||
rooms_.append(rooms["chunk"].toArray()[i].toObject()["room_id"].toString()); |
||||
} |
||||
|
||||
emit roomsChanged(rooms_); |
||||
} |
Loading…
Reference in new issue