フローティング中のツールバーを斜めにサイズ変更できるようにする
ツールバーなどのコントロールバーはフローティング中、
通常縦横にしかサイズ変更ができません。
カーソルをあわせても斜めのカーソルがでません。
まずはそれを修正します。
コントロールバーがフローティングを実現する時には、
CMiniDockFrameWndというクラスのウィンドウの
子(孫)ウィンドウになっています。
このCMiniDockFrameWndがカーソルを制御しています。
下記のCMiniDockFrameWndの派生元クラス
CMiniFrameWndのOnNcHitTestの部分で
縦横にしかカーソルがでないようにしています。
if (dwStyle & MFS_4THICKFRAME)
{
switch (nHit)
{
case HTTOPLEFT:
return point.y < rectWindow.top ? HTTOP : HTLEFT;
case HTTOPRIGHT:
return point.y < rectWindow.top ? HTTOP : HTRIGHT;
case HTBOTTOMLEFT:
return point.y > rectWindow.bottom ? HTBOTTOM : HTLEFT;
case HTGROWBOX:
case HTBOTTOMRIGHT:
return point.y > rectWindow.bottom ? HTBOTTOM : HTRIGHT;
}
}
ですので、CMiniDockFrameWndのウィンドウスタイルから
MFS_4THICKFRAMEを消してしまいましょう。
CControlBarから継承したクラスCMyControlBarに
以下の二つの関数を追加すればOKです。
CMiniDockFrameWnd* CMyControlBar::GetMiniDockFrameWnd()
{
CWnd *pWnd;
if( m_hWnd )
{
if( pWnd = GetParent() )
{
if( pWnd = pWnd->GetParent() )
{
if( pWnd->IsKindOf( RUNTIME_CLASS( CMiniDockFrameWnd ) ) )
{
return (CMiniDockFrameWnd*)pWnd;
}
}
}
}
return 0;
}
void CMyControlBar::OnSize(UINT nType, int cx, int cy)
{
CControlBar::OnSize(nType, cx, cy);
if( GetMiniDockFrameWnd() )
{
GetMiniDockFrameWnd()->ModifyStyle( MFS_4THICKFRAME, 0 );
}
}
これで斜めにもカーソルがでるようになります。
…が、まだサイズ変更はできません。
この原因を確かめるべく、
CMiniDockFrameWndのOnNcLButtonDownから追っていくと、
pBar->m_pDockContext->StartResize(nHitTest, point);
↓
Stretch(pt);
という関数のこの部分
if (m_nHitTest == HTLEFT || m_nHitTest == HTRIGHT)
{
if (m_nHitTest == HTLEFT)
m_rectRequestedSize.left += ptOffset.x;
else
m_rectRequestedSize.right += ptOffset.x;
nLength = m_rectRequestedSize.Width();
}
else
{
dwMode |= LM_LENGTHY;
if (m_nHitTest == HTTOP)
m_rectRequestedSize.top += ptOffset.y;
else
m_rectRequestedSize.bottom += ptOffset.y;
nLength = m_rectRequestedSize.Height();
}
このように上下左右しか扱っていません。
なので、HitTestが斜めの時は斜めに操作できるように
処理を奪い取りましょう。
アプリケーションクラスに以下の処理を追加してください。
長いですが。
BOOL CMyApp::PreTranslateMessage(MSG* pMsg)
{
CMiniDockFrameWnd *pMiniWnd = 0;
BOOL bMiniWnd;
CPoint pos;
switch( pMsg->message )
{
case WM_NCLBUTTONDOWN:
pMiniWnd = m_myBar.GetMiniDockFrameWnd();
if( pMiniWnd && pMiniWnd->m_hWnd == pMsg->hwnd )
{
switch( pMsg->wParam )
{
case HTTOPLEFT:
case HTTOPRIGHT:
case HTBOTTOMLEFT:
case HTBOTTOMRIGHT:
pos.x = (LONG)pMsg->lParam & 0xFFFF;
pos.y = (LONG)pMsg->lParam >> 16;
StretchWnd( pMiniWnd, (int)pMsg->wParam, pos );
return TRUE;
}
}
break;
}
return CWinApp::PreTranslateMessage(pMsg);
}
#define m_rectRequestedSize m_rectDragHorz
#define m_rectActualSize m_rectDragVert
#define m_rectActualFrameSize m_rectFrameDragHorz
#define m_rectFrameBorders m_rectFrameDragVert
struct AUX_DATA
{
// system metrics
int cxVScroll, cyHScroll;
int cxIcon, cyIcon;
int cxBorder2, cyBorder2;
// device metrics for screen
int cxPixelsPerInch, cyPixelsPerInch;
// convenient system color
HBRUSH hbrWindowFrame;
HBRUSH hbrBtnFace;
// color values of system colors used for CToolBar
COLORREF clrBtnFace, clrBtnShadow, clrBtnHilite;
COLORREF clrBtnText, clrWindowFrame;
// standard cursors
HCURSOR hcurWait;
HCURSOR hcurArrow;
HCURSOR hcurHelp; // cursor used in Shift+F1 help
// special GDI objects allocated on demand
HFONT hStatusFont;
HFONT hToolTipsFont;
HBITMAP hbmMenuDot;
// other system information
BOOL bWin95; // TRUE if Windows 95 (not NT)
// Implementation
AUX_DATA();
~AUX_DATA();
void UpdateSysColors();
void UpdateSysMetrics();
};
extern AUX_DATA afxData;
void CMyApp::StretchWnd( CMiniDockFrameWnd *pMiniWnd, int nHitTest, CPoint pt )
{
int nPos = 1;
CControlBar* pBar = NULL;
while(pBar == NULL && nPos < pMiniWnd->m_wndDockBar.m_arrBars.GetSize())
{
CControlBar* pResult = (CControlBar*)pMiniWnd->m_wndDockBar.m_arrBars[nPos];
if (DWORD_PTR(pResult) <= 0xffff)
pBar = NULL;
else
pBar = pResult;
}
ASSERT(pBar != NULL);
ASSERT_KINDOF(CControlBar, pBar);
ASSERT(pBar->m_pDockContext != NULL);
// CBRS_SIZE_DYNAMIC toolbars cannot have the CBRS_FLOAT_MULTI style
ASSERT((pMiniWnd->m_wndDockBar.m_dwStyle & CBRS_FLOAT_MULTI) == 0);
ASSERT_VALID(pBar->m_pDockContext->m_pBar);
ASSERT(pBar->m_pDockContext->m_pBar->m_dwStyle & CBRS_SIZE_DYNAMIC);
pBar->m_pDockContext->m_bDragging = FALSE;
pBar->m_pDockContext->InitLoop();
// GetWindowRect returns screen coordinates(not mirrored)
// So if the desktop is mirrored then turn off mirroring
// for the desktop dc so that we draw correct focus rect
if (pBar->m_pDockContext->m_pDC->GetLayout() & LAYOUT_RTL)
pBar->m_pDockContext->m_pDC->SetLayout(LAYOUT_LTR);
// get true bar size (including borders)
CRect rect;
pBar->m_pDockContext->m_pBar->GetWindowRect(rect);
pBar->m_pDockContext->m_ptLast = pt;
pBar->m_pDockContext->m_nHitTest = nHitTest;
CSize size = pBar->m_pDockContext->m_pBar->CalcDynamicLayout(0, LM_HORZ | LM_MRUWIDTH);
pBar->m_pDockContext->m_rectRequestedSize = CRect(rect.TopLeft(), size);
pBar->m_pDockContext->m_rectActualSize = CRect(rect.TopLeft(), size);
pBar->m_pDockContext->m_rectActualFrameSize = CRect(rect.TopLeft(), size);
// calculate frame rectangle
CMiniFrameWnd::CalcBorders(&pBar->m_pDockContext->m_rectActualFrameSize);
pBar->m_pDockContext->m_rectActualFrameSize.InflateRect(-afxData.cxBorder2, -afxData.cyBorder2);
pBar->m_pDockContext->m_rectFrameBorders = CRect(CPoint(0,0),
pBar->m_pDockContext->m_rectActualFrameSize.Size() - pBar->m_pDockContext->m_rectActualSize.Size());
// initialize tracking state and enter tracking loop
pBar->m_pDockContext->m_dwOverDockStyle = 0;
StretchMiniWnd( pBar, pt );
TrackMiniWnd( pBar );
return;
}
void CMyApp::StretchMiniWnd( CControlBar* pBar, CPoint pt )
{
CPoint ptOffset = pt - pBar->m_pDockContext->m_ptLast;
// offset all drag rects to new position
int nLength = 0;
DWORD dwMode = LM_HORZ;
switch( pBar->m_pDockContext->m_nHitTest )
{
case HTTOPLEFT:
pBar->m_pDockContext->m_rectRequestedSize.left += ptOffset.x;
pBar->m_pDockContext->m_rectRequestedSize.top += ptOffset.y;
break;
case HTTOPRIGHT:
pBar->m_pDockContext->m_rectRequestedSize.top += ptOffset.y;
pBar->m_pDockContext->m_rectRequestedSize.right += ptOffset.x;
break;
case HTBOTTOMLEFT:
pBar->m_pDockContext->m_rectRequestedSize.left += ptOffset.x;
pBar->m_pDockContext->m_rectRequestedSize.bottom += ptOffset.y;
break;
case HTBOTTOMRIGHT:
pBar->m_pDockContext->m_rectRequestedSize.right += ptOffset.x;
pBar->m_pDockContext->m_rectRequestedSize.bottom += ptOffset.y;
break;
}
CSize size;
nLength = pBar->m_pDockContext->m_rectRequestedSize.Width();
nLength = (nLength >= 0) ? nLength : 0;
size = pBar->m_pDockContext->m_pBar->CalcDynamicLayout(nLength, dwMode);
dwMode |= LM_LENGTHY;
nLength = pBar->m_pDockContext->m_rectRequestedSize.Height();
nLength = (nLength >= 0) ? nLength : 0;
size = pBar->m_pDockContext->m_pBar->CalcDynamicLayout(nLength, dwMode);
CRect rectDesk;
HWND hWndDesk = ::GetDesktopWindow();
::GetWindowRect(hWndDesk, &rectDesk);
CRect rectTemp = pBar->m_pDockContext->m_rectActualFrameSize;
CRect rect;
switch( pBar->m_pDockContext->m_nHitTest )
{
case HTTOPLEFT:
rectTemp.left = rectTemp.right - (size.cx + pBar->m_pDockContext->m_rectFrameBorders.Width());
rectTemp.top = rectTemp.bottom - (size.cy + pBar->m_pDockContext->m_rectFrameBorders.Height());
if (rect.IntersectRect(rectDesk, rectTemp))
{
pBar->m_pDockContext->m_rectActualSize.left = pBar->m_pDockContext->m_rectActualSize.right - size.cx;
pBar->m_pDockContext->m_rectActualSize.top = pBar->m_pDockContext->m_rectActualSize.bottom - size.cy;
pBar->m_pDockContext->m_rectActualFrameSize.left = rectTemp.left;
pBar->m_pDockContext->m_rectActualFrameSize.top = rectTemp.top;
}
break;
case HTTOPRIGHT:
rectTemp.right = rectTemp.left + (size.cx + pBar->m_pDockContext->m_rectFrameBorders.Width());
rectTemp.top = rectTemp.bottom - (size.cy + pBar->m_pDockContext->m_rectFrameBorders.Height());
if (rect.IntersectRect(rectDesk, rectTemp))
{
pBar->m_pDockContext->m_rectActualSize.top = pBar->m_pDockContext->m_rectActualSize.bottom - size.cy;
pBar->m_pDockContext->m_rectActualSize.right = pBar->m_pDockContext->m_rectActualSize.left + size.cx;
pBar->m_pDockContext->m_rectActualFrameSize.top = rectTemp.top;
pBar->m_pDockContext->m_rectActualFrameSize.right = rectTemp.right;
}
break;
case HTBOTTOMLEFT:
rectTemp.left = rectTemp.right - (size.cx + pBar->m_pDockContext->m_rectFrameBorders.Width());
rectTemp.bottom = rectTemp.top + (size.cy + pBar->m_pDockContext->m_rectFrameBorders.Height());
if (rect.IntersectRect(rectDesk, rectTemp))
{
pBar->m_pDockContext->m_rectActualSize.left = pBar->m_pDockContext->m_rectActualSize.right - size.cx;
pBar->m_pDockContext->m_rectActualSize.bottom = pBar->m_pDockContext->m_rectActualSize.top + size.cy;
pBar->m_pDockContext->m_rectActualFrameSize.left = rectTemp.left;
pBar->m_pDockContext->m_rectActualFrameSize.bottom = rectTemp.bottom;
}
break;
case HTBOTTOMRIGHT:
rectTemp.right = rectTemp.left + (size.cx + pBar->m_pDockContext->m_rectFrameBorders.Width());
rectTemp.bottom = rectTemp.top + (size.cy + pBar->m_pDockContext->m_rectFrameBorders.Height());
if (rect.IntersectRect(rectDesk, rectTemp))
{
pBar->m_pDockContext->m_rectActualSize.right = pBar->m_pDockContext->m_rectActualSize.left + size.cx;
pBar->m_pDockContext->m_rectActualSize.bottom = pBar->m_pDockContext->m_rectActualSize.top + size.cy;
pBar->m_pDockContext->m_rectActualFrameSize.right = rectTemp.right;
pBar->m_pDockContext->m_rectActualFrameSize.bottom = rectTemp.bottom;
}
break;
}
pBar->m_pDockContext->m_ptLast = pt;
// update feedback
pBar->m_pDockContext->DrawFocusRect();
}
BOOL CMyApp::TrackMiniWnd( CControlBar *pBar )
{
// don't handle if capture already set
if (::GetCapture() != NULL)
return FALSE;
// set capture to the window which received this message
pBar->m_pDockContext->m_pBar->SetCapture();
ASSERT(pBar->m_pDockContext->m_pBar == CWnd::GetCapture());
// get messages until capture lost or cancelled/accepted
while (CWnd::GetCapture() == pBar->m_pDockContext->m_pBar)
{
MSG msg;
if (!::GetMessage(&msg, NULL, 0, 0))
{
AfxPostQuitMessage((int)msg.wParam);
break;
}
switch (msg.message)
{
case WM_LBUTTONUP:
if (pBar->m_pDockContext->m_bDragging)
pBar->m_pDockContext->EndDrag();
else
pBar->m_pDockContext->EndResize();
return TRUE;
case WM_MOUSEMOVE:
if (pBar->m_pDockContext->m_bDragging)
pBar->m_pDockContext->Move(msg.pt);
else
StretchMiniWnd( pBar, msg.pt);
break;
case WM_KEYUP:
if (pBar->m_pDockContext->m_bDragging)
pBar->m_pDockContext->OnKey((int)msg.wParam, FALSE);
break;
case WM_KEYDOWN:
if (pBar->m_pDockContext->m_bDragging)
pBar->m_pDockContext->OnKey((int)msg.wParam, TRUE);
if (msg.wParam == VK_ESCAPE)
{
pBar->m_pDockContext->CancelLoop();
return FALSE;
}
break;
case WM_RBUTTONDOWN:
pBar->m_pDockContext->CancelLoop();
return FALSE;
// just dispatch rest of the messages
default:
DispatchMessage(&msg);
break;
}
}
pBar->m_pDockContext->CancelLoop();
return FALSE;
}
ヘッダにもちゃんと上記で作成した関数の定義を
書いておいてください。
長いですがこれで完成です。
時間と機能があまりにも割に合わない作業でした…。