mirror of https://github.com/Nheko-Reborn/nheko
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
300 lines
8.4 KiB
300 lines
8.4 KiB
#include <QPaintEvent>
|
|
#include <QPainter>
|
|
#include <QStyleOption>
|
|
|
|
#include "Config.h"
|
|
#include "SuggestionsPopup.h"
|
|
#include "Utils.h"
|
|
#include "ui/Avatar.h"
|
|
#include "ui/DropShadow.h"
|
|
|
|
constexpr int PopupHMargin = 4;
|
|
constexpr int PopupItemMargin = 3;
|
|
|
|
PopupItem::PopupItem(QWidget *parent)
|
|
: QWidget(parent)
|
|
, avatar_{new Avatar(this)}
|
|
, hovering_{false}
|
|
{
|
|
setMouseTracking(true);
|
|
setAttribute(Qt::WA_Hover);
|
|
|
|
topLayout_ = new QHBoxLayout(this);
|
|
topLayout_->setContentsMargins(
|
|
PopupHMargin, PopupItemMargin, PopupHMargin, PopupItemMargin);
|
|
|
|
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
|
|
}
|
|
|
|
void
|
|
PopupItem::paintEvent(QPaintEvent *)
|
|
{
|
|
QStyleOption opt;
|
|
opt.init(this);
|
|
QPainter p(this);
|
|
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
|
|
|
|
if (underMouse() || hovering_)
|
|
p.fillRect(rect(), hoverColor_);
|
|
}
|
|
|
|
UserItem::UserItem(QWidget *parent, const QString &user_id)
|
|
: PopupItem(parent)
|
|
, userId_{user_id}
|
|
{
|
|
QFont font;
|
|
font.setPixelSize(conf::popup::font);
|
|
|
|
auto displayName = Cache::displayName(ChatPage::instance()->currentRoom(), userId_);
|
|
|
|
avatar_->setSize(conf::popup::avatar);
|
|
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(font);
|
|
|
|
topLayout_->addWidget(avatar_);
|
|
topLayout_->addWidget(userName_, 1);
|
|
|
|
resolveAvatar(user_id);
|
|
}
|
|
|
|
void
|
|
UserItem::updateItem(const QString &user_id)
|
|
{
|
|
userId_ = user_id;
|
|
|
|
auto displayName = Cache::displayName(ChatPage::instance()->currentRoom(), userId_);
|
|
|
|
// If it's a matrix id we use the second letter.
|
|
if (displayName.size() > 1 && displayName.at(0) == '@')
|
|
avatar_->setLetter(QChar(displayName.at(1)));
|
|
else
|
|
avatar_->setLetter(utils::firstChar(displayName));
|
|
|
|
userName_->setText(displayName);
|
|
resolveAvatar(user_id);
|
|
}
|
|
|
|
void
|
|
UserItem::resolveAvatar(const QString &user_id)
|
|
{
|
|
AvatarProvider::resolve(
|
|
ChatPage::instance()->currentRoom(), userId_, this, [this, user_id](const QImage &img) {
|
|
// The user on the widget when the avatar is resolved,
|
|
// might be different from the user that made the call.
|
|
if (user_id == userId_)
|
|
avatar_->setImage(img);
|
|
else
|
|
// We try to resolve the avatar again.
|
|
resolveAvatar(userId_);
|
|
});
|
|
}
|
|
|
|
void
|
|
UserItem::mousePressEvent(QMouseEvent *event)
|
|
{
|
|
if (event->buttons() != Qt::RightButton)
|
|
emit clicked(
|
|
Cache::displayName(ChatPage::instance()->currentRoom(), selectedText()));
|
|
|
|
QWidget::mousePressEvent(event);
|
|
}
|
|
|
|
RoomItem::RoomItem(QWidget *parent, const RoomSearchResult &res)
|
|
: PopupItem(parent)
|
|
, roomId_{QString::fromStdString(res.room_id)}
|
|
{
|
|
auto name = QFontMetrics(QFont()).elidedText(
|
|
QString::fromStdString(res.info.name), Qt::ElideRight, parentWidget()->width() - 10);
|
|
|
|
avatar_->setSize(conf::popup::avatar + 6);
|
|
avatar_->setLetter(utils::firstChar(name));
|
|
|
|
roomName_ = new QLabel(name, this);
|
|
roomName_->setMargin(0);
|
|
|
|
topLayout_->addWidget(avatar_);
|
|
topLayout_->addWidget(roomName_, 1);
|
|
|
|
if (!res.img.isNull())
|
|
avatar_->setImage(res.img);
|
|
}
|
|
|
|
void
|
|
RoomItem::updateItem(const RoomSearchResult &result)
|
|
{
|
|
roomId_ = QString::fromStdString(std::move(result.room_id));
|
|
|
|
auto name =
|
|
QFontMetrics(QFont()).elidedText(QString::fromStdString(std::move(result.info.name)),
|
|
Qt::ElideRight,
|
|
parentWidget()->width() - 10);
|
|
|
|
roomName_->setText(name);
|
|
|
|
if (!result.img.isNull())
|
|
avatar_->setImage(result.img);
|
|
else
|
|
avatar_->setLetter(utils::firstChar(name));
|
|
}
|
|
|
|
void
|
|
RoomItem::mousePressEvent(QMouseEvent *event)
|
|
{
|
|
if (event->buttons() != Qt::RightButton)
|
|
emit clicked(selectedText());
|
|
|
|
QWidget::mousePressEvent(event);
|
|
}
|
|
|
|
SuggestionsPopup::SuggestionsPopup(QWidget *parent)
|
|
: QWidget(parent)
|
|
{
|
|
setAttribute(Qt::WA_ShowWithoutActivating, true);
|
|
setWindowFlags(Qt::ToolTip | Qt::NoDropShadowWindowHint);
|
|
|
|
layout_ = new QVBoxLayout(this);
|
|
layout_->setMargin(0);
|
|
layout_->setSpacing(0);
|
|
}
|
|
|
|
void
|
|
SuggestionsPopup::addRooms(const std::vector<RoomSearchResult> &rooms)
|
|
{
|
|
if (rooms.empty()) {
|
|
hide();
|
|
return;
|
|
}
|
|
|
|
const size_t layoutCount = layout_->count();
|
|
const size_t roomCount = rooms.size();
|
|
|
|
// Remove the extra widgets from the layout.
|
|
if (roomCount < layoutCount)
|
|
removeLayoutItemsAfter(roomCount - 1);
|
|
|
|
for (size_t i = 0; i < roomCount; ++i) {
|
|
auto item = layout_->itemAt(i);
|
|
|
|
// Create a new widget if there isn't already one in that
|
|
// layout position.
|
|
if (!item) {
|
|
auto room = new RoomItem(this, rooms.at(i));
|
|
connect(room, &RoomItem::clicked, this, &SuggestionsPopup::itemSelected);
|
|
layout_->addWidget(room);
|
|
} else {
|
|
// Update the current widget with the new data.
|
|
auto room = qobject_cast<RoomItem *>(item->widget());
|
|
if (room)
|
|
room->updateItem(rooms.at(i));
|
|
}
|
|
}
|
|
|
|
resetSelection();
|
|
adjustSize();
|
|
|
|
resize(geometry().width(), 40 * rooms.size());
|
|
|
|
selectNextSuggestion();
|
|
}
|
|
|
|
void
|
|
SuggestionsPopup::addUsers(const QVector<SearchResult> &users)
|
|
{
|
|
if (users.isEmpty()) {
|
|
hide();
|
|
return;
|
|
}
|
|
|
|
const size_t layoutCount = layout_->count();
|
|
const size_t userCount = users.size();
|
|
|
|
// Remove the extra widgets from the layout.
|
|
if (userCount < layoutCount)
|
|
removeLayoutItemsAfter(userCount - 1);
|
|
|
|
for (size_t i = 0; i < userCount; ++i) {
|
|
auto item = layout_->itemAt(i);
|
|
|
|
// Create a new widget if there isn't already one in that
|
|
// layout position.
|
|
if (!item) {
|
|
auto user = new UserItem(this, users.at(i).user_id);
|
|
connect(user, &UserItem::clicked, this, &SuggestionsPopup::itemSelected);
|
|
layout_->addWidget(user);
|
|
} else {
|
|
// Update the current widget with the new data.
|
|
auto userWidget = qobject_cast<UserItem *>(item->widget());
|
|
if (userWidget)
|
|
userWidget->updateItem(users.at(i).user_id);
|
|
}
|
|
}
|
|
|
|
resetSelection();
|
|
adjustSize();
|
|
|
|
selectNextSuggestion();
|
|
}
|
|
|
|
void
|
|
SuggestionsPopup::hoverSelection()
|
|
{
|
|
resetHovering();
|
|
setHovering(selectedItem_);
|
|
update();
|
|
}
|
|
|
|
void
|
|
SuggestionsPopup::selectNextSuggestion()
|
|
{
|
|
selectedItem_++;
|
|
if (selectedItem_ >= layout_->count())
|
|
selectFirstItem();
|
|
|
|
hoverSelection();
|
|
}
|
|
|
|
void
|
|
SuggestionsPopup::selectPreviousSuggestion()
|
|
{
|
|
selectedItem_--;
|
|
if (selectedItem_ < 0)
|
|
selectLastItem();
|
|
|
|
hoverSelection();
|
|
}
|
|
|
|
void
|
|
SuggestionsPopup::resetHovering()
|
|
{
|
|
for (int i = 0; i < layout_->count(); ++i) {
|
|
const auto item = qobject_cast<PopupItem *>(layout_->itemAt(i)->widget());
|
|
|
|
if (item)
|
|
item->setHovering(false);
|
|
}
|
|
}
|
|
|
|
void
|
|
SuggestionsPopup::setHovering(int pos)
|
|
{
|
|
const auto &item = layout_->itemAt(pos);
|
|
const auto &widget = qobject_cast<PopupItem *>(item->widget());
|
|
|
|
if (widget)
|
|
widget->setHovering(true);
|
|
}
|
|
|
|
void
|
|
SuggestionsPopup::paintEvent(QPaintEvent *)
|
|
{
|
|
QStyleOption opt;
|
|
opt.init(this);
|
|
QPainter p(this);
|
|
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
|
|
}
|
|
|