[CNSSplitter : (Netscape style) splitter window ] |
Environment: Win 95/98 VC6 SP4
CNSSplitter is another implementation that resembles the Netscape style of splitter windows . This implementation intends to be used as static (a static splitter) . This class overrides several virtual functions that reside within MFC CSplitterWnd , since some functions aren't documented in MSDN , I will also describe them shortly.
View that show appearance of vertical/horizontal CNSSplitter windows (flat+non flat styles)
In order to use that class you need first to create that class. The code to create it resides in OnCreateClient of CFrameWnd derived class : you will find remarks within that code :
BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT /*lpcs*/, CCreateContext* pContext) { // create splitter window (static type) if (!m_wndSplitter.CreateStatic(this, 1, 2)) return FALSE; // create some view if (!m_wndSplitter.CreateView(0, 0, RUNTIME_CLASS(CLeftView), CSize(195, 100), pContext)) { m_wndSplitter.DestroyWindow(); return FALSE; } if (!m_wndSplitter1.CreateStatic(&m_wndSplitter,2,1,WS_CHILD|WS_VISIBLE, m_wndSplitter.IdFromRowCol(0,1))) { m_wndSplitter.DestroyWindow(); return FALSE; } if (!m_wndSplitter1.CreateView(0, 0, RUNTIME_CLASS(CLeftView), CSize(0, 200), pContext)) { m_wndSplitter1.DestroyWindow(); m_wndSplitter.DestroyWindow(); return FALSE; } if (!m_wndSplitter1.CreateView(1, 0, RUNTIME_CLASS(CCustomSplitterView), CSize(0, 0), pContext)) { m_wndSplitter1.DestroyWindow(); m_wndSplitter.DestroyWindow(); return FALSE; } // until here general calls that are called for any CSplitterWnd class. // calls unique to this class // set bitmaps to the splitters m_wndSplitter.SetBitmaps(IDB_BITMAP2,IDB_BITMAP1); m_wndSplitter1.SetBitmaps(IDB_BITMAP9,IDB_BITMAP10); // set optiize size : to be restored in double-click event m_wndSplitter1.SetOptimizedSize(100,FALSE); m_wndSplitter.SetOptimizedSize(300,FALSE); return TRUE; }I included handlers in mainframe class the set flat/non flat style to that splitter : as you see these are commonly used handlers , notice that both use IsFlat() function (of CNSSplitter) that returns a boolean value : is splitter has flat style or not.
void CMainFrame::OnFlatBar() { BOOL bFlat = m_wndSplitter.IsFlat(); m_wndSplitter.SetAsFlat((bFlat) ? FALSE : TRUE); m_wndSplitter1.SetAsFlat((bFlat) ? FALSE : TRUE); } void CMainFrame::OnUpdateFlatBar(CCmdUI* pCmdUI) { BOOL bFlat = m_wndSplitter.IsFlat(); if (bFlat) pCmdUI->SetText(_T("Unset Flat Splitter")); else pCmdUI->SetText(_T("Set Flat Splitter")); }This class includes very few public functions :
BOOL CNSSplitter::SetAsFlat(BOOL bFlat) { // ... // NOTICE : these are non documented variables whithis CSplitterWnd class m_cxSplitter = m_cySplitter = m_nSplitterWidth + 2 + 2; m_cxBorderShare = m_cyBorderShare = 0; m_cxSplitterGap = m_cySplitterGap = m_nSplitterWidth + 2 + 2; // ... if (!bFlat) // edge parameters (not flat) { // borders of non flat are wider than flat m_cxBorder = m_cyBorder = 2; } else { // flat parameters m_cxBorder = m_cyBorder = 1; } // ... redraws the window }
SetOptimizedSize sets a default size that is restored when user double clicks the bars : again position is calculated , the optimized position is restored only when user double clicks the bitmap area , and when window size (width/height) is bigger than "cx" parameter. you can also use the second field to determine orientation of measurements : size is set topleft (default) or from bottomright (bFromLeftTop=FALSE).I used a negetive value to descriv\be the second condition , by that I wont need to add additinal variable (BOOL=int).
An importent function is "int SetSplitterWidth(int nWidth)" which sets a new width for bar.
(do you want it narrow or wide?) if you call this function during runtime - after splitter has been shown : you need to redraw the splitter. Default values are being used : make splitter wider than usual.
IsVerticalSplitter() checks an internal variable of CSplitterWnd (m_nRows) of size of 1 : if so splitter is vertical, or else - horizontal.
IsBarOnEdge(); checks whether bar is on edge : this is useful when redrawing : to decide to which direction triangles direct.
or to efine behaviour when bar's bitmap is double clicked.
UseHandCursor,IsUsingHandCursor : These functions set hand cursor whenever mouse hovers bitmap. Netscape navigator uses arrow cursor.
the second returns state to user.
True work is done behind the scenes : OnCreate handler calls LoadCursors() (loads arrow and hand cursors)
and sets a default timer to splitter.
Area of bitmap is saved every time the bitmap is redrawn : during bar redrawn . It's saved in CRect variable named "m_rcLastImageRect"
an extra area is added to the bitmap to widden its "influence" over client area. (more is added on flat style) ,
simple function CRect::PtInRect(POINT) return that value.
IsBarOnEdge checks whether bar is on edge by first defining whether its vertical/horizontal splitter , then it checks an internal variable of m_pColInfo[0].nCurSize or m_pRowInfo[0].nCurSize m_pColInfo,m_pRowInfo are really pointers to array of structures :CRowColInfo : every allocated structure describes a single row or column , also sizes are tracked , and reset every time bar is moved.
struct CRowColInfo // from Afxext.h { int nMinSize; // below that try not to show int nIdealSize; // user set size // variable depending on the available size layout int nCurSize; // 0 => invisible, -1 => nonexistant };
Drawing functions are a bit more complicated : at start OnDrawSplitter is overrided and provides a non default implementation whenever splitter isn't flat. in any case it calls DrawSplitterBitmap after bar is redrawn. this function draws the bar (simple drawing functions) and then creates 2 triangles : notice that triangles are created 8 pixels from left/right edges(in horizontal splitter) etc. (all measurements are defined as consts so you can change them easily) .This function also stores the last image rect in :
// set last rectangle of image m_rcLastImageRect = CRect(ptSplitterBarStartPoint.x,ptSplitterBarStartPoint.y, ptSplitterBarStartPoint.x+szBitmapToDraw.cx, ptSplitterBarStartPoint.y+szBitmapToDraw.cy);The last actions are creation of triangles that are painted
// from here draw triangle arrows on bar (predefined Win32 arrow icons are ugly so werent used) CRgn rgn1,rgn2; CBrush br; br.CreateSolidBrush((bMouseOnBitmap) ? ::GetSysColor(COLOR_3DDKSHADOW) : ::GetSysColor(COLOR_GRAYTEXT)); // creation of triangles CreateArrowRgn(rgn1,m_rcLastImageRect,bVerticalSplitter,!bBarOnEdge,TRUE); CreateArrowRgn(rgn2,m_rcLastImageRect,bVerticalSplitter,!bBarOnEdge,FALSE); // paint triangles in bars pDC->FillRgn(&rgn1,&br); pDC->FillRgn(&rgn2,&br);CreateArrowRgn needs some optimization. (I know).
OnMouseMove handler checks whether state of hover bitmap is changed : from non-hover to hover or else. If changed we need to repaint the bitmap. When repainting we use "m_rcLastImageRect" rect of bitmap that was previously saved . (it will hold the proper rect because whenever rect canges it is recalculated , splitter are redrawn and new rect is re-saved)
void CNSSplitter::OnMouseMove(UINT nFlags, CPoint point) { CSplitterWnd::OnMouseMove(nFlags, point); BOOL bMouseHoversBitmap = IsMouseOnBitmap(point); if ((bMouseHoversBitmap && !m_bHoverBitmapDrawn) || (!bMouseHoversBitmap && m_bHoverBitmapDrawn)) { UpdateSplitterBitmap(TRUE); } }The function OnLButtonDblClk provides a custom implementation :
void CNSSplitter::OnLButtonDblClk(UINT nFlags, CPoint point) { if (!IsMouseOnBitmap(point)) // custom behaviour only on bitmap { CSplitterWnd::OnLButtonDblClk(nFlags, point); return; } int nPrev = m_nOptimizedSplitterSize; CRect rect; GetClientRect(&rect); if (IsVerticalSplitter()) { while (rect.Width() < m_nOptimizedSplitterSize) m_nOptimizedSplitterSize/=2; int nSize = (IsBarOnEdge()) ? m_nOptimizedSplitterSize : 0; if (nSize>=0) TrackColumnSize(nSize,0); else { nSize = min(rect.Width()+nSize,rect.Width()+m_nOptimizedSplitterSize); TrackColumnSize(nSize,0); } } else { // horizontal splitter while (rect.Width() < m_nOptimizedSplitterSize) m_nOptimizedSplitterSize /= 2; int nSize = (IsBarOnEdge()) ? m_nOptimizedSplitterSize : 0; //... } RecalcLayout(); m_nOptimizedSplitterSize = nPrev; CSplitterWnd::OnLButtonDblClk(nFlags, point); }it positions the bar in new place by using TrackColumnSize/TrackRowSize for vertical/horizontal splitters respecively. and redraws the window.
Move to downloads downloads