This commit is contained in:
Jussi Kuokkanen 2020-08-28 23:59:27 +03:00
parent 5e344d2685
commit 7acd4b3307
4 changed files with 135 additions and 122 deletions

View file

@ -4,13 +4,17 @@
#include <QSortFilterProxyModel> #include <QSortFilterProxyModel>
class CompletionModel : public QSortFilterProxyModel { class CompletionModel : public QSortFilterProxyModel
{
public: public:
CompletionModel(QAbstractItemModel *model, QObject *parent = nullptr) : QSortFilterProxyModel(parent) { CompletionModel(QAbstractItemModel *model, QObject *parent = nullptr)
setSourceModel(model); : QSortFilterProxyModel(parent)
} {
int rowCount(const QModelIndex &parent) const override { setSourceModel(model);
auto row_count = QSortFilterProxyModel::rowCount(parent); }
return (row_count < 7) ? row_count : 7; int rowCount(const QModelIndex &parent) const override
} {
auto row_count = QSortFilterProxyModel::rowCount(parent);
return (row_count < 7) ? row_count : 7;
}
}; };

View file

@ -68,22 +68,22 @@ FilteredTextEdit::FilteredTextEdit(QWidget *parent)
connect(this, &QTextEdit::textChanged, this, &FilteredTextEdit::textChanged); connect(this, &QTextEdit::textChanged, this, &FilteredTextEdit::textChanged);
setAcceptRichText(false); setAcceptRichText(false);
completer_ = new QCompleter(this); completer_ = new QCompleter(this);
completer_->setWidget(this); completer_->setWidget(this);
auto model = new emoji::EmojiSearchModel(this); auto model = new emoji::EmojiSearchModel(this);
model->sort(0, Qt::AscendingOrder); model->sort(0, Qt::AscendingOrder);
completer_->setModel((emoji_completion_model_ = new CompletionModel(model, this))); completer_->setModel((emoji_completion_model_ = new CompletionModel(model, this)));
completer_->setModelSorting(QCompleter::UnsortedModel); completer_->setModelSorting(QCompleter::UnsortedModel);
completer_->popup()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); completer_->popup()->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
completer_->popup()->setVerticalScrollBarPolicy(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);
});
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_ = new QTimer(this);
typingTimer_->setInterval(1000); typingTimer_->setInterval(1000);
@ -126,14 +126,15 @@ FilteredTextEdit::FilteredTextEdit(QWidget *parent)
} }
void void
FilteredTextEdit::insertCompletion(QString completion) { FilteredTextEdit::insertCompletion(QString completion)
// Paint the current word and replace it with 'completion' {
auto cur_word = wordUnderCursor(); // Paint the current word and replace it with 'completion'
auto tc = textCursor(); auto cur_word = wordUnderCursor();
tc.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, cur_word.length()); auto tc = textCursor();
tc.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, cur_word.length()); tc.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, cur_word.length());
tc.insertText(completion); tc.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor, cur_word.length());
setTextCursor(tc); tc.insertText(completion);
setTextCursor(tc);
} }
void void
@ -158,7 +159,7 @@ FilteredTextEdit::showResults(const std::vector<SearchResult> &results)
void void
FilteredTextEdit::keyPressEvent(QKeyEvent *event) FilteredTextEdit::keyPressEvent(QKeyEvent *event)
{ {
const bool isModifier = (event->modifiers() != Qt::NoModifier); const bool isModifier = (event->modifiers() != Qt::NoModifier);
#if defined(Q_OS_MAC) #if defined(Q_OS_MAC)
if (event->modifiers() == (Qt::ControlModifier | Qt::MetaModifier) && if (event->modifiers() == (Qt::ControlModifier | Qt::MetaModifier) &&
@ -202,20 +203,20 @@ FilteredTextEdit::keyPressEvent(QKeyEvent *event)
} }
} }
if (emoji_popup_open_) { if (emoji_popup_open_) {
auto fake_key = (event->key() == Qt::Key_Backtab) ? Qt::Key_Up : Qt::Key_Down; auto fake_key = (event->key() == Qt::Key_Backtab) ? Qt::Key_Up : Qt::Key_Down;
switch (event->key()) { switch (event->key()) {
case Qt::Key_Backtab: case Qt::Key_Backtab:
case Qt::Key_Tab: { case Qt::Key_Tab: {
// Simulate up/down arrow press // Simulate up/down arrow press
auto ev = new QKeyEvent(QEvent::KeyPress, fake_key, Qt::NoModifier); auto ev = new QKeyEvent(QEvent::KeyPress, fake_key, Qt::NoModifier);
QCoreApplication::postEvent(completer_->popup(), ev); QCoreApplication::postEvent(completer_->popup(), ev);
return; return;
} }
default: default:
break; break;
} }
} }
switch (event->key()) { switch (event->key()) {
case Qt::Key_At: case Qt::Key_At:
@ -246,20 +247,20 @@ FilteredTextEdit::keyPressEvent(QKeyEvent *event)
break; break;
} }
case Qt::Key_Colon: { case Qt::Key_Colon: {
QTextEdit::keyPressEvent(event); QTextEdit::keyPressEvent(event);
emoji_popup_open_ = true; emoji_popup_open_ = true;
emoji_completion_model_->setFilterRegExp(wordUnderCursor()); emoji_completion_model_->setFilterRegExp(wordUnderCursor());
//completer_->setCompletionPrefix(wordUnderCursor()); // completer_->setCompletionPrefix(wordUnderCursor());
completer_->popup()->setCurrentIndex(completer_->completionModel()->index(0, 0)); completer_->popup()->setCurrentIndex(completer_->completionModel()->index(0, 0));
completer_->complete(completerRect()); completer_->complete(completerRect());
break; break;
} }
case Qt::Key_Return: case Qt::Key_Return:
case Qt::Key_Enter: case Qt::Key_Enter:
if (emoji_popup_open_) { if (emoji_popup_open_) {
event->ignore(); event->ignore();
return; return;
} }
if (!(event->modifiers() & Qt::ShiftModifier)) { if (!(event->modifiers() & Qt::ShiftModifier)) {
stopTyping(); stopTyping();
@ -305,24 +306,26 @@ FilteredTextEdit::keyPressEvent(QKeyEvent *event)
QTextEdit::keyPressEvent(event); QTextEdit::keyPressEvent(event);
if (isModifier) if (isModifier)
return; 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 || if (emoji_popup_open_) {
!wordUnderCursor().contains(QRegExp(":[^\r\n\t\f\v :]+$")))) { // Update completion
// No completions for this word or another word than the completer was started with
emoji_popup_open_ = false; emoji_completion_model_->setFilterRegExp(wordUnderCursor());
completer_->popup()->hide(); // 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) { if (textCursor().position() == 0) {
resetAnchor(); resetAnchor();
@ -436,22 +439,23 @@ FilteredTextEdit::stopTyping()
QRect QRect
FilteredTextEdit::completerRect() FilteredTextEdit::completerRect()
{ {
// Move left edge to the beginning of the word // Move left edge to the beginning of the word
auto cursor = textCursor(); auto cursor = textCursor();
auto rect = cursorRect(); auto rect = cursorRect();
cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, wordUnderCursor().length()); cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, wordUnderCursor().length());
auto cursor_global_x = viewport()->mapToGlobal(cursorRect(cursor).topLeft()).x(); auto cursor_global_x = viewport()->mapToGlobal(cursorRect(cursor).topLeft()).x();
auto rect_global_left = viewport()->mapToGlobal(rect.bottomLeft()).x(); auto rect_global_left = viewport()->mapToGlobal(rect.bottomLeft()).x();
auto dx = qAbs(rect_global_left - cursor_global_x); auto dx = qAbs(rect_global_left - cursor_global_x);
rect.moveLeft(rect.left() - dx); rect.moveLeft(rect.left() - dx);
auto item_height = completer_->popup()->sizeHintForRow(0); auto item_height = completer_->popup()->sizeHintForRow(0);
auto max_height = item_height * completer_->maxVisibleItems(); auto max_height = item_height * completer_->maxVisibleItems();
auto height = (completer_->completionCount() > completer_->maxVisibleItems()) ? max_height : auto height = (completer_->completionCount() > completer_->maxVisibleItems())
completer_->completionCount() * item_height; ? max_height
rect.setWidth(completer_->popup()->sizeHintForColumn(0)); : completer_->completionCount() * item_height;
rect.moveBottom(-height); rect.setWidth(completer_->popup()->sizeHintForColumn(0));
return rect; rect.moveBottom(-height);
return rect;
} }
QSize QSize

