No.004 - 04 縦横ドッキング |
( + ドラッグ中のドッキング先描画) |
| 縦や横にドッキングするツールバーをつくります。 上下左右にドッキングします。 |
・ツールバーを縦横に |
|
| ツールバーを縦横に変換するために TOOL_BARクラスに縦に変換するset_vert()、横に変換するset_horz() 関数を作りました。 縦型の設定に関しては各ボタンに対して改行スタイルを追加して実現していしています。 ツールバー生成時TBSTYLE_WRAPABLEもきちんと追加してやらないと動作が変になるので注意してください。 ちなみに改行スタイルを追加しないでTBSTYLE_WRAPABLE だけの方法もありますが、ここでは使いません。 (MFCも改行スタイル方式ですが、どうやら「OutLook」の「OutLookバー」ではTBSTYLE_WRAPABLEだけのようです・・・筆者は使いヅライと思いました。) とはいっても縦型ツールバーの形式は一筋縄ではいかないので各アプリケーションでうまくカスタム化して下さい。 |
|
・強制フローティング |
|
| ドラッグ中にシフトキーかコントロールキーを押していると強制的にフローティングします。 シフトキーなら縦横変換してフローティングです。 この縦横変換の処理は変数 flip というフラグで各関数の引数に使っています。 ドラッグ中この処理をフックするために、メインウィンドウ側にWM_KEYDOWN, WM_KEYUPメッセージ処理を追加しています。 メインウィンドウの負担を軽くするためにSetWindowsHookEx()関数を使ってもいいのですが、その場合かなり大きな処理になってしまうので、あえてメインウィンドウでWM_KEYDOWN, WM_KEYUPを処理しています。 |
|
・上下左右のドックバー |
|
| メインウィンドウの上下左右にドッキングできるようにするためにドックバーを上下左右に配置します。 DOCK_SYS クラスはDOCK_BARクラスを配列にして4つ持ちます。 各ドックバーへのアクセスは列挙型定数 ID_TOP, ID_LEFT, ID_BOTTOM, ID_RIGHT を使ってm_dock_bar[ID_TOP]のようにアクセスします。 メンバ変数 m_vh_type は縦横フラグをセットします。縦型なら'V'、横型なら'H'を代入しています。 |
|
| ツールバーのアイテムの矩形サイズを得るときのバグ? SendMessage(hwnd, TB_GETITEMRECT, item_num, (LPARAM)&rc) |
SendMessage の TB_GETITEMRECT でツールバーのアイテムの矩形サイズを得るとき少し問題があります。 通常のボタンは問題ないのですが、縦型のときでセパレータ(TBSTYLE_SEP)に改行スタイル(TBSTATE_WRAP)があると、 2回目以降のSendMessage(, TB_GETITEMRECT, , , ) でセパレータ横幅に正しい値が返らないのです。 また、SendMessage(m_control_tool_bar, TB_GETBUTTONINFO, 〜 も 2回目はTBSTYLE_SEPスタイルを取得できませんでした。 原因はわかりませんが、なんとか回避して処理します。 TB_GETMAXSIZEで得てもいいのですが、TB_GETMAXSIZE で得た全体サイズは、 セパレータがあっても正しい値が返りますが、ドロップダウンリストがある時誤った値が返ったりします。 |
以下のサンプルではメインウィンドウのクライアント領域をクリックすると
ツールバーが上下左右にドッキングおよび縦横フローティングします。
| 上部ドッキング | 左部ドッキング | 右部ドッキング | ||
![]() |
→ | ![]() |
→ | ![]() |
| ↑ | ↓ | |||
![]() |
← | ![]() |
← | ![]() |
| 縦フローティング | 横フローティング | 下部ドッキング |
![]() |
→ | ![]() |
→ | ![]() |
| シフトキーを押しながらドラッグすると縦横変換 強制フローティング | ||||
| docking_test_04_1.cpp |
#define _WIN32_IE 0x0501
#include <windows.h>
#include <commctrl.h>
// カスタムFillRect(矩形塗りつぶし)
FillRect(HDC hdc, int left, int top, int right, int bottom, HBRUSH hbr){
RECT rc;
rc.left = left;
rc.top = top;
rc.right = right;
rc.bottom = bottom;
FillRect(hdc, &rc, hbr);
}
// ドック・コンテキスト・構造体
class DOCK_CONTEXT {
public:
HRGN m_apply_rgn, m_work_rgn, m_work_rgn2, m_last_rgn; // リージョン
HDC m_desk_hdc; // デスクトップDC
int m_now_dragging; // ドラッグ中なら==1 そうでないなら==0
SIZE m_drag_size; // ドラッグ中の矩形サイズ
POINT m_cursor_offset; // ドラッグ中の矩形位置のマウス位置との差分
HBRUSH m_desk_old_brush; // デスクトップDC・ブラシ
HBRUSH m_half_tone_brush; // ハーフトーン・ブラシ
DOCK_CONTEXT(); // コンストラクタ
~DOCK_CONTEXT(); // デストラクタ
void start_drag(HWND hwnd); // ドラッグ開始
void drag(int flip); // ドラッグ中
void end_drag(); // ドラッグ終了
};
// クラスの仮宣言
class DOCK_SYS;
// ツールバークラス
class TOOL_BAR {
public:
HWND m_hwnd, m_control_tool_bar, m_popup_frame; // 各HWND
DOCK_SYS *m_p_dock_sys; // ドッキング・ツールバー統括クラス・ポインタ
int m_floating_flag; // フローティング・フラグ 浮いてれば==1 そうでないなら==0
int m_vh_type, m_parent_dock_id;
SIZE *m_p_size, m_horz_size, m_vert_size;
create(DOCK_SYS *p_dock_sys, TBBUTTON *tb_buttons, int tb_button_len); // ツールバー生成
set_horz(); // ツールバー・横型に変更
set_vert(); // ツールバー・縦型に変更
void renew_size(); // サイズ更新
// ポップアップ・フレーム・プロシージャ
PopupProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
static LRESULT CALLBACK StaticPopupProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
// ツールバー・プロシージャ
ToolBarFrameProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
static LRESULT CALLBACK StaticToolBarFrameProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
};
// ドックバークラス
class DOCK_BAR {
public:
enum { ID_TOP, ID_LEFT, ID_BOTTOM, ID_RIGHT, ID_DOCK_BAR_SIZE };
create(HWND main_hwnd, int pos_id);
HWND m_hwnd;
int m_vh_type;
};
// ドッキング・ツールバー統括クラス
class DOCK_SYS {
public:
HWND m_main_frame, m_hview; // 各HWND
int m_now_nc_activing; // WM_NCACTIVATEフラグ WM_NCACTIVATEの多重再送を防ぐ
DOCK_CONTEXT m_dock_context; // ドック・コンテキスト・構造体
DOCK_BAR m_dock_bar[4]; // ドックバークラス
TOOL_BAR m_tool_bar; // ツールバークラス
regist_winc(char *caption, HBRUSH hbr_bg, WNDPROC proc); // ウィンドウクラス登録
create(HWND main_hwnd, HWND child_view_hwnd); // 生成
void on_ncactivate(HWND hwnd, WPARAM wp); // WM_NCACTIVATE 処理
void recalc_layout(); // 子ウィンドウ位置再計算
void do_dock(int pos_id); // ドッキング実行
void do_float(int x, int y, int flip); // フローティング実行
void tool_on_lbuttondown(HWND hwnd); // ツールバー・マウス左ボタン押下
void tool_on_lbuttonup(); // ツールバー・マウス左ボタン押上
void tool_on_mousemove(); // ツールバー・マウス移動
void tool_drag(); // ツールバー・ドラッグ
void tool_on_keydown(WPARAM wp, LPARAM lp); // ツールバー・キー押下
void tool_on_keyup(WPARAM wp, LPARAM lp); // ツールバー・キー押上
} ;
// コンストラクタ
DOCK_CONTEXT::DOCK_CONTEXT(){
WORD gray_pattern[8];
HBITMAP gray_bitmap;
int i;
m_now_dragging=0;
// ハーフトーン・パレット生成
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) {
m_half_tone_brush = ::CreatePatternBrush(gray_bitmap);
DeleteObject(gray_bitmap);
}
}
// デストラクタ
DOCK_CONTEXT::~DOCK_CONTEXT(){
DeleteObject(m_half_tone_brush); // ハーフトーン・パレット破棄
}
// ドラッグ開始
void DOCK_CONTEXT::start_drag(HWND hwnd){
HWND desk_hwnd;
RECT rc;
m_now_dragging=1;
SetCapture(hwnd);
// デスクトップDCを得る
desk_hwnd=GetDesktopWindow();
LockWindowUpdate(desk_hwnd);
m_desk_hdc= GetDCEx(desk_hwnd, NULL, DCX_WINDOW|DCX_CACHE|DCX_LOCKWINDOWUPDATE);
m_desk_old_brush=(HBRUSH)SelectObject(m_desk_hdc, m_half_tone_brush);
// ドラッグ・サイズとマウスとの座標差
GetWindowRect(hwnd, &rc);
m_drag_size.cx = rc.right-rc.left;
m_drag_size.cy = rc.bottom-rc.top;
GetCursorPos(&m_cursor_offset);
m_cursor_offset.x -= rc.left;
m_cursor_offset.y -= rc.top;
// リージョン生成
m_apply_rgn = CreateRectRgn(0, 0, 0, 0);
m_work_rgn = CreateRectRgn(0, 0, 0, 0);
m_work_rgn2 = CreateRectRgn(0, 0, 0, 0);
m_last_rgn = CreateRectRgn(0, 0, 0, 0);
}
// ドラッグ中
void DOCK_CONTEXT::drag(int flip){
POINT po;
RECT rc;
SIZE size;
if (flip){ // 縦横変換
size.cx=m_drag_size.cy;
size.cy=m_drag_size.cx;
}
else size=m_drag_size;
GetCursorPos(&po);
po.x-=m_cursor_offset.x;
po.y-=m_cursor_offset.y;
SetRectRgn(m_work_rgn, po.x, po.y, po.x+size.cx, po.y+size.cy);
SetRectRgn(m_work_rgn2, po.x+4, po.y+4, po.x+size.cx-4, po.y+size.cy-4);
CombineRgn(m_apply_rgn, m_work_rgn, m_work_rgn2, RGN_XOR);
CombineRgn(m_work_rgn, m_apply_rgn, m_last_rgn, RGN_XOR);
// 反転による矩形描画
GetRgnBox(m_work_rgn, &rc);
SelectObject(m_desk_hdc, m_work_rgn);
PatBlt(m_desk_hdc, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, PATINVERT);
// 最後に描画した部分保持(再反転して消去)
CombineRgn(m_last_rgn, m_apply_rgn, NULL, RGN_COPY);
}
// ドラッグ終了
void DOCK_CONTEXT::end_drag(){
RECT rc;
m_now_dragging=0;
ReleaseCapture();
// 最後に描画した部分を消去(再反転してるだけ)
GetRgnBox(m_apply_rgn, &rc);
SelectObject(m_desk_hdc, m_apply_rgn);
PatBlt(m_desk_hdc, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, PATINVERT);
// リージョン破棄
DeleteObject(m_apply_rgn);
DeleteObject(m_work_rgn);
DeleteObject(m_work_rgn2);
DeleteObject(m_last_rgn);
// デスクトップDCを解放
LockWindowUpdate(NULL);
if (m_desk_hdc) ReleaseDC(GetDesktopWindow(), m_desk_hdc);
SelectObject(m_desk_hdc, m_desk_old_brush);
}
// ツールバー・サイズ更新
void TOOL_BAR::renew_size(){
RECT rc;
int i, len, offset;
if (!IsWindow(m_control_tool_bar)) return ;
TBBUTTONINFO tbbi;
memset(&tbbi, 0, sizeof(TBBUTTONINFO));
tbbi.cbSize=sizeof(TBBUTTONINFO);
tbbi.dwMask=TBIF_STYLE ;
// 各ボタンから全体サイズ取得
len=SendMessage(m_control_tool_bar, TB_BUTTONCOUNT, 0, 0);
m_horz_size.cx=m_horz_size.cy=0;
m_vert_size.cx=m_vert_size.cy=0;
for(i=0;i<len;i++){
if (!SendMessage(m_control_tool_bar, TB_GETITEMRECT, i, (LPARAM) (&rc))) continue;
m_horz_size.cx+=rc.right-rc.left;
m_vert_size.cy+=rc.bottom-rc.top;
}
if (len>0){
i=SendMessage(m_control_tool_bar, TB_GETBUTTONSIZE, 0, 0);
m_horz_size.cy=HIWORD(i);
m_vert_size.cx=LOWORD(i);
}
// パディング(ツールバーのボタン周りの余白部分)取得
DWORD padding=SendMessage(m_control_tool_bar, TB_GETPADDING, 0, 0);
m_horz_size.cx+=LOWORD(padding);
m_horz_size.cy+=HIWORD(padding);
m_vert_size.cx+=LOWORD(padding);
m_vert_size.cy+=HIWORD(padding);
// グリッパーサイズ計算
offset=(m_floating_flag)? 0 : 10;
m_horz_size.cx+=offset;
m_vert_size.cy+=offset;
SetWindowPos(m_hwnd, NULL, 0,0, m_p_size->cx, m_p_size->cy, SWP_NOMOVE);
// コモンコントロールのサイズ変更は親フレームウィンドウのあとで行う。(サイズ補正されるから)
// コモンコントロールのサイズ変更と位置移動のSetWindowPosは別に呼ぶ(位置補正されるから)
SetWindowPos(m_control_tool_bar, NULL, 0, 0, m_p_size->cx, m_p_size->cy, SWP_NOMOVE);
if (m_vh_type=='H') { SetWindowPos(m_control_tool_bar, NULL, offset, 0, 0, 0, SWP_NOSIZE); }
else { SetWindowPos(m_control_tool_bar, NULL, 4, offset, 0, 0, SWP_NOSIZE); }
return;
}
// ツールバー・縦型に変更
TOOL_BAR::set_vert(){
if (!m_control_tool_bar) return 0;
m_vh_type='V';
int style=GetWindowLong(m_control_tool_bar, GWL_STYLE);
SetWindowLong(m_control_tool_bar, GWL_STYLE, style | CCS_VERT | CCS_NODIVIDER);
int i, len=SendMessage(m_control_tool_bar, TB_BUTTONCOUNT, 0, 0);
TBBUTTONINFO tbbi;
memset(&tbbi, 0, sizeof(TBBUTTONINFO));
tbbi.cbSize=sizeof(TBBUTTONINFO);
tbbi.dwMask=TBIF_STATE ;
for(i=0;i<len;i++){
SendMessage(m_control_tool_bar, TB_GETBUTTONINFO, i, (LPARAM)&tbbi);
tbbi.fsState |=TBSTATE_WRAP;
SendMessage(m_control_tool_bar, TB_SETBUTTONINFO, i, (LPARAM)&tbbi);
}
m_p_size = &m_vert_size;
}
// ツールバー・横型に変更
TOOL_BAR::set_horz(){
if (!m_control_tool_bar) return 0;
m_vh_type='H';
int style=GetWindowLong(m_control_tool_bar, GWL_STYLE);
SetWindowLong(m_control_tool_bar, GWL_STYLE, style & (~CCS_NODIVIDER) & (~CCS_VERT));
int i, len=SendMessage(m_control_tool_bar, TB_BUTTONCOUNT, 0, 0);
TBBUTTONINFO tbbi={0};
memset(&tbbi, 0, sizeof(TBBUTTONINFO));
tbbi.cbSize=sizeof(TBBUTTONINFO);
tbbi.dwMask=TBIF_STATE;
for(i=0;i<len;i++){
SendMessage(m_control_tool_bar, TB_GETBUTTONINFO, i, (LPARAM)&tbbi);
tbbi.fsState &= (~TBSTATE_WRAP);
SendMessage(m_control_tool_bar, TB_SETBUTTONINFO, i, (LPARAM)&tbbi);
}
m_p_size = &m_horz_size;
}
// ツールバー生成
TOOL_BAR::create(DOCK_SYS *p_dock_sys, TBBUTTON *tb_buttons, int tb_button_len){
m_floating_flag=0;
m_p_dock_sys=p_dock_sys;
m_p_size=&m_horz_size;
HINSTANCE hInstance=GetModuleHandle(NULL);
// ポップアップ・フレーム生成
m_popup_frame= CreateWindowEx(
WS_EX_TOOLWINDOW, TEXT("popup_frame"), NULL, WS_OVERLAPPEDWINDOW | WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
300, 170, 200, 100, p_dock_sys->m_main_frame, NULL, hInstance, (LPVOID)this
);
InitCommonControls();
// コモンコントロールのツールバー生成
m_control_tool_bar = CreateToolbarEx(p_dock_sys->m_main_frame, WS_CHILD | TBSTYLE_AUTOSIZE | TBSTYLE_FLAT, 0, 6,
(HINSTANCE)HINST_COMMCTRL, IDB_STD_SMALL_COLOR, tb_buttons, tb_button_len, 0, 0, 0, 0, sizeof (TBBUTTON)
);
// ツールバーのフレーム生成
m_hwnd= CreateWindow(
TEXT("tool_bar_frame"), NULL, WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
0, 0, 200, 100, p_dock_sys->m_main_frame, NULL, hInstance, (LPVOID)this
);
SetParent(m_control_tool_bar, m_hwnd);
ShowWindow(m_control_tool_bar, SW_SHOWNA);
}
// ポップアップウィンドウ・プロシージャ(static版)
LRESULT CALLBACK TOOL_BAR::StaticPopupProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
TOOL_BAR *p_tool_bar =(TOOL_BAR*)GetWindowLong(hwnd, GWL_USERDATA);
if (!p_tool_bar && (msg==WM_NCCREATE || msg==WM_CREATE)){
p_tool_bar = (TOOL_BAR*)((LPCREATESTRUCT)lp)->lpCreateParams;
SetWindowLong(hwnd, GWL_USERDATA, (LONG)p_tool_bar);
}
if (p_tool_bar) return p_tool_bar->PopupProc(hwnd, msg, wp, lp);
return DefWindowProc(hwnd, msg, wp, lp);
}
// ポップアップウィンドウ・プロシージャ
TOOL_BAR::PopupProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
int res;
switch (msg) {
case WM_NCACTIVATE: // タイトルバーのアクティブ化・非アクティブ化
m_p_dock_sys->on_ncactivate(hwnd, wp);
break;
case WM_KEYDOWN: m_p_dock_sys->tool_on_keydown(wp, lp); break; // キー押下
case WM_KEYUP: m_p_dock_sys->tool_on_keyup(wp, lp); break; // キー押上
case WM_LBUTTONDOWN:m_p_dock_sys->tool_on_lbuttondown(hwnd); break; // ドラッグ開始
case WM_LBUTTONUP: m_p_dock_sys->tool_on_lbuttonup(); break; // ドラッグ終了
case WM_MOUSEMOVE: m_p_dock_sys->tool_on_mousemove(); break; // マウス移動(ドラッグ中)
case WM_NCHITTEST:
res=DefWindowProc(hwnd, msg, wp, lp);
if (res==HTCAPTION) return HTCLIENT;
return res;
}
return DefWindowProc(hwnd, msg, wp, lp);
}
// ツールバーのフレームウィンドウ・プロシージャ(static版)
LRESULT CALLBACK TOOL_BAR::StaticToolBarFrameProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp){
TOOL_BAR *p_tool_bar =(TOOL_BAR*)GetWindowLong(hwnd, GWL_USERDATA);
if (!p_tool_bar && (msg==WM_NCCREATE || msg==WM_CREATE)){
p_tool_bar = (TOOL_BAR*)((LPCREATESTRUCT)lp)->lpCreateParams;
SetWindowLong(hwnd, GWL_USERDATA, (LONG)p_tool_bar);
}
if (p_tool_bar) return p_tool_bar->ToolBarFrameProc(hwnd, msg, wp, lp);
return DefWindowProc(hwnd, msg, wp, lp);
}
// ツールバーのフレームウィンドウ・プロシージャ
TOOL_BAR::ToolBarFrameProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
RECT rc, rc2;
HDC hdc;
PAINTSTRUCT ps;
switch (msg) {
case WM_PAINT:
hdc = BeginPaint(hwnd , &ps);
GetClientRect(hwnd, &rc);
FillRect(hdc, 0, 0, rc.right, 1, GetSysColorBrush(COLOR_BTNSHADOW));
FillRect(hdc, 0, 1, rc.right, 2, GetSysColorBrush(COLOR_BTNHILIGHT));
if (!m_floating_flag){
// グリッパー描画
GetClientRect(hwnd, &rc);
if (m_vh_type=='H') { rc.left+=2; rc.right=rc.left+3; rc.top+=1; rc.bottom-=2; }
else { rc.left+=1; rc.right=rc.left+2; rc.top+=1; rc.bottom-=2; }
FillRect(hdc, rc.left, rc.top, rc.right-1, rc.top+1, GetSysColorBrush(COLOR_BTNHILIGHT));
FillRect(hdc, rc.left, rc.top, rc.left+1, rc.bottom-1, GetSysColorBrush(COLOR_BTNHILIGHT));
FillRect(hdc, rc.right, rc.top, rc.right-1, rc.bottom, GetSysColorBrush(COLOR_BTNSHADOW));
FillRect(hdc, rc.left, rc.bottom, rc.right, rc.bottom-1, GetSysColorBrush(COLOR_BTNSHADOW));
}
EndPaint(hwnd , &ps);
break;
case WM_LBUTTONDOWN:m_p_dock_sys->tool_on_lbuttondown(hwnd);break; // マウス左ボタン押下(ドラッグ終了)
case WM_LBUTTONUP: m_p_dock_sys->tool_on_lbuttonup(); break; // マウス左ボタン押上(ドラッグ開始)
case WM_MOUSEMOVE: m_p_dock_sys->tool_on_mousemove(); break; // マウス移動(ドラッグ中)
}
return DefWindowProc(hwnd, msg, wp, lp);
}
// ドックバー生成
DOCK_BAR::create(HWND main_hwnd, int pos_id){
HINSTANCE hInstance=GetModuleHandle(NULL);
if (pos_id==ID_TOP || pos_id==ID_BOTTOM) m_vh_type='H';
else m_vh_type='V';
m_hwnd = CreateWindow(
TEXT("dock_bar_frame"), NULL, WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
0, 0, 200, 100, main_hwnd, NULL, hInstance, (LPVOID)this
);
}
// ツールバー・ドラッグ
void DOCK_SYS::tool_drag(){
if (!m_dock_context.m_now_dragging) return;
int filp = (GetKeyState(VK_SHIFT)<0)? 1 : 0;
m_dock_context.drag(filp); // ドラッグ中
};
// ツールバー・キー押下
void DOCK_SYS::tool_on_keydown(WPARAM wp, LPARAM lp){
tool_drag();
}
// ツールバー・キー押上
void DOCK_SYS::tool_on_keyup(WPARAM wp, LPARAM lp){
tool_drag();
}
// マウス移動
void DOCK_SYS::tool_on_mousemove(){
tool_drag();
}
// マウス左ボタン押下
void DOCK_SYS::tool_on_lbuttondown(HWND hwnd){
if (m_dock_context.m_now_dragging) return;
m_dock_context.start_drag(hwnd); // ドラッグ開始
}
// マウス左ボタン押上
void DOCK_SYS::tool_on_lbuttonup(){
RECT drag_rc, dock_rc;
if (!m_dock_context.m_now_dragging) return;
m_dock_context.end_drag(); // ドラッグ終了
// ドッキング・フローティング判定
POINT po;
GetCursorPos(&po);
drag_rc.left=po.x-m_dock_context.m_cursor_offset.x;
drag_rc.top =po.y-m_dock_context.m_cursor_offset.y;
drag_rc.right = drag_rc.left + m_dock_context.m_drag_size.cx;
drag_rc.bottom= drag_rc.top + m_dock_context.m_drag_size.cy;
int flip = (GetKeyState(VK_SHIFT)<0)? 1 : 0;
int forced_float = (flip || (GetKeyState(VK_CONTROL)<0))? 1: 0; // 強制フロート
if (!forced_float){
for(int i=0;i<DOCK_BAR::ID_DOCK_BAR_SIZE;i++){
// ドッキング判定
GetWindowRect(m_dock_bar[i].m_hwnd, &dock_rc);
if (drag_rc.left<=dock_rc.right && drag_rc.right>=dock_rc.left
&& drag_rc.top<=dock_rc.bottom && drag_rc.bottom>=dock_rc.top){
do_dock(i);
return;
}
}
}
// フローティング判定
do_float(drag_rc.left, drag_rc.top, flip);
}
// ドッキングシステム生成
DOCK_SYS::create(HWND main_hwnd, HWND child_view_hwnd){
m_now_nc_activing=0;
m_main_frame=main_hwnd;
m_hview=child_view_hwnd;
regist_winc(TEXT("popup_frame"), (HBRUSH)(COLOR_BTNFACE+1), TOOL_BAR::StaticPopupProc); // フローティング・フレーム
regist_winc(TEXT("tool_bar_frame"), (HBRUSH)(COLOR_BTNFACE+1), TOOL_BAR::StaticToolBarFrameProc); // ツールバーの外枠
regist_winc(TEXT("dock_bar_frame"), (HBRUSH)(COLOR_BTNFACE+1), DefWindowProc); // ドックバー
m_dock_bar[DOCK_BAR::ID_TOP].create(main_hwnd, DOCK_BAR::ID_TOP);
m_dock_bar[DOCK_BAR::ID_LEFT].create(main_hwnd, DOCK_BAR::ID_LEFT);
m_dock_bar[DOCK_BAR::ID_BOTTOM].create(main_hwnd, DOCK_BAR::ID_BOTTOM);
m_dock_bar[DOCK_BAR::ID_RIGHT].create(main_hwnd, DOCK_BAR::ID_RIGHT);
// ツールバーの生成
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 },
};
m_tool_bar.create(this, tool_bar_buttons, 6);
do_dock(0);
}
// タイトルバーのアクティブ化・非アクティブ化
void DOCK_SYS::on_ncactivate(HWND hwnd, WPARAM wp){
if (m_now_nc_activing) return;
m_now_nc_activing=1;
SendMessage(m_main_frame, WM_NCACTIVATE, wp, 0);
SendMessage(m_tool_bar.m_popup_frame, WM_NCACTIVATE, wp, 0);
m_now_nc_activing=0;
}
// ドッキング
void DOCK_SYS::do_dock(int pos_id){
m_tool_bar.m_floating_flag = 0;
m_tool_bar.m_parent_dock_id = pos_id;
if (m_dock_bar[pos_id].m_vh_type=='V') m_tool_bar.set_vert();
else m_tool_bar.set_horz();
SetParent(m_tool_bar.m_hwnd, m_dock_bar[pos_id].m_hwnd);
ShowWindow(m_tool_bar.m_popup_frame, SW_HIDE);
m_tool_bar.renew_size(); // ツールバー・サイズ更新
recalc_layout();
}
// フローティング
void DOCK_SYS::do_float(int x, int y, int flip){
if (flip){
if (m_tool_bar.m_vh_type=='V') m_tool_bar.set_horz();
else m_tool_bar.set_vert();
}
SetParent(m_tool_bar.m_hwnd, m_tool_bar.m_popup_frame);
m_tool_bar.m_floating_flag = 1;
m_tool_bar.renew_size(); // ツールバー・サイズ更新
// ポップアップ・ウィンドウ位置サイズ
RECT rc;
rc.left=rc.top=0;
rc.right=m_tool_bar.m_p_size->cx;
rc.bottom=m_tool_bar.m_p_size->cy;
AdjustWindowRectEx(&rc, GetWindowLong(m_tool_bar.m_popup_frame, GWL_STYLE),
FALSE, GetWindowLong(m_tool_bar.m_popup_frame, GWL_EXSTYLE));
SetWindowPos(m_tool_bar.m_popup_frame, NULL, x, y, rc.right-rc.left, rc.bottom-rc.top, 0);
ShowWindow(m_tool_bar.m_popup_frame, SW_SHOW);
recalc_layout();
}
// レイアウト再計算
void DOCK_SYS::recalc_layout(){
if (IsIconic(m_main_frame)) return;
RECT rc, main_rc;
SIZE tool_size=*m_tool_bar.m_p_size;
// 子ウィンドウ位置の再計算
GetClientRect(m_main_frame, &rc);
main_rc = rc;
// ドックバー位置サイズ
if (!m_tool_bar.m_floating_flag) {
if (m_tool_bar.m_parent_dock_id==DOCK_BAR::ID_TOP) rc.top =tool_size.cy;
if (m_tool_bar.m_parent_dock_id==DOCK_BAR::ID_LEFT) rc.left =tool_size.cx;
if (m_tool_bar.m_parent_dock_id==DOCK_BAR::ID_BOTTOM) rc.bottom -=tool_size.cy;
if (m_tool_bar.m_parent_dock_id==DOCK_BAR::ID_RIGHT) rc.right -=tool_size.cx;
}
SetWindowPos(m_dock_bar[DOCK_BAR::ID_TOP].m_hwnd, NULL, rc.left, 0, rc.right, rc.top, 0);
SetWindowPos(m_dock_bar[DOCK_BAR::ID_LEFT].m_hwnd, NULL, 0, rc.top, rc.left, rc.bottom, 0);
SetWindowPos(m_dock_bar[DOCK_BAR::ID_BOTTOM].m_hwnd,NULL, rc.left, rc.bottom, rc.right-rc.left, main_rc.bottom-rc.bottom, 0);
SetWindowPos(m_dock_bar[DOCK_BAR::ID_RIGHT].m_hwnd, NULL, rc.right, rc.top, main_rc.right-rc.right, rc.bottom-rc.top, 0);
// ビューウィンドウ位置サイズ
if (IsWindow(m_hview)) {
SetWindowPos(m_hview, NULL, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, 0);
InvalidateRect(m_hview, NULL, TRUE);
}
}
// ウィンドウクラス登録
DOCK_SYS::regist_winc(char *caption, HBRUSH hbr_bg, WNDPROC proc){
WNDCLASS winc;
winc.style = CS_HREDRAW | CS_VREDRAW;
winc.lpfnWndProc = proc;
winc.cbClsExtra = winc.cbWndExtra = 0;
winc.hInstance = GetModuleHandle(NULL);
winc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
winc.hCursor = LoadCursor(NULL, IDC_ARROW);
winc.hbrBackground = hbr_bg;
winc.lpszMenuName = NULL;
winc.lpszClassName = caption;
if (!RegisterClass(&winc)) return -1;
return 0;
}
//======================================================================================
//============ 以下 メインウィンドウ ・ ビューウィンドウ ==============================
//======================================================================================
// ビューウィンドウ・プロシージャ
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 WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
static DOCK_SYS dock_sys; // ウィンドウ・ハンドルの一括化 構造体
static HWND hview;
static n=0;
switch (msg) {
case WM_CREATE:
hview= CreateWindowEx(
WS_EX_TOOLWINDOW, TEXT("child_view"), NULL, WS_CHILD | WS_VISIBLE,
0, 0, 200, 100, hwnd, NULL, ((LPCREATESTRUCT)(lp))->hInstance, NULL
);
dock_sys.create(hwnd, hview);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_NCACTIVATE: // タイトルバーのアクティブ化・非アクティブ化
dock_sys.on_ncactivate(hwnd, wp);
break;
case WM_KEYUP:
case WM_KEYDOWN:
dock_sys.tool_drag();
break;
case WM_SIZE:
dock_sys.recalc_layout();
break;
case WM_LBUTTONDOWN:
n++;
if ((n%6)==0) dock_sys.do_dock(DOCK_BAR::ID_TOP);
else if ((n%6)==1) dock_sys.do_dock(DOCK_BAR::ID_LEFT);
else if ((n%6)==2) dock_sys.do_dock(DOCK_BAR::ID_BOTTOM);
else if ((n%6)==3) dock_sys.do_dock(DOCK_BAR::ID_RIGHT);
else if ((n%6)==4) dock_sys.do_float(200, 200, 1);
else if ((n%6)==5) dock_sys.do_float(200, 200, 1);
break;
}
return DefWindowProc(hwnd, msg, wp, lp);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, int nCmdShow ) {
MSG msg;
WNDCLASS winc;
HWND hwnd;
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; // ビューウィンドウ
hwnd = CreateWindow(
TEXT("main_frame"), TEXT("ドッキング・ツールバーのテスト"),
WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN,
100, 100, 240, 200, NULL, NULL, hInstance, NULL
);
if (hwnd == NULL) return -1;
while(GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
|
・ドッキング先の描画 |
|
| ドッキング先を描画するためにドッキング先判定関数get_docking_by_drag()を用意します。 そしてドラッグ中とドラッグ終了の時にその関数を呼出します。 |
|
![]() |
![]() |
|
| ドッキング先は上部 | ドッキング先は左部 | |
![]() |
![]() |
![]() |
| ドッキング先は下部 | ドッキング先は右部 | フローティングしようとしている |
| docking_test_04_2.cpp |
#define _WIN32_IE 0x0501
#include <windows.h>
#include <commctrl.h>
// カスタムFillRect(矩形塗りつぶし)
FillRect(HDC hdc, int left, int top, int right, int bottom, HBRUSH hbr){
RECT rc;
rc.left = left;
rc.top = top;
rc.right = right;
rc.bottom = bottom;
FillRect(hdc, &rc, hbr);
}
// ドック・コンテキスト・構造体
class DOCK_CONTEXT {
public:
HRGN m_apply_rgn, m_work_rgn, m_work_rgn2, m_last_rgn, m_target_rgn; // リージョン
HDC m_desk_hdc; // デスクトップDC
int m_now_dragging; // ドラッグ中なら==1 そうでないなら==0
SIZE m_drag_size; // ドラッグ中の矩形サイズ
POINT m_cursor_offset; // ドラッグ中の矩形位置のマウス位置との差分
HBRUSH m_desk_old_brush; // デスクトップDC・ブラシ
HBRUSH m_half_tone_brush; // ハーフトーン・ブラシ
DOCK_CONTEXT(); // コンストラクタ
~DOCK_CONTEXT(); // デストラクタ
void start_drag(HWND hwnd); // ドラッグ開始
void drag(RECT target_rc, int target_rc_fill, int flip); // ドラッグ中
void end_drag(); // ドラッグ終了
void get_drag_rect(RECT *p_drag_rc); // ドラッグ矩形の取得
};
// クラスの仮宣言
class DOCK_SYS;
// ツールバークラス
class TOOL_BAR {
public:
HWND m_hwnd, m_control_tool_bar, m_popup_frame; // 各HWND
DOCK_SYS *m_p_dock_sys; // ドッキング・ツールバー統括クラス・ポインタ
int m_floating_flag; // フローティング・フラグ 浮いてれば==1 そうでないなら==0
int m_vh_type, m_parent_dock_id;
SIZE *m_p_size, m_horz_size, m_vert_size;
create(DOCK_SYS *p_dock_sys, TBBUTTON *tb_buttons, int tb_button_len); // ツールバー生成
set_horz(); // ツールバー・横型に変更
set_vert(); // ツールバー・縦型に変更
void renew_size(); // サイズ更新
// ポップアップ・フレーム・プロシージャ
PopupProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
static LRESULT CALLBACK StaticPopupProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
// ツールバー・プロシージャ
ToolBarFrameProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
static LRESULT CALLBACK StaticToolBarFrameProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
};
// ドックバークラス
class DOCK_BAR {
public:
enum { ID_TOP, ID_LEFT, ID_BOTTOM, ID_RIGHT, ID_DOCK_BAR_SIZE };
create(HWND main_hwnd, int pos_id);
HWND m_hwnd;
int m_vh_type; // 縦なら=='V' 横なら=='H'
};
// ドッキング・ツールバー統括クラス
class DOCK_SYS {
public:
HWND m_main_frame, m_hview; // 各HWND
int m_now_nc_activing; // WM_NCACTIVATEフラグ WM_NCACTIVATEの多重再送を防ぐ
DOCK_CONTEXT m_dock_context; // ドック・コンテキスト・構造体
DOCK_BAR m_dock_bar[4]; // ドックバークラス
TOOL_BAR m_tool_bar; // ツールバークラス
regist_winc(char *caption, HBRUSH hbr_bg, WNDPROC proc); // ウィンドウクラス登録
create(HWND main_hwnd, HWND child_view_hwnd); // 生成
void on_ncactivate(HWND hwnd, WPARAM wp); // WM_NCACTIVATE 処理
void recalc_layout(); // 子ウィンドウ位置再計算
void do_dock(int pos_id); // ドッキング実行
void do_float(int x, int y, int flip); // フローティング実行
void tool_on_lbuttondown(HWND hwnd); // ツールバー・マウス左ボタン押下
void tool_on_lbuttonup(); // ツールバー・マウス左ボタン押上
void tool_on_mousemove(); // ツールバー・マウス移動
void tool_drag(); // ツールバー・ドラッグ
void tool_on_keydown(WPARAM wp, LPARAM lp); // ツールバー・キー押下
void tool_on_keyup(WPARAM wp, LPARAM lp); // ツールバー・キー押上
get_docking_by_drag(RECT *p_drag_rc, int *p_flip); // ドラッグの時のドッキング先取得
} ;
// コンストラクタ
DOCK_CONTEXT::DOCK_CONTEXT(){
WORD gray_pattern[8];
HBITMAP gray_bitmap;
int i;
m_now_dragging=0;
// ハーフトーン・パレット生成
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) {
m_half_tone_brush = ::CreatePatternBrush(gray_bitmap);
DeleteObject(gray_bitmap);
}
}
// デストラクタ
DOCK_CONTEXT::~DOCK_CONTEXT(){
DeleteObject(m_half_tone_brush); // ハーフトーン・パレット破棄
}
// ドラッグ矩形の取得
void DOCK_CONTEXT::get_drag_rect(RECT *p_drag_rc){
POINT po;
GetCursorPos(&po);
p_drag_rc->left=po.x-m_cursor_offset.x;
p_drag_rc->top =po.y-m_cursor_offset.y;
p_drag_rc->right = p_drag_rc->left + m_drag_size.cx;
p_drag_rc->bottom= p_drag_rc->top + m_drag_size.cy;
}
// ドラッグ開始
void DOCK_CONTEXT::start_drag(HWND hwnd){
HWND desk_hwnd;
RECT rc;
m_now_dragging=1;
SetCapture(hwnd);
// デスクトップDCを得る
desk_hwnd=GetDesktopWindow();
LockWindowUpdate(desk_hwnd);
m_desk_hdc= GetDCEx(desk_hwnd, NULL, DCX_WINDOW|DCX_CACHE|DCX_LOCKWINDOWUPDATE);
m_desk_old_brush=(HBRUSH)SelectObject(m_desk_hdc, m_half_tone_brush);
// ドラッグ・サイズとマウスとの座標差
GetWindowRect(hwnd, &rc);
m_drag_size.cx = rc.right-rc.left;
m_drag_size.cy = rc.bottom-rc.top;
GetCursorPos(&m_cursor_offset);
m_cursor_offset.x -= rc.left;
m_cursor_offset.y -= rc.top;
// リージョン生成
m_apply_rgn = CreateRectRgn(0, 0, 0, 0);
m_work_rgn = CreateRectRgn(0, 0, 0, 0);
m_work_rgn2 = CreateRectRgn(0, 0, 0, 0);
m_last_rgn = CreateRectRgn(0, 0, 0, 0);
m_target_rgn = CreateRectRgn(0, 0, 0, 0);
}
// ドラッグ中
void DOCK_CONTEXT::drag(RECT target_rc, int target_rc_fill, int flip){
POINT po;
RECT rc;
SIZE size;
if (!target_rc_fill){
SetRectRgn(m_work_rgn, target_rc.left, target_rc.top, target_rc.right,target_rc.bottom );
SetRectRgn(m_work_rgn2, target_rc.left+4, target_rc.top+4, target_rc.right-4, target_rc.bottom-4);
CombineRgn(m_target_rgn, m_work_rgn, m_work_rgn2, RGN_XOR);
}
else SetRectRgn(m_target_rgn, target_rc.left, target_rc.top, target_rc.right, target_rc.bottom);
if (flip){ // 縦横変換
size.cx=m_drag_size.cy;
size.cy=m_drag_size.cx;
}
else size=m_drag_size;
GetCursorPos(&po);
po.x-=m_cursor_offset.x;
po.y-=m_cursor_offset.y;
SetRectRgn(m_work_rgn, po.x, po.y, po.x+size.cx, po.y+size.cy);
SetRectRgn(m_work_rgn2, po.x+4, po.y+4, po.x+size.cx-4, po.y+size.cy-4);
CombineRgn(m_apply_rgn, m_work_rgn, m_work_rgn2, RGN_XOR);
CombineRgn(m_work_rgn, m_apply_rgn, m_target_rgn, RGN_OR);
CombineRgn(m_apply_rgn, m_work_rgn, NULL, RGN_COPY);
CombineRgn(m_work_rgn, m_apply_rgn, m_last_rgn, RGN_XOR);
// 反転による矩形描画
GetRgnBox(m_work_rgn, &rc);
SelectObject(m_desk_hdc, m_work_rgn);
PatBlt(m_desk_hdc, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, PATINVERT);
// 最後に描画した部分保持(再反転して消去)
CombineRgn(m_last_rgn, m_apply_rgn, NULL, RGN_COPY);
}
// ドラッグ終了
void DOCK_CONTEXT::end_drag(){
RECT rc;
m_now_dragging=0;
ReleaseCapture();
// 最後に描画した部分を消去(再反転してるだけ)
GetRgnBox(m_apply_rgn, &rc);
SelectObject(m_desk_hdc, m_apply_rgn);
PatBlt(m_desk_hdc, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, PATINVERT);
// リージョン破棄
DeleteObject(m_apply_rgn);
DeleteObject(m_work_rgn);
DeleteObject(m_work_rgn2);
DeleteObject(m_last_rgn);
DeleteObject(m_target_rgn);
// デスクトップDCを解放
LockWindowUpdate(NULL);
if (m_desk_hdc) ReleaseDC(GetDesktopWindow(), m_desk_hdc);
SelectObject(m_desk_hdc, m_desk_old_brush);
}
// ツールバー・サイズ更新
void TOOL_BAR::renew_size(){
RECT rc;
int i, len, offset;
if (!IsWindow(m_control_tool_bar)) return ;
TBBUTTONINFO tbbi;
memset(&tbbi, 0, sizeof(TBBUTTONINFO));
tbbi.cbSize=sizeof(TBBUTTONINFO);
tbbi.dwMask=TBIF_STYLE ;
// 各ボタンから全体サイズ取得
len=SendMessage(m_control_tool_bar, TB_BUTTONCOUNT, 0, 0);
m_horz_size.cx=m_horz_size.cy=0;
m_vert_size.cx=m_vert_size.cy=0;
for(i=0;i<len;i++){
if (!SendMessage(m_control_tool_bar, TB_GETITEMRECT, i, (LPARAM) (&rc))) continue;
m_horz_size.cx+=rc.right-rc.left;
m_vert_size.cy+=rc.bottom-rc.top;
}
if (len>0){
i=SendMessage(m_control_tool_bar, TB_GETBUTTONSIZE, 0, 0);
m_horz_size.cy=HIWORD(i);
m_vert_size.cx=LOWORD(i);
}
// パディング(ツールバーのボタン周りの余白部分)取得
DWORD padding=SendMessage(m_control_tool_bar, TB_GETPADDING, 0, 0);
m_horz_size.cx+=LOWORD(padding);
m_horz_size.cy+=HIWORD(padding);
m_vert_size.cx+=LOWORD(padding);
m_vert_size.cy+=HIWORD(padding);
// グリッパーサイズ計算
offset=(m_floating_flag)? 0 : 10;
m_horz_size.cx+=offset;
m_vert_size.cy+=offset;
SetWindowPos(m_hwnd, NULL, 0,0, m_p_size->cx, m_p_size->cy, SWP_NOMOVE);
// コモンコントロールのサイズ変更は親フレームウィンドウのあとで行う。(サイズ補正されるから)
// コモンコントロールのサイズ変更と位置移動のSetWindowPosは別に呼ぶ(位置補正されるから)
SetWindowPos(m_control_tool_bar, NULL, 0, 0, m_p_size->cx, m_p_size->cy, SWP_NOMOVE);
if (m_vh_type=='H') { SetWindowPos(m_control_tool_bar, NULL, offset, 0, 0, 0, SWP_NOSIZE); }
else { SetWindowPos(m_control_tool_bar, NULL, 4, offset, 0, 0, SWP_NOSIZE); }
return;
}
// ツールバー・縦型に変更
TOOL_BAR::set_vert(){
if (!m_control_tool_bar) return 0;
m_vh_type='V';
int style=GetWindowLong(m_control_tool_bar, GWL_STYLE);
SetWindowLong(m_control_tool_bar, GWL_STYLE, style | CCS_VERT | CCS_NODIVIDER);
int i, len=SendMessage(m_control_tool_bar, TB_BUTTONCOUNT, 0, 0);
TBBUTTONINFO tbbi;
memset(&tbbi, 0, sizeof(TBBUTTONINFO));
tbbi.cbSize=sizeof(TBBUTTONINFO);
tbbi.dwMask=TBIF_STATE ;
for(i=0;i<len;i++){
SendMessage(m_control_tool_bar, TB_GETBUTTONINFO, i, (LPARAM)&tbbi);
tbbi.fsState |=TBSTATE_WRAP;
SendMessage(m_control_tool_bar, TB_SETBUTTONINFO, i, (LPARAM)&tbbi);
}
m_p_size = &m_vert_size;
}
// ツールバー・横型に変更
TOOL_BAR::set_horz(){
if (!m_control_tool_bar) return 0;
m_vh_type='H';
int style=GetWindowLong(m_control_tool_bar, GWL_STYLE);
SetWindowLong(m_control_tool_bar, GWL_STYLE, style & (~CCS_NODIVIDER) & (~CCS_VERT));
int i, len=SendMessage(m_control_tool_bar, TB_BUTTONCOUNT, 0, 0);
TBBUTTONINFO tbbi={0};
memset(&tbbi, 0, sizeof(TBBUTTONINFO));
tbbi.cbSize=sizeof(TBBUTTONINFO);
tbbi.dwMask=TBIF_STATE;
for(i=0;i<len;i++){
SendMessage(m_control_tool_bar, TB_GETBUTTONINFO, i, (LPARAM)&tbbi);
tbbi.fsState &= (~TBSTATE_WRAP);
SendMessage(m_control_tool_bar, TB_SETBUTTONINFO, i, (LPARAM)&tbbi);
}
m_p_size = &m_horz_size;
}
// ツールバー生成
TOOL_BAR::create(DOCK_SYS *p_dock_sys, TBBUTTON *tb_buttons, int tb_button_len){
m_floating_flag=0;
m_p_dock_sys=p_dock_sys;
m_p_size=&m_horz_size;
HINSTANCE hInstance=GetModuleHandle(NULL);
// ポップアップ・フレーム生成
m_popup_frame= CreateWindowEx(
WS_EX_TOOLWINDOW, TEXT("popup_frame"), NULL, WS_OVERLAPPEDWINDOW | WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
300, 170, 200, 100, p_dock_sys->m_main_frame, NULL, hInstance, (LPVOID)this
);
InitCommonControls();
// コモンコントロールのツールバー生成
m_control_tool_bar = CreateToolbarEx(p_dock_sys->m_main_frame, WS_CHILD | TBSTYLE_AUTOSIZE, 0, 6,
(HINSTANCE)HINST_COMMCTRL, IDB_STD_SMALL_COLOR, tb_buttons, tb_button_len, 0, 0, 0, 0, sizeof (TBBUTTON)
);
// ツールバーのフレーム生成
m_hwnd= CreateWindow(
TEXT("tool_bar_frame"), NULL, WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
0, 0, 200, 100, p_dock_sys->m_main_frame, NULL, hInstance, (LPVOID)this
);
SetParent(m_control_tool_bar, m_hwnd);
ShowWindow(m_control_tool_bar, SW_SHOWNA);
}
// ポップアップウィンドウ・プロシージャ(static版)
LRESULT CALLBACK TOOL_BAR::StaticPopupProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
TOOL_BAR *p_tool_bar =(TOOL_BAR*)GetWindowLong(hwnd, GWL_USERDATA);
if (!p_tool_bar && (msg==WM_NCCREATE || msg==WM_CREATE)){
p_tool_bar = (TOOL_BAR*)((LPCREATESTRUCT)lp)->lpCreateParams;
SetWindowLong(hwnd, GWL_USERDATA, (LONG)p_tool_bar);
}
if (p_tool_bar) return p_tool_bar->PopupProc(hwnd, msg, wp, lp);
return DefWindowProc(hwnd, msg, wp, lp);
}
// ポップアップウィンドウ・プロシージャ
TOOL_BAR::PopupProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
int res;
switch (msg) {
case WM_NCACTIVATE: // タイトルバーのアクティブ化・非アクティブ化
m_p_dock_sys->on_ncactivate(hwnd, wp);
break;
case WM_KEYDOWN: m_p_dock_sys->tool_on_keydown(wp, lp); break; // キー押下
case WM_KEYUP: m_p_dock_sys->tool_on_keyup(wp, lp); break; // キー押上
case WM_LBUTTONDOWN:m_p_dock_sys->tool_on_lbuttondown(hwnd); break; // ドラッグ開始
case WM_LBUTTONUP: m_p_dock_sys->tool_on_lbuttonup(); break; // ドラッグ終了
case WM_MOUSEMOVE: m_p_dock_sys->tool_on_mousemove(); break; // マウス移動(ドラッグ中)
case WM_NCHITTEST:
res=DefWindowProc(hwnd, msg, wp, lp);
if (res==HTCAPTION) return HTCLIENT;
return res;
}
return DefWindowProc(hwnd, msg, wp, lp);
}
// ツールバーのフレームウィンドウ・プロシージャ(static版)
LRESULT CALLBACK TOOL_BAR::StaticToolBarFrameProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp){
TOOL_BAR *p_tool_bar =(TOOL_BAR*)GetWindowLong(hwnd, GWL_USERDATA);
if (!p_tool_bar && (msg==WM_NCCREATE || msg==WM_CREATE)){
p_tool_bar = (TOOL_BAR*)((LPCREATESTRUCT)lp)->lpCreateParams;
SetWindowLong(hwnd, GWL_USERDATA, (LONG)p_tool_bar);
}
if (p_tool_bar) return p_tool_bar->ToolBarFrameProc(hwnd, msg, wp, lp);
return DefWindowProc(hwnd, msg, wp, lp);
}
// ツールバーのフレームウィンドウ・プロシージャ
TOOL_BAR::ToolBarFrameProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
RECT rc, rc2;
HDC hdc;
PAINTSTRUCT ps;
switch (msg) {
case WM_PAINT:
hdc = BeginPaint(hwnd , &ps);
GetClientRect(hwnd, &rc);
FillRect(hdc, 0, 0, rc.right, 1, GetSysColorBrush(COLOR_BTNSHADOW));
FillRect(hdc, 0, 1, rc.right, 2, GetSysColorBrush(COLOR_BTNHILIGHT));
if (!m_floating_flag){
// グリッパー描画
GetClientRect(hwnd, &rc);
if (m_vh_type=='H') { rc.left+=2; rc.right=rc.left+3; rc.top+=1; rc.bottom-=2; }
else { rc.left+=1; rc.right=rc.left+2; rc.top+=1; rc.bottom-=2; }
FillRect(hdc, rc.left, rc.top, rc.right-1, rc.top+1, GetSysColorBrush(COLOR_BTNHILIGHT));
FillRect(hdc, rc.left, rc.top, rc.left+1, rc.bottom-1, GetSysColorBrush(COLOR_BTNHILIGHT));
FillRect(hdc, rc.right, rc.top, rc.right-1, rc.bottom, GetSysColorBrush(COLOR_BTNSHADOW));
FillRect(hdc, rc.left, rc.bottom, rc.right, rc.bottom-1, GetSysColorBrush(COLOR_BTNSHADOW));
}
EndPaint(hwnd , &ps);
break;
case WM_LBUTTONDOWN:m_p_dock_sys->tool_on_lbuttondown(hwnd);break; // マウス左ボタン押下(ドラッグ終了)
case WM_LBUTTONUP: m_p_dock_sys->tool_on_lbuttonup(); break; // マウス左ボタン押上(ドラッグ開始)
case WM_MOUSEMOVE: m_p_dock_sys->tool_on_mousemove(); break; // マウス移動(ドラッグ中)
}
return DefWindowProc(hwnd, msg, wp, lp);
}
// ドックバー生成
DOCK_BAR::create(HWND main_hwnd, int pos_id){
HINSTANCE hInstance=GetModuleHandle(NULL);
if (pos_id==ID_TOP || pos_id==ID_BOTTOM) m_vh_type='H';
else m_vh_type='V';
m_hwnd = CreateWindow(
TEXT("dock_bar_frame"), NULL, WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
0, 0, 200, 100, main_hwnd, NULL, hInstance, (LPVOID)this
);
}
// ツールバー・キー押下
void DOCK_SYS::tool_on_keydown(WPARAM wp, LPARAM lp){
tool_drag(); // ツールバー・ドラッグ
}
// ツールバー・キー押上
void DOCK_SYS::tool_on_keyup(WPARAM wp, LPARAM lp){
tool_drag(); // ツールバー・ドラッグ
}
// マウス移動
void DOCK_SYS::tool_on_mousemove(){
tool_drag(); // ツールバー・ドラッグ
}
// マウス左ボタン押下
void DOCK_SYS::tool_on_lbuttondown(HWND hwnd){
if (m_dock_context.m_now_dragging) return;
m_dock_context.start_drag(hwnd); // ドラッグ開始
}
// マウス左ボタン押上
void DOCK_SYS::tool_on_lbuttonup(){
if (!m_dock_context.m_now_dragging) return;
m_dock_context.end_drag(); // ドラッグ終了
RECT drag_rc;
int dock_id, flip;
m_dock_context.get_drag_rect(&drag_rc); // ドラッグ矩形の取得
dock_id = get_docking_by_drag(&drag_rc, &flip); // ドラッグの時のドッキング先取得
if (dock_id>=0) do_dock(dock_id);
else do_float(drag_rc.left, drag_rc.top, flip); // フローティング判定
}
// ドラッグの時のドッキング先取得
int DOCK_SYS::get_docking_by_drag(RECT *p_drag_rc, int *p_flip){
RECT dock_rc;
*p_flip = (GetKeyState(VK_SHIFT)<0)? 1 : 0;
int forced_float = (*p_flip || (GetKeyState(VK_CONTROL)<0))? 1: 0; // 強制フロート
// ドッキング・フローティング判定
if (!forced_float){
// ドッキング判定
for(int i=0;i<DOCK_BAR::ID_DOCK_BAR_SIZE;i++){
GetWindowRect(m_dock_bar[i].m_hwnd, &dock_rc);
if (p_drag_rc->left<=dock_rc.right && p_drag_rc->right>=dock_rc.left
&& p_drag_rc->top<=dock_rc.bottom && p_drag_rc->bottom>=dock_rc.top){
return i;
}
}
}
// フローティング判定
return -1;
}
// ツールバー・ドラッグ
void DOCK_SYS::tool_drag(){
if (!m_dock_context.m_now_dragging) return;
int filp = (GetKeyState(VK_SHIFT)<0)? 1 : 0;
int dock_id, flip;
RECT drag_rc, rc;
m_dock_context.get_drag_rect(&drag_rc); // ドラッグ矩形の取得
dock_id = get_docking_by_drag(&drag_rc, &flip); // ドラッグの時のドッキング先取得
if (dock_id>=0){
GetWindowRect(m_dock_bar[dock_id].m_hwnd, &rc);
rc.right = rc.left + m_tool_bar.m_p_size->cx;
rc.bottom = rc.top + m_tool_bar.m_p_size->cy;
if (m_tool_bar.m_floating_flag || m_tool_bar.m_parent_dock_id!=dock_id){
if (m_dock_bar[dock_id].m_vh_type!=m_tool_bar.m_vh_type){
rc.right = rc.left + m_tool_bar.m_p_size->cy;
rc.bottom = rc.top + m_tool_bar.m_p_size->cx;
}
if (dock_id==DOCK_BAR::ID_TOP) rc.bottom = rc.top + 4;
if (dock_id==DOCK_BAR::ID_LEFT) rc.right = rc.left + 4;
if (dock_id==DOCK_BAR::ID_BOTTOM) rc.bottom = rc.top - 4;
if (dock_id==DOCK_BAR::ID_RIGHT) rc.right = rc.left - 4;
}
}
else rc.left=rc.top=rc.right=rc.bottom=0;
int fill_flag = (m_tool_bar.m_floating_flag || m_tool_bar.m_parent_dock_id!=dock_id);
m_dock_context.drag(rc, fill_flag, filp); // ドラッグ中
};
// ドッキングシステム生成
DOCK_SYS::create(HWND main_hwnd, HWND child_view_hwnd){
m_now_nc_activing=0;
m_main_frame=main_hwnd;
m_hview=child_view_hwnd;
regist_winc(TEXT("popup_frame"), (HBRUSH)(COLOR_BTNFACE+1), TOOL_BAR::StaticPopupProc); // フローティング・フレーム
regist_winc(TEXT("tool_bar_frame"), (HBRUSH)(COLOR_BTNFACE+1), TOOL_BAR::StaticToolBarFrameProc); // ツールバーの外枠
regist_winc(TEXT("dock_bar_frame"), (HBRUSH)(COLOR_BTNFACE+1), DefWindowProc); // ドックバー
m_dock_bar[DOCK_BAR::ID_TOP].create(main_hwnd, DOCK_BAR::ID_TOP);
m_dock_bar[DOCK_BAR::ID_LEFT].create(main_hwnd, DOCK_BAR::ID_LEFT);
m_dock_bar[DOCK_BAR::ID_BOTTOM].create(main_hwnd, DOCK_BAR::ID_BOTTOM);
m_dock_bar[DOCK_BAR::ID_RIGHT].create(main_hwnd, DOCK_BAR::ID_RIGHT);
// ツールバーの生成
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 },
};
m_tool_bar.create(this, tool_bar_buttons, 6);
do_dock(0);
}
// タイトルバーのアクティブ化・非アクティブ化
void DOCK_SYS::on_ncactivate(HWND hwnd, WPARAM wp){
if (m_now_nc_activing) return;
m_now_nc_activing=1;
SendMessage(m_main_frame, WM_NCACTIVATE, wp, 0);
SendMessage(m_tool_bar.m_popup_frame, WM_NCACTIVATE, wp, 0);
m_now_nc_activing=0;
}
// ドッキング
void DOCK_SYS::do_dock(int pos_id){
m_tool_bar.m_floating_flag = 0;
m_tool_bar.m_parent_dock_id = pos_id;
if (m_dock_bar[pos_id].m_vh_type=='V') m_tool_bar.set_vert();
else m_tool_bar.set_horz();
SetParent(m_tool_bar.m_hwnd, m_dock_bar[pos_id].m_hwnd);
ShowWindow(m_tool_bar.m_popup_frame, SW_HIDE);
m_tool_bar.renew_size(); // ツールバー・サイズ更新
recalc_layout();
}
// フローティング
void DOCK_SYS::do_float(int x, int y, int flip){
if (flip){
if (m_tool_bar.m_vh_type=='V') m_tool_bar.set_horz();
else m_tool_bar.set_vert();
}
SetParent(m_tool_bar.m_hwnd, m_tool_bar.m_popup_frame);
m_tool_bar.m_floating_flag = 1;
m_tool_bar.renew_size(); // ツールバー・サイズ更新
// ポップアップ・ウィンドウ位置サイズ
RECT rc;
rc.left=rc.top=0;
rc.right=m_tool_bar.m_p_size->cx;
rc.bottom=m_tool_bar.m_p_size->cy;
AdjustWindowRectEx(&rc, GetWindowLong(m_tool_bar.m_popup_frame, GWL_STYLE),
FALSE, GetWindowLong(m_tool_bar.m_popup_frame, GWL_EXSTYLE));
SetWindowPos(m_tool_bar.m_popup_frame, NULL, x, y, rc.right-rc.left, rc.bottom-rc.top, 0);
ShowWindow(m_tool_bar.m_popup_frame, SW_SHOW);
recalc_layout();
}
// レイアウト再計算
void DOCK_SYS::recalc_layout(){
if (IsIconic(m_main_frame)) return;
RECT rc, main_rc;
SIZE tool_size=*m_tool_bar.m_p_size;
// 子ウィンドウ位置の再計算
GetClientRect(m_main_frame, &rc);
main_rc = rc;
// ドックバー位置サイズ
if (!m_tool_bar.m_floating_flag) {
if (m_tool_bar.m_parent_dock_id==DOCK_BAR::ID_TOP) rc.top =tool_size.cy;
if (m_tool_bar.m_parent_dock_id==DOCK_BAR::ID_LEFT) rc.left =tool_size.cx;
if (m_tool_bar.m_parent_dock_id==DOCK_BAR::ID_BOTTOM) rc.bottom -=tool_size.cy;
if (m_tool_bar.m_parent_dock_id==DOCK_BAR::ID_RIGHT) rc.right -=tool_size.cx;
}
SetWindowPos(m_dock_bar[DOCK_BAR::ID_TOP].m_hwnd, NULL, rc.left, 0, rc.right, rc.top, 0);
SetWindowPos(m_dock_bar[DOCK_BAR::ID_LEFT].m_hwnd, NULL, 0, rc.top, rc.left, rc.bottom, 0);
SetWindowPos(m_dock_bar[DOCK_BAR::ID_BOTTOM].m_hwnd,NULL, rc.left, rc.bottom, rc.right-rc.left, main_rc.bottom-rc.bottom, 0);
SetWindowPos(m_dock_bar[DOCK_BAR::ID_RIGHT].m_hwnd, NULL, rc.right, rc.top, main_rc.right-rc.right, rc.bottom-rc.top, 0);
// ビューウィンドウ位置サイズ
if (IsWindow(m_hview)) {
SetWindowPos(m_hview, NULL, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, 0);
InvalidateRect(m_hview, NULL, TRUE);
}
}
// ウィンドウクラス登録
DOCK_SYS::regist_winc(char *caption, HBRUSH hbr_bg, WNDPROC proc){
WNDCLASS winc;
winc.style = CS_HREDRAW | CS_VREDRAW;
winc.lpfnWndProc = proc;
winc.cbClsExtra = winc.cbWndExtra = 0;
winc.hInstance = GetModuleHandle(NULL);
winc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
winc.hCursor = LoadCursor(NULL, IDC_ARROW);
winc.hbrBackground = hbr_bg;
winc.lpszMenuName = NULL;
winc.lpszClassName = caption;
if (!RegisterClass(&winc)) return -1;
return 0;
}
//======================================================================================
//============ 以下 メインウィンドウ ・ ビューウィンドウ ==============================
//======================================================================================
// ビューウィンドウ・プロシージャ
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 WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
static DOCK_SYS dock_sys; // ウィンドウ・ハンドルの一括化 構造体
static HWND hview;
static n=0;
switch (msg) {
case WM_CREATE:
hview= CreateWindowEx(
WS_EX_TOOLWINDOW, TEXT("child_view"), NULL, WS_CHILD | WS_VISIBLE,
0, 0, 200, 100, hwnd, NULL, ((LPCREATESTRUCT)(lp))->hInstance, NULL
);
dock_sys.create(hwnd, hview);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_NCACTIVATE: // タイトルバーのアクティブ化・非アクティブ化
dock_sys.on_ncactivate(hwnd, wp);
break;
case WM_KEYUP:
case WM_KEYDOWN:
dock_sys.tool_drag();
break;
case WM_SIZE:
dock_sys.recalc_layout();
break;
case WM_LBUTTONDOWN:
n++;
if ((n%6)==0) dock_sys.do_dock(DOCK_BAR::ID_TOP);
else if ((n%6)==1) dock_sys.do_dock(DOCK_BAR::ID_LEFT);
else if ((n%6)==2) dock_sys.do_dock(DOCK_BAR::ID_BOTTOM);
else if ((n%6)==3) dock_sys.do_dock(DOCK_BAR::ID_RIGHT);
else if ((n%6)==4) dock_sys.do_float(200, 200, 1);
else if ((n%6)==5) dock_sys.do_float(200, 200, 1);
break;
}
return DefWindowProc(hwnd, msg, wp, lp);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR lpCmdLine, int nCmdShow ) {
MSG msg;
WNDCLASS winc;
HWND hwnd;
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; // ビューウィンドウ
hwnd = CreateWindow(
TEXT("main_frame"), TEXT("ドッキング・ツールバーのテスト"),
WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN,
100, 100, 240, 200, NULL, NULL, hInstance, NULL
);
if (hwnd == NULL) return -1;
while(GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
|