004-01  前へ←  ホームへ  →次へ  004-03

No.004 - 02  ドッキング・ツールバーの実装 D&D編(矩形描画付き)


自前ドッキング・ツールバーの実装において最大のネックはドラッグ&ドロップだと思います。
今回はそのあたりを考察していこうと思います。

クラス階層(MFC)
CWnd
┣ CFrameWnd
┣ CToolBarCtrl
┗ CControlBar
   ┣ CDockBar
   ┗ CToolBar

ドッキング時 フローティング時

・MFCドッキングツールバーにおけるドラッグ&ドロップ
MFCはドッキングの準備としてメインウィンドウ生成時に4つのCDockBarクラスを生成します。
CDockBarはツールバーなどのドッキング先となります。
ドッキング時、ツールバーはCDockBarの子ウィンドウになります。

ドラッグ&ドロップ」はツールバーの持つCDockContextクラスで制御されます。
マウスのボタン押下を検知するとCDockContextクラスのメンバ関数StartDrag()が呼ばれる仕組みです。

フローティング・バーを移動させたときのドッキングする・しないのMFCでの判定は
ツールバーのウィンドウサイズから計算したRECT範囲にCDockBarがはいっているかどうかです。
CDockBarは何もドッキングされてないとき、そのCDockBarウィンドウサイズは0X0だがその場合は最小1X1に補正して計算しています。

・なぜ自前ドッキングツールバー?
実際問題、MFCの仕様はかなり複雑に、面倒に作ってあります。
C++はカスタマイズし易い言語のはずですが、MFCはひどくカスタマイズしにくいですC++の仕様が台無しです。
なめてんのかマイクロソフト。
しかもEXCELのドッキングツールバーはMFCのそれと全然違ったりします。社内の連携ゼロ?

というわけでMFCへの不満を解消しながら独自の使いやすいドッキングツールバーを作ろうと思います。
今回もととりあえずSDKのままサンプルを書きました。(いきなりクラス化するとごわかりにくくなるから)

・自前ドッキングツールバーの構想からSDK簡易実装
MFCではツールバー本体「グリッパー(つまみの部分)」を描画し、マウス操作をフックしています。
しかしそれではツールバーに送られるメッセージとこちらで制御すべき処理を分ける作業が必要になります。
さすがにそれは面倒、というか複雑化の原因なので、以下のサンプルではツールバーをそのままで操作せず、まわりにフレームウィンドウをつけています。
このフレームウィンドウの親を変更することでドッキング・フローティングの切り替えをします。

今回MFCのCDockBarにあたるウィンドウは生成していません。
描画に関してはCDockBarは背景処理だけなのでツールバーのフレームウィンドウにて簡易処理しています。
ドッキング時の範囲判定はメインウィンドウのクライアント領域 最上部1行X1ピクセルと
フローティングバーが重なったかどうかにした(MFCの判定はいまひとつわかりにくいので変えた)。




ドッキング時 フローティング時

このソースではドラッグ・ドロップの検知にWM_NCHITTESTHTCAPTIONを返しドラッグさせて〜WM_MOVEで移動完了処理をしています。
WM_MOVEで処理をしているのでSetCapture()系の処理がいらず、簡易なコーディングで済みます。
ただし後々、ドラッグ中の処理をしたいときには向きません(WM_MOVING中のフックがほとんどできないため)。
(WM_LBUTTONDOWN〜 版のソースは下の方にあります)。

docking_test_02_1.c


#define _WIN32_IE 0x0501
#include <windows.h>
#include <commctrl.h>

#define IDM_DOCK_FLOAT          0x2000
#define IDM_DOCK                0x2001
#define IDM_FLOAT               0x2002
#define IDM_RECALC_LAYOUT       0x2003

TBBUTTON tool_bar_buttons[] = {
    { STD_FILENEW, 1, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0 },
    { STD_FILEOPEN, 2, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0 },
    { STD_FILESAVE, 3, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0 },
    { 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0 },
    { STD_COPY, 4, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0 },
    { STD_CUT, 5, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0 },
    { STD_DELETE, 6, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0 },
    { 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0 },
    { VIEW_NEWFOLDER, 7, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0 },
    { VIEW_PARENTFOLDER, 8, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0 },
};

// ドッキング・ツールバー統括・構造体
typedef struct {
    HWND main_frame, popup_frame, htool_bar, htool_bar_frame, hview;
    int now_nc_activing;
    int floating_flag;
    int now_dragging;
} DOCK_STRUCT;

