mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-12-01 14:58:47 +03:00
parent
6dfb824d11
commit
b72e48cbab
8 changed files with 272 additions and 161 deletions
|
@ -106,6 +106,14 @@ from_json(const json &j, MemberInfo &info)
|
||||||
info.avatar_url = j.at("avatar_url");
|
info.avatar_url = j.at("avatar_url");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct RoomSearchResult
|
||||||
|
{
|
||||||
|
std::string room_id;
|
||||||
|
RoomInfo info;
|
||||||
|
QImage img;
|
||||||
|
};
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(RoomSearchResult)
|
||||||
Q_DECLARE_METATYPE(RoomInfo)
|
Q_DECLARE_METATYPE(RoomInfo)
|
||||||
|
|
||||||
class Cache : public QObject
|
class Cache : public QObject
|
||||||
|
@ -185,6 +193,11 @@ public:
|
||||||
UserReceipts readReceipts(const QString &event_id, const QString &room_id);
|
UserReceipts readReceipts(const QString &event_id, const QString &room_id);
|
||||||
|
|
||||||
QByteArray image(const QString &url) const;
|
QByteArray image(const QString &url) const;
|
||||||
|
QByteArray image(lmdb::txn &txn, const std::string &url) const;
|
||||||
|
QByteArray image(const std::string &url) const
|
||||||
|
{
|
||||||
|
return image(QString::fromStdString(url));
|
||||||
|
}
|
||||||
void saveImage(const QString &url, const QByteArray &data);
|
void saveImage(const QString &url, const QByteArray &data);
|
||||||
|
|
||||||
std::vector<std::string> roomsWithStateUpdates(const mtx::responses::Sync &res);
|
std::vector<std::string> roomsWithStateUpdates(const mtx::responses::Sync &res);
|
||||||
|
@ -194,9 +207,11 @@ public:
|
||||||
return getRoomInfo(roomsWithStateUpdates(sync));
|
return getRoomInfo(roomsWithStateUpdates(sync));
|
||||||
}
|
}
|
||||||
|
|
||||||
QVector<SearchResult> getAutocompleteMatches(const std::string &room_id,
|
QVector<SearchResult> searchUsers(const std::string &room_id,
|
||||||
const std::string &query,
|
const std::string &query,
|
||||||
std::uint8_t max_items = 5);
|
std::uint8_t max_items = 5);
|
||||||
|
std::vector<RoomSearchResult> searchRooms(const std::string &query,
|
||||||
|
std::uint8_t max_items = 5);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
//! Save an invited room.
|
//! Save an invited room.
|
||||||
|
|
|
@ -22,8 +22,12 @@
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include "Cache.h"
|
||||||
|
#include "SuggestionsPopup.hpp"
|
||||||
#include "TextField.h"
|
#include "TextField.h"
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(std::vector<RoomSearchResult>)
|
||||||
|
|
||||||
class RoomSearchInput : public TextField
|
class RoomSearchInput : public TextField
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -38,20 +42,20 @@ signals:
|
||||||
protected:
|
protected:
|
||||||
void keyPressEvent(QKeyEvent *event) override;
|
void keyPressEvent(QKeyEvent *event) override;
|
||||||
void hideEvent(QHideEvent *event) override;
|
void hideEvent(QHideEvent *event) override;
|
||||||
bool focusNextPrevChild(bool next) override;
|
bool focusNextPrevChild(bool) override { return false; };
|
||||||
};
|
};
|
||||||
|
|
||||||
class QuickSwitcher : public QWidget
|
class QuickSwitcher : public QWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
|
||||||
explicit QuickSwitcher(QWidget *parent = nullptr);
|
|
||||||
|
|
||||||
void setRoomList(const std::map<QString, QString> &rooms);
|
public:
|
||||||
|
QuickSwitcher(QSharedPointer<Cache> cache, QWidget *parent = nullptr);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void closing();
|
void closing();
|
||||||
void roomSelected(const QString &roomid);
|
void roomSelected(const QString &roomid);
|
||||||
|
void queryResults(const std::vector<RoomSearchResult> &rooms);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void keyPressEvent(QKeyEvent *event) override;
|
void keyPressEvent(QKeyEvent *event) override;
|
||||||
|
@ -64,7 +68,9 @@ private:
|
||||||
|
|
||||||
QVBoxLayout *topLayout_;
|
QVBoxLayout *topLayout_;
|
||||||
RoomSearchInput *roomSearch_;
|
RoomSearchInput *roomSearch_;
|
||||||
QCompleter *completer_;
|
|
||||||
|
|
||||||
std::map<QString, QString> rooms_;
|
//! Autocomplete popup box with the room suggestions.
|
||||||
|
SuggestionsPopup popup_;
|
||||||
|
//! Cache client for room quering.
|
||||||
|
QSharedPointer<Cache> cache_;
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,6 +5,11 @@
|
||||||
#include <QPoint>
|
#include <QPoint>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include "Avatar.h"
|
||||||
|
#include "AvatarProvider.h"
|
||||||
|
#include "Cache.h"
|
||||||
|
#include "ChatPage.h"
|
||||||
|
|
||||||
class Avatar;
|
class Avatar;
|
||||||
struct SearchResult;
|
struct SearchResult;
|
||||||
|
|
||||||
|
@ -16,9 +21,9 @@ class PopupItem : public QWidget
|
||||||
Q_PROPERTY(bool hovering READ hovering WRITE setHovering)
|
Q_PROPERTY(bool hovering READ hovering WRITE setHovering)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PopupItem(QWidget *parent, const QString &user_id);
|
PopupItem(QWidget *parent);
|
||||||
|
|
||||||
QString user() const { return user_id_; }
|
QString selectedText() const { return QString(); }
|
||||||
QColor hoverColor() const { return hoverColor_; }
|
QColor hoverColor() const { return hoverColor_; }
|
||||||
void setHoverColor(QColor &color) { hoverColor_ = color; }
|
void setHoverColor(QColor &color) { hoverColor_ = color; }
|
||||||
|
|
||||||
|
@ -30,14 +35,12 @@ protected:
|
||||||
void mousePressEvent(QMouseEvent *event) override;
|
void mousePressEvent(QMouseEvent *event) override;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void clicked(const QString &display_name);
|
void clicked(const QString &text);
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
QHBoxLayout *topLayout_;
|
QHBoxLayout *topLayout_;
|
||||||
|
|
||||||
Avatar *avatar_;
|
Avatar *avatar_;
|
||||||
QLabel *userName_;
|
|
||||||
QString user_id_;
|
|
||||||
|
|
||||||
QColor hoverColor_;
|
QColor hoverColor_;
|
||||||
|
|
||||||
|
@ -45,6 +48,33 @@ private:
|
||||||
bool hovering_;
|
bool hovering_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class UserItem : public PopupItem
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
UserItem(QWidget *parent, const QString &user_id);
|
||||||
|
QString selectedText() const { return userId_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
QLabel *userName_;
|
||||||
|
QString userId_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class RoomItem : public PopupItem
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
RoomItem(QWidget *parent, const RoomSearchResult &res);
|
||||||
|
QString selectedText() const { return roomId_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
QLabel *roomName_;
|
||||||
|
QString roomId_;
|
||||||
|
RoomSearchResult info_;
|
||||||
|
};
|
||||||
|
|
||||||
class SuggestionsPopup : public QWidget
|
class SuggestionsPopup : public QWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -52,9 +82,24 @@ class SuggestionsPopup : public QWidget
|
||||||
public:
|
public:
|
||||||
explicit SuggestionsPopup(QWidget *parent = nullptr);
|
explicit SuggestionsPopup(QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
template<class Item>
|
||||||
|
void selectHoveredSuggestion()
|
||||||
|
{
|
||||||
|
const auto item = layout_->itemAt(selectedItem_);
|
||||||
|
if (!item)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto &widget = qobject_cast<Item *>(item->widget());
|
||||||
|
emit itemSelected(
|
||||||
|
Cache::displayName(ChatPage::instance()->currentRoom(), widget->selectedText()));
|
||||||
|
|
||||||
|
resetSelection();
|
||||||
|
}
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void addUsers(const QVector<SearchResult> &users);
|
void addUsers(const QVector<SearchResult> &users);
|
||||||
void selectHoveredSuggestion();
|
void addRooms(const std::vector<RoomSearchResult> &rooms);
|
||||||
|
|
||||||
//! Move to the next available suggestion item.
|
//! Move to the next available suggestion item.
|
||||||
void selectNextSuggestion();
|
void selectNextSuggestion();
|
||||||
//! Move to the previous available suggestion item.
|
//! Move to the previous available suggestion item.
|
||||||
|
@ -75,6 +120,14 @@ private:
|
||||||
void resetSelection() { selectedItem_ = -1; }
|
void resetSelection() { selectedItem_ = -1; }
|
||||||
void selectFirstItem() { selectedItem_ = 0; }
|
void selectFirstItem() { selectedItem_ = 0; }
|
||||||
void selectLastItem() { selectedItem_ = layout_->count() - 1; }
|
void selectLastItem() { selectedItem_ = layout_->count() - 1; }
|
||||||
|
void removeItems()
|
||||||
|
{
|
||||||
|
QLayoutItem *item;
|
||||||
|
while ((item = layout_->takeAt(0)) != 0) {
|
||||||
|
delete item->widget();
|
||||||
|
delete item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QVBoxLayout *layout_;
|
QVBoxLayout *layout_;
|
||||||
|
|
||||||
|
|
64
src/Cache.cc
64
src/Cache.cc
|
@ -141,6 +141,27 @@ Cache::saveImage(const QString &url, const QByteArray &image)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QByteArray
|
||||||
|
Cache::image(lmdb::txn &txn, const std::string &url) const
|
||||||
|
{
|
||||||
|
if (url.empty())
|
||||||
|
return QByteArray();
|
||||||
|
|
||||||
|
try {
|
||||||
|
lmdb::val image;
|
||||||
|
bool res = lmdb::dbi_get(txn, mediaDb_, lmdb::val(url), image);
|
||||||
|
|
||||||
|
if (!res)
|
||||||
|
return QByteArray();
|
||||||
|
|
||||||
|
return QByteArray(image.data(), image.size());
|
||||||
|
} catch (const lmdb::error &e) {
|
||||||
|
qCritical() << "image:" << e.what() << QString::fromStdString(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
return QByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
QByteArray
|
QByteArray
|
||||||
Cache::image(const QString &url) const
|
Cache::image(const QString &url) const
|
||||||
{
|
{
|
||||||
|
@ -945,10 +966,47 @@ Cache::populateMembers()
|
||||||
txn.commit();
|
txn.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<RoomSearchResult>
|
||||||
|
Cache::searchRooms(const std::string &query, std::uint8_t max_items)
|
||||||
|
{
|
||||||
|
std::multimap<int, std::pair<std::string, RoomInfo>> items;
|
||||||
|
|
||||||
|
auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY);
|
||||||
|
auto cursor = lmdb::cursor::open(txn, roomsDb_);
|
||||||
|
|
||||||
|
std::string room_id, room_data;
|
||||||
|
while (cursor.get(room_id, room_data, MDB_NEXT)) {
|
||||||
|
RoomInfo tmp = json::parse(std::move(room_data));
|
||||||
|
|
||||||
|
const int score = utils::levenshtein_distance(
|
||||||
|
query, QString::fromStdString(tmp.name).toLower().toStdString());
|
||||||
|
items.emplace(score, std::make_pair(room_id, tmp));
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor.close();
|
||||||
|
|
||||||
|
auto end = items.begin();
|
||||||
|
|
||||||
|
if (items.size() >= max_items)
|
||||||
|
std::advance(end, max_items);
|
||||||
|
else if (items.size() > 0)
|
||||||
|
std::advance(end, items.size());
|
||||||
|
|
||||||
|
std::vector<RoomSearchResult> results;
|
||||||
|
for (auto it = items.begin(); it != end; it++) {
|
||||||
|
results.push_back(
|
||||||
|
RoomSearchResult{it->second.first,
|
||||||
|
it->second.second,
|
||||||
|
QImage::fromData(image(txn, it->second.second.avatar_url))});
|
||||||
|
}
|
||||||
|
|
||||||
|
txn.commit();
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
QVector<SearchResult>
|
QVector<SearchResult>
|
||||||
Cache::getAutocompleteMatches(const std::string &room_id,
|
Cache::searchUsers(const std::string &room_id, const std::string &query, std::uint8_t max_items)
|
||||||
const std::string &query,
|
|
||||||
std::uint8_t max_items)
|
|
||||||
{
|
{
|
||||||
std::multimap<int, std::pair<std::string, std::string>> items;
|
std::multimap<int, std::pair<std::string, std::string>> items;
|
||||||
|
|
||||||
|
|
|
@ -699,7 +699,7 @@ ChatPage::showQuickSwitcher()
|
||||||
{
|
{
|
||||||
if (quickSwitcher_.isNull()) {
|
if (quickSwitcher_.isNull()) {
|
||||||
quickSwitcher_ = QSharedPointer<QuickSwitcher>(
|
quickSwitcher_ = QSharedPointer<QuickSwitcher>(
|
||||||
new QuickSwitcher(this),
|
new QuickSwitcher(cache_, this),
|
||||||
[](QuickSwitcher *switcher) { switcher->deleteLater(); });
|
[](QuickSwitcher *switcher) { switcher->deleteLater(); });
|
||||||
|
|
||||||
connect(quickSwitcher_.data(),
|
connect(quickSwitcher_.data(),
|
||||||
|
@ -721,17 +721,7 @@ ChatPage::showQuickSwitcher()
|
||||||
quickSwitcherModal_->setColor(QColor(30, 30, 30, 170));
|
quickSwitcherModal_->setColor(QColor(30, 30, 30, 170));
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
std::map<QString, QString> rooms;
|
|
||||||
auto info = cache_->roomInfo(false);
|
|
||||||
for (auto it = info.begin(); it != info.end(); ++it)
|
|
||||||
rooms.emplace(QString::fromStdString(it.value().name).trimmed(), it.key());
|
|
||||||
quickSwitcher_->setRoomList(rooms);
|
|
||||||
quickSwitcherModal_->show();
|
quickSwitcherModal_->show();
|
||||||
} catch (const lmdb::error &e) {
|
|
||||||
const auto err = QString::fromStdString(e.what());
|
|
||||||
emit showNotification(QString("Failed to load room list: %1").arg(err));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <QStringListModel>
|
#include <QStringListModel>
|
||||||
#include <QStyleOption>
|
#include <QStyleOption>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
#include <QtConcurrent>
|
||||||
|
|
||||||
#include "QuickSwitcher.h"
|
#include "QuickSwitcher.h"
|
||||||
|
|
||||||
|
@ -27,14 +28,6 @@ RoomSearchInput::RoomSearchInput(QWidget *parent)
|
||||||
: TextField(parent)
|
: TextField(parent)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
bool
|
|
||||||
RoomSearchInput::focusNextPrevChild(bool next)
|
|
||||||
{
|
|
||||||
Q_UNUSED(next);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
RoomSearchInput::keyPressEvent(QKeyEvent *event)
|
RoomSearchInput::keyPressEvent(QKeyEvent *event)
|
||||||
{
|
{
|
||||||
|
@ -58,9 +51,11 @@ RoomSearchInput::hideEvent(QHideEvent *event)
|
||||||
TextField::hideEvent(event);
|
TextField::hideEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
QuickSwitcher::QuickSwitcher(QWidget *parent)
|
QuickSwitcher::QuickSwitcher(QSharedPointer<Cache> cache, QWidget *parent)
|
||||||
: QWidget(parent)
|
: QWidget(parent)
|
||||||
|
, cache_{cache}
|
||||||
{
|
{
|
||||||
|
qRegisterMetaType<std::vector<RoomSearchResult>>();
|
||||||
setMaximumWidth(450);
|
setMaximumWidth(450);
|
||||||
|
|
||||||
QFont font;
|
QFont font;
|
||||||
|
@ -68,88 +63,55 @@ QuickSwitcher::QuickSwitcher(QWidget *parent)
|
||||||
|
|
||||||
roomSearch_ = new RoomSearchInput(this);
|
roomSearch_ = new RoomSearchInput(this);
|
||||||
roomSearch_->setFont(font);
|
roomSearch_->setFont(font);
|
||||||
roomSearch_->setPlaceholderText(tr("Find a room..."));
|
roomSearch_->setPlaceholderText(tr("Search for a room..."));
|
||||||
|
|
||||||
completer_ = new QCompleter();
|
|
||||||
completer_->setCaseSensitivity(Qt::CaseInsensitive);
|
|
||||||
completer_->setCompletionMode(QCompleter::PopupCompletion);
|
|
||||||
completer_->setWidget(this);
|
|
||||||
|
|
||||||
topLayout_ = new QVBoxLayout(this);
|
topLayout_ = new QVBoxLayout(this);
|
||||||
topLayout_->addWidget(roomSearch_);
|
topLayout_->addWidget(roomSearch_);
|
||||||
|
|
||||||
connect(completer_, SIGNAL(highlighted(QString)), roomSearch_, SLOT(setText(QString)));
|
connect(this,
|
||||||
connect(roomSearch_, &QLineEdit::textEdited, this, [this](const QString &prefix) {
|
&QuickSwitcher::queryResults,
|
||||||
if (prefix.isEmpty()) {
|
this,
|
||||||
completer_->popup()->hide();
|
[this](const std::vector<RoomSearchResult> &rooms) {
|
||||||
selection_ = -1;
|
auto pos = mapToGlobal(roomSearch_->geometry().bottomLeft());
|
||||||
|
|
||||||
|
popup_.setFixedWidth(width());
|
||||||
|
popup_.addRooms(rooms);
|
||||||
|
popup_.move(pos.x() - topLayout_->margin(), pos.y() + topLayout_->margin());
|
||||||
|
popup_.show();
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(roomSearch_, &QLineEdit::textEdited, this, [this](const QString &query) {
|
||||||
|
if (query.isEmpty()) {
|
||||||
|
popup_.hide();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prefix != completer_->completionPrefix()) {
|
QtConcurrent::run([this, query = query.toLower()]() {
|
||||||
completer_->setCompletionPrefix(prefix);
|
try {
|
||||||
selection_ = -1;
|
emit queryResults(cache_->searchRooms(query.toStdString()));
|
||||||
|
} catch (const lmdb::error &e) {
|
||||||
|
qWarning() << "room search failed:" << e.what();
|
||||||
}
|
}
|
||||||
|
});
|
||||||
completer_->popup()->setWindowFlags(completer_->popup()->windowFlags() |
|
|
||||||
Qt::ToolTip | Qt::NoDropShadowWindowHint);
|
|
||||||
completer_->popup()->setAttribute(Qt::WA_ShowWithoutActivating);
|
|
||||||
completer_->complete();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(roomSearch_, &RoomSearchInput::selectNextCompletion, this, [this]() {
|
connect(roomSearch_,
|
||||||
selection_ += 1;
|
&RoomSearchInput::selectNextCompletion,
|
||||||
|
&popup_,
|
||||||
if (!completer_->setCurrentRow(selection_)) {
|
&SuggestionsPopup::selectNextSuggestion);
|
||||||
selection_ = 0;
|
connect(roomSearch_,
|
||||||
completer_->setCurrentRow(selection_);
|
&RoomSearchInput::selectPreviousCompletion,
|
||||||
}
|
&popup_,
|
||||||
|
&SuggestionsPopup::selectPreviousSuggestion);
|
||||||
completer_->popup()->setCurrentIndex(completer_->currentIndex());
|
connect(&popup_, &SuggestionsPopup::itemSelected, this, &QuickSwitcher::roomSelected);
|
||||||
});
|
connect(roomSearch_, &RoomSearchInput::hiding, this, [this]() { popup_.hide(); });
|
||||||
|
|
||||||
connect(roomSearch_, &RoomSearchInput::selectPreviousCompletion, this, [this]() {
|
|
||||||
selection_ -= 1;
|
|
||||||
|
|
||||||
if (!completer_->setCurrentRow(selection_)) {
|
|
||||||
selection_ = completer_->completionCount() - 1;
|
|
||||||
completer_->setCurrentRow(selection_);
|
|
||||||
}
|
|
||||||
|
|
||||||
completer_->popup()->setCurrentIndex(completer_->currentIndex());
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(
|
|
||||||
roomSearch_, &RoomSearchInput::hiding, this, [this]() { completer_->popup()->hide(); });
|
|
||||||
connect(roomSearch_, &QLineEdit::returnPressed, this, [this]() {
|
connect(roomSearch_, &QLineEdit::returnPressed, this, [this]() {
|
||||||
emit closing();
|
emit closing();
|
||||||
|
|
||||||
QString text("");
|
|
||||||
|
|
||||||
if (selection_ == -1) {
|
|
||||||
completer_->setCurrentRow(0);
|
|
||||||
text = completer_->currentCompletion();
|
|
||||||
} else {
|
|
||||||
text = this->roomSearch_->text().trimmed();
|
|
||||||
}
|
|
||||||
emit roomSelected(rooms_[text]);
|
|
||||||
|
|
||||||
roomSearch_->clear();
|
roomSearch_->clear();
|
||||||
|
popup_.selectHoveredSuggestion<RoomItem>();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
QuickSwitcher::setRoomList(const std::map<QString, QString> &rooms)
|
|
||||||
{
|
|
||||||
rooms_ = rooms;
|
|
||||||
|
|
||||||
QStringList items;
|
|
||||||
for (const auto &room : rooms)
|
|
||||||
items << room.first;
|
|
||||||
|
|
||||||
completer_->setModel(new QStringListModel(items));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
QuickSwitcher::paintEvent(QPaintEvent *)
|
QuickSwitcher::paintEvent(QPaintEvent *)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
#include "Avatar.h"
|
#include "Avatar.h"
|
||||||
#include "AvatarProvider.h"
|
#include "AvatarProvider.h"
|
||||||
#include "Cache.h"
|
|
||||||
#include "ChatPage.h"
|
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include "DropShadow.h"
|
#include "DropShadow.h"
|
||||||
#include "SuggestionsPopup.hpp"
|
#include "SuggestionsPopup.hpp"
|
||||||
|
@ -15,10 +13,9 @@
|
||||||
constexpr int PopupHMargin = 5;
|
constexpr int PopupHMargin = 5;
|
||||||
constexpr int PopupItemMargin = 4;
|
constexpr int PopupItemMargin = 4;
|
||||||
|
|
||||||
PopupItem::PopupItem(QWidget *parent, const QString &user_id)
|
PopupItem::PopupItem(QWidget *parent)
|
||||||
: QWidget(parent)
|
: QWidget(parent)
|
||||||
, avatar_{new Avatar(this)}
|
, avatar_{new Avatar(this)}
|
||||||
, user_id_{user_id}
|
|
||||||
, hovering_{false}
|
, hovering_{false}
|
||||||
{
|
{
|
||||||
setMouseTracking(true);
|
setMouseTracking(true);
|
||||||
|
@ -27,29 +24,6 @@ PopupItem::PopupItem(QWidget *parent, const QString &user_id)
|
||||||
topLayout_ = new QHBoxLayout(this);
|
topLayout_ = new QHBoxLayout(this);
|
||||||
topLayout_->setContentsMargins(
|
topLayout_->setContentsMargins(
|
||||||
PopupHMargin, PopupItemMargin, PopupHMargin, PopupItemMargin);
|
PopupHMargin, PopupItemMargin, PopupHMargin, PopupItemMargin);
|
||||||
|
|
||||||
QFont font;
|
|
||||||
font.setPixelSize(conf::popup::font);
|
|
||||||
|
|
||||||
auto displayName = Cache::displayName(ChatPage::instance()->currentRoom(), user_id);
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
AvatarProvider::resolve(ChatPage::instance()->currentRoom(),
|
|
||||||
user_id,
|
|
||||||
this,
|
|
||||||
[this](const QImage &img) { avatar_->setImage(img); });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -68,11 +42,61 @@ void
|
||||||
PopupItem::mousePressEvent(QMouseEvent *event)
|
PopupItem::mousePressEvent(QMouseEvent *event)
|
||||||
{
|
{
|
||||||
if (event->buttons() != Qt::RightButton)
|
if (event->buttons() != Qt::RightButton)
|
||||||
emit clicked(Cache::displayName(ChatPage::instance()->currentRoom(), user_id_));
|
// TODO: should be abstracted.
|
||||||
|
emit clicked(
|
||||||
|
Cache::displayName(ChatPage::instance()->currentRoom(), selectedText()));
|
||||||
|
|
||||||
QWidget::mousePressEvent(event);
|
QWidget::mousePressEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
AvatarProvider::resolve(ChatPage::instance()->currentRoom(),
|
||||||
|
userId_,
|
||||||
|
this,
|
||||||
|
[this](const QImage &img) { avatar_->setImage(img); });
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
SuggestionsPopup::SuggestionsPopup(QWidget *parent)
|
SuggestionsPopup::SuggestionsPopup(QWidget *parent)
|
||||||
: QWidget(parent)
|
: QWidget(parent)
|
||||||
{
|
{
|
||||||
|
@ -84,15 +108,32 @@ SuggestionsPopup::SuggestionsPopup(QWidget *parent)
|
||||||
layout_->setSpacing(0);
|
layout_->setSpacing(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SuggestionsPopup::addRooms(const std::vector<RoomSearchResult> &rooms)
|
||||||
|
{
|
||||||
|
removeItems();
|
||||||
|
|
||||||
|
if (rooms.empty()) {
|
||||||
|
hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto &r : rooms) {
|
||||||
|
auto room = new RoomItem(this, r);
|
||||||
|
layout_->addWidget(room);
|
||||||
|
connect(room, &RoomItem::clicked, this, &SuggestionsPopup::itemSelected);
|
||||||
|
}
|
||||||
|
|
||||||
|
resetSelection();
|
||||||
|
adjustSize();
|
||||||
|
|
||||||
|
resize(geometry().width(), 40 * rooms.size());
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
SuggestionsPopup::addUsers(const QVector<SearchResult> &users)
|
SuggestionsPopup::addUsers(const QVector<SearchResult> &users)
|
||||||
{
|
{
|
||||||
// Remove all items from the layout.
|
removeItems();
|
||||||
QLayoutItem *item;
|
|
||||||
while ((item = layout_->takeAt(0)) != 0) {
|
|
||||||
delete item->widget();
|
|
||||||
delete item;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (users.isEmpty()) {
|
if (users.isEmpty()) {
|
||||||
hide();
|
hide();
|
||||||
|
@ -100,9 +141,9 @@ SuggestionsPopup::addUsers(const QVector<SearchResult> &users)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto &u : users) {
|
for (const auto &u : users) {
|
||||||
auto user = new PopupItem(this, u.user_id);
|
auto user = new UserItem(this, u.user_id);
|
||||||
layout_->addWidget(user);
|
layout_->addWidget(user);
|
||||||
connect(user, &PopupItem::clicked, this, &SuggestionsPopup::itemSelected);
|
connect(user, &UserItem::clicked, this, &SuggestionsPopup::itemSelected);
|
||||||
}
|
}
|
||||||
|
|
||||||
resetSelection();
|
resetSelection();
|
||||||
|
@ -160,19 +201,6 @@ SuggestionsPopup::setHovering(int pos)
|
||||||
widget->setHovering(true);
|
widget->setHovering(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
SuggestionsPopup::selectHoveredSuggestion()
|
|
||||||
{
|
|
||||||
const auto item = layout_->itemAt(selectedItem_);
|
|
||||||
if (!item)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const auto &widget = qobject_cast<PopupItem *>(item->widget());
|
|
||||||
emit itemSelected(Cache::displayName(ChatPage::instance()->currentRoom(), widget->user()));
|
|
||||||
|
|
||||||
resetSelection();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
SuggestionsPopup::paintEvent(QPaintEvent *)
|
SuggestionsPopup::paintEvent(QPaintEvent *)
|
||||||
{
|
{
|
||||||
|
|
|
@ -95,10 +95,9 @@ FilteredTextEdit::FilteredTextEdit(QWidget *parent)
|
||||||
&FilteredTextEdit::selectPreviousSuggestion,
|
&FilteredTextEdit::selectPreviousSuggestion,
|
||||||
&popup_,
|
&popup_,
|
||||||
&SuggestionsPopup::selectPreviousSuggestion);
|
&SuggestionsPopup::selectPreviousSuggestion);
|
||||||
connect(this,
|
connect(this, &FilteredTextEdit::selectHoveredSuggestion, this, [this]() {
|
||||||
&FilteredTextEdit::selectHoveredSuggestion,
|
popup_.selectHoveredSuggestion<UserItem>();
|
||||||
&popup_,
|
});
|
||||||
&SuggestionsPopup::selectHoveredSuggestion);
|
|
||||||
|
|
||||||
previewDialog_.hide();
|
previewDialog_.hide();
|
||||||
}
|
}
|
||||||
|
@ -459,7 +458,7 @@ TextInputWidget::TextInputWidget(QWidget *parent)
|
||||||
|
|
||||||
QtConcurrent::run([this, q = q.toLower().toStdString()]() {
|
QtConcurrent::run([this, q = q.toLower().toStdString()]() {
|
||||||
try {
|
try {
|
||||||
emit input_->resultsRetrieved(cache_->getAutocompleteMatches(
|
emit input_->resultsRetrieved(cache_->searchUsers(
|
||||||
ChatPage::instance()->currentRoom().toStdString(), q));
|
ChatPage::instance()->currentRoom().toStdString(), q));
|
||||||
} catch (const lmdb::error &e) {
|
} catch (const lmdb::error &e) {
|
||||||
std::cout << e.what() << '\n';
|
std::cout << e.what() << '\n';
|
||||||
|
|
Loading…
Reference in a new issue