From 7a74b863402e5f67ce7fd0a99ab3ad64b7296344 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Sat, 31 Oct 2020 23:24:07 +0100 Subject: Pasteable textinput --- src/timeline/TimelineModel.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/timeline/TimelineModel.cpp') diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index 8b80ea51..aeb4e8f5 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -1567,3 +1567,4 @@ TimelineModel::roomTopic() const return utils::replaceEmoji(utils::linkifyMessage( utils::escapeBlacklistedHtml(QString::fromStdString(info[room_id_].topic)))); } + -- cgit 1.5.1 From 0bb488563288da75566e7da883fda31914ecf281 Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Mon, 9 Nov 2020 03:12:37 +0100 Subject: Basic text input in qml --- resources/qml/MessageInput.qml | 9 +- src/ChatPage.cpp | 48 ----------- src/ChatPage.h | 2 +- src/TextInputWidget.cpp | 65 +------------- src/TextInputWidget.h | 5 -- src/timeline/InputBar.cpp | 160 ++++++++++++++++++++++++++++++++++- src/timeline/InputBar.h | 15 +++- src/timeline/TimelineModel.cpp | 1 - src/timeline/TimelineViewManager.cpp | 75 ---------------- src/timeline/TimelineViewManager.h | 8 -- 10 files changed, 178 insertions(+), 210 deletions(-) (limited to 'src/timeline/TimelineModel.cpp') 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, 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, 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 &)), @@ -685,38 +654,6 @@ TextInputWidget::addSelectedEmoji(const QString &emoji) input_->show(); } -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() { 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 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 #include +#include + +#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 +#include 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 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 @@ -474,81 +474,6 @@ TimelineViewManager::initWithMessages(const std::vector &roomIds) addRoom(roomId); } -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) { 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 &file, @@ -139,12 +137,6 @@ public slots: void updateEncryptedDescriptions(); - void clearCurrentRoomTimeline() - { - if (timeline_) - timeline_->clearTimeline(); - } - void enableBackButton() { if (isNarrowView_) -- cgit 1.5.1 From 921379a4cc3bc6381c4562bfdb9ce0dcde6b1f2c Mon Sep 17 00:00:00 2001 From: Nicolas Werner Date: Tue, 17 Nov 2020 02:37:43 +0100 Subject: Send typing updates from QML --- src/ChatPage.cpp | 47 ------------------------------------------ src/ChatPage.h | 4 ---- src/TextInputWidget.cpp | 30 --------------------------- src/TextInputWidget.h | 10 --------- src/timeline/InputBar.cpp | 41 ++++++++++++++++++++++++++++++++++++ src/timeline/InputBar.h | 16 +++++++++++++- src/timeline/TimelineModel.cpp | 1 - 7 files changed, 56 insertions(+), 93 deletions(-) (limited to 'src/timeline/TimelineModel.cpp') diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index c703d95f..1b235a95 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -141,9 +141,6 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) text_input_ = new TextInputWidget(this); contentLayout_->addWidget(text_input_); - typingRefresher_ = new QTimer(this); - typingRefresher_->setInterval(TYPING_REFRESH_TIMEOUT); - connect(this, &ChatPage::connectionLost, this, [this]() { nhlog::net()->info("connectivity lost"); isConnected_ = false; @@ -221,9 +218,7 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) connect(room_list_, &RoomList::roomChanged, this, [this](QString room_id) { this->current_room_ = room_id; }); - connect(room_list_, &RoomList::roomChanged, text_input_, &TextInputWidget::stopTyping); connect(room_list_, &RoomList::roomChanged, splitter, &Splitter::showChatView); - connect(room_list_, &RoomList::roomChanged, text_input_, &TextInputWidget::focusLineEdit); connect( room_list_, &RoomList::roomChanged, view_manager_, &TimelineViewManager::setHistoryView); @@ -237,27 +232,6 @@ ChatPage::ChatPage(QSharedPointer userSettings, QWidget *parent) room_list_->removeRoom(room_id, currentRoom() == room_id); }); - connect( - text_input_, &TextInputWidget::startedTyping, this, &ChatPage::sendTypingNotifications); - connect(typingRefresher_, &QTimer::timeout, this, &ChatPage::sendTypingNotifications); - connect(text_input_, &TextInputWidget::stoppedTyping, this, [this]() { - if (!userSettings_->typingNotifications()) - return; - - typingRefresher_->stop(); - - if (current_room_.isEmpty()) - return; - - http::client()->stop_typing( - current_room_.toStdString(), [](mtx::http::RequestErr err) { - if (err) { - nhlog::net()->warn("failed to stop typing notifications: {}", - err->matrix_error.error); - } - }); - }); - connect(view_manager_, &TimelineViewManager::updateRoomsLastMessage, room_list_, @@ -435,12 +409,6 @@ ChatPage::resetUI() emit unreadMessages(0); } -void -ChatPage::focusMessageInput() -{ - this->text_input_->focusLineEdit(); -} - void ChatPage::deleteConfigs() { @@ -1099,21 +1067,6 @@ ChatPage::receivedSessionKey(const std::string &room_id, const std::string &sess view_manager_->receivedSessionKey(room_id, session_id); } -void -ChatPage::sendTypingNotifications() -{ - if (!userSettings_->typingNotifications()) - return; - - http::client()->start_typing( - current_room_.toStdString(), 10'000, [](mtx::http::RequestErr err) { - if (err) { - nhlog::net()->warn("failed to send typing notification: {}", - err->matrix_error.error); - } - }); -} - QString ChatPage::status() const { diff --git a/src/ChatPage.h b/src/ChatPage.h index 7eb37f04..37abafa0 100644 --- a/src/ChatPage.h +++ b/src/ChatPage.h @@ -100,7 +100,6 @@ public: //! Show the room/group list (if it was visible). void showSideBars(); void initiateLogout(); - void focusMessageInput(); QString status() const; void setStatus(const QString &status); @@ -191,7 +190,6 @@ private slots: void removeRoom(const QString &room_id); void dropToLoginPage(const QString &msg); - void sendTypingNotifications(); void handleSyncResponse(const mtx::responses::Sync &res); private: @@ -265,8 +263,6 @@ private: popups::UserMentions *user_mentions_popup_; - QTimer *typingRefresher_; - // Global user settings. QSharedPointer userSettings_; diff --git a/src/TextInputWidget.cpp b/src/TextInputWidget.cpp index 92449231..30589b61 100644 --- a/src/TextInputWidget.cpp +++ b/src/TextInputWidget.cpp @@ -83,12 +83,6 @@ FilteredTextEdit::FilteredTextEdit(QWidget *parent) insertCompletion(emoji); }); - typingTimer_ = new QTimer(this); - typingTimer_->setInterval(1000); - typingTimer_->setSingleShot(true); - - connect(typingTimer_, &QTimer::timeout, this, &FilteredTextEdit::stopTyping); - connect(this, &FilteredTextEdit::resultsRetrieved, this, &FilteredTextEdit::showResults); connect( &suggestionsPopup_, &SuggestionsPopup::itemSelected, this, [this](const QString &text) { @@ -164,13 +158,6 @@ FilteredTextEdit::keyPressEvent(QKeyEvent *event) if (event->modifiers() == Qt::ControlModifier && event->key() == Qt::Key_U) QTextEdit::setText(""); - if (!isModifier) { - if (!typingTimer_->isActive()) - emit startedTyping(); - - typingTimer_->start(); - } - // calculate the new query if (textCursor().position() < atTriggerPosition_ || !isAnchorValid()) { resetAnchor(); @@ -264,7 +251,6 @@ FilteredTextEdit::keyPressEvent(QKeyEvent *event) } if (!(event->modifiers() & Qt::ShiftModifier)) { - stopTyping(); submit(); } else { QTextEdit::keyPressEvent(event); @@ -351,13 +337,6 @@ FilteredTextEdit::keyPressEvent(QKeyEvent *event) } } -void -FilteredTextEdit::stopTyping() -{ - typingTimer_->stop(); - emit stoppedTyping(); -} - QRect FilteredTextEdit::completerRect() { @@ -494,15 +473,6 @@ TextInputWidget::TextInputWidget(QWidget *parent) connect(sendMessageBtn_, &FlatButton::clicked, input_, &FilteredTextEdit::submit); connect(sendFileBtn_, SIGNAL(clicked()), this, SLOT(openFileSelection())); - connect(input_, &FilteredTextEdit::startedTyping, this, &TextInputWidget::startedTyping); - - connect(input_, &FilteredTextEdit::stoppedTyping, this, &TextInputWidget::stoppedTyping); -} - -void -TextInputWidget::stopTyping() -{ - input_->stopTyping(); } void diff --git a/src/TextInputWidget.h b/src/TextInputWidget.h index afd29439..c62a98be 100644 --- a/src/TextInputWidget.h +++ b/src/TextInputWidget.h @@ -43,8 +43,6 @@ class FilteredTextEdit : public QTextEdit public: explicit FilteredTextEdit(QWidget *parent = nullptr); - void stopTyping(); - QSize sizeHint() const override; QSize minimumSizeHint() const override; @@ -52,8 +50,6 @@ public: signals: void heightChanged(int height); - void startedTyping(); - void stoppedTyping(); void startedUpload(); //! Trigger the suggestion popup. @@ -81,7 +77,6 @@ private: int trigger_pos_; // Where emoji completer was triggered size_t history_index_; QCompleter *completer_; - QTimer *typingTimer_; SuggestionsPopup suggestionsPopup_; @@ -136,8 +131,6 @@ class TextInputWidget : public QWidget public: TextInputWidget(QWidget *parent = nullptr); - void stopTyping(); - QColor borderColor() const { return borderColor_; } void setBorderColor(QColor &color) { borderColor_ = color; } void disableInput() @@ -164,9 +157,6 @@ signals: void sendUnbanRoomRequest(const QString &userid, const QString &reason); void changeRoomNick(const QString &displayname); - void startedTyping(); - void stoppedTyping(); - protected: void focusInEvent(QFocusEvent *event) override; void paintEvent(QPaintEvent *) override; diff --git a/src/timeline/InputBar.cpp b/src/timeline/InputBar.cpp index dc287f94..6603287b 100644 --- a/src/timeline/InputBar.cpp +++ b/src/timeline/InputBar.cpp @@ -114,6 +114,11 @@ InputBar::paste(bool fromMouse) void InputBar::updateState(int selectionStart_, int selectionEnd_, int cursorPosition_, QString text_) { + if (text_.isEmpty()) + stopTyping(); + else + startTyping(); + selectionStart = selectionStart_; selectionEnd = selectionEnd_; cursorPosition = cursorPosition_; @@ -556,3 +561,39 @@ InputBar::callButton() } } } + +void +InputBar::startTyping() +{ + if (!typingRefresh_.isActive()) { + typingRefresh_.start(); + + if (ChatPage::instance()->userSettings()->typingNotifications()) { + http::client()->start_typing( + room->roomId().toStdString(), 10'000, [](mtx::http::RequestErr err) { + if (err) { + nhlog::net()->warn( + "failed to send typing notification: {}", + err->matrix_error.error); + } + }); + } + } + typingTimeout_.start(); +} +void +InputBar::stopTyping() +{ + typingRefresh_.stop(); + typingTimeout_.stop(); + + if (!ChatPage::instance()->userSettings()->typingNotifications()) + return; + + http::client()->stop_typing(room->roomId().toStdString(), [](mtx::http::RequestErr err) { + if (err) { + nhlog::net()->warn("failed to stop typing notifications: {}", + err->matrix_error.error); + } + }); +} diff --git a/src/timeline/InputBar.h b/src/timeline/InputBar.h index a52a3904..0e9ef592 100644 --- a/src/timeline/InputBar.h +++ b/src/timeline/InputBar.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -19,7 +20,14 @@ public: InputBar(TimelineModel *parent) : QObject() , room(parent) - {} + { + typingRefresh_.setInterval(10'000); + typingRefresh_.setSingleShot(true); + typingTimeout_.setInterval(5'000); + typingTimeout_.setSingleShot(true); + connect(&typingRefresh_, &QTimer::timeout, this, &InputBar::startTyping); + connect(&typingTimeout_, &QTimer::timeout, this, &InputBar::stopTyping); + } public slots: void send(); @@ -29,6 +37,10 @@ public slots: bool uploading() const { return uploading_; } void callButton(); +private slots: + void startTyping(); + void stopTyping(); + signals: void insertText(QString text); void uploadingChanged(bool value); @@ -69,6 +81,8 @@ private: } } + QTimer typingRefresh_; + QTimer typingTimeout_; TimelineModel *room; QString text; std::deque history_; diff --git a/src/timeline/TimelineModel.cpp b/src/timeline/TimelineModel.cpp index 8b80ea51..4cbd5777 100644 --- a/src/timeline/TimelineModel.cpp +++ b/src/timeline/TimelineModel.cpp @@ -800,7 +800,6 @@ void TimelineModel::replyAction(QString id) { setReply(id); - ChatPage::instance()->focusMessageInput(); } RelatedInfo -- cgit 1.5.1