本稿では以下の実装を行う。
QLineEdit には returnPressed() シグナルはあるのだが、escPressed() シグナルが無い。 Esc が押されたことを検知するには QLineEdit のサブクラスを定義して、keyEvent() をリインプリメントしてもいいのだが、 イベントフィルターを使った方がクラスを新たに宣言・定義する必要が無くお手軽だ。
1: class MainWindow : public QMainWindow
2: {
3: .....
4: protected:
5: bool eventFilter(QObject *obj, QEvent *event);
6: };
1: MainWindow::MainWindow(QWidget *parent, Qt::WFlags flags)
2: : QMainWindow(parent, flags)
3: {
4: .....
5: statusBar()->addWidget(m_cmdLineEdit = new QLineEdit(), 1);
6: m_cmdLineEdit->installEventFilter(this);
7: .....
8: }
9: bool MainWindow::eventFilter(QObject *obj, QEvent *event)
10: {
11: if( obj == m_cmdLineEdit && event->type() == QEvent::KeyPress &&
12: m_viEngine->mode() == CMDLINE )
13: {
14: QKeyEvent *keyEvent = static_cast(event);
15: if( keyEvent->key() == Qt::Key_Escape ) {
16: m_viEngine->setMode(CMD);
17: return true;
18: }
19: }
20: return false;
21: }
以上で Esc でキャンセルする機能の実装は終わりだ。追加行数も少なく、なんと簡単ではなイカ。
Home の動作を変更するので、Esc 同様にイベントフィルターで Home が押されたことを検出して、 :(コロン)直後にカーソルを設定してもいいのだが、もっと簡単な方法がある。
それは QLineEdit::cursorPositionChanged(int, int) シグナルを処理する方法だ。
下記の様に、シグナルを処理スロットにコネクトし、カーソルが先頭に移動した場合は:(コロン)直後にカーソルを再設定してあげる。
実は←カーソルキーでも:(コロン)直後までしか移動出来ないようにする必要があったのだが、この方法であればその対処も自動的に行われてしまう。
さらに、マウスでカーソルを設定した場合にもちゃんとうまくいく。
最小限のコードでいくつもの問題対処ができた。理想的である。
1: MainWindow::MainWindow(QWidget *parent, Qt::WFlags flags)
2: : QMainWindow(parent, flags)
3: {
4: .....
5: statusBar()->addWidget(m_cmdLineEdit = new QLineEdit(), 1);
6: connect(m_cmdLineEdit, SIGNAL(cursorPositionChanged(int, int)),
7: this, SLOT(cmdLineCursorPositionChanged(int, int)));
8: .....
9: }
10: void MainWindow::cmdLineCursorPositionChanged(int oldPos, int newPos)
11: {
12: if( newPos == 0 )
13: m_cmdLineEdit->setCursorPosition(1);
14: }
BackSpace により行頭の:(コロン)が削除されたら ex モードをキャンセルする処理も簡単だ。
QLineEdit::textChanged(int, int) シグナルを受けて、行頭の文字が:(コロン)でなくなったら、CMDモードに遷移するとよい。
コードは以下のようになる。
1: MainWindow::MainWindow(QWidget *parent, Qt::WFlags flags)
2: : QMainWindow(parent, flags)
3: {
4: .....
5: statusBar()->addWidget(m_cmdLineEdit = new QLineEdit(), 1);
6: connect(m_cmdLineEdit, SIGNAL(textChanged(const QString &)),
7: this, SLOT(cmdLineTextChanged(const QString &)));
8: .....
9: }
10: void MainWindow::cmdLineTextChanged(const QString & text)
11: {
12: if( text.isEmpty() || text[0] != ':' )
13: m_viEngine->setMode(CMD);
14: }
本稿のソースコードは以下からDLできます。※ VS2008 でプロジェクトを作成しています。
http://vivi.dyndns.org/dist2/qvi-006-src.zip
本稿ではイベントフィルターを用いて QLineEdit の機能を変更したが、同様のことを QLineEdit の派生クラスを宣言・実装することで実現しなさい。
そのコードと本稿のコードとを比較し、それぞれの方法の長短所を述べなさい。