前へ 次へ
技術文章qvi

exモード

本稿では :(コロン)で ex-mode に遷移できるようにしてみる。 単に遷移できるだけだ。実用的なコマンド実装はまだまだ先の予定。急いては事を仕損じる。

まずはモードを表す enum Mode に新入りを追加する。

 1: enum Mode {
 2:     CMD = 0,
 3:     INSERT,
 4:     CMDLINE,
 5: };

vi コマンド解析部分で、':' を認識して、モードを変更する。

 1: bool ViEngine::cmdModeKeyPressEvent(QKeyEvent *event)
 2: {
 3:     .....
 4:     switch( ch ) {
 5:     case ':':
 6:         setMode(CMDLINE);   //  : が押されたらexモードへ
 7:         break;
 8:     .....
 9: }

ViEngine では上記のようにモードを変更するだけだ。それによって何が起こるかはいっさい感知しない。
どこかに ':' が表示され、ex コマンドを入力・編集できるようになり、Enter が押されると確定したコマンド文字列が送られてくる。 その詳細にいっさい依存しないのだ。だから後で ex コマンド編集部分の実装を変更しても、ViEngine の方はいっさい修正する必要がない。
依存性除去の重要性がいまいちピンと来ない人もいるかもしれないが、 プロジェクトが大きくなればなるほど依存性というものはやっかいなもので、依存性が少なければ少ないほど開発効率が向上するものなのだ。

ex モードに遷移した時は、ステータスバーに QLineEdit を表示し、そこで文字入力・編集が可能なようにする。
QLineEdit へのポインタを MainWindow のメンバ変数とし、コンストラクタで生成し、 ステータスバーに statusBar()->addWidget() で配置する。非常に簡単だ。

ちなみに MFC で開発した ViVi でも、同様にステータスバーにエディットコントロールを配置する作業を行ったことがあるが、 かなりの時間を要し、非常に苦労した覚えがある。
それは、コントロールをステータスバーに配置することをMFCがあまり考慮していないからだ。 どんなフレームワークでもそうだが、フレームワークが想定している範囲内のことは簡単にできるが、 範囲外のことをやろうとすると急に難易度が上がる。
そのこと自体はQtも例外ではないのだが、Qtは非常に対称性が高いので、やりたいことがそのまま出来ることが多い。 非常に柔軟性の高いフレームワークと言える。

 1: class MainWindow : public QMainWindow
 2: {
 3:     .....
 4: public slots:
 5:     void    cmdLineReturnPressed();
 6: private:
 7:     QLineEdit   *m_cmdLineEdit;
 8: };
 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(returnPressed()), this, SLOT(cmdLineReturnPressed()));
 7:     onModeChanged(m_viEngine->mode());
 8: }

モードが遷移すると、MainWindow::onModeChanged() スロットがコールされるので、コマンドラインを表示し、フォーカスを設定する。

 1: void MainWindow::onModeChanged(Mode mode)
 2: {
 3:     QString text;
 4:     switch( mode ) {
 5:     case CMD:
 6:         text = "CMD";
 7:         break;
 8:     case INSERT:
 9:         text = "INSERT";
10:         break;
11:     case CMDLINE:
12:         m_cmdLineEdit->setText(":");
13:         m_cmdLineEdit->show();
14:         m_cmdLineEdit->setFocus(Qt::OtherFocusReason);
15:         return;
16:     }
17:     m_cmdLineEdit->hide();
18:     statusBar()->showMessage(text);
19: }

以上で、コマンドモードの時に ':' を押すと、下図の様にステータスバーにコマンドラインが表示され、編集可能になる。

次にコマンドラインで Enter が押された場合は、入力された文字列を ViEngine に渡して処理させるようにする。
QLineEdit には returnPressed() シグナルがあるので、それを MainWindow::cmdLineReturnPressed() にコネクトしておき、 その中で入力テキストを取得して、ViEngine::processExCommand() をコールする。

 1: void MainWindow::cmdLineReturnPressed()
 2: {
 3:     QString text = m_cmdLineEdit->text();
 4:     if( !text.isEmpty() && text[0] == ':' )
 5:         m_viEngine->processExCommand(text.mid(1));
 6: }

processExCommand() でコマンド処理を行うのだが、とりあえずは単にモードを CMD に戻すだけにしておく。

 1: void ViEngine::processExCommand(const QString &text)
 2: {
 3:     setMode(CMD);
 4: }

以上で、ex モード処理の枠組みは出来上がった。 Esc でキャンセルや、先頭の : を消した場合の処理など、細かいことは色々あるが、それは次稿で実装することにする。

まとめ

本稿のソースコードは以下からDLできます。※ VS2008 でプロジェクトを作成しています。

    http://vivi.dyndns.org/dist2/qvi-005.zip

Tweet


前へ 次へ
技術文章qvi