File indexing completed on 2025-01-05 05:14:51
0001 /* 0002 SPDX-FileCopyrightText: 2021 Hamed Masafi <hamed.masfi@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-3.0-or-later 0005 */ 0006 0007 #include "completiontextedit.h" 0008 #include <QAbstractItemView> 0009 #include <QCompleter> 0010 #include <QKeyEvent> 0011 #include <QScrollBar> 0012 #include <QStringListModel> 0013 0014 CompletionTextEdit::CompletionTextEdit(QWidget *parent) 0015 : QTextEdit(parent) 0016 , mCompletionModel(new QStringListModel(this)) 0017 , mCompleter(new QCompleter(this)) 0018 { 0019 mCompleter->setWidget(this); 0020 mCompleter->setModel(mCompletionModel); 0021 mCompleter->setCompletionMode(QCompleter::PopupCompletion); 0022 mCompleter->setCaseSensitivity(Qt::CaseInsensitive); 0023 QObject::connect(mCompleter, QOverload<const QString &>::of(&QCompleter::activated), this, &CompletionTextEdit::insertCompletion); 0024 } 0025 0026 void CompletionTextEdit::setCompleter(QCompleter *completer) 0027 { 0028 Q_UNUSED(completer) 0029 0030 if (mCompleter) 0031 mCompleter->disconnect(this); 0032 } 0033 0034 QCompleter *CompletionTextEdit::completer() const 0035 { 0036 return mCompleter; 0037 } 0038 0039 void CompletionTextEdit::addWord(const QString &word) 0040 { 0041 mWords.append(word); 0042 } 0043 0044 void CompletionTextEdit::addWords(const QStringList &words) 0045 { 0046 mWords.append(words); 0047 } 0048 0049 void CompletionTextEdit::insertCompletion(const QString &completion) 0050 { 0051 if (mCompleter->widget() != this) 0052 return; 0053 QTextCursor tc = textCursor(); 0054 const int extra = completion.length() - mCompleter->completionPrefix().length(); 0055 tc.movePosition(QTextCursor::Left); 0056 tc.movePosition(QTextCursor::EndOfWord); 0057 tc.insertText(completion.right(extra)); 0058 setTextCursor(tc); 0059 } 0060 0061 QString CompletionTextEdit::textUnderCursor() const 0062 { 0063 QTextCursor tc = textCursor(); 0064 tc.select(QTextCursor::WordUnderCursor); 0065 return tc.selectedText(); 0066 } 0067 0068 const QStringList &CompletionTextEdit::words() const 0069 { 0070 return mWords; 0071 } 0072 0073 void CompletionTextEdit::setWords(const QStringList &newWords) 0074 { 0075 mWords = newWords; 0076 } 0077 0078 void CompletionTextEdit::begin() 0079 { 0080 mCompletionModel->setStringList(mWords); 0081 } 0082 0083 void CompletionTextEdit::focusInEvent(QFocusEvent *e) 0084 { 0085 if (mCompleter) 0086 mCompleter->setWidget(this); 0087 QTextEdit::focusInEvent(e); 0088 } 0089 0090 void CompletionTextEdit::keyPressEvent(QKeyEvent *e) 0091 { 0092 if (mCompleter && mCompleter->popup()->isVisible()) { 0093 // The following keys are forwarded by the completer to the widget 0094 switch (e->key()) { 0095 case Qt::Key_Enter: 0096 case Qt::Key_Return: 0097 case Qt::Key_Escape: 0098 case Qt::Key_Tab: 0099 case Qt::Key_Backtab: 0100 e->ignore(); 0101 return; // let the completer do default behavior 0102 default: 0103 break; 0104 } 0105 } 0106 0107 const bool isShortcut = (e->modifiers().testFlag(Qt::ControlModifier) && e->key() == Qt::Key_E); // CTRL+E 0108 if (!mCompleter || !isShortcut) // do not process the shortcut when we have a completer 0109 QTextEdit::keyPressEvent(e); 0110 0111 const bool ctrlOrShift = e->modifiers().testFlag(Qt::ControlModifier) || e->modifiers().testFlag(Qt::ShiftModifier); 0112 if (!mCompleter || (ctrlOrShift && e->text().isEmpty())) 0113 return; 0114 0115 static QString eow(QStringLiteral("~!@#$%^&*()_+{}|:\"<>?,./;'[]\\-=")); // end of word 0116 const bool hasModifier = (e->modifiers() != Qt::NoModifier) && !ctrlOrShift; 0117 QString completionPrefix = textUnderCursor(); 0118 0119 if (!isShortcut && (hasModifier || e->text().isEmpty() || completionPrefix.length() < 3 || eow.contains(e->text().right(1)))) { 0120 mCompleter->popup()->hide(); 0121 return; 0122 } 0123 0124 if (completionPrefix != mCompleter->completionPrefix()) { 0125 mCompleter->setCompletionPrefix(completionPrefix); 0126 mCompleter->popup()->setCurrentIndex(mCompleter->completionModel()->index(0, 0)); 0127 } 0128 QRect cr = cursorRect(); 0129 cr.setWidth(mCompleter->popup()->sizeHintForColumn(0) + mCompleter->popup()->verticalScrollBar()->sizeHint().width()); 0130 mCompleter->complete(cr); // popup it up! 0131 } 0132 0133 #include "moc_completiontextedit.cpp"