// ビューウィンドウ・プロシージャ
LRESULT CALLBACK ChildViewProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
    HDC hdc;
    PAINTSTRUCT ps;
    char str[]="ChildViewウィンドウ(座標0,0)";

    switch (msg) {
    case WM_LBUTTONDOWN:
        SendMessage(GetParent(hwnd), WM_LBUTTONDOWN, wp, lp);
        break;
    case WM_PAINT:
        hdc = BeginPaint(hwnd , &ps);
        TextOut(hdc, 0, 0, str, lstrlen(str));
        EndPaint(hwnd , &ps);
        break;
    }
    return DefWindowProc(hwnd, msg, wp, lp);
}
// ポップアップウィンドウ・プロシージャ
LRESULT CALLBACK PopupProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
    static DOCK_STRUCT *p_dock_struct=NULL;
    RECT popup_rc, main_rc;
    switch (msg) {
    case WM_CREATE:
        p_dock_struct=(DOCK_STRUCT*)((LPCREATESTRUCT)(lp))->lpCreateParams;
        break;
    case WM_MOVE:
        GetClientRect(p_dock_struct->main_frame, &main_rc);
        GetWindowRect(hwnd, &popup_rc);
        MapWindowPoints(NULL, p_dock_struct->main_frame, (LPPOINT)&popup_rc, 2);
        if (popup_rc.left<main_rc.right && popup_rc.right>main_rc.left && popup_rc.bottom>0 && popup_rc.top<1)
                SendMessage(p_dock_struct->main_frame, WM_COMMAND, IDM_DOCK, 0);
        break;
    case WM_NCACTIVATE: // タイトルバーのアクティブ化・非アクティブ化
        if (p_dock_struct->now_nc_activing) break;
        p_dock_struct->now_nc_activing=1;
        SendMessage(GetWindow(hwnd, GW_OWNER), WM_NCACTIVATE, wp, 0);
        p_dock_struct->now_nc_activing=0;
        break;
    }
    return DefWindowProc(hwnd, msg, wp, lp);
}
// ツールバーのフレームウィンドウ・プロシージャ
LRESULT CALLBACK ToolBarFrameProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
    static DOCK_STRUCT *p_dock_struct=NULL;
    RECT rc, rc2;
    HDC hdc;
    POINT po;
    PAINTSTRUCT ps;
    switch (msg) {
    case WM_CREATE:
        p_dock_struct=(DOCK_STRUCT*)((LPCREATESTRUCT)(lp))->lpCreateParams;

        InitCommonControls();
        p_dock_struct->htool_bar = CreateToolbarEx( hwnd, WS_CHILD | WS_VISIBLE | TBSTYLE_AUTOSIZE, 0, 6, 
            (HINSTANCE)HINST_COMMCTRL, IDB_STD_SMALL_COLOR, tool_bar_buttons, 7, 0, 0, 0, 0, sizeof (TBBUTTON)
        );
        GetWindowRect(p_dock_struct->htool_bar, &rc);
        SetWindowPos(hwnd, NULL, 0,0, rc.right-rc.left, rc.bottom-rc.top, SWP_NOMOVE);
        break;
    case WM_PAINT:
        hdc = BeginPaint(hwnd , &ps);
        GetClientRect(hwnd, &rc);

        rc.left=0; rc.top=0; rc.bottom=1;   FillRect(hdc, &rc, GetSysColorBrush(COLOR_BTNSHADOW));
        rc.top++;   rc.bottom++;            FillRect(hdc, &rc, GetSysColorBrush(COLOR_BTNHILIGHT));

        if (GetParent(hwnd)!=p_dock_struct->popup_frame){
            // グリッパー描画
            GetClientRect(hwnd, &rc);
            rc.left+=2; rc.right=rc.left+3;
            rc.top+=1;  rc.bottom-=2;

            rc2.left=rc.left;       rc2.top=rc.top; 
            rc2.right=rc.right-1;   rc2.bottom=rc.top+1;    FillRect(hdc, &rc2, GetSysColorBrush(COLOR_BTNHILIGHT));
            rc2.right=rc.left+1;    rc2.bottom=rc.bottom-1; FillRect(hdc, &rc2, GetSysColorBrush(COLOR_BTNHILIGHT));

            rc2.left=rc.right;      rc2.top=rc.top;
            rc2.right=rc.right-1;   rc2.bottom=rc.bottom;   FillRect(hdc, &rc2, GetSysColorBrush(COLOR_BTNSHADOW));
            rc2.left=rc.left;       rc2.top=rc.bottom;
            rc2.right=rc.right;     rc2.bottom=rc.bottom-1; FillRect(hdc, &rc2, GetSysColorBrush(COLOR_BTNSHADOW));
        }
        EndPaint(hwnd , &ps);
        break;
    case WM_MOVE:
        if (!p_dock_struct->now_dragging) break;
        GetWindowRect(hwnd, &rc);
        po.x=LOWORD(lp);
        po.y=HIWORD(lp);
        if (po.x<=(rc.right-rc.left) && po.x>=0 && po.y<=(rc.bottom-rc.top) && po.y>=0)
            SetWindowPos(hwnd, NULL, 0,0, 0,0, SWP_NOSIZE);
        else if (GetParent(hwnd)!=p_dock_struct->popup_frame) {
            ClientToScreen(GetParent(hwnd), &po);
            SetWindowPos(hwnd, NULL, 0,0, 0,0, SWP_NOSIZE);
            SetWindowPos(p_dock_struct->popup_frame, NULL, po.x, po.y, 0,0, SWP_NOSIZE);
            SendMessage(p_dock_struct->main_frame, WM_COMMAND, IDM_FLOAT, 0);
        }
        break;
    case WM_SIZE:
        SendMessage(p_dock_struct->htool_bar, WM_SIZE, wp, lp); // ツーバーのリサイズ
        if (!p_dock_struct->floating_flag) SetWindowPos(p_dock_struct->htool_bar, NULL, 10,0, LOWORD(lp),HIWORD(lp), 0);
        break;
    case WM_NCLBUTTONDOWN:
        p_dock_struct->now_dragging=1;
        break;
    case WM_NCLBUTTONUP:
        p_dock_struct->now_dragging=0;
        break;
    case WM_NCHITTEST:
        return HTCAPTION;
    }
    return DefWindowProc(hwnd, msg, wp, lp);
}