View file

@ -109,7 +109,7 @@ private:
{ {
return pos == atTriggerPosition_ + anchorWidth(anchor); return pos == atTriggerPosition_ + anchorWidth(anchor);
} }
QRect completerRect(); QRect completerRect();
QString query() QString query()
{ {
auto cursor = textCursor(); auto cursor = textCursor();
@ -118,18 +118,18 @@ private:
} }
QString wordUnderCursor() QString wordUnderCursor()
{ {
auto tc = textCursor(); auto tc = textCursor();
auto editor_text = toPlainText(); auto editor_text = toPlainText();
// Text before cursor // Text before cursor
auto text = editor_text.chopped(editor_text.length() - tc.position()); auto text = editor_text.chopped(editor_text.length() - tc.position());
// Revert to find the first space (last before cursor in the original) // Revert to find the first space (last before cursor in the original)
std::reverse(text.begin(), text.end()); std::reverse(text.begin(), text.end());
auto space_idx = text.indexOf(" "); auto space_idx = text.indexOf(" ");
if (space_idx > -1) if (space_idx > -1)
text.chop(text.length() - space_idx); text.chop(text.length() - space_idx);
// Revert back // Revert back
std::reverse(text.begin(), text.end()); std::reverse(text.begin(), text.end());
return text; return text;
} }
dialogs::PreviewUploadOverlay previewDialog_; dialogs::PreviewUploadOverlay previewDialog_;
@ -137,7 +137,7 @@ private:
//! Latest position of the '@' character that triggers the username completer. //! Latest position of the '@' character that triggers the username completer.
int atTriggerPosition_ = -1; int atTriggerPosition_ = -1;
void insertCompletion(QString completion); void insertCompletion(QString completion);
void textChanged(); void textChanged();
void uploadData(const QByteArray data, const QString &media, const QString &filename); void uploadData(const QByteArray data, const QString &media, const QString &filename);
void afterCompletion(int); void afterCompletion(int);

