ウィンドウの形状を変更してみる


今回は、ウィンドウの形を長方形以外にする方法を説明しましょう。

ウィンドウの形は、楕円形や多角形などにすることができます。

リージョンオブジェクト

ウィンドウの形状を設定するためには、まず、設定したい形のリージョンオブジェクトを作成する必要があります。リージョンオブジェクトとは、Windowsが提供するGDIオブジェクトの1つで、画面イメージの再描画やクリッピングなどを行なうときに、実際に描画や塗りつぶしなどを行なう「領域(region:リージョン)」を定義しているオブジェクトのことです。矩形(長方形)や多角形、楕円などの図形をいくつも組み合わせた複雑な領域をもつリージョンを作成することもできます。

リージョンオブジェクトは、作成するリージョンの形状に応じたAPI関数を呼び出すことによって作成することができます。

長方形リージョンの作成

矩形(長方形)のリージョンオブジェクトを作成するには、 CreateRectRgn 関数を呼び出します。

HRGN CreateRectRgn(
    int nLeftRect,      // 左上x座標
    int nTopRect,       // 左上y座標
    int nRightRect,     // 右下x座標
    int nBottomRect     // 右下y座標
);

この関数には、それぞれ長方形の左上と右下のx座標・y座標を指定します。すべてのパラメータに 0 を指定すると、空の(領域を持たない)リージョンオブジェクトが作成されます。

リージョンオブジェクトが作成されると、この関数は戻り値として作成されたリージョンオブジェクトのハンドルを返します。以降、このリージョンに対して操作を行なうためには、このハンドルを指定することになります。

丸みのある長方形リージョンの作成

丸みのある長方形のリージョンオブジェクトを作成するには、 CreateRoundRectRgn 関数を呼び出します。

HRGN CreateRoundRectRgn(
    int nLeftRect,      // 左上x座標
    int nTopRect,       // 左上y座標
    int nRightRect,     // 右下x座標
    int nBottomRect,    // 右下y座標
    int nWidthEllipse,  // 丸みの幅
    int nHeightEllipse  // 丸みの高さ
);

始めの4つのパラメータは、長方形リージョンの場合と同じです。それぞれ、長方形の左上と右下のx座標・y座標を指定します。後の2つ(nWidthEllipse パラメータと nHeightEllipse パラメータ)には、角を丸めるときの丸みの幅と高さを指定します。

長方形リージョンの場合と同様に、この関数も、戻り値として作成されたリージョンオブジェクトのハンドルを返します。

楕円形リージョンの作成

楕円のリージョンオブジェクトを作成するには、 CreateEllipticRgn 関数を呼び出します。

HRGN CreateEllipticRgn(
    int nLeftRect,      // 長方形の左上x座標
    int nTopRect,       // 長方形の左上y座標
    int nRightRect,     // 長方形の右下x座標
    int nBottomRect     // 長方形の右下y座標
);

この関数には、それぞれ楕円の位置とサイズを定義するための長方形の左上と右下のx座標・y座標を指定します。これらのパラメータで指定された長方形に内接するような楕円形のリージョンオブジェクトが作成されることになります。

この関数もまた、戻り値として作成されたリージョンオブジェクトのハンドルを返します。

多角形リージョンの作成

多角形のリージョンオブジェクトを作成するには、 CreatePolygonRgn 関数を呼び出します。

HRGN CreatePolygonRgn(
    CONST POINT *ppt,      // 頂点座標の配列
    int cPoints,           // 頂点の数
    int fnPolyFillMode     // 多角形充填形式
);

この関数の最初のパラメータ(ppt パラメータ)には、多角形の頂点の座標を格納した POINT 構造体の配列のアドレスを指定します。この配列は、多角形の頂点の数だけ要素数を持っている必要があります。

第2パラメータ(cPoints パラメータ)には、多角形の頂点の数を指定します。

第3パラメータ(fnPolyFillMode パラメータ)には、多角形リージョンを作成する際のフィリングモード(塗りつぶし形式)と呼ばれるものを指定します。フィリングモードには 1 (ALTERNATE) または 2 (WINDING) のいずれかを指定します。この2つのモードの違いは、実際に下のサンプルプログラムを実行させて試してみてください。

ウィンドウリージョン

本来、リージョンオブジェクトは、GDIオブジェクトの1つとして、画面への描画において描画領域や塗りつぶし領域などを定義するためのオブジェクトなのですが、このリージョンオブジェクトをウィンドウに割り当てることで、ウィンドウそのものの形状を定義することができます。このようにウィンドウの形状を定義するためにウィンドウに割り当てられたリージョンオブジェクトのことをウィンドウリージョンと呼びます。

リージョンオブジェクトをウィンドウリージョンに割り当てるには、 SetWindowRgn 関数を呼び出します。

