diff --git a/resources/qml/MessageInput.qml b/resources/qml/MessageInput.qml
index 3b424bb3..b76a44f3 100644
--- a/resources/qml/MessageInput.qml
+++ b/resources/qml/MessageInput.qml
@@ -58,9 +58,14 @@ Rectangle {
onSelectionStartChanged: TimelineManager.timeline.input.updateState(selectionStart, selectionEnd, cursorPosition, text)
onSelectionEndChanged: TimelineManager.timeline.input.updateState(selectionStart, selectionEnd, cursorPosition, text)
+ Connections {
+ target: TimelineManager.timeline.input
+ function onInsertText(text_) { textArea.insert(textArea.cursorPosition, text_); }
+ }
+
Keys.onPressed: {
if (event.matches(StandardKey.Paste)) {
- TimelineManager.timeline.input.paste(false) || textArea.paste()
+ TimelineManager.timeline.input.paste(false)
event.accepted = true
}
else if (event.matches(StandardKey.InsertParagraphSeparator)) {
@@ -75,7 +80,7 @@ Rectangle {
anchors.fill: parent
acceptedButtons: Qt.MiddleButton
cursorShape: Qt.IBeamCursor
- onClicked: TimelineManager.timeline.input.paste(true) || textArea.paste()
+ onClicked: TimelineManager.timeline.input.paste(true)
}
background: Rectangle {
diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp
index 78987fb9..6e1ed8ca 100644
--- a/src/ChatPage.cpp
+++ b/src/ChatPage.cpp
@@ -160,15 +160,6 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
trySync();
});
- connect(text_input_,
- &TextInputWidget::clearRoomTimeline,
- view_manager_,
- &TimelineViewManager::clearCurrentRoomTimeline);
-
- connect(text_input_, &TextInputWidget::rotateMegolmSession, this, [this]() {
- cache::dropOutboundMegolmSession(current_room_.toStdString());
- });
-
connect(
new QShortcut(QKeySequence("Ctrl+Down"), this), &QShortcut::activated, this, [this]() {
if (isVisible())
@@ -277,45 +268,6 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
this,
SIGNAL(unreadMessages(int)));
- connect(text_input_,
- &TextInputWidget::sendTextMessage,
- view_manager_,
- &TimelineViewManager::queueTextMessage);
-
- connect(text_input_,
- &TextInputWidget::sendEmoteMessage,
- view_manager_,
- &TimelineViewManager::queueEmoteMessage);
-
- connect(text_input_, &TextInputWidget::sendJoinRoomRequest, this, &ChatPage::joinRoom);
-
- // invites and bans via quick command
- connect(text_input_, &TextInputWidget::sendInviteRoomRequest, this, &ChatPage::inviteUser);
- connect(text_input_, &TextInputWidget::sendKickRoomRequest, this, &ChatPage::kickUser);
- connect(text_input_, &TextInputWidget::sendBanRoomRequest, this, &ChatPage::banUser);
- connect(text_input_, &TextInputWidget::sendUnbanRoomRequest, this, &ChatPage::unbanUser);
-
- connect(
- text_input_, &TextInputWidget::changeRoomNick, this, [this](const QString &displayName) {
- mtx::events::state::Member member;
- member.display_name = displayName.toStdString();
- member.avatar_url =
- cache::avatarUrl(currentRoom(),
- QString::fromStdString(http::client()->user_id().to_string()))
- .toStdString();
- member.membership = mtx::events::state::Membership::Join;
-
- http::client()->send_state_event(
- currentRoom().toStdString(),
- http::client()->user_id().to_string(),
- member,
- [](mtx::responses::EventId, mtx::http::RequestErr err) {
- if (err)
- nhlog::net()->error("Failed to set room displayname: {}",
- err->matrix_error.error);
- });
- });
-
connect(
text_input_,
&TextInputWidget::uploadMedia,
diff --git a/src/ChatPage.h b/src/ChatPage.h
index 0c12d89f..9a38fd6e 100644
--- a/src/ChatPage.h
+++ b/src/ChatPage.h
@@ -109,6 +109,7 @@ public:
public slots:
void leaveRoom(const QString &room_id);
void createRoom(const mtx::requests::CreateRoom &req);
+ void joinRoom(const QString &room);
void inviteUser(QString userid, QString reason);
void kickUser(QString userid, QString reason);
@@ -200,7 +201,6 @@ private slots:
void removeRoom(const QString &room_id);
void dropToLoginPage(const QString &msg);
- void joinRoom(const QString &room);
void sendTypingNotifications();
void handleSyncResponse(const mtx::responses::Sync &res);
diff --git a/src/TextInputWidget.cpp b/src/TextInputWidget.cpp
index 454353fd..dec7a574 100644
--- a/src/TextInputWidget.cpp
+++ b/src/TextInputWidget.cpp
@@ -486,36 +486,7 @@ FilteredTextEdit::minimumSizeHint() const
void
FilteredTextEdit::submit()
-{
- if (toPlainText().trimmed().isEmpty())
- return;
-
- if (true_history_.size() == INPUT_HISTORY_SIZE)
- true_history_.pop_back();
- true_history_.push_front(toPlainText());
- working_history_ = true_history_;
- working_history_.push_front("");
- history_index_ = 0;
-
- QString text = toPlainText();
-
- if (text.startsWith('/')) {
- int command_end = text.indexOf(' ');
- if (command_end == -1)
- command_end = text.size();
- auto name = text.mid(1, command_end - 1);
- auto args = text.mid(command_end + 1);
- if (name.isEmpty() || name == "/") {
- message(args);
- } else {
- command(name, args);
- }
- } else {
- message(std::move(text));
- }
-
- clear();
-}
+{}
void
FilteredTextEdit::textChanged()
@@ -653,8 +624,6 @@ TextInputWidget::TextInputWidget(QWidget *parent)
#endif
connect(sendMessageBtn_, &FlatButton::clicked, input_, &FilteredTextEdit::submit);
connect(sendFileBtn_, SIGNAL(clicked()), this, SLOT(openFileSelection()));
- connect(input_, &FilteredTextEdit::message, this, &TextInputWidget::sendTextMessage);
- connect(input_, &FilteredTextEdit::command, this, &TextInputWidget::command);
connect(input_, &FilteredTextEdit::media, this, &TextInputWidget::uploadMedia);
connect(emojiBtn_,
SIGNAL(emojiSelected(const QString &)),
@@ -686,38 +655,6 @@ TextInputWidget::addSelectedEmoji(const QString &emoji)
}
void
-TextInputWidget::command(QString command, QString args)
-{
- if (command == "me") {
- emit sendEmoteMessage(args);
- } else if (command == "join") {
- emit sendJoinRoomRequest(args);
- } else if (command == "invite") {
- emit sendInviteRoomRequest(args.section(' ', 0, 0), args.section(' ', 1, -1));
- } else if (command == "kick") {
- emit sendKickRoomRequest(args.section(' ', 0, 0), args.section(' ', 1, -1));
- } else if (command == "ban") {
- emit sendBanRoomRequest(args.section(' ', 0, 0), args.section(' ', 1, -1));
- } else if (command == "unban") {
- emit sendUnbanRoomRequest(args.section(' ', 0, 0), args.section(' ', 1, -1));
- } else if (command == "roomnick") {
- emit changeRoomNick(args);
- } else if (command == "shrug") {
- emit sendTextMessage("¯\\_(ツ)_/¯" + (args.isEmpty() ? "" : " " + args));
- } else if (command == "fliptable") {
- emit sendTextMessage("(╯°□°)╯︵ ┻━┻");
- } else if (command == "unfliptable") {
- emit sendTextMessage(" ┯━┯╭( º _ º╭)");
- } else if (command == "sovietflip") {
- emit sendTextMessage("ノ┬─┬ノ ︵ ( \\o°o)\\");
- } else if (command == "clear-timeline") {
- emit clearRoomTimeline();
- } else if (command == "rotate-megolm-session") {
- emit rotateMegolmSession();
- }
-}
-
-void
TextInputWidget::openFileSelection()
{
const QString homeFolder = QStandardPaths::writableLocation(QStandardPaths::HomeLocation);
diff --git a/src/TextInputWidget.h b/src/TextInputWidget.h
index 7cc73e98..f9d84871 100644
--- a/src/TextInputWidget.h
+++ b/src/TextInputWidget.h
@@ -170,9 +170,6 @@ private slots:
void addSelectedEmoji(const QString &emoji);
signals:
- void sendTextMessage(const QString &msg);
- void sendEmoteMessage(QString msg);
- void clearRoomTimeline();
void heightChanged(int height);
void uploadMedia(const QSharedPointer<QIODevice> data,
@@ -186,7 +183,6 @@ signals:
void sendBanRoomRequest(const QString &userid, const QString &reason);
void sendUnbanRoomRequest(const QString &userid, const QString &reason);
void changeRoomNick(const QString &displayname);
- void rotateMegolmSession();
void startedTyping();
void stoppedTyping();
@@ -197,7 +193,6 @@ protected:
private:
void showUploadSpinner();
- void command(QString name, QString args);
QHBoxLayout *topLayout_;
FilteredTextEdit *input_;
diff --git a/src/timeline/InputBar.cpp b/src/timeline/InputBar.cpp
index d128631d..dcd4a106 100644
--- a/src/timeline/InputBar.cpp
+++ b/src/timeline/InputBar.cpp
@@ -4,9 +4,19 @@
#include <QGuiApplication>
#include <QMimeData>
+#include <mtx/responses/common.hpp>
+
+#include "Cache.h"
+#include "ChatPage.h"
#include "Logging.h"
+#include "MatrixClient.h"
+#include "TimelineModel.h"
+#include "UserSettingsPage.h"
+#include "Utils.h"
+
+static constexpr size_t INPUT_HISTORY_SIZE = 10;
-bool
+void
InputBar::paste(bool fromMouse)
{
const QMimeData *md = nullptr;
@@ -20,13 +30,13 @@ InputBar::paste(bool fromMouse)
}
if (!md)
- return false;
+ return;
if (md->hasImage()) {
- return true;
+ } else if (md->hasText()) {
+ emit insertText(md->text());
} else {
nhlog::ui()->debug("formats: {}", md->formats().join(", ").toStdString());
- return false;
}
}
@@ -42,5 +52,147 @@ InputBar::updateState(int selectionStart_, int selectionEnd_, int cursorPosition
void
InputBar::send()
{
+ if (text.trimmed().isEmpty())
+ return;
+
+ if (history_.size() == INPUT_HISTORY_SIZE)
+ history_.pop_back();
+ history_.push_front(text);
+ history_index_ = 0;
+
+ if (text.startsWith('/')) {
+ int command_end = text.indexOf(' ');
+ if (command_end == -1)
+ command_end = text.size();
+ auto name = text.mid(1, command_end - 1);
+ auto args = text.mid(command_end + 1);
+ if (name.isEmpty() || name == "/") {
+ message(args);
+ } else {
+ command(name, args);
+ }
+ } else {
+ message(text);
+ }
+
nhlog::ui()->debug("Send: {}", text.toStdString());
}
+
+void
+InputBar::message(QString msg)
+{
+ mtx::events::msg::Text text = {};
+ text.body = msg.trimmed().toStdString();
+
+ if (ChatPage::instance()->userSettings()->markdown()) {
+ text.formatted_body = utils::markdownToHtml(msg).toStdString();
+
+ // Don't send formatted_body, when we don't need to
+ if (text.formatted_body.find("<") == std::string::npos)
+ text.formatted_body = "";
+ else
+ text.format = "org.matrix.custom.html";
+ }
+
+ if (!room->reply().isEmpty()) {
+ auto related = room->relatedInfo(room->reply());
+
+ QString body;
+ bool firstLine = true;
+ for (const auto &line : related.quoted_body.split("\n")) {
+ if (firstLine) {
+ firstLine = false;
+ body = QString("> <%1> %2\n").arg(related.quoted_user).arg(line);
+ } else {
+ body = QString("%1\n> %2\n").arg(body).arg(line);
+ }
+ }
+
+ text.body = QString("%1\n%2").arg(body).arg(msg).toStdString();
+
+ // NOTE(Nico): rich replies always need a formatted_body!
+ text.format = "org.matrix.custom.html";
+ if (ChatPage::instance()->userSettings()->markdown())
+ text.formatted_body =
+ utils::getFormattedQuoteBody(related, utils::markdownToHtml(msg))
+ .toStdString();
+ else
+ text.formatted_body =
+ utils::getFormattedQuoteBody(related, msg.toHtmlEscaped()).toStdString();
+
+ text.relates_to.in_reply_to.event_id = related.related_event;
+ room->resetReply();
+ }
+
+ room->sendMessageEvent(text, mtx::events::EventType::RoomMessage);
+}
+
+void
+InputBar::emote(QString msg)
+{
+ auto html = utils::markdownToHtml(msg);
+
+ mtx::events::msg::Emote emote;
+ emote.body = msg.trimmed().toStdString();
+
+ if (html != msg.trimmed().toHtmlEscaped() &&
+ ChatPage::instance()->userSettings()->markdown()) {
+ emote.formatted_body = html.toStdString();
+ emote.format = "org.matrix.custom.html";
+ }
+
+ if (!room->reply().isEmpty()) {
+ emote.relates_to.in_reply_to.event_id = room->reply().toStdString();
+ room->resetReply();
+ }
+
+ room->sendMessageEvent(emote, mtx::events::EventType::RoomMessage);
+}
+
+void
+InputBar::command(QString command, QString args)
+{
+ if (command == "me") {
+ emote(args);
+ } else if (command == "join") {
+ ChatPage::instance()->joinRoom(args);
+ } else if (command == "invite") {
+ ChatPage::instance()->inviteUser(args.section(' ', 0, 0), args.section(' ', 1, -1));
+ } else if (command == "kick") {
+ ChatPage::instance()->kickUser(args.section(' ', 0, 0), args.section(' ', 1, -1));
+ } else if (command == "ban") {
+ ChatPage::instance()->banUser(args.section(' ', 0, 0), args.section(' ', 1, -1));
+ } else if (command == "unban") {
+ ChatPage::instance()->unbanUser(args.section(' ', 0, 0), args.section(' ', 1, -1));
+ } else if (command == "roomnick") {
+ mtx::events::state::Member member;
+ member.display_name = args.toStdString();
+ member.avatar_url =
+ cache::avatarUrl(room->roomId(),
+ QString::fromStdString(http::client()->user_id().to_string()))
+ .toStdString();
+ member.membership = mtx::events::state::Membership::Join;
+
+ http::client()->send_state_event(
+ room->roomId().toStdString(),
+ http::client()->user_id().to_string(),
+ member,
+ [](mtx::responses::EventId, mtx::http::RequestErr err) {
+ if (err)
+ nhlog::net()->error("Failed to set room displayname: {}",
+ err->matrix_error.error);
+ });
+ } else if (command == "shrug") {
+ message("¯\\_(ツ)_/¯" + (args.isEmpty() ? "" : " " + args));
+ } else if (command == "fliptable") {
+ message("(╯°□°)╯︵ ┻━┻");
+ } else if (command == "unfliptable") {
+ message(" ┯━┯╭( º _ º╭)");
+ } else if (command == "sovietflip") {
+ message("ノ┬─┬ノ ︵ ( \\o°o)\\");
+ } else if (command == "clear-timeline") {
+ room->clearTimeline();
+ } else if (command == "rotate-megolm-session") {
+ cache::dropOutboundMegolmSession(room->roomId().toStdString());
+ }
+}
diff --git a/src/timeline/InputBar.h b/src/timeline/InputBar.h
index 78b06960..f3a38c2e 100644
--- a/src/timeline/InputBar.h
+++ b/src/timeline/InputBar.h
@@ -1,10 +1,12 @@
#pragma once
#include <QObject>
+#include <deque>
class TimelineModel;
-class InputBar : public QObject {
+class InputBar : public QObject
+{
Q_OBJECT
public:
@@ -15,11 +17,20 @@ public:
public slots:
void send();
- bool paste(bool fromMouse);
+ void paste(bool fromMouse);
void updateState(int selectionStart, int selectionEnd, int cursorPosition, QString text);
+signals:
+ void insertText(QString text);
+
private:
+ void message(QString body);
+ void emote(QString body);
+ void command(QString name, QString args);
+
TimelineModel *room;
QString text;
+ std::deque<QString> history_;
+ std::size_t history_index_ = 0;
int selectionStart = 0, selectionEnd = 0, cursorPosition = 0;
};
diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp
index aeb4e8f5..8b80ea51 100644
--- a/src/timeline/TimelineModel.cpp
+++ b/src/timeline/TimelineModel.cpp
@@ -1567,4 +1567,3 @@ TimelineModel::roomTopic() const
return utils::replaceEmoji(utils::linkifyMessage(
utils::escapeBlacklistedHtml(QString::fromStdString(info[room_id_].topic))));
}
-
diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp
index 3b80d020..f949498d 100644
--- a/src/timeline/TimelineViewManager.cpp
+++ b/src/timeline/TimelineViewManager.cpp
@@ -475,81 +475,6 @@ TimelineViewManager::initWithMessages(const std::vector<QString> &roomIds)
}
void
-TimelineViewManager::queueTextMessage(const QString &msg)
-{
- if (!timeline_)
- return;
-
- mtx::events::msg::Text text = {};
- text.body = msg.trimmed().toStdString();
-
- if (ChatPage::instance()->userSettings()->markdown()) {
- text.formatted_body = utils::markdownToHtml(msg).toStdString();
-
- // Don't send formatted_body, when we don't need to
- if (text.formatted_body.find("<") == std::string::npos)
- text.formatted_body = "";
- else
- text.format = "org.matrix.custom.html";
- }
-
- if (!timeline_->reply().isEmpty()) {
- auto related = timeline_->relatedInfo(timeline_->reply());
-
- QString body;
- bool firstLine = true;
- for (const auto &line : related.quoted_body.split("\n")) {
- if (firstLine) {
- firstLine = false;
- body = QString("> <%1> %2\n").arg(related.quoted_user).arg(line);
- } else {
- body = QString("%1\n> %2\n").arg(body).arg(line);
- }
- }
-
- text.body = QString("%1\n%2").arg(body).arg(msg).toStdString();
-
- // NOTE(Nico): rich replies always need a formatted_body!
- text.format = "org.matrix.custom.html";
- if (ChatPage::instance()->userSettings()->markdown())
- text.formatted_body =
- utils::getFormattedQuoteBody(related, utils::markdownToHtml(msg))
- .toStdString();
- else
- text.formatted_body =
- utils::getFormattedQuoteBody(related, msg.toHtmlEscaped()).toStdString();
-
- text.relates_to.in_reply_to.event_id = related.related_event;
- timeline_->resetReply();
- }
-
- timeline_->sendMessageEvent(text, mtx::events::EventType::RoomMessage);
-}
-
-void
-TimelineViewManager::queueEmoteMessage(const QString &msg)
-{
- auto html = utils::markdownToHtml(msg);
-
- mtx::events::msg::Emote emote;
- emote.body = msg.trimmed().toStdString();
-
- if (html != msg.trimmed().toHtmlEscaped() &&
- ChatPage::instance()->userSettings()->markdown()) {
- emote.formatted_body = html.toStdString();
- emote.format = "org.matrix.custom.html";
- }
-
- if (!timeline_->reply().isEmpty()) {
- emote.relates_to.in_reply_to.event_id = timeline_->reply().toStdString();
- timeline_->resetReply();
- }
-
- if (timeline_)
- timeline_->sendMessageEvent(emote, mtx::events::EventType::RoomMessage);
-}
-
-void
TimelineViewManager::queueReactionMessage(const QString &reactedEvent, const QString &reactionKey)
{
if (!timeline_)
diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h
index f330d870..02e0e132 100644
--- a/src/timeline/TimelineViewManager.h
+++ b/src/timeline/TimelineViewManager.h
@@ -104,8 +104,6 @@ public slots:
void setHistoryView(const QString &room_id);
void updateColorPalette();
void queueReactionMessage(const QString &reactedEvent, const QString &reactionKey);
- void queueTextMessage(const QString &msg);
- void queueEmoteMessage(const QString &msg);
void queueImageMessage(const QString &roomid,
const QString &filename,
const std::optional<mtx::crypto::EncryptedFile> &file,
@@ -139,12 +137,6 @@ public slots:
void updateEncryptedDescriptions();
- void clearCurrentRoomTimeline()
- {
- if (timeline_)
- timeline_->clearTimeline();
- }
-
void enableBackButton()
{
if (isNarrowView_)
|