// メインウィンドウ・プロシージャ
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
    RECT rc, rc2;
    static DOCK_STRUCT *p_dock_struct=NULL;
    SIZE tool_size, popup_size;

    switch (msg) {
    case WM_CREATE:
        p_dock_struct=(DOCK_STRUCT*)((LPCREATESTRUCT)(lp))->lpCreateParams;

        p_dock_struct->hview= CreateWindowEx(
            WS_EX_TOOLWINDOW, TEXT("child_view"), NULL, WS_CHILD | WS_VISIBLE,
            0, 0, 200, 100, hwnd, NULL, ((LPCREATESTRUCT)(lp))->hInstance, NULL
        );
        p_dock_struct->popup_frame= CreateWindowEx(
            WS_EX_TOOLWINDOW, TEXT("popup_frame"), NULL, WS_OVERLAPPEDWINDOW | WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
            300, 170, 200, 100, hwnd, NULL, ((LPCREATESTRUCT)(lp))->hInstance, (LPVOID)p_dock_struct
        );
        p_dock_struct->htool_bar_frame= CreateWindow(
            TEXT("tool_bar_frame"), NULL, WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
            0, 0, 200, 100, hwnd, NULL, ((LPCREATESTRUCT)(lp))->hInstance, (LPVOID)p_dock_struct
        );
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    case WM_NCACTIVATE: // タイトルバーのアクティブ化・非アクティブ化
        if (p_dock_struct->now_nc_activing) break;
        p_dock_struct->now_nc_activing=1;
        SendMessage(p_dock_struct->popup_frame, WM_NCACTIVATE, wp, 0);
        p_dock_struct->now_nc_activing=0;
        break;
    case WM_COMMAND:
        switch(LOWORD(wp)){
        case IDM_DOCK_FLOAT:
            // ドッキング・フローティング切り替え
            if (p_dock_struct->floating_flag) SendMessage(hwnd, WM_COMMAND, IDM_DOCK, 0);
            else                              SendMessage(hwnd, WM_COMMAND, IDM_FLOAT, 0);
            break;
        case IDM_FLOAT:
            // フローティング
            SetParent(p_dock_struct->htool_bar_frame, p_dock_struct->popup_frame);
            p_dock_struct->floating_flag = 1;
            ShowWindow(p_dock_struct->popup_frame, SW_SHOW);
            SendMessage(hwnd, WM_COMMAND, IDM_RECALC_LAYOUT, 0);
            break;
        case IDM_DOCK:
            // ドッキング
            SetParent(p_dock_struct->htool_bar_frame, hwnd);
            p_dock_struct->floating_flag = 0;
            ShowWindow(p_dock_struct->popup_frame, SW_HIDE);
            SendMessage(hwnd, WM_COMMAND, IDM_RECALC_LAYOUT, 0);
            break;
        case IDM_RECALC_LAYOUT:
            // 子ウィンドウ位置の再計算
            SendMessage(p_dock_struct->htool_bar, TB_GETMAXSIZE, 0, (LPARAM)&tool_size);        // ツールバー・サイズ取得
            GetWindowRect(p_dock_struct->htool_bar, &rc);
            tool_size.cy=rc.bottom-rc.top;

            // ビューウィンドウ位置サイズ
            GetClientRect(hwnd, &rc);
            if (!p_dock_struct->floating_flag) rc.top =tool_size.cy;
            SetWindowPos(p_dock_struct->hview, NULL, rc.left, rc.top, rc.right, rc.bottom-rc.top, 0);

            if(p_dock_struct->floating_flag){
                // ポップアップ・ウィンドウのウィンドウサイズ計算
                GetClientRect(p_dock_struct->popup_frame, &rc);
                GetWindowRect(p_dock_struct->popup_frame, &rc2);
                popup_size.cx=(rc2.right-rc2.left)-rc.right;    // ウィンドウサイズとクライアント領域の差  ヨコ幅
                popup_size.cy=(rc2.bottom-rc2.top)-rc.bottom;   //        〃                   タテ幅

                popup_size.cx+=tool_size.cx;
                popup_size.cy+=tool_size.cy;
                SetWindowPos(p_dock_struct->popup_frame, NULL, 0, 0, popup_size.cx, popup_size.cy, SWP_NOMOVE);
            }

            // ツールバー横サイズ補正
            GetClientRect(GetParent(p_dock_struct->htool_bar_frame), &rc);
            SetWindowPos(p_dock_struct->htool_bar_frame, NULL, 0, 0, rc.right, tool_size.cy, 0);

            break;
        }
        break;
    case WM_SIZE:
        SendMessage(hwnd, WM_COMMAND, IDM_RECALC_LAYOUT, 0);
        break;
    case WM_LBUTTONDOWN:
        SendMessage(hwnd, WM_COMMAND, IDM_DOCK_FLOAT, 0);
        break;
    }
    return DefWindowProc(hwnd, msg, wp, lp);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, int nCmdShow ) {
    MSG msg;
    WNDCLASS winc;
    DOCK_STRUCT dock_struct;    // ウィンドウ・ハンドルの一括化 構造体

    winc.style              = CS_HREDRAW | CS_VREDRAW;
    winc.lpfnWndProc        = WndProc;
    winc.cbClsExtra = winc.cbWndExtra       = 0;
    winc.hInstance          = hInstance;
    winc.hIcon              = LoadIcon(NULL, IDI_APPLICATION);
    winc.hCursor            = LoadCursor(NULL, IDC_ARROW);
    winc.hbrBackground      = (HBRUSH)GetStockObject(WHITE_BRUSH);
    winc.lpszMenuName       = NULL;
    winc.lpszClassName      = TEXT("main_frame");

    if (!RegisterClass(&winc)) return -1;   // メイン・ウィンドウ

    winc.hbrBackground      = (HBRUSH)GetStockObject(WHITE_BRUSH);
    winc.lpfnWndProc        = ChildViewProc;
    winc.lpszClassName      = TEXT("child_view");
    if (!RegisterClass(&winc)) return -1;   // ビューウィンドウ

    winc.hbrBackground      = (HBRUSH)(COLOR_BTNFACE+1);
    winc.lpfnWndProc        = PopupProc;
    winc.lpszClassName      = TEXT("popup_frame");
    if (!RegisterClass(&winc)) return -1;   // フローティング・フレーム・ウィンドウ

    winc.hbrBackground      = (HBRUSH)(COLOR_BTNFACE+1);
    winc.lpfnWndProc        = ToolBarFrameProc;
    winc.lpszClassName      = TEXT("tool_bar_frame");
    if (!RegisterClass(&winc)) return -1;   // ツールバーの外枠ウィンドウ

    dock_struct.now_nc_activing=0;
    dock_struct.floating_flag=0;
    dock_struct.now_dragging=0;

    dock_struct.main_frame = CreateWindow(
        TEXT("main_frame"), TEXT("ドッキング・ツールバーのテスト"),
        WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN,
        100, 100, 240, 200, NULL, NULL, hInstance, (LPVOID)&dock_struct
    );
    if (dock_struct.main_frame == NULL) return -1;

    while(GetMessage(&msg, NULL, 0, 0)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
    }
    return msg.wParam;
}