View file

@ -11,27 +11,32 @@
namespace emoji { namespace emoji {
// Map emoji data to searchable data // Map emoji data to searchable data
class EmojiSearchModel : public QSortFilterProxyModel { class EmojiSearchModel : public QSortFilterProxyModel
{
public: public:
EmojiSearchModel(QObject *parent = nullptr) : QSortFilterProxyModel(parent) { EmojiSearchModel(QObject *parent = nullptr)
setSourceModel(new EmojiModel(this)); : QSortFilterProxyModel(parent)
} {
QVariant data(const QModelIndex &index, int role = Qt::UserRole + 1) const override { setSourceModel(new EmojiModel(this));
if (role == Qt::DisplayRole) { }
auto emoji = QSortFilterProxyModel::data(index, role).toString(); QVariant data(const QModelIndex &index, int role = Qt::UserRole + 1) const override
return emoji + " :" + toShortcode(data(index, EmojiModel::ShortName).toString()) {
+ ":"; if (role == Qt::DisplayRole) {
} auto emoji = QSortFilterProxyModel::data(index, role).toString();
return QSortFilterProxyModel::data(index, role); return emoji + " :" +
} toShortcode(data(index, EmojiModel::ShortName).toString()) + ":";
/*int rowCount(const QModelIndex &parent) const override { }
auto row_count = QSortFilterProxyModel::rowCount(parent); 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; return (row_count < 7) ? row_count : 7;
}*/ }*/
private: private:
QString toShortcode(QString shortname) const { QString toShortcode(QString shortname) const
return shortname.replace(" ", "-").replace(":", "-").replace("--", "-").toLower(); {
} return shortname.replace(" ", "-").replace(":", "-").replace("--", "-").toLower();
}
}; };
} }