summary refs log tree commit diff
path: root/src/TextInputWidget.cpp
diff options
context:
space:
mode:
authorWeblate <noreply@weblate.org>2020-09-13 17:20:51 -0400
committerWeblate <noreply@weblate.org>2020-09-13 17:20:51 -0400
commitb5669310e5d272418480def6d0a78ad26eda6c72 (patch)
tree62e3681090e7d5d9ecc502223cd63037bc86d5ba /src/TextInputWidget.cpp
parentAdd simpified chinese translations (diff)
parentMerge pull request #275 from Chethan2k1/master (diff)
downloadnheko-b5669310e5d272418480def6d0a78ad26eda6c72.tar.xz
Merge branch 'master' of github.com:Nheko-Reborn/nheko
Diffstat (limited to 'src/TextInputWidget.cpp')
-rw-r--r--src/TextInputWidget.cpp133
1 files changed, 120 insertions, 13 deletions
diff --git a/src/TextInputWidget.cpp b/src/TextInputWidget.cpp

index a3392170..4a25c4cf 100644 --- a/src/TextInputWidget.cpp +++ b/src/TextInputWidget.cpp
@@ -15,9 +15,11 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <QAbstractItemView> #include <QAbstractTextDocumentLayout> #include <QBuffer> #include <QClipboard> +#include <QCompleter> #include <QFileDialog> #include <QMimeData> #include <QMimeDatabase> @@ -28,9 +30,12 @@ #include "Cache.h" #include "ChatPage.h" +#include "CompletionModel.h" #include "Logging.h" #include "TextInputWidget.h" #include "Utils.h" +#include "emoji/EmojiSearchModel.h" +#include "emoji/Provider.h" #include "ui/FlatButton.h" #include "ui/LoadingIndicator.h" @@ -61,6 +66,23 @@ FilteredTextEdit::FilteredTextEdit(QWidget *parent) connect(this, &QTextEdit::textChanged, this, &FilteredTextEdit::textChanged); setAcceptRichText(false); + completer_ = new QCompleter(this); + completer_->setWidget(this); + auto model = new emoji::EmojiSearchModel(this); + model->sort(0, Qt::AscendingOrder); + completer_->setModel((emoji_completion_model_ = new CompletionModel(model, this))); + completer_->setModelSorting(QCompleter::UnsortedModel); + completer_->popup()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + completer_->popup()->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + + connect(completer_, + QOverload<const QModelIndex &>::of(&QCompleter::activated), + [this](auto &index) { + emoji_popup_open_ = false; + auto emoji = index.data(emoji::EmojiModel::Unicode).toString(); + insertCompletion(emoji); + }); + typingTimer_ = new QTimer(this); typingTimer_->setInterval(1000); typingTimer_->setSingleShot(true); @@ -102,6 +124,18 @@ FilteredTextEdit::FilteredTextEdit(QWidget *parent) } void +FilteredTextEdit::insertCompletion(QString completion) +{ + // Paint the current word and replace it with 'completion' + auto cur_text = textAfterPosition(trigger_pos_); + auto tc = textCursor(); + tc.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, cur_text.length()); + tc.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, cur_text.length()); + tc.insertText(completion); + setTextCursor(tc); +} + +void FilteredTextEdit::showResults(const std::vector<SearchResult> &results) { QPoint pos; @@ -167,6 +201,21 @@ FilteredTextEdit::keyPressEvent(QKeyEvent *event) } } + if (emoji_popup_open_) { + auto fake_key = (event->key() == Qt::Key_Backtab) ? Qt::Key_Up : Qt::Key_Down; + switch (event->key()) { + case Qt::Key_Backtab: + case Qt::Key_Tab: { + // Simulate up/down arrow press + auto ev = new QKeyEvent(QEvent::KeyPress, fake_key, Qt::NoModifier); + QCoreApplication::postEvent(completer_->popup(), ev); + return; + } + default: + break; + } + } + switch (event->key()) { case Qt::Key_At: atTriggerPosition_ = textCursor().position(); @@ -195,8 +244,26 @@ FilteredTextEdit::keyPressEvent(QKeyEvent *event) break; } + case Qt::Key_Colon: { + QTextEdit::keyPressEvent(event); + trigger_pos_ = textCursor().position() - 1; + emoji_completion_model_->setFilterRegExp(""); + emoji_popup_open_ = true; + break; + } case Qt::Key_Return: case Qt::Key_Enter: + if (emoji_popup_open_) { + if (!completer_->popup()->currentIndex().isValid()) { + // No completion to select, do normal behavior + completer_->popup()->hide(); + emoji_popup_open_ = false; + } else { + event->ignore(); + return; + } + } + if (!(event->modifiers() & Qt::ShiftModifier)) { stopTyping(); submit(); @@ -243,6 +310,21 @@ FilteredTextEdit::keyPressEvent(QKeyEvent *event) if (isModifier) return; + if (emoji_popup_open_ && textAfterPosition(trigger_pos_).length() > 2) { + // Update completion + emoji_completion_model_->setFilterRegExp(textAfterPosition(trigger_pos_)); + completer_->complete(completerRect()); + } + + if (emoji_popup_open_ && (completer_->completionCount() < 1 || + !textAfterPosition(trigger_pos_) + .contains(QRegularExpression(":[^\r\n\t\f\v :]+$")))) { + // No completions for this word or another word than the completer was + // started with + emoji_popup_open_ = false; + completer_->popup()->hide(); + } + if (textCursor().position() == 0) { resetAnchor(); closeSuggestions(); @@ -352,6 +434,29 @@ FilteredTextEdit::stopTyping() emit stoppedTyping(); } +QRect +FilteredTextEdit::completerRect() +{ + // Move left edge to the beginning of the word + auto cursor = textCursor(); + auto rect = cursorRect(); + cursor.movePosition( + QTextCursor::Left, QTextCursor::MoveAnchor, textAfterPosition(trigger_pos_).length()); + auto cursor_global_x = viewport()->mapToGlobal(cursorRect(cursor).topLeft()).x(); + auto rect_global_left = viewport()->mapToGlobal(rect.bottomLeft()).x(); + auto dx = qAbs(rect_global_left - cursor_global_x); + rect.moveLeft(rect.left() - dx); + + auto item_height = completer_->popup()->sizeHintForRow(0); + auto max_height = item_height * completer_->maxVisibleItems(); + auto height = (completer_->completionCount() > completer_->maxVisibleItems()) + ? max_height + : completer_->completionCount() * item_height; + rect.setWidth(completer_->popup()->sizeHintForColumn(0)); + rect.moveBottom(-height); + return rect; +} + QSize FilteredTextEdit::sizeHint() const { @@ -581,27 +686,29 @@ void TextInputWidget::command(QString command, QString args) { if (command == "me") { - sendEmoteMessage(args); + emit sendEmoteMessage(args); } else if (command == "join") { - sendJoinRoomRequest(args); + emit sendJoinRoomRequest(args); } else if (command == "invite") { - sendInviteRoomRequest(args.section(' ', 0, 0), args.section(' ', 1, -1)); + emit sendInviteRoomRequest(args.section(' ', 0, 0), args.section(' ', 1, -1)); } else if (command == "kick") { - sendKickRoomRequest(args.section(' ', 0, 0), args.section(' ', 1, -1)); + emit sendKickRoomRequest(args.section(' ', 0, 0), args.section(' ', 1, -1)); } else if (command == "ban") { - sendBanRoomRequest(args.section(' ', 0, 0), args.section(' ', 1, -1)); + emit sendBanRoomRequest(args.section(' ', 0, 0), args.section(' ', 1, -1)); } else if (command == "unban") { - sendUnbanRoomRequest(args.section(' ', 0, 0), args.section(' ', 1, -1)); + emit sendUnbanRoomRequest(args.section(' ', 0, 0), args.section(' ', 1, -1)); } else if (command == "roomnick") { - changeRoomNick(args); + emit changeRoomNick(args); } else if (command == "shrug") { - sendTextMessage("¯\\_(ツ)_/¯"); + emit sendTextMessage("¯\\_(ツ)_/¯"); } else if (command == "fliptable") { - sendTextMessage("(╯°□°)╯︵ ┻━┻"); + emit sendTextMessage("(╯°□°)╯︵ ┻━┻"); } else if (command == "unfliptable") { - sendTextMessage(" ┯━┯╭( º _ º╭)"); + emit sendTextMessage(" ┯━┯╭( º _ º╭)"); } else if (command == "sovietflip") { - sendTextMessage("ノ┬─┬ノ ︵ ( \\o°o)\\"); + emit sendTextMessage("ノ┬─┬ノ ︵ ( \\o°o)\\"); + } else if (command == "clear-timeline") { + emit clearRoomTimeline(); } } @@ -633,7 +740,7 @@ TextInputWidget::showUploadSpinner() topLayout_->removeWidget(sendFileBtn_); sendFileBtn_->hide(); - topLayout_->insertWidget(0, spinner_); + topLayout_->insertWidget(1, spinner_); spinner_->start(); } @@ -641,7 +748,7 @@ void TextInputWidget::hideUploadSpinner() { topLayout_->removeWidget(spinner_); - topLayout_->insertWidget(0, sendFileBtn_); + topLayout_->insertWidget(1, sendFileBtn_); sendFileBtn_->show(); spinner_->stop(); }