ドッキング時 フローティング時
このソースではドラッグ・ドロップの検知に
WM_LBUTTONDOWN
ドラッグ開始〜WM_LBUTTONDOWNドラッグ終了をしています。
ドラッグ中(WM_MOUSEMOVE)の矩形描画などで多少、複雑なコーディングになっています(ポップアップフレームもWM_LBUTTONDOWN〜の処理)。

ドラッグ中に矩形を描画する
ドラッグ中に矩形を描画するため、MFCCDockContextクラスにあたる構造体DOCK_CONTEXTを新定義しています。
(コンテキストって便利な言葉だなあ)
ドラッグ開始にデスクトップDCをロックし、ドラッグ終了時に解放する。

ドラッグ中の矩形描画にはMFC同様デスクトップDCをロックしてPatBlt関数反転描画しています。
ドラッグ開始にデスクトップDCをロックし、ドラッグ終了時に解放します。
なぜ反転描画かというと、塗りつぶしてしまった場合では再描画が必要なんですが、
再描画要求を送信してもデスクトップDCロック中のため再描画されず、その再描画分のメモリはこちら側で保持しないといけなくなってしまうからです。
反転描画
なら再び反転してやれば元の色に戻るので処理速度、メモリ効率 ともに申し分ないのです。
ドラッグ中の矩形描画はハーフトーンブラシを使っています。
ハーフトーンブラシは右図のような1ピクセルのチェック模様のブラシです。
ドラッグ描画はこのパレットを軸に反転しています。
WinMainで生成しています。

参考: 0x5555  →  (2bit)  0101 0101 0101 0101


docking_test_02_2.c

#define _WIN32_IE 0x0501
#include <windows.h>
#include <commctrl.h>

#define IDM_DOCK_FLOAT          0x2000
#define IDM_DOCK                0x2001
#define IDM_FLOAT               0x2002
#define IDM_RECALC_LAYOUT       0x2003

TBBUTTON tool_bar_buttons[] = {
    { STD_FILENEW, 1, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0 },
    { STD_FILEOPEN, 2, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0 },
    { STD_FILESAVE, 3, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0 },
    { 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0 },
    { STD_COPY, 4, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0 },
    { STD_CUT, 5, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0 },
    { STD_DELETE, 6, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0 },
    { 0, 0, TBSTATE_ENABLED, TBSTYLE_SEP, 0, 0, 0 },
    { VIEW_NEWFOLDER, 7, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0 },
    { VIEW_PARENTFOLDER, 8, TBSTATE_ENABLED, TBSTYLE_BUTTON, 0, 0, 0 },
};

