diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp
index dd23fb80..5b838259 100644
--- a/src/ChatPage.cpp
+++ b/src/ChatPage.cpp
@@ -265,6 +265,11 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
SLOT(queueTextMessage(const QString &)));
connect(text_input_,
+ SIGNAL(sendReplyMessage(const QString &, const QString &)),
+ view_manager_,
+ SLOT(queueReplyMessage(const QString &, const QString &)));
+
+ connect(text_input_,
SIGNAL(sendEmoteMessage(const QString &)),
view_manager_,
SLOT(queueEmoteMessage(const QString &)));
diff --git a/src/ChatPage.h b/src/ChatPage.h
index 7d3b3273..09e7a2c6 100644
--- a/src/ChatPage.h
+++ b/src/ChatPage.h
@@ -83,7 +83,7 @@ signals:
void connectionLost();
void connectionRestored();
- void messageReply(const QString &username, const QString &msg);
+ void messageReply(const QString &username, const QString &msg, const QString &related_event);
void notificationsRetrieved(const mtx::responses::Notifications &);
diff --git a/src/QuickSwitcher.cpp b/src/QuickSwitcher.cpp
index eb79a427..f8f6c001 100644
--- a/src/QuickSwitcher.cpp
+++ b/src/QuickSwitcher.cpp
@@ -23,7 +23,7 @@
#include <QtConcurrent>
#include "QuickSwitcher.h"
-#include "SuggestionsPopup.h"
+#include "popups/SuggestionsPopup.h"
RoomSearchInput::RoomSearchInput(QWidget *parent)
: TextField(parent)
diff --git a/src/QuickSwitcher.h b/src/QuickSwitcher.h
index 24b9adfa..05f7be07 100644
--- a/src/QuickSwitcher.h
+++ b/src/QuickSwitcher.h
@@ -22,7 +22,7 @@
#include <QVBoxLayout>
#include <QWidget>
-#include "SuggestionsPopup.h"
+#include "popups/SuggestionsPopup.h"
#include "ui/TextField.h"
Q_DECLARE_METATYPE(std::vector<RoomSearchResult>)
diff --git a/src/TextInputWidget.cpp b/src/TextInputWidget.cpp
index 934f2b2c..b4251a0e 100644
--- a/src/TextInputWidget.cpp
+++ b/src/TextInputWidget.cpp
@@ -48,7 +48,8 @@ static constexpr int ButtonHeight = 22;
FilteredTextEdit::FilteredTextEdit(QWidget *parent)
: QTextEdit{parent}
, history_index_{0}
- , popup_{parent}
+ , suggestionsPopup_{parent}
+ , replyPopup_{parent}
, previewDialog_{parent}
{
setFrameStyle(QFrame::NoFrame);
@@ -75,29 +76,34 @@ FilteredTextEdit::FilteredTextEdit(QWidget *parent)
&FilteredTextEdit::uploadData);
connect(this, &FilteredTextEdit::resultsRetrieved, this, &FilteredTextEdit::showResults);
- connect(&popup_, &SuggestionsPopup::itemSelected, this, [this](const QString &text) {
- popup_.hide();
+ connect(&replyPopup_, &ReplyPopup::userSelected, this, [this](const QString &text) {
+ // TODO: Show user avatar window.
+ nhlog::ui()->info("User selected: " + text.toStdString());
+ });
+ connect(
+ &suggestionsPopup_, &SuggestionsPopup::itemSelected, this, [this](const QString &text) {
+ suggestionsPopup_.hide();
- auto cursor = textCursor();
- const int end = cursor.position();
+ auto cursor = textCursor();
+ const int end = cursor.position();
- cursor.setPosition(atTriggerPosition_, QTextCursor::MoveAnchor);
- cursor.setPosition(end, QTextCursor::KeepAnchor);
- cursor.removeSelectedText();
- cursor.insertText(text);
- });
+ cursor.setPosition(atTriggerPosition_, QTextCursor::MoveAnchor);
+ cursor.setPosition(end, QTextCursor::KeepAnchor);
+ cursor.removeSelectedText();
+ cursor.insertText(text);
+ });
// For cycling through the suggestions by hitting tab.
connect(this,
&FilteredTextEdit::selectNextSuggestion,
- &popup_,
+ &suggestionsPopup_,
&SuggestionsPopup::selectNextSuggestion);
connect(this,
&FilteredTextEdit::selectPreviousSuggestion,
- &popup_,
+ &suggestionsPopup_,
&SuggestionsPopup::selectPreviousSuggestion);
connect(this, &FilteredTextEdit::selectHoveredSuggestion, this, [this]() {
- popup_.selectHoveredSuggestion<UserItem>();
+ suggestionsPopup_.selectHoveredSuggestion<UserItem>();
});
previewDialog_.hide();
@@ -117,9 +123,9 @@ FilteredTextEdit::showResults(const QVector<SearchResult> &results)
pos = viewport()->mapToGlobal(rect.topLeft());
}
- popup_.addUsers(results);
- popup_.move(pos.x(), pos.y() - popup_.height() - 10);
- popup_.show();
+ suggestionsPopup_.addUsers(results);
+ suggestionsPopup_.move(pos.x(), pos.y() - suggestionsPopup_.height() - 10);
+ suggestionsPopup_.show();
}
void
@@ -146,7 +152,7 @@ FilteredTextEdit::keyPressEvent(QKeyEvent *event)
closeSuggestions();
}
- if (popup_.isVisible()) {
+ if (suggestionsPopup_.isVisible()) {
switch (event->key()) {
case Qt::Key_Down:
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()) {
case Qt::Key_At:
atTriggerPosition_ = textCursor().position();
@@ -398,18 +417,46 @@ FilteredTextEdit::submit()
auto name = text.mid(1, command_end - 1);
auto args = text.mid(command_end + 1);
if (name.isEmpty() || name == "/") {
- message(args);
+ if (!related_event_.isEmpty()) {
+ reply(args, related_event_);
+ } else {
+ message(args);
+ }
} else {
command(name, args);
}
} else {
- message(std::move(text));
+ if (!related_event_.isEmpty()) {
+ reply(std::move(text), std::move(related_event_));
+ } else {
+ message(std::move(text));
+ }
}
+ related_event_ = "";
+
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
FilteredTextEdit::textChanged()
{
working_history_[history_index_] = toPlainText();
@@ -536,6 +583,7 @@ TextInputWidget::TextInputWidget(QWidget *parent)
connect(sendMessageBtn_, &FlatButton::clicked, input_, &FilteredTextEdit::submit);
connect(sendFileBtn_, SIGNAL(clicked()), this, SLOT(openFileSelection()));
connect(input_, &FilteredTextEdit::message, this, &TextInputWidget::sendTextMessage);
+ connect(input_, &FilteredTextEdit::reply, this, &TextInputWidget::sendReplyMessage);
connect(input_, &FilteredTextEdit::command, this, &TextInputWidget::command);
connect(input_, &FilteredTextEdit::image, this, &TextInputWidget::uploadImage);
connect(input_, &FilteredTextEdit::audio, this, &TextInputWidget::uploadAudio);
@@ -653,12 +701,14 @@ TextInputWidget::paintEvent(QPaintEvent *)
}
void
-TextInputWidget::addReply(const QString &username, const QString &msg)
+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_->showReplyPopup(username, msg, replied_event);
auto cursor = input_->textCursor();
cursor.movePosition(QTextCursor::End);
input_->setTextCursor(cursor);
+ input_->setRelatedEvent(replied_event);
}
diff --git a/src/TextInputWidget.h b/src/TextInputWidget.h
index 8f634f6b..2936340b 100644
--- a/src/TextInputWidget.h
+++ b/src/TextInputWidget.h
@@ -28,7 +28,8 @@
#include <QTextEdit>
#include <QWidget>
-#include "SuggestionsPopup.h"
+#include "popups/SuggestionsPopup.h"
+#include "popups/ReplyPopup.h"
#include "dialogs/PreviewUploadOverlay.h"
#include "emoji/PickButton.h"
@@ -54,6 +55,8 @@ public:
QSize minimumSizeHint() const override;
void submit();
+ void setRelatedEvent(const QString &event) { related_event_ = event; }
+ void showReplyPopup(const QString &user, const QString &msg, const QString &event_id);
signals:
void heightChanged(int height);
@@ -61,6 +64,7 @@ signals:
void stoppedTyping();
void startedUpload();
void message(QString);
+ void reply(QString, QString);
void command(QString name, QString args);
void image(QSharedPointer<QIODevice> data, const QString &filename);
void audio(QSharedPointer<QIODevice> data, const QString &filename);
@@ -83,7 +87,7 @@ protected:
void insertFromMimeData(const QMimeData *source) override;
void focusOutEvent(QFocusEvent *event) override
{
- popup_.hide();
+ suggestionsPopup_.hide();
QTextEdit::focusOutEvent(event);
}
@@ -92,7 +96,11 @@ private:
size_t history_index_;
QTimer *typingTimer_;
- SuggestionsPopup popup_;
+ SuggestionsPopup suggestionsPopup_;
+ ReplyPopup replyPopup_;
+
+ // Used for replies
+ QString related_event_;
enum class AnchorType
{
@@ -104,7 +112,8 @@ private:
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; }
bool isAnchorValid() { return atTriggerPosition_ != -1; }
bool hasAnchor(int pos, AnchorType anchor)
@@ -158,13 +167,14 @@ public slots:
void openFileSelection();
void hideUploadSpinner();
void focusLineEdit() { input_->setFocus(); }
- void addReply(const QString &username, const QString &msg);
+ void addReply(const QString &username, const QString &msg, const QString &related_event);
private slots:
void addSelectedEmoji(const QString &emoji);
signals:
void sendTextMessage(QString msg);
+ void sendReplyMessage(QString msg, QString event_id);
void sendEmoteMessage(QString msg);
void heightChanged(int height);
@@ -189,6 +199,9 @@ private:
QHBoxLayout *topLayout_;
FilteredTextEdit *input_;
+ // Used for replies
+ QString related_event_;
+
LoadingIndicator *spinner_;
FlatButton *sendFileBtn_;
diff --git a/src/SuggestionsPopup.cpp b/src/popups/PopupItem.cpp
index 952d2ef3..b10cd32e 100644
--- a/src/SuggestionsPopup.cpp
+++ b/src/popups/PopupItem.cpp
@@ -2,11 +2,9 @@
#include <QPainter>
#include <QStyleOption>
-#include "Config.h"
-#include "SuggestionsPopup.h"
-#include "Utils.h"
-#include "ui/Avatar.h"
-#include "ui/DropShadow.h"
+#include "PopupItem.h"
+#include "../Utils.h"
+#include "../ui/Avatar.h"
constexpr int PopupHMargin = 4;
constexpr int PopupItemMargin = 3;
@@ -146,151 +144,4 @@ RoomItem::mousePressEvent(QMouseEvent *event)
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);
-}
+}
\ No newline at end of file
diff --git a/src/popups/PopupItem.h b/src/popups/PopupItem.h
new file mode 100644
index 00000000..1fc54bf7
--- /dev/null
+++ b/src/popups/PopupItem.h
@@ -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_;
+};
\ No newline at end of file
diff --git a/src/popups/ReplyPopup.cpp b/src/popups/ReplyPopup.cpp
new file mode 100644
index 00000000..a883739f
--- /dev/null
+++ b/src/popups/ReplyPopup.cpp
@@ -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);
+}
diff --git a/src/popups/ReplyPopup.h b/src/popups/ReplyPopup.h
new file mode 100644
index 00000000..d8355e53
--- /dev/null
+++ b/src/popups/ReplyPopup.h
@@ -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_;
+
+};
diff --git a/src/popups/SuggestionsPopup.cpp b/src/popups/SuggestionsPopup.cpp
new file mode 100644
index 00000000..6861a76a
--- /dev/null
+++ b/src/popups/SuggestionsPopup.cpp
@@ -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);
+}
diff --git a/src/SuggestionsPopup.h b/src/popups/SuggestionsPopup.h
index 72d6c7eb..4fbb97b3 100644
--- a/src/SuggestionsPopup.h
+++ b/src/popups/SuggestionsPopup.h
@@ -5,82 +5,11 @@
#include <QPoint>
#include <QWidget>
-#include "AvatarProvider.h"
-#include "Cache.h"
-#include "ChatPage.h"
+#include "../AvatarProvider.h"
+#include "../Cache.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
{
diff --git a/src/timeline/TimelineItem.cpp b/src/timeline/TimelineItem.cpp
index faae1da3..dd09ec78 100644
--- a/src/timeline/TimelineItem.cpp
+++ b/src/timeline/TimelineItem.cpp
@@ -875,7 +875,7 @@ TimelineItem::replyAction()
return;
emit ChatPage::instance()->messageReply(
- Cache::displayName(room_id_, descriptionMsg_.userid), body_->toPlainText());
+ Cache::displayName(room_id_, descriptionMsg_.userid), body_->toPlainText(), eventId());
}
void
diff --git a/src/timeline/TimelineView.cpp b/src/timeline/TimelineView.cpp
index 2b4f979b..ee7021d8 100644
--- a/src/timeline/TimelineView.cpp
+++ b/src/timeline/TimelineView.cpp
@@ -690,7 +690,7 @@ TimelineView::updatePendingMessage(const std::string &txn_id, const QString &eve
}
void
-TimelineView::addUserMessage(mtx::events::MessageType ty, const QString &body)
+TimelineView::addUserMessage(mtx::events::MessageType ty, const QString &body, const QString &related_event)
{
auto with_sender = (lastSender_ != local_user_) || isDateDifference(lastMsgTimestamp_);
@@ -702,6 +702,9 @@ TimelineView::addUserMessage(mtx::events::MessageType ty, const QString &body)
message.txn_id = http::client()->generate_txn_id();
message.body = body;
message.widget = view_item;
+ if (!related_event.isEmpty()) {
+ message.related_event = related_event.toStdString();
+ }
try {
message.is_encrypted = cache::client()->isRoomEncrypted(room_id_.toStdString());
@@ -723,6 +726,12 @@ TimelineView::addUserMessage(mtx::events::MessageType ty, const QString &body)
}
void
+TimelineView::addUserMessage(mtx::events::MessageType ty, const QString &body)
+{
+ addUserMessage(ty, body, "");
+}
+
+void
TimelineView::handleNewUserMessage(PendingMessage msg)
{
pending_msgs_.enqueue(msg);
@@ -1267,6 +1276,10 @@ toRoomMessage<mtx::events::msg::Text>(const PendingMessage &m)
if (html != m.body.trimmed().toHtmlEscaped())
text.formatted_body = html.toStdString();
+ if (!m.related_event.empty()) {
+ text.relates_to.in_reply_to.event_id = m.related_event;
+ }
+
return text;
}
diff --git a/src/timeline/TimelineView.h b/src/timeline/TimelineView.h
index b0909b44..450b5dfa 100644
--- a/src/timeline/TimelineView.h
+++ b/src/timeline/TimelineView.h
@@ -63,6 +63,7 @@ struct PendingMessage
{
mtx::events::MessageType ty;
std::string txn_id;
+ std::string related_event;
QString body;
QString filename;
QString mime;
@@ -120,6 +121,7 @@ public:
// Add new events at the end of the timeline.
void addEvents(const mtx::responses::Timeline &timeline);
+ void addUserMessage(mtx::events::MessageType ty, const QString &body, const QString &related_event);
void addUserMessage(mtx::events::MessageType ty, const QString &msg);
template<class Widget, mtx::events::MessageType MsgType>
diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp
index feab46a3..10c2d747 100644
--- a/src/timeline/TimelineViewManager.cpp
+++ b/src/timeline/TimelineViewManager.cpp
@@ -79,6 +79,19 @@ TimelineViewManager::queueEmoteMessage(const QString &msg)
}
void
+TimelineViewManager::queueReplyMessage(const QString &reply,
+ const QString &related_event)
+{
+ if (active_room_.isEmpty())
+ return;
+
+ auto room_id = active_room_;
+ auto view = views_[room_id];
+
+ view->addUserMessage(mtx::events::MessageType::Text, reply, related_event);
+}
+
+void
TimelineViewManager::queueImageMessage(const QString &roomid,
const QString &filename,
const QString &url,
diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h
index d23345d3..0ac6d67e 100644
--- a/src/timeline/TimelineViewManager.h
+++ b/src/timeline/TimelineViewManager.h
@@ -63,6 +63,7 @@ public slots:
void setHistoryView(const QString &room_id);
void queueTextMessage(const QString &msg);
+ void queueReplyMessage(const QString &reply, const QString &related_event);
void queueEmoteMessage(const QString &msg);
void queueImageMessage(const QString &roomid,
const QString &filename,
|