diff --git a/src/CompletionModel.h b/src/CompletionModel.h index 66d300b0..ed021051 100644 --- a/src/CompletionModel.h +++ b/src/CompletionModel.h @@ -4,13 +4,17 @@ #include -class CompletionModel : public QSortFilterProxyModel { +class CompletionModel : public QSortFilterProxyModel +{ public: - CompletionModel(QAbstractItemModel *model, QObject *parent = nullptr) : QSortFilterProxyModel(parent) { - setSourceModel(model); - } - int rowCount(const QModelIndex &parent) const override { - auto row_count = QSortFilterProxyModel::rowCount(parent); - return (row_count < 7) ? row_count : 7; - } + CompletionModel(QAbstractItemModel *model, QObject *parent = nullptr) + : QSortFilterProxyModel(parent) + { + setSourceModel(model); + } + int rowCount(const QModelIndex &parent) const override + { + auto row_count = QSortFilterProxyModel::rowCount(parent); + return (row_count < 7) ? row_count : 7; + } }; diff --git a/src/TextInputWidget.cpp b/src/TextInputWidget.cpp index bdc430f5..17018392 100644 --- a/src/TextInputWidget.cpp +++ b/src/TextInputWidget.cpp @@ -68,22 +68,22 @@ 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::of(&QCompleter::activated), - [this](auto &index) { - emoji_popup_open_ = false; - auto emoji = index.data(emoji::EmojiModel::Unicode).toString(); - insertCompletion(emoji); - }); + 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::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); @@ -126,14 +126,15 @@ FilteredTextEdit::FilteredTextEdit(QWidget *parent) } void -FilteredTextEdit::insertCompletion(QString completion) { - // Paint the current word and replace it with 'completion' - auto cur_word = wordUnderCursor(); - auto tc = textCursor(); - tc.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, cur_word.length()); - tc.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, cur_word.length()); - tc.insertText(completion); - setTextCursor(tc); +FilteredTextEdit::insertCompletion(QString completion) +{ + // Paint the current word and replace it with 'completion' + auto cur_word = wordUnderCursor(); + auto tc = textCursor(); + tc.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, cur_word.length()); + tc.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, cur_word.length()); + tc.insertText(completion); + setTextCursor(tc); } void @@ -158,7 +159,7 @@ FilteredTextEdit::showResults(const std::vector &results) void FilteredTextEdit::keyPressEvent(QKeyEvent *event) { - const bool isModifier = (event->modifiers() != Qt::NoModifier); + const bool isModifier = (event->modifiers() != Qt::NoModifier); #if defined(Q_OS_MAC) if (event->modifiers() == (Qt::ControlModifier | Qt::MetaModifier) && @@ -202,20 +203,20 @@ 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; - } - } + 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: @@ -246,20 +247,20 @@ FilteredTextEdit::keyPressEvent(QKeyEvent *event) break; } case Qt::Key_Colon: { - QTextEdit::keyPressEvent(event); - emoji_popup_open_ = true; - emoji_completion_model_->setFilterRegExp(wordUnderCursor()); - //completer_->setCompletionPrefix(wordUnderCursor()); - completer_->popup()->setCurrentIndex(completer_->completionModel()->index(0, 0)); - completer_->complete(completerRect()); - break; - } + QTextEdit::keyPressEvent(event); + emoji_popup_open_ = true; + emoji_completion_model_->setFilterRegExp(wordUnderCursor()); + // completer_->setCompletionPrefix(wordUnderCursor()); + completer_->popup()->setCurrentIndex(completer_->completionModel()->index(0, 0)); + completer_->complete(completerRect()); + break; + } case Qt::Key_Return: case Qt::Key_Enter: - if (emoji_popup_open_) { - event->ignore(); - return; - } + if (emoji_popup_open_) { + event->ignore(); + return; + } if (!(event->modifiers() & Qt::ShiftModifier)) { stopTyping(); @@ -305,24 +306,26 @@ FilteredTextEdit::keyPressEvent(QKeyEvent *event) QTextEdit::keyPressEvent(event); if (isModifier) - return; - - - if (emoji_popup_open_) { - // Update completion - - emoji_completion_model_->setFilterRegExp(wordUnderCursor()); - //completer_->setCompletionPrefix(wordUnderCursor()); - completer_->popup()->setCurrentIndex(completer_->completionModel()->index(0, 0)); - completer_->complete(completerRect()); - } + return; - if (emoji_popup_open_ && (completer_->completionCount() < 1 || - !wordUnderCursor().contains(QRegExp(":[^\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 (emoji_popup_open_) { + // Update completion + + emoji_completion_model_->setFilterRegExp(wordUnderCursor()); + // completer_->setCompletionPrefix(wordUnderCursor()); + completer_->popup()->setCurrentIndex( + completer_->completionModel()->index(0, 0)); + completer_->complete(completerRect()); + } + + if (emoji_popup_open_ && + (completer_->completionCount() < 1 || + !wordUnderCursor().contains(QRegExp(":[^\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(); @@ -436,22 +439,23 @@ FilteredTextEdit::stopTyping() QRect FilteredTextEdit::completerRect() { - // Move left edge to the beginning of the word - auto cursor = textCursor(); - auto rect = cursorRect(); - cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, wordUnderCursor().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; + // Move left edge to the beginning of the word + auto cursor = textCursor(); + auto rect = cursorRect(); + cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, wordUnderCursor().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 diff --git a/src/TextInputWidget.h b/src/TextInputWidget.h index 8cd61b6a..4ae68798 100644 --- a/src/TextInputWidget.h +++ b/src/TextInputWidget.h @@ -109,7 +109,7 @@ private: { return pos == atTriggerPosition_ + anchorWidth(anchor); } - QRect completerRect(); + QRect completerRect(); QString query() { auto cursor = textCursor(); @@ -118,18 +118,18 @@ private: } QString wordUnderCursor() { - auto tc = textCursor(); - auto editor_text = toPlainText(); - // Text before cursor - auto text = editor_text.chopped(editor_text.length() - tc.position()); - // Revert to find the first space (last before cursor in the original) - std::reverse(text.begin(), text.end()); - auto space_idx = text.indexOf(" "); - if (space_idx > -1) - text.chop(text.length() - space_idx); - // Revert back - std::reverse(text.begin(), text.end()); - return text; + auto tc = textCursor(); + auto editor_text = toPlainText(); + // Text before cursor + auto text = editor_text.chopped(editor_text.length() - tc.position()); + // Revert to find the first space (last before cursor in the original) + std::reverse(text.begin(), text.end()); + auto space_idx = text.indexOf(" "); + if (space_idx > -1) + text.chop(text.length() - space_idx); + // Revert back + std::reverse(text.begin(), text.end()); + return text; } dialogs::PreviewUploadOverlay previewDialog_; @@ -137,7 +137,7 @@ private: //! Latest position of the '@' character that triggers the username completer. int atTriggerPosition_ = -1; - void insertCompletion(QString completion); + void insertCompletion(QString completion); void textChanged(); void uploadData(const QByteArray data, const QString &media, const QString &filename); void afterCompletion(int); diff --git a/src/emoji/EmojiSearchModel.h b/src/emoji/EmojiSearchModel.h index bce96998..87fa0403 100644 --- a/src/emoji/EmojiSearchModel.h +++ b/src/emoji/EmojiSearchModel.h @@ -11,27 +11,32 @@ namespace emoji { // Map emoji data to searchable data -class EmojiSearchModel : public QSortFilterProxyModel { +class EmojiSearchModel : public QSortFilterProxyModel +{ public: - EmojiSearchModel(QObject *parent = nullptr) : QSortFilterProxyModel(parent) { - setSourceModel(new EmojiModel(this)); - } - QVariant data(const QModelIndex &index, int role = Qt::UserRole + 1) const override { - if (role == Qt::DisplayRole) { - auto emoji = QSortFilterProxyModel::data(index, role).toString(); - return emoji + " :" + toShortcode(data(index, EmojiModel::ShortName).toString()) - + ":"; - } - return QSortFilterProxyModel::data(index, role); - } - /*int rowCount(const QModelIndex &parent) const override { - auto row_count = QSortFilterProxyModel::rowCount(parent); + EmojiSearchModel(QObject *parent = nullptr) + : QSortFilterProxyModel(parent) + { + setSourceModel(new EmojiModel(this)); + } + QVariant data(const QModelIndex &index, int role = Qt::UserRole + 1) const override + { + if (role == Qt::DisplayRole) { + auto emoji = QSortFilterProxyModel::data(index, role).toString(); + return emoji + " :" + + toShortcode(data(index, EmojiModel::ShortName).toString()) + ":"; + } + return QSortFilterProxyModel::data(index, role); + } + /*int rowCount(const QModelIndex &parent) const override { + auto row_count = QSortFilterProxyModel::rowCount(parent); return (row_count < 7) ? row_count : 7; }*/ private: - QString toShortcode(QString shortname) const { - return shortname.replace(" ", "-").replace(":", "-").replace("--", "-").toLower(); - } + QString toShortcode(QString shortname) const + { + return shortname.replace(" ", "-").replace(":", "-").replace("--", "-").toLower(); + } }; }