最終更新日 2024-09-25
チュートリアル② ~ウィンドウの描画~
概要
前節では、コールバックからデバッグ領域に表示するといったものでした。
これだけの単純な仕組みであっても 自分なりの制作物を展開していくことが出来ることでしょう。
単純なウィンドウ表示
#include <windows.h>
using namespace System;
using namespace System::Drawing;
using namespace System::Windows::Forms;
public ref class ACHelpForm : public Form
{
public:
static ACHelpForm^ f; // 自分自身の置き場
public:
ACHelpForm()
{
SetFormAttr();
// 最初は非表示
this->Visible = false;
}
public:
// 入力補完の候補リストのどれかを選択している時に呼ばれる。
void Update(HWND hWnd, int iListBoxSelectedIndex, String^ strListBoxSelectedItem, int iItemHeight)
{
RECT rect;
GetWindowRect(hWnd, &rect);
this->Left = rect.right + 24;
this->Top = rect.top;
this->Width = (rect.right - rect.left) * 2;
this->Height = 160;
this->Show();
}
protected:
// 独立したアプリケーショではなく、子窓のように見せるための工夫
void SetFormAttr()
{
//タイトルバーを消す。秀丸の中のウィンドウかのように振る舞うための工夫。
this->ControlBox = false;
this->Text = "";
this->FormBorderStyle = ::FormBorderStyle::None;
this->BackColor = ::Color::Gray;
}
// このフォームが表示された際にアクティブにならないようにする
property bool ShowWithoutActivation
{
virtual bool get() override
{
return true;
}
}
// このフォームがマウスクリックされた際にアクティブにならないようにする。
virtual void WndProc(Message %m) override
{
if (m.Msg == WM_MOUSEACTIVATE)
{
m.Result = (IntPtr)MA_NOACTIVATE;
return;
}
Form::WndProc(m);
}
};
extern "C" __declspec(dllexport) int OnCreate(HWND hWnd, LPCTSTR szFileName) {
// ウィンドウの作成
if (ACHelpForm::f == nullptr || ACHelpForm::f->IsDisposed) {
ACHelpForm::f = gcnew ACHelpForm();
}
return TRUE;
}
extern "C" __declspec(dllexport) int OnListBoxSelectedIndexChanged(HWND hWnd, int iListBoxSelectedIndex, LPCTSTR szListBoxSelectedItem, int iItemHeight) {
// そのまま伝達
ACHelpForm::f->Update(hWnd, iListBoxSelectedIndex, gcnew String(szListBoxSelectedItem), iItemHeight);
return TRUE;
}
extern "C" __declspec(dllexport) int OnDestroy(HWND hWnd) {
// ウィンドウの破棄
if (ACHelpForm::f) {
ACHelpForm::f->Close();
}
// このOnDestroyの後に、インスタンスが残っているのに任せるのは不安である。明示的に解放
if (ACHelpForm::f) {
OutputDebugString(L"明示解放\n");
delete ACHelpForm::f;
}
return TRUE;
}
再度コンパイル
再度コンパイルし、「HmAutoCompleteExPlug.dll」を秀丸ディレクトリにコピーしましょう。
秀丸が立ち上がりっぱなしだと、コピー出来ないので、一旦秀丸を終了させる必要があります。
再び、.plファイルを読み込んで、何か入力補完を出し、単語を選択してみましょう。
以下のように、ねずみ色のウィンドウが表示されたでしょうか。
大切なことはこのウィンドウが、
- 秀丸より前方に描画されているが、アクティブではない。
- マウスで選択しても、アクティブにはならない
ということです。

解説
全体としては、.NETのフォームアプリケーションの代表的な形そのものです。
「System.Windows.Forms.Form」から継承しているため、ボタンやラベルや描画など、
便利なものが全て極めてわかりやすい形で利用可能です。
とはいえ、おそらくあまり見慣れない記述もあるため、要点だけピックアップしましょう。
// 独立したアプリケーショではなく、子窓のように見せるための工夫
void SetFormAttr()
{
//タイトルバーを消す。秀丸の中のウィンドウかのように振る舞うための工夫。
this->ControlBox = false;
this->Text = "";
this->FormBorderStyle = ::FormBorderStyle::None;
this->BackColor = ::Color::Gray;
}
// このフォームが表示された際にアクティブにならないようにする
property bool ShowWithoutActivation
{
virtual bool get() override
{
return true;
}
}
// このフォームがマウスクリックされた際にアクティブにならないようにする。
virtual void WndProc(Message %m) override
{
if (m.Msg == WM_MOUSEACTIVATE)
{
m.Result = (IntPtr)MA_NOACTIVATE;
return;
}
Form::WndProc(m);
}
これらは、いずれも先ほどの
「フォーカスを取らない・アクティブにしない」といったことを実装するための工夫です。
// 入力補完の候補リストのどれかを選択している時に呼ばれる。
void Update(HWND hWnd, int iListBoxSelectedIndex, String^ strListBoxSelectedItem, int iItemHeight)
{
RECT rect;
GetWindowRect(hWnd, &rect);
this->Left = rect.right + 24;
this->Top = rect.top;
this->Width = (rect.right - rect.left) * 2;
this->Height = 160;
this->Show();
}
この部分は入力補完窓の位置に合わせて、自分自身(Form)が位置や大きさを決めている部分です。
単語補完ウィンドウが大きい人は、このフォームも大きくなり、小さい人は小さくなるということです。