// ドック・コンテキスト・構造体
typedef struct {
    HDC desk_hdc;           // デスクトップDC
    HRGN apply_rgn, work_rgn, work_rgn2, last_rgn;  // リージョン
    int now_dragging;       // ドラッグ中なら==1  そうでないなら==0
    SIZE drag_size;         // ドラッグ中の矩形サイズ
    POINT cursor_offset;    // ドラッグ中の矩形位置のマウス位置との差分
    HBRUSH desk_old_brush;  // デスクトップDC・ブラシ
    HBRUSH half_tone_brush; // ハーフトーン・ブラシ
}DOCK_CONTEXT;

// ドッキング・ツールバー統括・構造体
typedef struct {
    HWND main_frame, popup_frame, htool_bar, htool_bar_frame, hview;    // 各HWND
    int now_nc_activing;    // WM_NCACTIVATEフラグ WM_NCACTIVATEの多重再送を防ぐ
    int floating_flag;      // フローティング・フラグ  浮いてれば==1  そうでないなら==0
    DOCK_CONTEXT dock_context;  // ドック・コンテキスト・構造体
} DOCK_STRUCT;

void start_drag(DOCK_CONTEXT *p_dock_context, HWND hwnd){
    HWND desk_hwnd;
    RECT rc;
    p_dock_context->now_dragging=1;
    SetCapture(hwnd);
    // デスクトップDCを得る
    desk_hwnd=GetDesktopWindow();
    LockWindowUpdate(desk_hwnd);
    p_dock_context->desk_hdc= GetDCEx(desk_hwnd, NULL, DCX_WINDOW|DCX_CACHE|DCX_LOCKWINDOWUPDATE);
    p_dock_context->desk_old_brush=(HBRUSH)SelectObject(p_dock_context->desk_hdc, p_dock_context->half_tone_brush);

    // ドラッグ・サイズとマウスとの座標差
    GetWindowRect(hwnd, &rc);
    p_dock_context->drag_size.cx = rc.right-rc.left;
    p_dock_context->drag_size.cy = rc.bottom-rc.top;

    GetCursorPos(&p_dock_context->cursor_offset);
    p_dock_context->cursor_offset.x -= rc.left;
    p_dock_context->cursor_offset.y -= rc.top;

    // リージョン生成
    p_dock_context->apply_rgn = CreateRectRgn(0, 0, 0, 0);
    p_dock_context->work_rgn  = CreateRectRgn(0, 0, 0, 0);
    p_dock_context->work_rgn2 = CreateRectRgn(0, 0, 0, 0);
    p_dock_context->last_rgn  = CreateRectRgn(0, 0, 0, 0);
}
void drag(DOCK_CONTEXT *p_dock_context){
    POINT po;
    RECT rc;
    // 最後に描画した部分を消去(再反転してるだけ)
    CombineRgn(p_dock_context->last_rgn, p_dock_context->apply_rgn, NULL, RGN_COPY);

    GetCursorPos(&po);
    po.x-=p_dock_context->cursor_offset.x;
    po.y-=p_dock_context->cursor_offset.y;
    SetRectRgn(p_dock_context->work_rgn,    po.x, po.y, po.x+p_dock_context->drag_size.cx, po.y+p_dock_context->drag_size.cy);
    SetRectRgn(p_dock_context->work_rgn2,   po.x+4, po.y+4, po.x+p_dock_context->drag_size.cx-4, po.y+p_dock_context->drag_size.cy-4);
    CombineRgn(p_dock_context->apply_rgn, p_dock_context->work_rgn, p_dock_context->work_rgn2, RGN_XOR);

    CombineRgn(p_dock_context->work_rgn, p_dock_context->apply_rgn, p_dock_context->last_rgn, RGN_XOR);

    // 反転による矩形描画
    GetRgnBox(p_dock_context->work_rgn, &rc);
    SelectObject(p_dock_context->desk_hdc, p_dock_context->work_rgn);
    PatBlt(p_dock_context->desk_hdc, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, PATINVERT);
}
// ドラッグ終了
void end_drag(DOCK_CONTEXT *p_dock_context){
    RECT rc;
    p_dock_context->now_dragging=0;
    ReleaseCapture();

    // 最後に描画した部分を消去(再反転してるだけ)
    GetRgnBox(p_dock_context->apply_rgn, &rc);
    SelectObject(p_dock_context->desk_hdc, p_dock_context->apply_rgn);
    PatBlt(p_dock_context->desk_hdc, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, PATINVERT);

    // リージョン破棄
    DeleteObject(p_dock_context->apply_rgn);
    DeleteObject(p_dock_context->work_rgn);
    DeleteObject(p_dock_context->work_rgn2);
    DeleteObject(p_dock_context->last_rgn);

    // デスクトップDCを解放
    LockWindowUpdate(NULL);
    if (p_dock_context->desk_hdc) ReleaseDC(GetDesktopWindow(), p_dock_context->desk_hdc);
    SelectObject(p_dock_context->desk_hdc, p_dock_context->desk_old_brush);
}

