チュートリアル② ~ウィンドウの描画~

概要

前節では、コールバックからデバッグ領域に表示するといったものでした。

これだけの単純な仕組みであっても 自分なりの制作物を展開していくことが出来ることでしょう。

単純なウィンドウ表示

#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ファイルを読み込んで、何か入力補完を出し、単語を選択してみましょう。
以下のように、ねずみ色のウィンドウが表示されたでしょうか。

大切なことはこのウィンドウが、

  • 秀丸より前方に描画されているが、アクティブではない。
  • マウスで選択しても、アクティブにはならない

ということです。

PICTURE

解説

全体としては、.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)が位置や大きさを決めている部分です。
単語補完ウィンドウが大きい人は、このフォームも大きくなり、小さい人は小さくなるということです。