int SetWindowRgn(
    HWND hWnd,        // ウィンドウハンドル
    HRGN hRgn,        // リージョンハンドル
    BOOL fRedraw      // 再描画フラグ
);

hWnd パラメータには、ウィンドウのハンドルを指定します。このパラメータで指定されたウィンドウに対して、新しいウィンドウリージョンが割り当てられます。

hRgn パラメータには、ウィンドウリージョンとして割り当てるリージョンオブジェクトのハンドルを指定します。このパラメータに 0 (NULL) を指定すると、それまでウィンドウに割り当てられていたウィンドウリージョンが削除されて、ウィンドウにウィンドウリージョンが割り当てられていない状態になります。

fRedraw パラメータには、ウィンドウリージョンを割り当てた後でウィンドウを再描画するかどうかを指定します。再描画する場合には 1 (TRUE) を、再描画しない場合には 0 (FALSE) を指定します。ウィンドウが表示されている場合には、再描画しないと、リージョンに割り当てられなかった領域がそのまま残ってしまうことになるので、ウィンドウが表示されているなら再描画しておきましょう。

この関数によって新しいウィンドウリージョンが割り当てられると、それまでウィンドウリージョンとして割り当てられていたリージョンオブジェクトは自動的に削除されます。

リージョンオブジェクトの削除について

リージョンオブジェクトが Windows GDI オブジェクトの1つである以上は、本来なら必要なくなったリージョンオブジェクトは DeleteObject 関数によって削除されなければならないはずです。

しかし、実際には SetWindowRgn 関数によってウィンドウリージョンとして割り当てられたリージョンオブジェクトは、不要となった時点でシステムによって削除されることになっています。そのため、ウィンドウリージョンとして割り当てられているリージョンを明示的に削除してはいけません。

サンプルスクリプト

さて、実際にスクリプトを書いてみます。

このサンプルスクリプトでは画像を読み込んでいますが、実際には画像を読み込む必要はありません。ウィンドウID 2 のウィンドウを適当な大きさに初期化しておけば十分です。ちなみに、このスクリプトで読み込んでいる画像ファイル hsp2ttl.jpg は、HSP ver2.55 のサンプルに含まれているものをそのまま使用しています。

ウィンドウにウィンドウリージョンを割り当てていない状態と、長方形のウィンドウリージョンを割り当てた状態がまったく同じように表示されていますが、ウィンドウと同じ大きさの長方形リージョンを作成しているので、それは当然のことですね。長方形リージョンをウィンドウサイズよりも小さめに作成すると、異なって表示されます。

サンプルスクリプト main.as

    #include "llmod.as"

    screen 0, 150, 200
    objsize 150, 24
    button "なし", *lb_change_winrgn
    button "長方形", *lb_change_winrgn
    button "丸みのある長方形", *lb_change_winrgn
    button "楕円形", *lb_change_winrgn
    button "多角形 (ALTERNATE)", *lb_change_winrgn
    button "多角形 (WINDING)", *lb_change_winrgn

    bgscr 2,0,0,0,200,100
    picload "hsp2ttl.jpg"

    stop

*lb_change_winrgn
    rgnmode = stat

    if rgnmode == 0 {
        ; ウィンドウリージョンなし
        hrgn = 0
    }

    if rgnmode == 1 {
        ; 長方形リージョン
        pm = 0, 0, winx, winy
        dllproc "CreateRectRgn", pm, 4, D_GDI
        hrgn = stat
    }

    if rgnmode == 2 {
        ; 丸みのある長方形リージョン
        pm = 0, 0, winx, winy, 50, 50
        dllproc "CreateRoundRectRgn", pm, 6, D_GDI
        hrgn = stat
    }

    if rgnmode == 3 {
        ; 楕円形リージョン
        pm = 0, 0, winx, winy
        dllproc "CreateEllipticRgn", pm, 4, D_GDI
        hrgn = stat
    }

    if (rgnmode == 4) | (rgnmode == 5) {
        ; 多角形リージョン
        ; POINT 構造体の配列(5個の頂点の座標)
        pt.0 = winx/2, 0
        pt.2 = winx/5, winy
        pt.4 = winx, winy/3
        pt.6 = 0, winy/3
        pt.8 = winx*4/5, winy

        getptr pm.0, pt         ; POINT 構造体の配列のアドレス
        pm.1 = 5                ; 頂点の数
        if rgnmode == 4 {
            ; Alternate(交互)モード
            pm.2 = 1            ; ALTERNATE
        } else {
            ; Winding(螺旋)モード
            pm.2 = 2            ; WINDING
        }
        dllproc "CreatePolygonRgn", pm, 3, D_GDI
        hrgn = stat
    }

    ; ウィンドウリージョンの割り当て
    gsel 2
    mref bmscr, 67
    pm = bmscr.13, hrgn, 1
    dllproc "SetWindowRgn", pm, 3, D_USER
    stop