QCompleter, QTextedit, etc.
Bonjour.
Je suis en train d’améliorer mon moteur de jeu (http://www.haiecapique.fr/marble.html)
Pour l’éditeur de script j'utilise QScintilla, mais la complétion de la forme "toto." affiche "tata" n'est pas géré.
On m'a conseillé de passer par QCompleter.
J'arrive à refaire la même chose qu'avec QScintilla, mais mon système d’auto-complétion ne fonctionne que sur un QLineEdit, pas sur un QTextEdit.
Je vous met mon code :
treemodelcompleter.h
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
#ifndef TREEMODELCOMPLETER_H
#define TREEMODELCOMPLETER_H
#include <QCompleter>
class TreeModelCompleter : public QCompleter {
Q_OBJECT
Q_PROPERTY(QString separator READ separator WRITE setSeparator)
public:
TreeModelCompleter(QObject *parent = 0);
TreeModelCompleter(QAbstractItemModel *model, QObject *parent = 0);
QString separator() const;
public slots:
void setSeparator(const QString &separator);
protected:
QStringList splitPath(const QString &path) const;
QString pathFromIndex(const QModelIndex &index) const;
private:
QString sep;
};
#endif // TREEMODELCOMPLETER_H |
treemodelcompleter.cpp
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
|
#include "treemodelcompleter.h"
#include <QStringList>
TreeModelCompleter::TreeModelCompleter(QObject *parent) :
QCompleter(parent) {
}
TreeModelCompleter::TreeModelCompleter(QAbstractItemModel *model, QObject *parent) :
QCompleter(model, parent) {
}
void TreeModelCompleter::setSeparator(const QString &separator) {
sep = separator;
}
QString TreeModelCompleter::separator() const {
return sep;
}
QStringList TreeModelCompleter::splitPath(const QString &path) const {
if (sep.isNull()) {
return QCompleter::splitPath(path);
}
return path.split(sep);
}
QString TreeModelCompleter::pathFromIndex(const QModelIndex &index) const {
if (sep.isNull()) {
return QCompleter::pathFromIndex(index);
}
// navigate up and accumulate data
QStringList dataList;
for (QModelIndex i = index; i.isValid(); i = i.parent()) {
dataList.prepend(model()->data(i, completionRole()).toString());
}
QString t = dataList.join(sep);
return dataList.join(sep);
} |
mainwindow.h
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
|
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QTextEdit>
class TreeModelCompleter;
class QAbstractItemModel;
class QLineEdit;
class MainWindow : public QTextEdit {
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
protected:
void keyPressEvent(QKeyEvent *e);
void focusInEvent(QFocusEvent *e);
private slots:
void insertCompletion(const QString &completion);
private:
QString textUnderCursor() const;
private:
QAbstractItemModel *modelFromFile(const QString& fileName);
TreeModelCompleter *completer;
};
#endif // MAINWINDOW_H |
mainwindow.cpp
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
|
#include <QtGui>
#include <QModelIndex>
#include "treemodelcompleter.h"
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent) :
QTextEdit(parent) {
completer = new TreeModelCompleter(this);
completer->setWidget(this);
completer->setModel(modelFromFile(":/resources/treemodel.txt"));
completer->setSeparator(QLatin1String("."));
completer->setCaseSensitivity(Qt::CaseInsensitive);
completer->setModelSorting(QCompleter::CaseInsensitivelySortedModel);
completer->setCompletionMode(QCompleter::PopupCompletion);
completer->setWrapAround(true);
QObject::connect(completer, SIGNAL(activated(QString)), this, SLOT(insertCompletion(QString)));
setWindowTitle(tr("Tree Model Completer"));
}
void MainWindow::insertCompletion(const QString& completion) {
if (completer->widget() != this)
return;
QTextCursor tc = textCursor();
int extra = completion.length() - completer->completionPrefix().length();
tc.movePosition(QTextCursor::Left);
tc.movePosition(QTextCursor::EndOfWord);
tc.insertText(completion.right(extra));
setTextCursor(tc);
}
QString MainWindow::textUnderCursor() const {
QTextCursor tc = textCursor();
tc.select(QTextCursor::WordUnderCursor);
QString t = tc.selectedText();
return tc.selectedText();
}
void MainWindow::focusInEvent(QFocusEvent *e) {
if (completer)
completer->setWidget(this);
QTextEdit::focusInEvent(e);
}
void MainWindow::keyPressEvent(QKeyEvent *e) {
if (completer && completer->popup()->isVisible()) {
// The following keys are forwarded by the completer to the widget
switch (e->key()) {
case Qt::Key_Enter:
case Qt::Key_Return:
case Qt::Key_Escape:
case Qt::Key_Tab:
case Qt::Key_Backtab:
e->ignore();
return; // let the completer do default behavior
default:
break;
}
}
bool isShortcut = ((e->modifiers() & Qt::ControlModifier) && e->key() == Qt::Key_E); // CTRL+E
if (!completer || !isShortcut) // do not process the shortcut when we have a completer
QTextEdit::keyPressEvent(e);
const bool ctrlOrShift = e->modifiers() & (Qt::ControlModifier | Qt::ShiftModifier);
if (!completer || (ctrlOrShift && e->text().isEmpty()))
return;
static QString eow("~!@#$%^&*()_+{}|:\"<>?,./;'[]\\-="); // end of word
bool hasModifier = (e->modifiers() != Qt::NoModifier) && !ctrlOrShift;
QString completionPrefix = textUnderCursor();
if (!isShortcut && (hasModifier || e->text().isEmpty() || eow.contains(e->text().right(1)))) {
completer->popup()->hide();
return;
}
if (completionPrefix != completer->completionPrefix()) {
completer->setCompletionPrefix(completionPrefix);
completer->popup()->setCurrentIndex(completer->completionModel()->index(0, 0));
}
QRect cr = cursorRect();
cr.setWidth(completer->popup()->sizeHintForColumn(0) + completer->popup()->verticalScrollBar()->sizeHint().width());
completer->complete(cr); // popup it up!
}
QAbstractItemModel *MainWindow::modelFromFile(const QString& fileName) {
QFile file(fileName);
if (!file.open(QFile::ReadOnly))
return new QStringListModel(completer);
#ifndef QT_NO_CURSOR
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
#endif
QStringList words;
QStandardItemModel *model = new QStandardItemModel(completer);
QVector<QStandardItem *> parents(10);
parents[0] = model->invisibleRootItem();
while (!file.atEnd()) {
QString line = file.readLine();
QString trimmedLine = line.trimmed();
if (line.isEmpty() || trimmedLine.isEmpty())
continue;
QRegExp re("^\\s+");
int nonws = re.indexIn(line);
int level = 0;
if (nonws == -1) {
level = 0;
} else {
if (line.startsWith("\t")) {
level = re.cap(0).length();
} else {
level = re.cap(0).length()/4;
}
}
if (level+1 >= parents.size())
parents.resize(parents.size()*2);
QStandardItem *item = new QStandardItem;
item->setText(trimmedLine);
parents[level]->appendRow(item);
parents[level+1] = item;
}
#ifndef QT_NO_CURSOR
QApplication::restoreOverrideCursor();
#endif
return model;
} |
treemodel.txt
Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
Parent1
Child1
GrandChild1
GrandChild2
GrandChild3
GrandGrandChild1
Child2
GrandChild1
GrandGrandChild1
GrandChild2
Child3
Parent2
Child1
GrandChild1
Child2
Child3
GrandChild1
GrandChild2 |
Si vous trouvez comment je doit faire, je serais très heureux de mettre une note sur l'auteur dans mon éditeur...