本稿では以下の実装を行う。
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 の派生クラスを宣言・実装することで実現しなさい。
そのコードと本稿のコードとを比較し、それぞれの方法の長短所を述べなさい。