// ビューウィンドウ・プロシージャ
LRESULT CALLBACK ChildViewProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
    HDC hdc;
    PAINTSTRUCT ps;
    char str[]="ChildViewウィンドウ(座標0,0)";

    switch (msg) {
    case WM_LBUTTONDOWN:
        SendMessage(GetParent(hwnd), WM_LBUTTONDOWN, wp, lp);
        break;
    case WM_PAINT:
        hdc = BeginPaint(hwnd , &ps);
        TextOut(hdc, 0, 0, str, lstrlen(str));
        EndPaint(hwnd , &ps);
        break;
    }
    return DefWindowProc(hwnd, msg, wp, lp);
}
// ポップアップウィンドウ・プロシージャ
LRESULT CALLBACK PopupProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
    static DOCK_STRUCT *p_dock_struct=NULL;
    RECT popup_rc, main_rc;
    int res;
    switch (msg) {
    case WM_CREATE:
        p_dock_struct=(DOCK_STRUCT*)((LPCREATESTRUCT)(lp))->lpCreateParams;
        break;
    case WM_NCACTIVATE: // タイトルバーのアクティブ化・非アクティブ化
        if (p_dock_struct->now_nc_activing) break;
        p_dock_struct->now_nc_activing=1;
        SendMessage(p_dock_struct->main_frame, WM_NCACTIVATE, wp, 0);
        p_dock_struct->now_nc_activing=0;
        break;
    case WM_LBUTTONDOWN:
        if (p_dock_struct->dock_context.now_dragging) break;
        start_drag(&p_dock_struct->dock_context, hwnd); // ドラッグ開始
        break;
    case WM_LBUTTONUP:
        if (!p_dock_struct->dock_context.now_dragging) break;
        end_drag(&p_dock_struct->dock_context); // ドラッグ終了

        GetClientRect(p_dock_struct->main_frame, &main_rc);
        GetCursorPos((POINT*)&popup_rc);
        popup_rc.left  -= p_dock_struct->dock_context.cursor_offset.x;
        popup_rc.top   -= p_dock_struct->dock_context.cursor_offset.y;
        popup_rc.right = popup_rc.left   + p_dock_struct->dock_context.drag_size.cx;
        popup_rc.bottom= popup_rc.bottom + p_dock_struct->dock_context.drag_size.cy;
        MapWindowPoints(p_dock_struct->main_frame, NULL, (LPPOINT)&main_rc, 2);
        if (popup_rc.left<main_rc.right && popup_rc.right>main_rc.left
                && popup_rc.bottom>main_rc.top && popup_rc.top<(main_rc.top+1))
                SendMessage(p_dock_struct->main_frame, WM_COMMAND, IDM_DOCK, 0);
        else {
            SetWindowPos(hwnd, NULL, popup_rc.left, popup_rc.top, 0, 0, SWP_NOSIZE);
        }
        break;
    case WM_MOUSEMOVE:
        if (!p_dock_struct->dock_context.now_dragging) break;
        drag(&p_dock_struct->dock_context); // ドラッグ中
        break;
    case WM_NCHITTEST:
        res=DefWindowProc(hwnd, msg, wp, lp);
        if (res==HTCAPTION) return HTCLIENT;
        return res;
    }
    return DefWindowProc(hwnd, msg, wp, lp);
}

