mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-22 11:00:48 +03:00
Initial Support for Rich Replies
Add placeholder UI for showing replies in the text entry widget. Existing quoting capability has been removed (Temporarily), as it was replaced with the new reply capability. Replies sent from nheko do not currently appear correctly in the timeline (this will be fixed in a future commit).
This commit is contained in:
parent
b9dde957a8
commit
9159b9ce22
11 changed files with 412 additions and 255 deletions
|
@ -237,7 +237,9 @@ set(SRC_FILES
|
||||||
src/RunGuard.cpp
|
src/RunGuard.cpp
|
||||||
src/SideBarActions.cpp
|
src/SideBarActions.cpp
|
||||||
src/Splitter.cpp
|
src/Splitter.cpp
|
||||||
src/SuggestionsPopup.cpp
|
src/popups/SuggestionsPopup.cpp
|
||||||
|
src/popups/PopupItem.cpp
|
||||||
|
src/popups/ReplyPopup.cpp
|
||||||
src/TextInputWidget.cpp
|
src/TextInputWidget.cpp
|
||||||
src/TopRoomBar.cpp
|
src/TopRoomBar.cpp
|
||||||
src/TrayIcon.cpp
|
src/TrayIcon.cpp
|
||||||
|
@ -375,7 +377,9 @@ qt5_wrap_cpp(MOC_HEADERS
|
||||||
src/RoomList.h
|
src/RoomList.h
|
||||||
src/SideBarActions.h
|
src/SideBarActions.h
|
||||||
src/Splitter.h
|
src/Splitter.h
|
||||||
src/SuggestionsPopup.h
|
src/popups/SuggestionsPopup.h
|
||||||
|
src/popups/ReplyPopup.h
|
||||||
|
src/popups/PopupItem.h
|
||||||
src/TextInputWidget.h
|
src/TextInputWidget.h
|
||||||
src/TopRoomBar.h
|
src/TopRoomBar.h
|
||||||
src/TrayIcon.h
|
src/TrayIcon.h
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
#include <QtConcurrent>
|
#include <QtConcurrent>
|
||||||
|
|
||||||
#include "QuickSwitcher.h"
|
#include "QuickSwitcher.h"
|
||||||
#include "SuggestionsPopup.h"
|
#include "popups/SuggestionsPopup.h"
|
||||||
|
|
||||||
RoomSearchInput::RoomSearchInput(QWidget *parent)
|
RoomSearchInput::RoomSearchInput(QWidget *parent)
|
||||||
: TextField(parent)
|
: TextField(parent)
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
#include "SuggestionsPopup.h"
|
#include "popups/SuggestionsPopup.h"
|
||||||
#include "ui/TextField.h"
|
#include "ui/TextField.h"
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(std::vector<RoomSearchResult>)
|
Q_DECLARE_METATYPE(std::vector<RoomSearchResult>)
|
||||||
|
|
|
@ -48,7 +48,8 @@ static constexpr int ButtonHeight = 22;
|
||||||
FilteredTextEdit::FilteredTextEdit(QWidget *parent)
|
FilteredTextEdit::FilteredTextEdit(QWidget *parent)
|
||||||
: QTextEdit{parent}
|
: QTextEdit{parent}
|
||||||
, history_index_{0}
|
, history_index_{0}
|
||||||
, popup_{parent}
|
, suggestionsPopup_{parent}
|
||||||
|
, replyPopup_{parent}
|
||||||
, previewDialog_{parent}
|
, previewDialog_{parent}
|
||||||
{
|
{
|
||||||
setFrameStyle(QFrame::NoFrame);
|
setFrameStyle(QFrame::NoFrame);
|
||||||
|
@ -75,29 +76,34 @@ FilteredTextEdit::FilteredTextEdit(QWidget *parent)
|
||||||
&FilteredTextEdit::uploadData);
|
&FilteredTextEdit::uploadData);
|
||||||
|
|
||||||
connect(this, &FilteredTextEdit::resultsRetrieved, this, &FilteredTextEdit::showResults);
|
connect(this, &FilteredTextEdit::resultsRetrieved, this, &FilteredTextEdit::showResults);
|
||||||
connect(&popup_, &SuggestionsPopup::itemSelected, this, [this](const QString &text) {
|
connect(&replyPopup_, &ReplyPopup::userSelected, this, [this](const QString &text) {
|
||||||
popup_.hide();
|
// TODO: Show user avatar window.
|
||||||
|
nhlog::ui()->info("User selected: " + text.toStdString());
|
||||||
auto cursor = textCursor();
|
|
||||||
const int end = cursor.position();
|
|
||||||
|
|
||||||
cursor.setPosition(atTriggerPosition_, QTextCursor::MoveAnchor);
|
|
||||||
cursor.setPosition(end, QTextCursor::KeepAnchor);
|
|
||||||
cursor.removeSelectedText();
|
|
||||||
cursor.insertText(text);
|
|
||||||
});
|
});
|
||||||
|
connect(
|
||||||
|
&suggestionsPopup_, &SuggestionsPopup::itemSelected, this, [this](const QString &text) {
|
||||||
|
suggestionsPopup_.hide();
|
||||||
|
|
||||||
|
auto cursor = textCursor();
|
||||||
|
const int end = cursor.position();
|
||||||
|
|
||||||
|
cursor.setPosition(atTriggerPosition_, QTextCursor::MoveAnchor);
|
||||||
|
cursor.setPosition(end, QTextCursor::KeepAnchor);
|
||||||
|
cursor.removeSelectedText();
|
||||||
|
cursor.insertText(text);
|
||||||
|
});
|
||||||
|
|
||||||
// For cycling through the suggestions by hitting tab.
|
// For cycling through the suggestions by hitting tab.
|
||||||
connect(this,
|
connect(this,
|
||||||
&FilteredTextEdit::selectNextSuggestion,
|
&FilteredTextEdit::selectNextSuggestion,
|
||||||
&popup_,
|
&suggestionsPopup_,
|
||||||
&SuggestionsPopup::selectNextSuggestion);
|
&SuggestionsPopup::selectNextSuggestion);
|
||||||
connect(this,
|
connect(this,
|
||||||
&FilteredTextEdit::selectPreviousSuggestion,
|
&FilteredTextEdit::selectPreviousSuggestion,
|
||||||
&popup_,
|
&suggestionsPopup_,
|
||||||
&SuggestionsPopup::selectPreviousSuggestion);
|
&SuggestionsPopup::selectPreviousSuggestion);
|
||||||
connect(this, &FilteredTextEdit::selectHoveredSuggestion, this, [this]() {
|
connect(this, &FilteredTextEdit::selectHoveredSuggestion, this, [this]() {
|
||||||
popup_.selectHoveredSuggestion<UserItem>();
|
suggestionsPopup_.selectHoveredSuggestion<UserItem>();
|
||||||
});
|
});
|
||||||
|
|
||||||
previewDialog_.hide();
|
previewDialog_.hide();
|
||||||
|
@ -117,9 +123,9 @@ FilteredTextEdit::showResults(const QVector<SearchResult> &results)
|
||||||
pos = viewport()->mapToGlobal(rect.topLeft());
|
pos = viewport()->mapToGlobal(rect.topLeft());
|
||||||
}
|
}
|
||||||
|
|
||||||
popup_.addUsers(results);
|
suggestionsPopup_.addUsers(results);
|
||||||
popup_.move(pos.x(), pos.y() - popup_.height() - 10);
|
suggestionsPopup_.move(pos.x(), pos.y() - suggestionsPopup_.height() - 10);
|
||||||
popup_.show();
|
suggestionsPopup_.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -146,7 +152,7 @@ FilteredTextEdit::keyPressEvent(QKeyEvent *event)
|
||||||
closeSuggestions();
|
closeSuggestions();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (popup_.isVisible()) {
|
if (suggestionsPopup_.isVisible()) {
|
||||||
switch (event->key()) {
|
switch (event->key()) {
|
||||||
case Qt::Key_Down:
|
case Qt::Key_Down:
|
||||||
case Qt::Key_Tab:
|
case Qt::Key_Tab:
|
||||||
|
@ -169,6 +175,19 @@ FilteredTextEdit::keyPressEvent(QKeyEvent *event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (replyPopup_.isVisible()) {
|
||||||
|
switch (event->key())
|
||||||
|
{
|
||||||
|
case Qt::Key_Escape:
|
||||||
|
closeReply();
|
||||||
|
return;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
switch (event->key()) {
|
switch (event->key()) {
|
||||||
case Qt::Key_At:
|
case Qt::Key_At:
|
||||||
atTriggerPosition_ = textCursor().position();
|
atTriggerPosition_ = textCursor().position();
|
||||||
|
@ -419,6 +438,24 @@ FilteredTextEdit::submit()
|
||||||
clear();
|
clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
FilteredTextEdit::showReplyPopup(const QString &user, const QString &msg, const QString &event_id)
|
||||||
|
{
|
||||||
|
QPoint pos;
|
||||||
|
|
||||||
|
if (isAnchorValid()) {
|
||||||
|
auto cursor = textCursor();
|
||||||
|
cursor.setPosition(atTriggerPosition_);
|
||||||
|
pos = viewport()->mapToGlobal(cursorRect(cursor).topLeft());
|
||||||
|
} else {
|
||||||
|
auto rect = cursorRect();
|
||||||
|
pos = viewport()->mapToGlobal(rect.topLeft());
|
||||||
|
}
|
||||||
|
replyPopup_.setReplyContent(user, msg, event_id);
|
||||||
|
replyPopup_.move(pos.x(), pos.y() - replyPopup_.height() - 10);
|
||||||
|
replyPopup_.show();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
FilteredTextEdit::textChanged()
|
FilteredTextEdit::textChanged()
|
||||||
{
|
{
|
||||||
|
@ -666,9 +703,10 @@ TextInputWidget::paintEvent(QPaintEvent *)
|
||||||
void
|
void
|
||||||
TextInputWidget::addReply(const QString &username, const QString &msg, const QString &replied_event)
|
TextInputWidget::addReply(const QString &username, const QString &msg, const QString &replied_event)
|
||||||
{
|
{
|
||||||
input_->setText(QString("> %1: %2\n\n").arg(username).arg(msg));
|
// input_->setText(QString("> %1: %2\n\n").arg(username).arg(msg));
|
||||||
input_->setFocus();
|
input_->setFocus();
|
||||||
|
|
||||||
|
input_->showReplyPopup(username, msg, replied_event);
|
||||||
auto cursor = input_->textCursor();
|
auto cursor = input_->textCursor();
|
||||||
cursor.movePosition(QTextCursor::End);
|
cursor.movePosition(QTextCursor::End);
|
||||||
input_->setTextCursor(cursor);
|
input_->setTextCursor(cursor);
|
||||||
|
|
|
@ -28,7 +28,8 @@
|
||||||
#include <QTextEdit>
|
#include <QTextEdit>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
#include "SuggestionsPopup.h"
|
#include "popups/SuggestionsPopup.h"
|
||||||
|
#include "popups/ReplyPopup.h"
|
||||||
#include "dialogs/PreviewUploadOverlay.h"
|
#include "dialogs/PreviewUploadOverlay.h"
|
||||||
#include "emoji/PickButton.h"
|
#include "emoji/PickButton.h"
|
||||||
|
|
||||||
|
@ -55,6 +56,7 @@ public:
|
||||||
|
|
||||||
void submit();
|
void submit();
|
||||||
void setRelatedEvent(const QString &event) { related_event_ = event; }
|
void setRelatedEvent(const QString &event) { related_event_ = event; }
|
||||||
|
void showReplyPopup(const QString &user, const QString &msg, const QString &event_id);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void heightChanged(int height);
|
void heightChanged(int height);
|
||||||
|
@ -85,7 +87,7 @@ protected:
|
||||||
void insertFromMimeData(const QMimeData *source) override;
|
void insertFromMimeData(const QMimeData *source) override;
|
||||||
void focusOutEvent(QFocusEvent *event) override
|
void focusOutEvent(QFocusEvent *event) override
|
||||||
{
|
{
|
||||||
popup_.hide();
|
suggestionsPopup_.hide();
|
||||||
QTextEdit::focusOutEvent(event);
|
QTextEdit::focusOutEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,7 +96,8 @@ private:
|
||||||
size_t history_index_;
|
size_t history_index_;
|
||||||
QTimer *typingTimer_;
|
QTimer *typingTimer_;
|
||||||
|
|
||||||
SuggestionsPopup popup_;
|
SuggestionsPopup suggestionsPopup_;
|
||||||
|
ReplyPopup replyPopup_;
|
||||||
|
|
||||||
// Used for replies
|
// Used for replies
|
||||||
QString related_event_;
|
QString related_event_;
|
||||||
|
@ -109,7 +112,8 @@ private:
|
||||||
|
|
||||||
int anchorWidth(AnchorType anchor) { return static_cast<int>(anchor); }
|
int anchorWidth(AnchorType anchor) { return static_cast<int>(anchor); }
|
||||||
|
|
||||||
void closeSuggestions() { popup_.hide(); }
|
void closeSuggestions() { suggestionsPopup_.hide(); }
|
||||||
|
void closeReply() { replyPopup_.hide(); }
|
||||||
void resetAnchor() { atTriggerPosition_ = -1; }
|
void resetAnchor() { atTriggerPosition_ = -1; }
|
||||||
bool isAnchorValid() { return atTriggerPosition_ != -1; }
|
bool isAnchorValid() { return atTriggerPosition_ != -1; }
|
||||||
bool hasAnchor(int pos, AnchorType anchor)
|
bool hasAnchor(int pos, AnchorType anchor)
|
||||||
|
|
|
@ -2,11 +2,9 @@
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QStyleOption>
|
#include <QStyleOption>
|
||||||
|
|
||||||
#include "Config.h"
|
#include "PopupItem.h"
|
||||||
#include "SuggestionsPopup.h"
|
#include "../Utils.h"
|
||||||
#include "Utils.h"
|
#include "../ui/Avatar.h"
|
||||||
#include "ui/Avatar.h"
|
|
||||||
#include "ui/DropShadow.h"
|
|
||||||
|
|
||||||
constexpr int PopupHMargin = 4;
|
constexpr int PopupHMargin = 4;
|
||||||
constexpr int PopupItemMargin = 3;
|
constexpr int PopupItemMargin = 3;
|
||||||
|
@ -146,151 +144,4 @@ RoomItem::mousePressEvent(QMouseEvent *event)
|
||||||
emit clicked(selectedText());
|
emit clicked(selectedText());
|
||||||
|
|
||||||
QWidget::mousePressEvent(event);
|
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);
|
|
||||||
}
|
|
83
src/popups/PopupItem.h
Normal file
83
src/popups/PopupItem.h
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QPoint>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include "../AvatarProvider.h"
|
||||||
|
#include "../Cache.h"
|
||||||
|
#include "../ChatPage.h"
|
||||||
|
|
||||||
|
class Avatar;
|
||||||
|
struct SearchResult;
|
||||||
|
|
||||||
|
class PopupItem : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(QColor hoverColor READ hoverColor WRITE setHoverColor)
|
||||||
|
Q_PROPERTY(bool hovering READ hovering WRITE setHovering)
|
||||||
|
|
||||||
|
public:
|
||||||
|
PopupItem(QWidget *parent);
|
||||||
|
|
||||||
|
QString selectedText() const { return QString(); }
|
||||||
|
QColor hoverColor() const { return hoverColor_; }
|
||||||
|
void setHoverColor(QColor &color) { hoverColor_ = color; }
|
||||||
|
|
||||||
|
bool hovering() const { return hovering_; }
|
||||||
|
void setHovering(const bool hover) { hovering_ = hover; };
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent *event) override;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void clicked(const QString &text);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
QHBoxLayout *topLayout_;
|
||||||
|
Avatar *avatar_;
|
||||||
|
QColor hoverColor_;
|
||||||
|
|
||||||
|
//! Set if the item is currently being
|
||||||
|
//! hovered during tab completion (cycling).
|
||||||
|
bool hovering_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class UserItem : public PopupItem
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
UserItem(QWidget *parent, const QString &user_id);
|
||||||
|
QString selectedText() const { return userId_; }
|
||||||
|
void updateItem(const QString &user_id);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void mousePressEvent(QMouseEvent *event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void resolveAvatar(const QString &user_id);
|
||||||
|
|
||||||
|
QLabel *userName_;
|
||||||
|
QString userId_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class RoomItem : public PopupItem
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
RoomItem(QWidget *parent, const RoomSearchResult &res);
|
||||||
|
QString selectedText() const { return roomId_; }
|
||||||
|
void updateItem(const RoomSearchResult &res);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void mousePressEvent(QMouseEvent *event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
QLabel *roomName_;
|
||||||
|
QString roomId_;
|
||||||
|
RoomSearchResult info_;
|
||||||
|
};
|
60
src/popups/ReplyPopup.cpp
Normal file
60
src/popups/ReplyPopup.cpp
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
#include <QPaintEvent>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QStyleOption>
|
||||||
|
|
||||||
|
#include "../Config.h"
|
||||||
|
#include "../Utils.h"
|
||||||
|
#include "../ui/Avatar.h"
|
||||||
|
#include "../ui/DropShadow.h"
|
||||||
|
#include "ReplyPopup.h"
|
||||||
|
|
||||||
|
ReplyPopup::ReplyPopup(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
|
||||||
|
ReplyPopup::setReplyContent(const QString &user, const QString &msg, const QString &srcEvent)
|
||||||
|
{
|
||||||
|
QLayoutItem *child;
|
||||||
|
while ((child = layout_->takeAt(0)) != 0) {
|
||||||
|
delete child->widget();
|
||||||
|
delete child;
|
||||||
|
}
|
||||||
|
// Create a new widget if there isn't already one in that
|
||||||
|
// layout position.
|
||||||
|
// if (!item) {
|
||||||
|
auto userItem = new UserItem(this, user);
|
||||||
|
auto *text = new QLabel(this);
|
||||||
|
text->setText(msg);
|
||||||
|
auto *event = new QLabel(this);
|
||||||
|
event->setText(srcEvent);
|
||||||
|
connect(userItem, &UserItem::clicked, this, &ReplyPopup::userSelected);
|
||||||
|
layout_->addWidget(userItem);
|
||||||
|
layout_->addWidget(text);
|
||||||
|
layout_->addWidget(event);
|
||||||
|
// } 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);
|
||||||
|
// }
|
||||||
|
|
||||||
|
adjustSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ReplyPopup::paintEvent(QPaintEvent *)
|
||||||
|
{
|
||||||
|
QStyleOption opt;
|
||||||
|
opt.init(this);
|
||||||
|
QPainter p(this);
|
||||||
|
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
|
||||||
|
}
|
32
src/popups/ReplyPopup.h
Normal file
32
src/popups/ReplyPopup.h
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QPoint>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include "../AvatarProvider.h"
|
||||||
|
#include "../Cache.h"
|
||||||
|
#include "../ChatPage.h"
|
||||||
|
#include "PopupItem.h"
|
||||||
|
|
||||||
|
class ReplyPopup : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit ReplyPopup(QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void setReplyContent(const QString &user, const QString &msg, const QString &srcEvent);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent *event) override;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void userSelected(const QString &user);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QVBoxLayout *layout_;
|
||||||
|
|
||||||
|
};
|
156
src/popups/SuggestionsPopup.cpp
Normal file
156
src/popups/SuggestionsPopup.cpp
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
#include <QPaintEvent>
|
||||||
|
#include <QPainter>
|
||||||
|
#include <QStyleOption>
|
||||||
|
|
||||||
|
#include "../Config.h"
|
||||||
|
#include "SuggestionsPopup.h"
|
||||||
|
#include "../Utils.h"
|
||||||
|
#include "../ui/Avatar.h"
|
||||||
|
#include "../ui/DropShadow.h"
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
|
@ -5,82 +5,11 @@
|
||||||
#include <QPoint>
|
#include <QPoint>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
#include "AvatarProvider.h"
|
#include "../AvatarProvider.h"
|
||||||
#include "Cache.h"
|
#include "../Cache.h"
|
||||||
#include "ChatPage.h"
|
#include "../ChatPage.h"
|
||||||
|
#include "PopupItem.h"
|
||||||
|
|
||||||
class Avatar;
|
|
||||||
struct SearchResult;
|
|
||||||
|
|
||||||
class PopupItem : public QWidget
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
Q_PROPERTY(QColor hoverColor READ hoverColor WRITE setHoverColor)
|
|
||||||
Q_PROPERTY(bool hovering READ hovering WRITE setHovering)
|
|
||||||
|
|
||||||
public:
|
|
||||||
PopupItem(QWidget *parent);
|
|
||||||
|
|
||||||
QString selectedText() const { return QString(); }
|
|
||||||
QColor hoverColor() const { return hoverColor_; }
|
|
||||||
void setHoverColor(QColor &color) { hoverColor_ = color; }
|
|
||||||
|
|
||||||
bool hovering() const { return hovering_; }
|
|
||||||
void setHovering(const bool hover) { hovering_ = hover; };
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void paintEvent(QPaintEvent *event) override;
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void clicked(const QString &text);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
QHBoxLayout *topLayout_;
|
|
||||||
Avatar *avatar_;
|
|
||||||
QColor hoverColor_;
|
|
||||||
|
|
||||||
//! Set if the item is currently being
|
|
||||||
//! hovered during tab completion (cycling).
|
|
||||||
bool hovering_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class UserItem : public PopupItem
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
UserItem(QWidget *parent, const QString &user_id);
|
|
||||||
QString selectedText() const { return userId_; }
|
|
||||||
void updateItem(const QString &user_id);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void mousePressEvent(QMouseEvent *event) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void resolveAvatar(const QString &user_id);
|
|
||||||
|
|
||||||
QLabel *userName_;
|
|
||||||
QString userId_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class RoomItem : public PopupItem
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
RoomItem(QWidget *parent, const RoomSearchResult &res);
|
|
||||||
QString selectedText() const { return roomId_; }
|
|
||||||
void updateItem(const RoomSearchResult &res);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void mousePressEvent(QMouseEvent *event) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
QLabel *roomName_;
|
|
||||||
QString roomId_;
|
|
||||||
RoomSearchResult info_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class SuggestionsPopup : public QWidget
|
class SuggestionsPopup : public QWidget
|
||||||
{
|
{
|
Loading…
Reference in a new issue