|
|
|
@ -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<const QModelIndex&>::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<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); |
|
|
|
@ -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<SearchResult> &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()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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(); |
|
|
|
|
} |
|
|
|
|
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()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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 |
|
|
|
|