// ツールバーのフレームウィンドウ・プロシージャ
LRESULT CALLBACK ToolBarFrameProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
    static DOCK_STRUCT *p_dock_struct=NULL;
    RECT rc, rc2;
    HDC hdc;
    POINT po;
    PAINTSTRUCT ps;
    switch (msg) {
    case WM_CREATE:
        p_dock_struct=(DOCK_STRUCT*)((LPCREATESTRUCT)(lp))->lpCreateParams;

        InitCommonControls();
        p_dock_struct->htool_bar = CreateToolbarEx( hwnd, WS_CHILD | WS_VISIBLE | TBSTYLE_AUTOSIZE, 0, 6, 
            (HINSTANCE)HINST_COMMCTRL, IDB_STD_SMALL_COLOR, tool_bar_buttons, 7, 0, 0, 0, 0, sizeof (TBBUTTON)
        );
        GetWindowRect(p_dock_struct->htool_bar, &rc);
        SetWindowPos(hwnd, NULL, 0,0, rc.right-rc.left, rc.bottom-rc.top, SWP_NOMOVE);
        break;
    case WM_PAINT:
        hdc = BeginPaint(hwnd , &ps);
        GetClientRect(hwnd, &rc);

        rc.left=0; rc.top=0; rc.bottom=1;   FillRect(hdc, &rc, GetSysColorBrush(COLOR_BTNSHADOW));
        rc.top++;   rc.bottom++;            FillRect(hdc, &rc, GetSysColorBrush(COLOR_BTNHILIGHT));

        if (GetParent(hwnd)!=p_dock_struct->popup_frame){
            // グリッパー描画
            GetClientRect(hwnd, &rc);
            rc.left+=2; rc.right=rc.left+3;
            rc.top+=1;  rc.bottom-=2;

            rc2.left=rc.left;       rc2.top=rc.top; 
            rc2.right=rc.right-1;   rc2.bottom=rc.top+1;    FillRect(hdc, &rc2, GetSysColorBrush(COLOR_BTNHILIGHT));
            rc2.right=rc.left+1;    rc2.bottom=rc.bottom-1; FillRect(hdc, &rc2, GetSysColorBrush(COLOR_BTNHILIGHT));

            rc2.left=rc.right;      rc2.top=rc.top;
            rc2.right=rc.right-1;   rc2.bottom=rc.bottom;   FillRect(hdc, &rc2, GetSysColorBrush(COLOR_BTNSHADOW));
            rc2.left=rc.left;       rc2.top=rc.bottom;
            rc2.right=rc.right;     rc2.bottom=rc.bottom-1; FillRect(hdc, &rc2, GetSysColorBrush(COLOR_BTNSHADOW));
        }
        EndPaint(hwnd , &ps);
        break;
    case WM_SIZE:
        SendMessage(p_dock_struct->htool_bar, WM_SIZE, wp, lp); // ツーバーのリサイズ
        if (!p_dock_struct->floating_flag) SetWindowPos(p_dock_struct->htool_bar, NULL, 10,0, LOWORD(lp),HIWORD(lp), 0);
        break;
    case WM_LBUTTONDOWN:
        if (p_dock_struct->dock_context.now_dragging) break;
        start_drag(&p_dock_struct->dock_context, hwnd); // ドラッグ開始
        break;
    case WM_LBUTTONUP:
        if (!p_dock_struct->dock_context.now_dragging) break;
        end_drag(&p_dock_struct->dock_context); // ドラッグ終了

        // ドッキング・フローティング判定
        GetClientRect(p_dock_struct->main_frame, &rc);
        GetCursorPos(&po);
        po.x-=p_dock_struct->dock_context.cursor_offset.x;
        po.y-=p_dock_struct->dock_context.cursor_offset.y;
        MapWindowPoints(p_dock_struct->main_frame, NULL, (LPPOINT)&rc, 2);  // クライアント座標→スクリーン座標
        if (po.x<=rc.right && po.x>=rc.left && po.y<=(rc.top+1) && po.y>=rc.top)    // ドッキング判定
            SetWindowPos(hwnd, NULL, 0,0, 0,0, SWP_NOSIZE);
        else if (!p_dock_struct->dock_context.now_dragging) {       // フローティング判定
            SetWindowPos(hwnd, NULL, 0,0, 0,0, SWP_NOSIZE);
            SetWindowPos(p_dock_struct->popup_frame, NULL, po.x, po.y, 0,0, SWP_NOSIZE);
            SendMessage(p_dock_struct->main_frame, WM_COMMAND, IDM_FLOAT, 0);
        }
        break;
    case WM_MOUSEMOVE:
        if (!p_dock_struct->dock_context.now_dragging) break;
        drag(&p_dock_struct->dock_context); // ドラッグ中
        break;
    }
    return DefWindowProc(hwnd, msg, wp, lp);
}

