ソフトウェア開発 Win32プログラミング

ラバーバンドを実装する

戻る


[ラバーバンドのイメージ]

ラバーバンドというのは、図形やオブジェクトを拡大縮小するための枠である。 ラバーバンドには、グリップと呼ばれるつまみが存在し、グリップをドラッグすることでユーザーはラバーバンドのサイズを変更できる。 Win32でラバーバンドを実装し、ボタンを拡大縮小できるようにする方法を下記に示す。

class MRubberBand : public MWindowBase
{
public:
    HRGN m_hRgn;
    HWND m_hwndTarget;
    enum { m_nGripSize = 3 };

    MRubberBand() : m_hRgn(NULL), m_hwndTarget(NULL)
    {
    }

    BOOL CreateDx(HWND hwndParent, BOOL bVisible = FALSE,
                  INT x = 0, INT y = 0, INT cx = 0, INT cy = 0)
    {
        return CreateWindowDx(hwndParent, NULL,
            (bVisible ? WS_VISIBLE : 0) | WS_CHILD | WS_THICKFRAME,
            WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
            x, y, cx, cy);
    }

    virtual LRESULT CALLBACK
    WindowProcDx(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        switch (uMsg)
        {
            HANDLE_MSG(hwnd, WM_CREATE, OnCreate);
            HANDLE_MSG(hwnd, WM_MOVE, OnMove);
            HANDLE_MSG(hwnd, WM_SIZE, OnSize);
            HANDLE_MSG(hwnd, WM_NCCALCSIZE, OnNCCalcSize);
            HANDLE_MSG(hwnd, WM_NCPAINT, OnNCPaint);
            HANDLE_MSG(hwnd, WM_PAINT, OnPaint);
            HANDLE_MSG(hwnd, WM_DESTROY, OnDestroy);
            HANDLE_MSG(hwnd, WM_NCHITTEST, OnNCHitTest);
            HANDLE_MSG(hwnd, WM_SETCURSOR, OnSetCursor);
        default:
            return DefaultProcDx(hwnd, uMsg, wParam, lParam);
        }
        return 0;
    }

    BOOL OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct)
    {
        assert(m_hwndTarget == NULL);
        m_hwndTarget = NULL;
        return TRUE;
    }

    void GetIdealClientRect(LPRECT prc)
    {
        GetClientRect(m_hwnd, prc);
        InflateRect(prc, -2 * m_nGripSize, -2 * m_nGripSize);
    }

    void FitToBand()
    {
        if (m_hwndTarget == NULL)
            return;

        RECT rc;
        GetIdealClientRect(&rc);
        MapWindowRect(m_hwnd, GetParent(m_hwndTarget), &rc);

        MoveWindow(m_hwndTarget, rc.left, rc.top,
            rc.right - rc.left, rc.bottom - rc.top, TRUE);
        BringWindowToTop(m_hwnd);
    }

    void FitToTarget()
    {
        if (m_hwndTarget == NULL)
            return;

        RECT rc;
        GetWindowRect(m_hwndTarget, &rc);
        MapWindowRect(NULL, GetParent(m_hwnd), &rc);

        InflateRect(&rc, 2 * m_nGripSize, 2 * m_nGripSize);

        MoveWindow(m_hwnd, rc.left, rc.top,
            rc.right - rc.left, rc.bottom - rc.top, TRUE);
        BringWindowToTop(m_hwnd);
    }

    void OnDestroy(HWND hwnd)
    {
        DeleteObject(m_hRgn);
        m_hRgn = NULL;
        m_hwndTarget = NULL;
    }

    void GetRect(HWND hwnd, LPRECT prc)
    {
        GetWindowRect(hwnd, prc);
        OffsetRect(prc, -prc->left, -prc->top);
    }

    void SetRgn(HRGN hRgn, BOOL bClient = TRUE)
    {
        if (bClient && m_hwndTarget)
        {
            RECT rc;
            GetIdealClientRect(&rc);

            HRGN hClientRgn = CreateRectRgnIndirect(&rc);
            UnionRgn(hRgn, hRgn, hClientRgn);
            DeleteObject(hClientRgn);
        }

        DeleteObject(m_hRgn);
        SetWindowRgn(m_hwnd, hRgn, TRUE);
        m_hRgn = hRgn;
    }

    void OnNCPaint(HWND hwnd, HRGN hrgn)
    {
        RECT rc;
        GetRect(hwnd, &rc);

        HDC hDC = GetWindowDC(hwnd);
        HPEN hPenOld = SelectPen(hDC, GetStockPen(BLACK_PEN));
        HBRUSH hbrOld = SelectBrush(hDC, GetStockBrush(WHITE_BRUSH));
        {
            GetRgnOrDrawOrHitTest(hwnd, hDC, NULL);
        }
        SelectBrush(hDC, hbrOld);
        SelectPen(hDC, hPenOld);
        ReleaseDC(hwnd, hDC);
    }

    void OnPaint(HWND hwnd)
    {
        PAINTSTRUCT ps;
        BeginPaint(hwnd, &ps);
        EndPaint(hwnd, &ps);
    }

    UINT OnNCHitTest(HWND hwnd, int x, int y)
    {
        RECT rc;
        GetWindowRect(hwnd, &rc);
        x -= rc.left;
        y -= rc.top;

        POINT pt = { x, y };
        HRGN hRgn = GetRgnOrDrawOrHitTest(hwnd, NULL, &pt);
        return (UINT)(UINT_PTR)hRgn;
    }

    BOOL OnSetCursor(HWND hwnd, HWND hwndCursor, UINT codeHitTest, UINT msg)
    {
        switch (codeHitTest)
        {
        case HTTOPLEFT:         SetCursor(LoadCursor(NULL, IDC_SIZENWSE)); break;
        case HTLEFT:            SetCursor(LoadCursor(NULL, IDC_SIZEWE)); break;
        case HTBOTTOMLEFT:      SetCursor(LoadCursor(NULL, IDC_SIZENESW)); break;
        case HTTOP:             SetCursor(LoadCursor(NULL, IDC_SIZENS)); break;
        case HTBOTTOM:          SetCursor(LoadCursor(NULL, IDC_SIZENS)); break;
        case HTTOPRIGHT:        SetCursor(LoadCursor(NULL, IDC_SIZENESW)); break;
        case HTRIGHT:           SetCursor(LoadCursor(NULL, IDC_SIZEWE)); break;
        case HTBOTTOMRIGHT:     SetCursor(LoadCursor(NULL, IDC_SIZENWSE)); break;
        default:                SetCursor(LoadCursor(NULL, IDC_ARROW)); break;
        }
        return TRUE;
    }

    void OnMove(HWND hwnd, int x, int y)
    {
        HRGN hRgn = GetRgnOrDrawOrHitTest(hwnd);
        SetRgn(hRgn, FALSE);

        if (m_hwnd && m_hwndTarget)
        {
            FitToBand();
        }

        hRgn = GetRgnOrDrawOrHitTest(hwnd);
        SetRgn(hRgn, TRUE);
    }

    void OnSize(HWND hwnd, UINT state, int cx, int cy)
    {
        if (m_hwndTarget)
        {
            FitToBand();
        }

        HRGN hRgn = GetRgnOrDrawOrHitTest(hwnd);
        SetRgn(hRgn);
        InvalidateRect(hwnd, NULL, TRUE);
    }

    HRGN GetRgnOrDrawOrHitTest(HWND hwnd, HDC hDC = NULL, LPPOINT ppt = NULL)
    {
        RECT rc;
        GetRect(hwnd, &rc);
        INT cx = rc.right - rc.left;
        INT cy = rc.bottom - rc.top;

        INT ax[] = { m_nGripSize, cx / 2, cx - m_nGripSize };
        INT ay[] = { m_nGripSize, cy / 2, cy - m_nGripSize };
        INT ahits[] =
        {
            HTTOPLEFT,    HTTOP,    HTTOPRIGHT,
            HTLEFT,                 HTRIGHT,
            HTBOTTOMLEFT, HTBOTTOM, HTBOTTOMRIGHT
        };

        HRGN hRgn = NULL;
        if (hDC == NULL && ppt == NULL)
            hRgn = CreateRectRgn(0, 0, 0, 0);

        for (INT k = 0, n = 0; k < 3; ++k)
        {
            for (INT i = 0; i < 3; ++i)
            {
                if (i == 1 && k == 1)
                    continue;

                if (hDC)
                {
                    ::Rectangle(hDC,
                        ax[i] - m_nGripSize, ay[k] - m_nGripSize,
                        ax[i] + m_nGripSize, ay[k] + m_nGripSize);
                }
                else if (ppt)
                {
                    RECT rect;
                    SetRect(&rect,
                        ax[i] - m_nGripSize, ay[k] - m_nGripSize,
                        ax[i] + m_nGripSize, ay[k] + m_nGripSize);
                    if (PtInRect(&rect, *ppt))
                    {
                        return (HRGN)(INT_PTR)ahits[n];
                    }
                }
                else
                {
                    HRGN hRgn2 = CreateRectRgn(
                        ax[i] - m_nGripSize, ay[k] - m_nGripSize,
                        ax[i] + m_nGripSize, ay[k] + m_nGripSize);
                    UnionRgn(hRgn, hRgn, hRgn2);
                    DeleteObject(hRgn2);
                }
                ++n;
            }
        }

        if (ppt)
        {
            hRgn = (HRGN)(INT_PTR)HTCAPTION;
        }

        return hRgn;
    }

    virtual LPCTSTR GetWndClassNameDx() const
    {
        return TEXT("katahiromz's Rubber Band Class");
    }

    virtual void ModifyWndClassDx(WNDCLASSEX& wcx)
    {
        wcx.hIcon = NULL;
        wcx.hbrBackground = GetStockBrush(NULL_BRUSH);
        wcx.hIconSm = NULL;
    }

    UINT OnNCCalcSize(HWND hwnd, BOOL fCalcValidRects, NCCALCSIZE_PARAMS *lpcsp)
    {
        return 0;
    }
};

WM_NCHITTEST (OnNCHitTest) で通常の当たり判定を改変し、拡大縮小できるようにした。 WM_SETCURSOR (OnSetCursor) でマウス入力時に、カーソルを適切なものに改変する。

サイズが変更されたときに、WM_SIZE (OnSize) メッセージが届く。 ターゲットのコントロール(m_hwndTarget)があるときは、FitToBand関数でターゲットのサイズを合わせる。 CreateRectRgn(0, 0, 0, 0);は、空の領域を作成する。 ラバーバンドのグリップ用の領域を<windowsx.h>のUnionRgn関数マクロで追加する。 UnionRgnは、CombineRgn APIを呼ぶ。 SetWindowRgnで有効な領域を指定する。 指定した領域は、次のSetWindowRgnを呼ぶまで保持する。 これでグリップ以外は透過になる。

ソース: https://github.com/katahiromz/RubberBandSample


戻る

©片山博文MZ
katayama.hirofumi.mz@gmail.com

inserted by FC2 system