パソコンでプログラミングしよう ウィンドウズC++プログラミング環境の構築
1.6.3.6(15)
折り返し表示バグの対処

KTxtEditの高機能化にあたりScintillaの折り返し表示に関するバグに対処する。

ウィンドウズ実装Scintillaは大きなファイルを折り返し表示すると処理に多大な時間を要して操作を阻害する。これを完全に解決することはできないが、影響をなるべく小さくするため以下の二点に対処する。

  • 折り返し処理中にアプリケーションを終了できない。
  • 折り返し処理中はタイトルバーのファイル情報とステータスバーのテキストカーソル位置表示が更新されない。

プログラミング手順

  1. コア実装クラス(TMyCoreImpl)の修正
  2. メインウィンドウ(KTxtEditFrame)の修正
  3. リポジトリ更新とビルドテスト

コア実装クラス(TMyCoreImpl)の修正

情報表示の更新をメインウィンドウに要求するwxEVT_MY_UPDATE_INFOイベントを追加する。アプリケーション終了時にドキュメントを全消去して折り返し処理を強制終了する。

  • wxEVT_MY_UPDATE_INFOを定義する。
  • TMyCoreImpl::ImplクラスにwxEVT_MY_UPDATE_INFOイベントを発生するUpdateInfoメンバ関数を追加する。
  • Scintillaメッセージを処理するOnScintillaNotifyメンバ関数で、メッセージが選択変更の場合にUpdateInfoをコールする。
  • ファイル変更メッセージを処理するOnFileChangeメンバ関数でUpdateInfoをコールする。
  • 旧バージョンはアイドルイベントを用いて起動時の情報表示を明示する必要は無かったが、wxEVT_MY_UPDATE_INFOイベントへの変更で起動時イベントを明示しなければならずコンストラクタでUpdateInfoをコールする。
  • CanCloseメンバ関数でアプリケーション終了許可の場合にドキュメント全消去処理を加える。

TMyCoreImpl.h(修正)

...
wxDECLARE_EVENT(wxEVT_MY_UPDATE_INFO,wxCommandEvent);
class TMyCoreImpl final
{
...
};

TMyCoreImpl.cpp(修正)

...
wxDEFINE_EVENT(wxEVT_MY_UPDATE_INFO,wxCommandEvent);
class TMyCoreImpl::Impl:public TMyOptionDialog::Observer
{
private:
...
void UpdateInfo()
{
auto evt=wxCommandEvent{wxEVT_MY_UPDATE_INFO,textCtrl_->GetId()};
evt.SetEventObject(textCtrl_);
wxPostEvent(textCtrl_,evt);
}
void OnScintillaNotify(wxCommandEvent& evt)
{
if (auto* notification=static_cast<SCNotification*>(evt.GetClientData()))
{
codingAssistance_.OnScintillaNotify(notification);
if (notification->nmhdr.code==SCN_UPDATEUI&&notification->updated&SC_UPDATE_SELECTION)
{UpdateInfo();}
}
}
void OnFileChange(wxCommandEvent& evt)
{
codingAssistance_.SetProgrammingLanguage(evt.GetString());
UpdateInfo();
}
public:
Impl(wxWindow* parent)
: ...
{
Update(TMyOptionDialog::GetInstance());
UpdateInfo();
...
}
...
bool CanClose() const
{
if (ConfirmIfModified())
{
textCtrl_->SendMsg(SCI_SETREADONLY,false,0);
textCtrl_->SendMsg(SCI_CLEARALL,0,0);
return true;
}
else
{
return false;
}
}
...
};
...

メインウィンドウ(KTxtEditFrame)の修正

情報表示の更新をアイドルイベントからwxEVT_MY_UPDATE_INFOへ変更する。

  • KTxtEditFrame::ImplクラスのOnIdleメンバ関数の名称をOnTextCtrlUpdateInfoに変更し、仮引数型をwxIdleEvent&からwxCommandEvent&に変更する。
  • コンストラクタのwxEVT_IDLEイベント登録をwxEVT_MY_UPDATE_INFOに変更し、ハンドラをOnTextCtrlUpdateInfoメンバ関数に変更する。

KTxtEditFrame.cpp(修正)

...
class KTxtEditFrame::Impl
{
...
public:
Impl(KTxtEditFrame* form): ...
{
...
evtHandlerManager_.Add(wxEVT_MY_UPDATE_INFO,&Impl::OnTextCtrlUpdateInfo,this);
evtHandlerManager_.Add(wxEVT_CLOSE_WINDOW,&Impl::OnClose,this);
...
}
private:
...
void OnTextCtrlUpdateInfo(wxCommandEvent& event)
{
form_->SetTitle(coreImpl_.GetTitleString());
statusBar_->SetStatusText(coreImpl_.GetStatusString(),1);
menuItemManager_.CallEnablers();
event.Skip();
}
void OnClose(wxCloseEvent& event)
{
...
}
};
...

リポジトリ更新とビルドテスト

Git for Windowsリポジトリを更新する。ビルド実行して動作を確認する。大きなテキストファイルを読み込んでも情報表示の更新が行われ、アプリケーションも終了できることを確認する。残念ながら一つのバグが残り、折り返し処理中にファイル末までスクロールしてマウスでテキストカーソルを置く事はできるが、そこから矢印キーで移動するとハング状態に陥る。

UMLクラス図

TMyCoreImpl::ImplからKTxtEditFrame::Implへ情報表示の更新を要求するメッセージを追加する。