// メインウィンドウ・プロシージャ
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
    RECT rc, rc2;
    static DOCK_STRUCT *p_dock_struct=NULL;
    SIZE tool_size, popup_size;

    switch (msg) {
    case WM_CREATE:
        p_dock_struct=(DOCK_STRUCT*)((LPCREATESTRUCT)(lp))->lpCreateParams;

        p_dock_struct->hview= CreateWindowEx(
            WS_EX_TOOLWINDOW, TEXT("child_view"), NULL, WS_CHILD | WS_VISIBLE,
            0, 0, 200, 100, hwnd, NULL, ((LPCREATESTRUCT)(lp))->hInstance, NULL
        );
        p_dock_struct->popup_frame= CreateWindowEx(
            WS_EX_TOOLWINDOW, TEXT("popup_frame"), NULL, WS_OVERLAPPEDWINDOW | WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
            300, 170, 200, 100, hwnd, NULL, ((LPCREATESTRUCT)(lp))->hInstance, (LPVOID)p_dock_struct
        );
        p_dock_struct->htool_bar_frame= CreateWindow(
            TEXT("tool_bar_frame"), NULL, WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
            0, 0, 200, 100, hwnd, NULL, ((LPCREATESTRUCT)(lp))->hInstance, (LPVOID)p_dock_struct
        );
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    case WM_NCACTIVATE: // タイトルバーのアクティブ化・非アクティブ化
        if (p_dock_struct->now_nc_activing) break;
        p_dock_struct->now_nc_activing=1;
        SendMessage(p_dock_struct->popup_frame, WM_NCACTIVATE, wp, 0);
        p_dock_struct->now_nc_activing=0;
        break;
    case WM_COMMAND:
        switch(LOWORD(wp)){
        case IDM_DOCK_FLOAT:
            // ドッキング・フローティング切り替え
            if (p_dock_struct->floating_flag) SendMessage(hwnd, WM_COMMAND, IDM_DOCK, 0);
            else                              SendMessage(hwnd, WM_COMMAND, IDM_FLOAT, 0);
            break;
        case IDM_FLOAT:
            // フローティング
            SetParent(p_dock_struct->htool_bar_frame, p_dock_struct->popup_frame);
            p_dock_struct->floating_flag = 1;
            ShowWindow(p_dock_struct->popup_frame, SW_SHOW);
            SendMessage(hwnd, WM_COMMAND, IDM_RECALC_LAYOUT, 0);
            break;
        case IDM_DOCK:
            // ドッキング
            SetParent(p_dock_struct->htool_bar_frame, hwnd);
            p_dock_struct->floating_flag = 0;
            ShowWindow(p_dock_struct->popup_frame, SW_HIDE);
            SendMessage(hwnd, WM_COMMAND, IDM_RECALC_LAYOUT, 0);
            break;
        case IDM_RECALC_LAYOUT:
            // 子ウィンドウ位置の再計算
            SendMessage(p_dock_struct->htool_bar, TB_GETMAXSIZE, 0, (LPARAM)&tool_size);        // ツールバー・サイズ取得
            GetWindowRect(p_dock_struct->htool_bar, &rc);
            tool_size.cy=rc.bottom-rc.top;

            // ビューウィンドウ位置サイズ
            GetClientRect(hwnd, &rc);
            if (!p_dock_struct->floating_flag) rc.top =tool_size.cy;
            SetWindowPos(p_dock_struct->hview, NULL, rc.left, rc.top, rc.right, rc.bottom-rc.top, 0);

            if(p_dock_struct->floating_flag){
                // ポップアップ・ウィンドウのウィンドウサイズ計算
                GetClientRect(p_dock_struct->popup_frame, &rc);
                GetWindowRect(p_dock_struct->popup_frame, &rc2);
                popup_size.cx=(rc2.right-rc2.left)-rc.right;    // ウィンドウサイズとクライアント領域の差  ヨコ幅
                popup_size.cy=(rc2.bottom-rc2.top)-rc.bottom;   //        〃                   タテ幅

                popup_size.cx+=tool_size.cx;
                popup_size.cy+=tool_size.cy;
                SetWindowPos(p_dock_struct->popup_frame, NULL, 0, 0, popup_size.cx, popup_size.cy, SWP_NOMOVE);
            }

            // ツールバー横サイズ補正
            GetClientRect(GetParent(p_dock_struct->htool_bar_frame), &rc);
            SetWindowPos(p_dock_struct->htool_bar_frame, NULL, 0, 0, rc.right, tool_size.cy, 0);

            break;
        }
        break;
    case WM_SIZE:
        SendMessage(hwnd, WM_COMMAND, IDM_RECALC_LAYOUT, 0);
        break;
    case WM_LBUTTONDOWN:
        SendMessage(hwnd, WM_COMMAND, IDM_DOCK_FLOAT, 0);
        break;
    }
    return DefWindowProc(hwnd, msg, wp, lp);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, int nCmdShow ) {
    MSG msg;
    WNDCLASS winc;
    DOCK_STRUCT dock_struct;    // ウィンドウ・ハンドルの一括化 構造体
    WORD gray_pattern[8];
    HBITMAP gray_bitmap;
    int i;

    winc.style              = CS_HREDRAW | CS_VREDRAW;
    winc.lpfnWndProc        = WndProc;
    winc.cbClsExtra = winc.cbWndExtra       = 0;
    winc.hInstance          = hInstance;
    winc.hIcon              = LoadIcon(NULL, IDI_APPLICATION);
    winc.hCursor            = LoadCursor(NULL, IDC_ARROW);
    winc.hbrBackground      = (HBRUSH)GetStockObject(WHITE_BRUSH);
    winc.lpszMenuName       = NULL;
    winc.lpszClassName      = TEXT("main_frame");

    if (!RegisterClass(&winc)) return -1;   // メイン・ウィンドウ

    winc.hbrBackground      = (HBRUSH)GetStockObject(WHITE_BRUSH);
    winc.lpfnWndProc        = ChildViewProc;
    winc.lpszClassName      = TEXT("child_view");
    if (!RegisterClass(&winc)) return -1;   // ビューウィンドウ

    winc.hbrBackground      = (HBRUSH)(COLOR_BTNFACE+1);
    winc.lpfnWndProc        = PopupProc;
    winc.lpszClassName      = TEXT("popup_frame");
    if (!RegisterClass(&winc)) return -1;   // フローティング・フレーム・ウィンドウ

    winc.hbrBackground      = (HBRUSH)(COLOR_BTNFACE+1);
    winc.lpfnWndProc        = ToolBarFrameProc;
    winc.lpszClassName      = TEXT("tool_bar_frame");
    if (!RegisterClass(&winc)) return -1;   // ツールバーの外枠ウィンドウ

    dock_struct.now_nc_activing=0;
    dock_struct.floating_flag=0;
    dock_struct.dock_context.now_dragging=0;
    dock_struct.main_frame = CreateWindow(
        TEXT("main_frame"), TEXT("ドッキング・ツールバーのテスト"),
        WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN,
        100, 100, 240, 200, NULL, NULL, hInstance, (LPVOID)&dock_struct
    );

    // ハーフトーン・パレット生成
    for (i=0;i<8;i++) gray_pattern[i] = (WORD)(0x5555 << (i & 1));  // 0x5555 は2bitで  01010101 01010101
    gray_bitmap = CreateBitmap(8, 8, 1, 1, &gray_pattern);
    if (gray_bitmap != NULL) {
        dock_struct.dock_context.half_tone_brush = ::CreatePatternBrush(gray_bitmap);
        DeleteObject(gray_bitmap);
    }
    if (dock_struct.main_frame == NULL) return -1;

    while(GetMessage(&msg, NULL, 0, 0)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
    }

    DeleteObject(half_tone_brush); // ハーフトーン・パレット破棄
    return msg.wParam;
}




 004-01  前へ←  ホームへ  →次へ  004-03