📄 dlgsplitter.cpp
字号:
m_rectTracker2.OffsetRect(-CX_BORDER, -CY_BORDER);
if (bAccept)
{
if (m_htTrack == vSplitterBox)
{
SplitRow(m_rectTracker.top);
}
else if (m_htTrack >= vSplitterBar1 && m_htTrack <= vSplitterBar15)
{
// set row height
TrackRowSize(m_rectTracker.top, m_htTrack - vSplitterBar1);
RecalcLayout();
}
else if (m_htTrack == hSplitterBox)
{
SplitColumn(m_rectTracker.left);
}
else if (m_htTrack >= hSplitterBar1 && m_htTrack <= hSplitterBar15)
{
// set column width
TrackColumnSize(m_rectTracker.left, m_htTrack - hSplitterBar1);
RecalcLayout();
}
else if (m_htTrack >= splitterIntersection1 &&
m_htTrack <= splitterIntersection225)
{
// set row height and column width
int row = (m_htTrack - splitterIntersection1) / 15;
int col = (m_htTrack - splitterIntersection1) % 15;
TrackRowSize(m_rectTracker.top, row);
TrackColumnSize(m_rectTracker2.left, col);
RecalcLayout();
}
else if (m_htTrack == bothSplitterBox)
{
// rectTracker is vSplitter (splits rows)
// rectTracker2 is hSplitter (splits cols)
SplitRow(m_rectTracker.top);
SplitColumn(m_rectTracker2.left);
}
}
if (pOldActiveView == GetActivePane())
{
if (pOldActiveView != NULL)
{
SetActivePane(-1, -1, pOldActiveView); // re-activate
pOldActiveView->SetFocus(); // make sure focus is restored
}
}
}
void CDlgSplitterWnd::GetHitRect(int ht, CRect& rectHit)
{
ASSERT_VALID(this);
CRect rectClient;
GetClientRect(&rectClient);
rectClient.InflateRect(-m_cxBorder, -m_cyBorder);
int cx = rectClient.Width();
int cy = rectClient.Height();
int x = rectClient.top;
int y = rectClient.left;
// hit rectangle does not include border
// m_rectLimit will be limited to valid tracking rect
// m_ptTrackOffset will be set to appropriate tracking offset
m_ptTrackOffset.x = 0;
m_ptTrackOffset.y = 0;
if (ht == vSplitterBox)
{
cy = m_cySplitter - (2*m_cyBorder - afxData.bWin4);
m_ptTrackOffset.y = -(cy / 2);
ASSERT(m_pRowInfo[0].nCurSize > 0);
m_rectLimit.bottom -= cy;
}
else if (ht == hSplitterBox)
{
cx = m_cxSplitter - (2*m_cxBorder - afxData.bWin4);
m_ptTrackOffset.x = -(cx / 2);
ASSERT(m_pColInfo[0].nCurSize > 0);
m_rectLimit.right -= cx;
}
else if (ht >= vSplitterBar1 && ht <= vSplitterBar15)
{
cy = m_cySplitter - (2*m_cyBorder - afxData.bWin4);
m_ptTrackOffset.y = -(cy / 2);
for (int row = 0; row < ht - vSplitterBar1; row++)
y += m_pRowInfo[row].nCurSize + m_cySplitterGap;
m_rectLimit.top = y;
y += m_pRowInfo[row].nCurSize + m_cyBorderShare + afxData.bWin4;
m_rectLimit.bottom -= cy;
}
else if (ht >= hSplitterBar1 && ht <= hSplitterBar15)
{
cx = m_cxSplitter - (2*m_cxBorder - afxData.bWin4);
m_ptTrackOffset.x = -(cx / 2);
for (int col = 0; col < ht - hSplitterBar1; col++)
x += m_pColInfo[col].nCurSize + m_cxSplitterGap;
m_rectLimit.left = x;
x += m_pColInfo[col].nCurSize + m_cxBorderShare + afxData.bWin4;
m_rectLimit.right -= cx;
}
else
{
TRACE1("Error: GetHitRect(%d): Not Found!\n", ht);
ASSERT(FALSE);
}
rectHit.right = (rectHit.left = x) + cx;
rectHit.bottom = (rectHit.top = y) + cy;
}
int CDlgSplitterWnd::HitTest(CPoint pt) const
{
ASSERT_VALID(this);
CRect rectClient;
GetClientRect(&rectClient);
rectClient.InflateRect(-m_cxBorder, -m_cyBorder);
CRect rectInside;
GetInsideRect(rectInside);
if (m_bHasVScroll && m_nRows < m_nMaxRows &&
CRect(rectInside.right, rectClient.top, rectClient.right,
rectClient.top + m_cySplitter - afxData.bWin4).PtInRect(pt))
{
return vSplitterBox;
}
if (m_bHasHScroll && m_nCols < m_nMaxCols &&
CRect(rectClient.left, rectInside.bottom,
rectClient.left + m_cxSplitter - afxData.bWin4,
rectClient.bottom).PtInRect(pt))
{
return hSplitterBox;
}
// for hit detect, include the border of splitters
CRect rect;
rect = rectClient;
for (int col = 0; col < m_nCols - 1; col++)
{
rect.left += m_pColInfo[col].nCurSize;
rect.right = rect.left + m_cxSplitterGap;
if (rect.PtInRect(pt))
break;
rect.left = rect.right;
}
rect = rectClient;
for (int row = 0; row < m_nRows - 1; row++)
{
rect.top += m_pRowInfo[row].nCurSize;
rect.bottom = rect.top + m_cySplitterGap;
if (rect.PtInRect(pt))
break;
rect.top = rect.bottom;
}
// row and col set for hit splitter (if not hit will be past end)
if (col != m_nCols - 1)
{
if (row != m_nRows - 1)
return splitterIntersection1 + row * 15 + col;
return hSplitterBar1 + col;
}
if (row != m_nRows - 1)
return vSplitterBar1 + row;
return noHit;
}
/////////////////////////////////////////////////////////////////////////////
// CDlgSplitterWnd tracking visuals
void CDlgSplitterWnd::OnInvertTracker(const CRect& rect)
{
ASSERT_VALID(this);
ASSERT(!rect.IsRectEmpty());
ASSERT((GetStyle() & WS_CLIPCHILDREN) == 0);
// pat-blt without clip children on
CDC* pDC = GetDC();
// invert the brush pattern (looks just like frame window sizing)
CBrush* pBrush = CDC::GetHalftoneBrush();
HBRUSH hOldBrush = NULL;
if (pBrush != NULL)
hOldBrush = (HBRUSH)SelectObject(pDC->m_hDC, pBrush->m_hObject);
pDC->PatBlt(rect.left, rect.top, rect.Width(), rect.Height(), PATINVERT);
if (hOldBrush != NULL)
SelectObject(pDC->m_hDC, hOldBrush);
ReleaseDC(pDC);
}
/////////////////////////////////////////////////////////////////////////////
// CDlgSplitterWnd commands
// Keyboard interface
BOOL CDlgSplitterWnd::DoKeyboardSplit()
{
ASSERT_VALID(this);
int ht;
if (m_nRows > 1 && m_nCols > 1)
ht = splitterIntersection1; // split existing row+col
else if (m_nRows > 1)
ht = vSplitterBar1; // split existing row
else if (m_nCols > 1)
ht = hSplitterBar1; // split existing col
else if (m_nMaxRows > 1 && m_nMaxCols > 1)
ht = bothSplitterBox; // we can split both
else if (m_nMaxRows > 1)
ht = vSplitterBox; // we can split rows
else if (m_nMaxCols > 1)
ht = hSplitterBox; // we can split columns
else
return FALSE; // can't split
// start tracking
StartTracking(ht);
CRect rect;
rect.left = m_rectTracker.Width() / 2;
rect.top = m_rectTracker.Height() / 2;
if (m_ptTrackOffset.y != 0)
rect.top = m_rectTracker.top;
if (m_ptTrackOffset.x != 0)
rect.left = m_bTracking2 ? m_rectTracker2.left :m_rectTracker.left;
rect.OffsetRect(-m_ptTrackOffset.x, -m_ptTrackOffset.y);
ClientToScreen(&rect);
SetCursorPos(rect.left, rect.top);
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
// Main drawing and layout
void CDlgSplitterWnd::OnDisplayChange()
{
if (!IsIconic() && IsWindowVisible())
RecalcLayout();
}
void CDlgSplitterWnd::OnSize(UINT nType, int cx, int cy)
{
if (nType != SIZE_MINIMIZED && cx > 0 && cy > 0)
RecalcLayout();
CWnd::OnSize(nType, cx, cy);
}
// Generic routine:
// for X direction: pInfo = m_pColInfo, nMax = m_nMaxCols, nSize = cx
// for Y direction: pInfo = m_pRowInfo, nMax = m_nMaxRows, nSize = cy
AFX_STATIC void AFXAPI _AfxLayoutRowCol(CDlgSplitterWnd::CRowColInfo* pInfoArray,
int nMax, int nSize, int nSizeSplitter)
{
ASSERT(pInfoArray != NULL);
ASSERT(nMax > 0);
ASSERT(nSizeSplitter > 0);
CDlgSplitterWnd::CRowColInfo* pInfo;
int i;
if (nSize < 0)
nSize = 0; // if really too small, layout as zero size
// start with ideal sizes
for (i = 0, pInfo = pInfoArray; i < nMax-1; i++, pInfo++)
{
if (pInfo->nIdealSize < pInfo->nMinSize)
pInfo->nIdealSize = 0; // too small to see
pInfo->nCurSize = pInfo->nIdealSize;
}
pInfo->nCurSize = INT_MAX; // last row/column takes the rest
for (i = 0, pInfo = pInfoArray; i < nMax; i++, pInfo++)
{
ASSERT(nSize >= 0);
if (nSize == 0)
{
// no more room (set pane to be invisible)
pInfo->nCurSize = 0;
continue; // don't worry about splitters
}
else if (nSize < pInfo->nMinSize && i != 0)
{
// additional panes below the recommended minimum size
// aren't shown and the size goes to the previous pane
pInfo->nCurSize = 0;
// previous pane already has room for splitter + border
// add remaining size and remove the extra border
ASSERT(afxData.cxBorder2 == afxData.cyBorder2);
(pInfo-1)->nCurSize += nSize + afxData.cxBorder2;
nSize = 0;
}
else
{
// otherwise we can add the second pane
ASSERT(nSize > 0);
if (pInfo->nCurSize == 0)
{
// too small to see
if (i != 0)
pInfo->nCurSize = 0;
}
else if (nSize < pInfo->nCurSize)
{
// this row/col won't fit completely - make as small as possible
pInfo->nCurSize = nSize;
nSize = 0;
}
else
{
// can fit everything
nSize -= pInfo->nCurSize;
}
}
// see if we should add a splitter
ASSERT(nSize >= 0);
if (i != nMax - 1)
{
// should have a splitter
if (nSize > nSizeSplitter)
{
nSize -= nSizeSplitter; // leave room for splitter + border
ASSERT(nSize > 0);
}
else
{
// not enough room - add left over less splitter size
ASSERT(afxData.cxBorder2 == afxData.cyBorder2);
pInfo->nCurSize += nSize;
if (pInfo->nCurSize > (nSizeSplitter - afxData.cxBorder2))
pInfo->nCurSize -= (nSizeSplitter - afxData.cyBorder2);
nSize = 0;
}
}
}
ASSERT(nSize == 0); // all space should be allocated
}
// repositions client area of specified window
// assumes everything has WS_BORDER or is inset like it does
// (includes scroll bars)
AFX_STATIC void AFXAPI _AfxDeferClientPos(AFX_SIZEPARENTPARAMS* lpLayout,
CWnd* pWnd, int x, int y, int cx, int cy, BOOL bScrollBar)
{
ASSERT(pWnd != NULL);
ASSERT(pWnd->m_hWnd != NULL);
if (bScrollBar)
{
// if there is enough room, draw scroll bar without border
// if there is not enough room, set the WS_BORDER bit so that
// we will at least get a proper border drawn
BOOL bNeedBorder = (cx <= CX_BORDER || cy <= CY_BORDER);
pWnd->ModifyStyle(bNeedBorder ? 0 : WS_BORDER,
bNeedBorder ? WS_BORDER : 0);
}
CRect rect(x, y, x+cx, y+cy);
// adjust for border size (even if zero client size)
if (!afxData.bWin4)
{
if (bScrollBar)
rect.InflateRect(CX_BORDER, CY_BORDER);
else
pWnd->CalcWindowRect(&rect);
}
// adjust for 3d border (splitter windows have implied border)
if ((pWnd->GetExStyle() & WS_EX_CLIENTEDGE) ||
pWnd->IsKindOf(RUNTIME_CLASS(CDlgSplitterWnd)))
rect.InflateRect(afxData.cxBorder2, afxData.cyBorder2);
// first check if the new rectangle is the same as the current
CRect rectOld;
pWnd->GetWindowRect(rectOld);
pWnd->GetParent()->ScreenToClient(&rectOld);
if (rect != rectOld)
AfxRepositionWindow(lpLayout, pWnd->m_hWnd, rect);
}
CWnd* CDlgSplitterWnd::GetSizingParent()
{
ASSERT_VALID(this);
if (!afxData.bWin4)
return NULL;
// size box is in lower right corner of this window
CRect rectClient;
GetClientRect(rectClient);
// find sizeable parent window
CWnd* pParent = this;
if (!(pParent->GetStyle() & WS_THICKFRAME))
pParent = GetParent();
// only allow if not maximized and has thick frame
ASSERT_VALID(pParent);
if ((pParent->GetStyle() & (WS_THICKFRAME|WS_MAXIMIZE)) == WS_THICKFRAME)
{
// convert client area of frame window relative to splitter
CRect rect;
pParent->GetClientRect(rect);
pParent->ClientToScreen(rect);
ScreenToClient(rect);
// must match exactly to get the size box
if (rectClient.BottomRight() == rect.BottomRight())
return pParent;
}
return NULL; // no sizeable parent found
}
void CDlgSplitterWnd::RecalcLayout()
{
ASSERT_VALID(this);
ASSERT(m_nRows > 0 && m_nCols > 0); // must have at least one pane
CRect rectClient;
GetClientRect(rectClient);
rectClient.InflateRect(-m_cxBorder, -m_cyBorder);
CRect rectInside;
GetInsideRect(rectInside);
// layout columns (restrict to possible sizes)
_AfxLayoutRowCol(m_pColInfo, m_nCols, rectInside.Width(), m_cxSplitterGap);
_AfxLayoutRowCol(m_pRowInfo, m_nRows, rectInside.Height(), m_cySplitterGap);
// adjust the panes (and optionally scroll bars)
// give the hint for the maximum number of HWNDs
AFX_SIZEPARENTPARAMS layout;
layout.hDWP = ::BeginDeferWindowPos((m_nCols + 1) * (m_nRows + 1) + 1);
// size of scrollbars
int cx = (rectClient.right - rectInside.right) - afxData.bNotWin4;
int cy = (rectClient.bottom - rectInside.bottom) - afxData.bNotWin4;
// reposition size box
if (m_bHasHScroll && m_bHasVScroll)
{
CWnd* pScrollBar = GetDlgItem(AFX_IDW_SIZE_BOX);
ASSERT(pScrollBar != NULL);
// fix style if necessary
BOOL bSizingParent = (GetSizingParent() != NULL);
// modifyStyle returns TRUE if style changes
if (pScrollBar->ModifyStyle(SBS_SIZEGRIP|SBS_SIZEBOX,
bSizingParent ? SBS_SIZEGRIP : SBS_SIZEBOX))
pScrollBar->Invalidate();
pScrollBar->EnableWindow(bSizingParent);
// reposition the size box
_AfxDeferClientPos(&layout, pScrollBar,
rectInside.right + afxData.bNotWin4,
rectInside.bottom + afxData.bNotWin4, cx, cy, TRUE);
}
// reposition scroll bars
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -