From 2c91d2b35e6df8247491062464ca630fe3a00d9f Mon Sep 17 00:00:00 2001 From: defnax Date: Tue, 9 Jul 2024 23:00:14 +0200 Subject: [PATCH 1/3] Added Circle Avatars feature --- .../src/gui/common/FriendListModel.cpp | 44 ++++++++++++++++++- .../src/gui/common/FriendListModel.h | 4 +- .../src/gui/common/NewFriendList.cpp | 11 ++++- retroshare-gui/src/gui/common/NewFriendList.h | 1 + .../src/gui/common/NewFriendList.ui | 8 ++++ 5 files changed, 65 insertions(+), 3 deletions(-) diff --git a/retroshare-gui/src/gui/common/FriendListModel.cpp b/retroshare-gui/src/gui/common/FriendListModel.cpp index 35cefa7ac..77e589382 100644 --- a/retroshare-gui/src/gui/common/FriendListModel.cpp +++ b/retroshare-gui/src/gui/common/FriendListModel.cpp @@ -65,7 +65,7 @@ static const uint32_t NODE_DETAILS_UPDATE_DELAY = 5; // update each node every 5 RsFriendListModel::RsFriendListModel(QObject *parent) : QAbstractItemModel(parent) - , mDisplayGroups(true), mDisplayStatusString(true), mDisplayStatusIcon (false) + , mDisplayGroups(true), mDisplayStatusString(true), mDisplayStatusIcon (false), mDisplayCircleAvatars (false) , mLastInternalDataUpdate(0), mLastNodeUpdate(0) { mFilterStrings.clear(); @@ -160,6 +160,27 @@ static QIcon createAvatar(const QPixmap &avatar, const QPixmap &overlay) return icon; } +static QImage getCirclePhoto(const QImage original, int sizePhoto) +{ + QImage target(sizePhoto, sizePhoto, QImage::Format_ARGB32_Premultiplied); + target.fill(Qt::transparent); + + QPainter painter(&target); + painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform); + painter.setBrush(QBrush(Qt::white)); + auto scaledPhoto = original + .scaled(sizePhoto, sizePhoto, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation) + .convertToFormat(QImage::Format_ARGB32_Premultiplied); + int margin = 0; + if (scaledPhoto.width() > sizePhoto) { + margin = (scaledPhoto.width() - sizePhoto) / 2; + } + painter.drawEllipse(0, 0, sizePhoto, sizePhoto); + painter.setCompositionMode(QPainter::CompositionMode_SourceIn); + painter.drawImage(0, 0, scaledPhoto, margin, 0); + return target; +} + void RsFriendListModel::setDisplayStatusString(bool b) { mDisplayStatusString = b; @@ -172,6 +193,12 @@ void RsFriendListModel::setDisplayStatusIcon(bool b) postMods(); } +void RsFriendListModel::setDisplayCircleAvatars(bool b) +{ + mDisplayCircleAvatars = b; + postMods(); +} + void RsFriendListModel::setDisplayGroups(bool b) { mDisplayGroups = b; @@ -1023,6 +1050,13 @@ QVariant RsFriendListModel::decorationRole(const EntryIndex& entry,int col) cons if (!foundAvatar || sslAvatar.isNull()) { sslAvatar = FilesDefs::getPixmapFromQtResourcePath(AVATAR_DEFAULT_IMAGE); } + + if(mDisplayCircleAvatars) { + //make avatar as circle avatar + QImage orginalImage = sslAvatar.toImage(); + QImage circleImage = getCirclePhoto(orginalImage,orginalImage.size().width()); + sslAvatar.convertFromImage(circleImage); + } if (mDisplayStatusIcon) { if (bestNodeInformation) { @@ -1046,6 +1080,14 @@ QVariant RsFriendListModel::decorationRole(const EntryIndex& entry,int col) cons QPixmap sslAvatar; AvatarDefs::getAvatarFromSslId(RsPeerId(hn->node_info.id.toStdString()), sslAvatar); + + if(mDisplayCircleAvatars) { + //make avatar as circle avatar + QImage orginalImage = sslAvatar.toImage(); + QImage circleImage = getCirclePhoto(orginalImage,orginalImage.size().width()); + sslAvatar.convertFromImage(circleImage); + } + if (mDisplayStatusIcon) { QPixmap sslOverlayIcon = FilesDefs::getPixmapFromQtResourcePath(StatusDefs::imageStatus(statusRole(entry, col).toInt())); return QVariant(QIcon(createAvatar(sslAvatar, sslOverlayIcon))); diff --git a/retroshare-gui/src/gui/common/FriendListModel.h b/retroshare-gui/src/gui/common/FriendListModel.h index e19a84f04..67feac9ed 100644 --- a/retroshare-gui/src/gui/common/FriendListModel.h +++ b/retroshare-gui/src/gui/common/FriendListModel.h @@ -131,7 +131,8 @@ public: bool getDisplayStatusString() const { return mDisplayStatusString; } void setDisplayStatusIcon(bool b); bool getDisplayStatusIcon() const { return mDisplayStatusIcon; } - + void setDisplayCircleAvatars(bool b); + bool getDisplayCircleAvatars() const { return mDisplayCircleAvatars; } EntryType getType(const QModelIndex&) const; @@ -231,6 +232,7 @@ private: bool mDisplayGroups ; bool mDisplayStatusString ; bool mDisplayStatusIcon ; + bool mDisplayCircleAvatars ; rstime_t mLastInternalDataUpdate; rstime_t mLastNodeUpdate;; diff --git a/retroshare-gui/src/gui/common/NewFriendList.cpp b/retroshare-gui/src/gui/common/NewFriendList.cpp index 6acb8da9a..ec5133521 100644 --- a/retroshare-gui/src/gui/common/NewFriendList.cpp +++ b/retroshare-gui/src/gui/common/NewFriendList.cpp @@ -267,6 +267,7 @@ NewFriendList::NewFriendList(QWidget */*parent*/) : /* RsAutoUpdatePage(5000,par connect(ui->actionShowState, SIGNAL(triggered(bool)), this, SLOT(setShowState(bool)) ); connect(ui->actionShowStateIcon, SIGNAL(triggered(bool)), this, SLOT(setShowStateIcon(bool)) ); connect(ui->actionShowGroups, SIGNAL(triggered(bool)), this, SLOT(setShowGroups(bool)) ); + connect(ui->actionShowCircleAvatars, SIGNAL(triggered(bool)), this, SLOT(setShowCircleAvatars(bool)) ); connect(ui->actionExportFriendlist, SIGNAL(triggered()) , this, SLOT(exportFriendlistClicked())); connect(ui->actionImportFriendlist, SIGNAL(triggered()) , this, SLOT(importFriendlistClicked())); @@ -347,6 +348,7 @@ void NewFriendList::headerContextMenuRequested(QPoint /*p*/) displayMenu.addAction(ui->actionShowOfflineFriends); displayMenu.addAction(ui->actionShowState); displayMenu.addAction(ui->actionShowStateIcon); + displayMenu.addAction(ui->actionShowCircleAvatars); displayMenu.addAction(ui->actionShowGroups); ui->actionShowOfflineFriends->setChecked(mProxyModel->showOfflineNodes()); @@ -513,6 +515,7 @@ void NewFriendList::processSettings(bool load) mModel->setDisplayStatusString(Settings->value("showState", mModel->getDisplayStatusString()).toBool()); mModel->setDisplayGroups(Settings->value("showGroups", mModel->getDisplayGroups()).toBool()); mModel->setDisplayStatusIcon(Settings->value("showStateIcon", mModel->getDisplayStatusIcon()).toBool()); + mModel->setDisplayCircleAvatars(Settings->value("showCircleAvatars", mModel->getDisplayCircleAvatars()).toBool()); setColumnVisible(RsFriendListModel::COLUMN_THREAD_IP,Settings->value("showIP", isColumnVisible(RsFriendListModel::COLUMN_THREAD_IP)).toBool()); @@ -538,7 +541,7 @@ void NewFriendList::processSettings(bool load) Settings->setValue("showState", mModel->getDisplayStatusString()); Settings->setValue("showGroups", mModel->getDisplayGroups()); Settings->setValue("showStateIcon", mModel->getDisplayStatusIcon()); - + Settings->setValue("showCircleAvatars", mModel->getDisplayCircleAvatars()); Settings->setValue("showIP",isColumnVisible(RsFriendListModel::COLUMN_THREAD_IP)); Settings->setValue("showID",isColumnVisible(RsFriendListModel::COLUMN_THREAD_ID)); @@ -1660,6 +1663,12 @@ void NewFriendList::setShowStateIcon(bool show) processSettings(false); } +void NewFriendList::setShowCircleAvatars(bool show) +{ + applyWhileKeepingTree([show,this]() { mModel->setDisplayCircleAvatars(show) ; }); + processSettings(false); +} + void NewFriendList::setShowGroups(bool show) { applyWhileKeepingTree([show,this]() { mModel->setDisplayGroups(show) ; }); diff --git a/retroshare-gui/src/gui/common/NewFriendList.h b/retroshare-gui/src/gui/common/NewFriendList.h index 7424bcfcf..d62e90be7 100644 --- a/retroshare-gui/src/gui/common/NewFriendList.h +++ b/retroshare-gui/src/gui/common/NewFriendList.h @@ -88,6 +88,7 @@ public slots: void setShowUnconnected(bool hidden); void setShowState(bool show); void setShowStateIcon(bool show); + void setShowCircleAvatars(bool show); void headerContextMenuRequested(QPoint); void exportFriendlistClicked(); diff --git a/retroshare-gui/src/gui/common/NewFriendList.ui b/retroshare-gui/src/gui/common/NewFriendList.ui index f41d88a8b..b5e553ad3 100644 --- a/retroshare-gui/src/gui/common/NewFriendList.ui +++ b/retroshare-gui/src/gui/common/NewFriendList.ui @@ -135,6 +135,14 @@ Show status icons + + + true + + + Circle Avatars + + From e49de2f25b1798a02175e48d93e354ecfc7091d9 Mon Sep 17 00:00:00 2001 From: defnax Date: Thu, 22 Aug 2024 12:25:19 +0200 Subject: [PATCH 2/3] Fixed check state --- retroshare-gui/src/gui/common/NewFriendList.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/retroshare-gui/src/gui/common/NewFriendList.cpp b/retroshare-gui/src/gui/common/NewFriendList.cpp index ec5133521..7904e56d0 100644 --- a/retroshare-gui/src/gui/common/NewFriendList.cpp +++ b/retroshare-gui/src/gui/common/NewFriendList.cpp @@ -355,6 +355,7 @@ void NewFriendList::headerContextMenuRequested(QPoint /*p*/) ui->actionShowState->setChecked(mModel->getDisplayStatusString()); ui->actionShowStateIcon->setChecked(mModel->getDisplayStatusIcon()); ui->actionShowGroups->setChecked(mModel->getDisplayGroups()); + ui->actionShowCircleAvatars->setChecked(mModel->getDisplayCircleAvatars()); QHeaderView *header = ui->peerTreeWidget->header(); From 3fcc1e9774db387958ef5ccb84b108e972c6e415 Mon Sep 17 00:00:00 2001 From: defnax Date: Fri, 23 Aug 2024 07:59:30 +0200 Subject: [PATCH 3/3] Fixed to show when there is a chat from a friend as overlay icon, when popup chat is disable --- .../src/gui/common/FriendListModel.cpp | 53 +++++++++++++++++-- 1 file changed, 48 insertions(+), 5 deletions(-) diff --git a/retroshare-gui/src/gui/common/FriendListModel.cpp b/retroshare-gui/src/gui/common/FriendListModel.cpp index 77e589382..992be4af8 100644 --- a/retroshare-gui/src/gui/common/FriendListModel.cpp +++ b/retroshare-gui/src/gui/common/FriendListModel.cpp @@ -34,6 +34,8 @@ #include "gui/common/FriendListModel.h" #include "gui/gxs/GxsIdDetails.h" #include "gui/gxs/GxsIdTreeWidgetItem.h" +#include "gui/chat/ChatUserNotify.h" + #include "retroshare/rsexpr.h" #include "retroshare/rsmsgs.h" @@ -1023,6 +1025,12 @@ QVariant RsFriendListModel::decorationRole(const EntryIndex& entry,int col) cons { QPixmap sslAvatar; bool foundAvatar = false; + bool hasPrivateChat = false; + + // get peers with waiting incoming chats + std::vector privateChatIds; + ChatUserNotify::getPeersWithWaitingChat(privateChatIds); + const HierarchicalProfileInformation *hn = getProfileInfo(entry); uint32_t status = RS_STATUS_OFFLINE; const HierarchicalNodeInformation *bestNodeInformation = NULL; @@ -1057,11 +1065,26 @@ QVariant RsFriendListModel::decorationRole(const EntryIndex& entry,int col) cons QImage circleImage = getCirclePhoto(orginalImage,orginalImage.size().width()); sslAvatar.convertFromImage(circleImage); } + + if (mDisplayStatusIcon) { + for(uint32_t i=0;ichild_node_indices.size();++i) { + if (std::find(privateChatIds.begin(), privateChatIds.end(), RsPeerId(mLocations[hn->child_node_indices[i]].node_info.id.toStdString())) != privateChatIds.end()) { + // private chat is available + hasPrivateChat = true; + break; + } + } + } if (mDisplayStatusIcon) { if (bestNodeInformation) { - QPixmap sslOverlayIcon = FilesDefs::getPixmapFromQtResourcePath(StatusDefs::imageStatus(status)); - return QVariant(QIcon(createAvatar(sslAvatar, sslOverlayIcon))); + if (hasPrivateChat) { + QPixmap sslOverlayIcon = FilesDefs::getPixmapFromQtResourcePath(":/images/orange-bubble-64.png"); + return QVariant(QIcon(createAvatar(sslAvatar, sslOverlayIcon))); + } else { + QPixmap sslOverlayIcon = FilesDefs::getPixmapFromQtResourcePath(StatusDefs::imageStatus(status)); + return QVariant(QIcon(createAvatar(sslAvatar, sslOverlayIcon))); + } } } @@ -1079,18 +1102,38 @@ QVariant RsFriendListModel::decorationRole(const EntryIndex& entry,int col) cons return QVariant(); QPixmap sslAvatar; + bool hasPrivateChat = false; AvatarDefs::getAvatarFromSslId(RsPeerId(hn->node_info.id.toStdString()), sslAvatar); - + + // get peers with waiting incoming chats + std::vector privateChatIds; + ChatUserNotify::getPeersWithWaitingChat(privateChatIds); + if(mDisplayCircleAvatars) { //make avatar as circle avatar QImage orginalImage = sslAvatar.toImage(); QImage circleImage = getCirclePhoto(orginalImage,orginalImage.size().width()); sslAvatar.convertFromImage(circleImage); } + + if (mDisplayStatusIcon) { + for(uint32_t i=0;inode_info.id.toStdString())) != privateChatIds.end()) { + // private chat is available + hasPrivateChat = true; + break; + } + } + } if (mDisplayStatusIcon) { - QPixmap sslOverlayIcon = FilesDefs::getPixmapFromQtResourcePath(StatusDefs::imageStatus(statusRole(entry, col).toInt())); - return QVariant(QIcon(createAvatar(sslAvatar, sslOverlayIcon))); + if (hasPrivateChat) { + QPixmap sslOverlayIcon = FilesDefs::getPixmapFromQtResourcePath(":/images/orange-bubble-64.png"); + return QVariant(QIcon(createAvatar(sslAvatar, sslOverlayIcon))); + } else { + QPixmap sslOverlayIcon = FilesDefs::getPixmapFromQtResourcePath(StatusDefs::imageStatus(statusRole(entry, col).toInt())); + return QVariant(QIcon(createAvatar(sslAvatar, sslOverlayIcon))); + } } return QVariant(QIcon(sslAvatar));