📄 atldock.h
字号:
template< class T, class TBase = CWindow, class TWinTraits = CControlWinTraits >
class ATL_NO_VTABLE CDockingPaneWindowImpl :
public CWindowImpl< T, TBase, TWinTraits >,
public CSplitterBar<CDockingPaneWindowImpl>
{
public:
DECLARE_WND_CLASS_EX(NULL, CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS, COLOR_WINDOW)
typedef CDockingPaneWindowImpl< T , TBase, TWinTraits > thisClass;
BEGIN_MSG_MAP(CDockingPaneWindowImpl)
MESSAGE_HANDLER(WM_PAINT, OnPaint)
MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
MESSAGE_HANDLER(WM_SETCURSOR, OnSetCursor)
MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
MESSAGE_HANDLER(WM_LBUTTONDOWN, OnButtonDown)
MESSAGE_HANDLER(WM_SIZE, OnSize)
MESSAGE_HANDLER(WM_DOCK_UPDATELAYOUT, OnRecalcSpace)
END_MSG_MAP()
CDockMap m_map; // Panels docking in this pane
// Pointers are owned by the parent window
DWORD m_dwExtStyle; // Mirror of extended style of parent window (dock window)
int m_cy; // Current size of panel
int m_cyOld; // Last size when panel was visible
short m_Side; // The side the panel is shown the
RECT m_rcSplitter; // Rectangle of the splitter
CDockingPaneWindowImpl() :
m_cy(0),
m_cyOld(DEFAULT_DOCKPANE_SIZE),
m_dwExtStyle(0L)
{
::SetRectEmpty(&m_rcSplitter);
}
// Implementation
void DockWindow(DOCKCONTEXT* ctx)
{
ATLASSERT(ctx);
if( m_map.GetSize() == 0 ) {
m_cy = m_cyOld;
ShowWindow(SW_SHOWNOACTIVATE);
}
ctx->Side = m_Side;
ctx->LastSide = m_Side;
::SetParent(ctx->hwndDocked, m_hWnd);
::SetParent(ctx->hwndChild, ctx->hwndDocked);
::ShowWindow(ctx->hwndDocked, SW_SHOWNOACTIVATE);
m_map.Add(ctx);
PostMessage(WM_DOCK_UPDATELAYOUT);
}
void UnDockWindow(DOCKCONTEXT* ctx)
{
ATLASSERT(ctx);
for( int i = 0; i < m_map.GetSize(); i++ ) {
if( m_map[i] == ctx ) {
m_map.RemoveAt(i);
::ShowWindow(ctx->hwndDocked, SW_HIDE);
break;
}
}
if( m_map.GetSize() == 0 ) {
if( m_dwExtStyle & DCK_EX_REMEMBERSIZE ) {
m_cyOld = m_cy; // remember pane size to until next time
}
m_cy = 0;
ShowWindow(SW_HIDE);
}
else {
SendMessage(WM_DOCK_UPDATELAYOUT);
}
}
void RepositionWindow(DOCKCONTEXT* ctx, int iPos)
{
if( iPos >= m_map.GetSize() ) return;
for( int i = 0; i < m_map.GetSize(); i++ ) {
if( m_map[i] == ctx ) {
DOCKCONTEXT* pCtx = m_map[iPos];
m_map.SetAtIndex(iPos, ctx);
m_map.SetAtIndex(i, pCtx);
break;
}
}
SendMessage(WM_DOCK_UPDATELAYOUT);
Invalidate();
}
// Message handlers
LRESULT OnRecalcSpace(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
{
if( m_map.GetSize() == 0 ) return 0;
for( int i = 0; i < m_map.GetSize(); i++ ) {
::SendMessage(m_map[i]->hwndDocked, WM_DOCK_UPDATELAYOUT, 0,0);
}
OnSize(0,0,0,bHandled);
return 0;
}
LRESULT OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
CPaintDC dc(m_hWnd);
// Draw splitter along the pane side
T* pT = static_cast<T*>(this);
pT->DrawSplitterBar((HDC) dc, !IsDockedVertically(m_Side), m_rcSplitter);
return 0;
}
LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
return 1; // handled, no background painting needed
}
LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
{
bHandled = FALSE;
if( m_map.GetSize() == 0 ) return 1;
T* pT = static_cast<T*>(this);
pT->UpdateLayout();
HDWP hdwp = BeginDeferWindowPos(m_map.GetSize());
for( int i = 0; i < m_map.GetSize(); i++ ) {
RECT& rc = m_map[i]->rcWindow;
::DeferWindowPos(hdwp, m_map[i]->hwndDocked, NULL, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOZORDER | SWP_NOACTIVATE);
}
EndDeferWindowPos(hdwp);
return 1;
}
LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& bHandled)
{
POINT ptPos = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
if( PtInSplitter(ptPos, m_Side, 0, m_rcSplitter) ) {
bool bVertical = !IsDockedVertically(m_Side);
::SetCursor( bVertical ? s_hVertCursor : s_hHorizCursor);
bHandled = FALSE;
}
return 0;
}
LRESULT OnButtonDown(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
{
POINT ptPos = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
if( PtInSplitter(ptPos, m_Side, 0, m_rcSplitter) ) {
// Click on the spiltter? Determine how har we can drag the pane and
// start tracking...
::ClientToScreen(m_hWnd,& ptPos);
m_ptStartDragPoint = m_ptEndDragPoint = m_ptDeltaDragPoint = ptPos;
//
RECT rcWin;
GetWindowRect(&rcWin);
m_rcTracker = m_rcSplitter;
::OffsetRect(&m_rcTracker, rcWin.left, rcWin.top);
//
::GetWindowRect(GetParent(),& m_rcTrackerBounds);
RECT rcLimit;
::SendMessage(GetParent(), WM_DOCK_QUERYRECT, DOCK_INFO_CHILD, (LPARAM)&rcLimit);
switch( m_Side ) {
case DOCK_LEFT: m_rcTrackerBounds.right = rcLimit.right; break;
case DOCK_RIGHT: m_rcTrackerBounds.left = rcLimit.left; break;
case DOCK_TOP: m_rcTrackerBounds.bottom = rcLimit.bottom; break;
case DOCK_BOTTOM: m_rcTrackerBounds.top = rcLimit.top; break;
}
// The actual dragging area is slightly smaller than the client area
::InflateRect(&m_rcTrackerBounds, -MIN_DOCKPANE_SIZE, -MIN_DOCKPANE_SIZE);
// Enter tracking loop
bool res = Track(false);
if( res ) {
int nOffset = ( !IsDockedVertically(m_Side) ? (m_ptEndDragPoint.y - m_ptStartDragPoint.y) : (m_ptEndDragPoint.x - m_ptStartDragPoint.x) );
if( m_Side == DOCK_RIGHT || m_Side == DOCK_BOTTOM ) nOffset = -nOffset;
m_cy += nOffset;
::SendMessage(GetParent(), WM_DOCK_UPDATELAYOUT, 0,0);
}
}
return 0;
}
// Track methods
void OnStretch(POINT& pt)
{
DrawGhostBar();
if( !IsDockedVertically(m_Side) ) {
int nOffset = pt.y - m_ptDeltaDragPoint.y;
if( m_rcTracker.top + nOffset <= m_rcTrackerBounds.top ) nOffset = m_rcTrackerBounds.top - m_rcTracker.top;
if( m_rcTracker.bottom + nOffset >= m_rcTrackerBounds.bottom ) nOffset = m_rcTrackerBounds.bottom - m_rcTracker.bottom;
::OffsetRect(&m_rcTracker, 0,nOffset);
m_ptDeltaDragPoint.y += nOffset;
}
else {
int nOffset = pt.x - m_ptDeltaDragPoint.x;
if( m_rcTracker.left + nOffset <= m_rcTrackerBounds.left ) nOffset = m_rcTrackerBounds.left - m_rcTracker.left;
if( m_rcTracker.right + nOffset >= m_rcTrackerBounds.right ) nOffset = m_rcTrackerBounds.right - m_rcTracker.right;
::OffsetRect(&m_rcTracker, nOffset,0);
m_ptDeltaDragPoint.x += nOffset;
}
DrawGhostBar();
}
void OnEndResize()
{
m_ptEndDragPoint = m_ptDeltaDragPoint;
}
// Overridables
void UpdateLayout()
{
if( m_map.GetSize() == 0 ) return;
if( !IsWindowVisible() ) return;
int nPanes = m_map.GetSize();
bool bVertical = IsDockedVertically(m_Side);
// Place side splitter for this docking area
RECT rect;
GetClientRect(&rect);
switch( m_Side ) {
case DOCK_LEFT:
::SetRect(&m_rcSplitter, rect.right - m_cxySplitter, rect.top, rect.right, rect.bottom);
rect.right -= m_cxySplitter;
break;
case DOCK_TOP:
::SetRect(&m_rcSplitter, rect.left, rect.bottom - m_cxySplitter, rect.right, rect.bottom);
rect.bottom -= m_cxySplitter;
break;
case DOCK_RIGHT:
::SetRect(&m_rcSplitter, rect.left, rect.top, rect.left + m_cxySplitter, rect.bottom);
rect.left += m_cxySplitter;
break;
case DOCK_BOTTOM:
::SetRect(&m_rcSplitter, rect.left, rect.top, rect.right, rect.top + m_cxySplitter);
rect.top += m_cxySplitter;
break;
}
// Place splitters in each child panel (except in the last one)
for( int i = 0; i < nPanes - 1; i++ ) {
::SendMessage(m_map[i]->hwndDocked, WM_DOCK_SETSPLITTER, DEFAULT_SPLITTER_SIZE, 0L);
}
// The last panel does not have a splitter
::SendMessage(m_map[i]->hwndDocked, WM_DOCK_SETSPLITTER, 0, 0L);
// Get actual height of all panels
int nActualHeight = 0;
for( i = 0; i < nPanes; i++ ) {
const RECT& rc = m_map[i]->rcWindow;
int iPaneHeight = (bVertical ? rc.bottom - rc.top : rc.right - rc.left);
if( iPaneHeight < 10 ) iPaneHeight = 30;
nActualHeight += iPaneHeight;
}
// Get height of docking area
int nTop, nHeight;
if( bVertical ) {
nTop = rect.top;
nHeight = rect.bottom - rect.top;
}
else {
nTop = rect.left;
nHeight = rect.right - rect.left;
}
// Distribute the difference among panels
int nDelta = ((nHeight - nActualHeight) / nPanes);
for( i = 0; i < nPanes; i++ ) {
const RECT& rc = m_map[i]->rcWindow;
int nSize = (bVertical ? rc.bottom - rc.top : rc.right - rc.left);
if( !m_map[i]->bKeepSize ) nSize += nDelta;
if( nSize < MIN_DOCKPANE_SIZE ) nSize = MIN_DOCKPANE_SIZE;
if( nTop + nSize >= nHeight ) nSize /= 2;
if( bVertical ) {
::SetRect(&m_map[i]->rcWindow, rect.left, nTop, rect.right, nTop + nSize);
}
else {
::SetRect(&m_map[i]->rcWindow, nTop, rect.top, nTop + nSize, rect.bottom);
}
nTop += nSize;
m_map[i]->bKeepSize = false;
}
// Stretch the last window to the size of the docking window
(bVertical ? m_map[nPanes - 1]->rcWindow.bottom : m_map[nPanes - 1]->rcWindow.right ) = nHeight;
}
};
class CDockingPaneWindow : public CDockingPaneWindowImpl<CDockingPaneWindow>
{
public:
DECLARE_WND_CLASS_EX(_T("WTL_DockingPaneWindow"), CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS, COLOR_WINDOW)
};
///////////////////////////////////////////////////////
// CDockingWindow
template< class T,
class TPaneWindow = CDockingPaneWindow,
class TDockWindow = CDockingPaneChildWindow,
class TFloatWindow = CFloatingWindow,
class TBase = CWindow,
class TWinTraits = CControlWinTraits >
class ATL_NO_VTABLE CDockingWindowImpl :
public CWindowImpl< T, TBase, TWinTraits >
{
public:
DECLARE_WND_CLASS_EX(NULL, CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS, NULL)
typedef CDockingWindowImpl< T , TBase, TWinTraits > thisClass;
BEGIN_MSG_MAP(CDockingWindowImpl)
MESSAGE_HANDLER(WM_CREATE, OnCreate)
MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
MESSAGE_HANDLER(WM_SIZE, OnSize)
MESSAGE_HANDLER(WM_DOCK_QUERYRECT, OnQueryRect)
MESSAGE_HANDLER(WM_DOCK_QUERYTRACK, OnQueryTrack)
MESSAGE_HANDLER(WM_DOCK_DOCK, OnDock)
MESSAGE_HANDLER(WM_DOCK_FLOAT, OnFloat)
MESSAGE_HANDLER(WM_DOCK_UNDOCK, OnUnDock)
MESSAGE_HANDLER(WM_DOCK_UNFLOAT, OnUnFloat)
MESSAGE_HANDLER(WM_DOCK_UPDATELAYOUT, OnSize)
MESSAGE_HANDLER(WM_DOCK_REPOSITIONWINDOW, OnRepositionWindow)
MESSAGE_HANDLER(WM_DOCK_CLIENT_CLOSE, OnClientClose)
END_MSG_MAP()
TPaneWindow m_panes[4]; // The 4 panel windows (one for each side)
CDockMap m_map; // The master map of dockable windows
HWND m_hwndClient; // The client window contained in the center
DWORD m_dwExtStyle; // Optional styles
SIZE m_sizeBorder; // Size of window borders
// Operations
void SetClient(HWND hWnd)
{
ATLASSERT(::IsWindow(hWnd));
ATLASSERT(::GetWindowLong(hWnd, GWL_STYLE) & WS_CHILD);
m_hwndClient = hWnd;
}
DWORD GetExtendedDockStyle() const
{
return m_dwExtStyle;
}
void SetExtendedDockStyle(DWORD dwStyle)
{
ATLASSERT(::IsWindow(m_hWnd));
m_dwExtStyle = dwStyle;
// Duplicate style settings to panels
for( short i = 0; i < 4; i++ ) m_panes[i].m_dwExtStyle = dwStyle;
}
BOOL IsDockPane(HWND hWnd) const
{
ATLASSERT(::IsWindow(hWnd));
for( int i = 0; i < m_map.GetSize(); i++ ) if( m_map[i]->hwndChild == hWnd ) return TRUE;
return FALSE;
}
void AddWindow(HWND hWnd, DWORD dwFlags = 0)
{
ATLASSERT(::IsWindow(hWnd));
// Create docking context
DOCKCONTEXT* ctx;
ATLTRY(ctx = new DOCKCONTEXT);
if( ctx == NULL ) return;
ctx->Side = DOCK_HIDDEN;
ctx->LastSide = DOCK_LEFT;
ctx->hwndChild = hWnd;
ctx->hwndRoot = m_hWnd;
::SetRect(&ctx->rcWindow, 0, 0, MIN_DOCKPANE_SIZE, MIN_DOCKPANE_SIZE);
ctx->sizeFloat.cx = ctx->sizeFloat.cy = DEFAULT_FLOAT_SIZE;
ctx->dwFlags = dwFlags;
ctx->bKeepSize = false;
ctx->hwndOrigPrnt = ::GetParent(hWnd);
// Create docking child
TDockWindow* wndDock;
ATLTRY(wndDock = new TDockWindow(ctx));
if( wndDock == NULL ) return;
wndDock->Create(m_hWnd, rcDefault, NULL);
ATLASSERT(::IsWindow(wndDock->m_hWnd));
ctx->hwndDocked = *wndDock;
// Create floating child
TFloatWindow* wndFloat;
TCHAR szCaption[128]; // max text length is 127 for floating caption
::GetWindowText(hWnd, szCaption, sizeof(szCaption)/sizeof(TCHAR));
ATLTRY(wndFloat = new TFloatWindow(ctx));
if( wndFloat == NULL ) return;
wndFloat->Create(m_hWnd, rcDefault, szCaption);
ATLASSERT(::IsWindow(wndFloat->m_hWnd));
ctx->hwndFloated = *wndFloat;
::SetParent(ctx->hwndChild, ctx->hwndDocked);
// Add Context to master list
m_map.Add(ctx);
}
BOOL RemoveWindow(HWND hWnd)
{
ATLASSERT(::IsWindow(hWnd));
DOCKCONTEXT* pCtx = _GetContext(hWnd);
ATLASSERT(pCtx);
if( pCtx == NULL ) return FALSE;
// Hide and destrow panel
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -