summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ChatPage.cpp5
-rw-r--r--src/ChatPage.h2
-rw-r--r--src/QuickSwitcher.cpp2
-rw-r--r--src/QuickSwitcher.h2
-rw-r--r--src/TextInputWidget.cpp92
-rw-r--r--src/TextInputWidget.h23
-rw-r--r--src/popups/PopupItem.cpp (renamed from src/SuggestionsPopup.cpp)157
-rw-r--r--src/popups/PopupItem.h83
-rw-r--r--src/popups/ReplyPopup.cpp60
-rw-r--r--src/popups/ReplyPopup.h32
-rw-r--r--src/popups/SuggestionsPopup.cpp156
-rw-r--r--src/popups/SuggestionsPopup.h (renamed from src/SuggestionsPopup.h)79
-rw-r--r--src/timeline/TimelineItem.cpp2
-rw-r--r--src/timeline/TimelineView.cpp15
-rw-r--r--src/timeline/TimelineView.h2
-rw-r--r--src/timeline/TimelineViewManager.cpp13
-rw-r--r--src/timeline/TimelineViewManager.h1
17 files changed, 467 insertions, 259 deletions
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,