From 39ed87570bdb2f86969d4be821c94b722dc71179 Mon Sep 17 00:00:00 2001 From: Joe Ludwig Date: Wed, 26 Jun 2013 15:22:04 -0700 Subject: First version of the SOurce SDK 2013 --- mp/src/vgui2/vgui_controls/Splitter.cpp | 765 ++++++++++++++++++++++++++++++++ 1 file changed, 765 insertions(+) create mode 100644 mp/src/vgui2/vgui_controls/Splitter.cpp (limited to 'mp/src/vgui2/vgui_controls/Splitter.cpp') diff --git a/mp/src/vgui2/vgui_controls/Splitter.cpp b/mp/src/vgui2/vgui_controls/Splitter.cpp new file mode 100644 index 00000000..fc127a38 --- /dev/null +++ b/mp/src/vgui2/vgui_controls/Splitter.cpp @@ -0,0 +1,765 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#include +#include +#include +#include +#include "tier1/KeyValues.h" +#include + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + + +enum +{ + SPLITTER_HANDLE_WIDTH = 4 +}; + + +//----------------------------------------------------------------------------- +// Splitter handle +//----------------------------------------------------------------------------- +namespace vgui +{ + +class SplitterHandle : public Panel +{ + DECLARE_CLASS_SIMPLE( SplitterHandle, Panel ); + +public: + SplitterHandle( Splitter *parent, const char *name, SplitterMode_t mode, int nIndex ); + ~SplitterHandle(); + + virtual void ApplySchemeSettings( IScheme *pScheme ); + virtual void OnMousePressed( MouseCode code ); + virtual void OnMouseReleased( MouseCode code ); + virtual void OnCursorMoved( int x, int y ); + virtual void OnMouseDoublePressed( MouseCode code ); + +private: + SplitterMode_t m_nMode; + int m_nIndex; + bool m_bDragging; +}; + +} // end namespace vgui + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +SplitterHandle::SplitterHandle( Splitter *parent, const char *name, SplitterMode_t mode, int nIndex ) : BaseClass( parent, name ) +{ + int w, h; + parent->GetSize( w, h ); + + if ( mode == SPLITTER_MODE_HORIZONTAL ) + { + SetSize( w, SPLITTER_HANDLE_WIDTH ); + SetCursor( dc_sizens ); + } + else + { + SetSize( SPLITTER_HANDLE_WIDTH, h ); + SetCursor( dc_sizewe ); + } + + SetVisible( true ); + SetPaintBackgroundEnabled( false ); + SetPaintEnabled( false ); + SetPaintBorderEnabled( true ); + m_bDragging = false; + m_nIndex = nIndex; + m_nMode = mode; +} + + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +SplitterHandle::~SplitterHandle() +{ +} + + +//----------------------------------------------------------------------------- +// Scheme settings +//----------------------------------------------------------------------------- +void SplitterHandle::ApplySchemeSettings(IScheme *pScheme) +{ + // Cache off background color stored in SetSplitterColor + Color c = GetBgColor(); + SetBorder(pScheme->GetBorder("ButtonDepressedBorder")); + BaseClass::ApplySchemeSettings(pScheme); + SetBgColor( c ); +} + + +//----------------------------------------------------------------------------- +// Capture mouse when dragging +//----------------------------------------------------------------------------- +void SplitterHandle::OnMousePressed(MouseCode code) +{ + if ( !m_bDragging ) + { + input()->SetMouseCapture(GetVPanel()); + m_bDragging = true; + } +} + + +//----------------------------------------------------------------------------- +// Release mouse capture when finished dragging +//----------------------------------------------------------------------------- +void SplitterHandle::OnMouseReleased(MouseCode code) +{ + if ( m_bDragging ) + { + input()->SetMouseCapture(NULL); + m_bDragging = false; + } +} + + +//----------------------------------------------------------------------------- +// While dragging, update the splitter position +//----------------------------------------------------------------------------- +void SplitterHandle::OnCursorMoved(int x, int y) +{ + if (m_bDragging) + { + input()->GetCursorPos( x, y ); + Splitter *pSplitter = assert_cast( GetParent() ); + pSplitter->ScreenToLocal( x,y ); + pSplitter->SetSplitterPosition( m_nIndex, (m_nMode == SPLITTER_MODE_HORIZONTAL) ? y : x ); + } +} + + +//----------------------------------------------------------------------------- +// Double-click: make both panels on either side of the splitter equal size +//----------------------------------------------------------------------------- +void SplitterHandle::OnMouseDoublePressed( MouseCode code ) +{ + Splitter *pSplitter = assert_cast( GetParent() ); + pSplitter->EvenlyRespaceSplitters(); +} + + + +//----------------------------------------------------------------------------- +// Returns a panel that chains user configs +//----------------------------------------------------------------------------- +namespace vgui +{ + +class SplitterChildPanel : public EditablePanel +{ + DECLARE_CLASS_SIMPLE( SplitterChildPanel, EditablePanel ); + +public: + SplitterChildPanel( Panel *parent, const char *panelName ) : BaseClass( parent, panelName ) + { + SetPaintBackgroundEnabled( false ); + SetPaintEnabled( false ); + SetPaintBorderEnabled( false ); + } + + virtual ~SplitterChildPanel() {} + + // Children may have user config settings + bool HasUserConfigSettings() + { + return true; + } +}; + +} // end namespace vgui + +//----------------------------------------------------------------------------- +// +// Splitter panel +// +//----------------------------------------------------------------------------- +vgui::Panel *Splitter_V_Factory() +{ + return new Splitter( NULL, NULL, SPLITTER_MODE_VERTICAL, 1 ); +} + +vgui::Panel *Splitter_H_Factory() +{ + return new Splitter( NULL, NULL, SPLITTER_MODE_HORIZONTAL, 1 ); +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +Splitter::Splitter( Panel *parent, const char *name, SplitterMode_t mode, int nCount ) : BaseClass( parent, name ) +{ + Assert( nCount >= 1 ); + m_Mode = mode; + + SetPaintBackgroundEnabled( false ); + SetPaintEnabled( false ); + SetPaintBorderEnabled( false ); + + RecreateSplitters( nCount ); + + EvenlyRespaceSplitters(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +Splitter::~Splitter() +{ + m_Splitters.RemoveAll(); +} + +void Splitter::RecreateSplitters( int nCount ) +{ + int i; + int c = m_Splitters.Count(); + for ( i = 0; i < c; ++i ) + { + delete m_Splitters[ i ].m_pPanel; + delete m_Splitters[ i ].m_pHandle; + } + m_Splitters.RemoveAll(); + + for ( i = 0; i < (nCount + 1); ++i ) + { + char pBuffer[512]; + Q_snprintf( pBuffer, sizeof(pBuffer), "child%d", i ); + + int nIndex = m_Splitters.AddToTail( ); + SplitterChildPanel *pEditablePanel = new SplitterChildPanel( this, pBuffer ); + m_Splitters[nIndex].m_pPanel = pEditablePanel; + m_Splitters[nIndex].m_bLocked = false; + m_Splitters[nIndex].m_nLockedSize = 0; + } + + // We do this in 2 loops so that the first N children are actual child panels + for ( i = 0; i < nCount; ++i ) + { + SplitterHandle *pHandle = new SplitterHandle( this, "SplitterHandle", m_Mode, i ); + m_Splitters[i].m_pHandle = pHandle; + pHandle->MoveToFront(); + } + m_Splitters[nCount].m_pHandle = NULL; +} + + +//----------------------------------------------------------------------------- +// Sets the splitter color +//----------------------------------------------------------------------------- +void Splitter::SetSplitterColor( Color c ) +{ + int nCount = m_Splitters.Count() - 1; + if ( c.a() != 0 ) + { + for ( int i = 0; i < nCount; ++i ) + { + m_Splitters[i].m_pHandle->SetBgColor( c ); + m_Splitters[i].m_pHandle->SetPaintBackgroundEnabled( true ); + } + } + else + { + for ( int i = 0; i < nCount; ++i ) + { + m_Splitters[i].m_pHandle->SetPaintBackgroundEnabled( false ); + } + } +} + + +//----------------------------------------------------------------------------- +// Enables borders on the splitters +//----------------------------------------------------------------------------- +void Splitter::EnableBorders( bool bEnable ) +{ + int nCount = m_Splitters.Count() - 1; + for ( int i = 0; i < nCount; ++i ) + { + m_Splitters[i].m_pHandle->SetPaintBorderEnabled( bEnable ); + } +} + + +//----------------------------------------------------------------------------- +// controls splitters +//----------------------------------------------------------------------------- +int Splitter::GetSplitterCount() const +{ + return m_Splitters.Count() - 1; +} + + +//----------------------------------------------------------------------------- +// controls splitters +//----------------------------------------------------------------------------- +int Splitter::GetSubPanelCount() const +{ + return m_Splitters.Count(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Applies resouce settings +//----------------------------------------------------------------------------- +void Splitter::ApplySettings(KeyValues *inResourceData) +{ + BaseClass::ApplySettings(inResourceData); + + // Look for splitter positions + int nSplitterCount = GetSplitterCount(); + for ( int i = 0; i < nSplitterCount; ++i ) + { + char pBuffer[512]; + Q_snprintf( pBuffer, sizeof(pBuffer), "splitter%d", i ); + + int nSplitterPos = inResourceData->GetInt( pBuffer , -1 ); + if ( nSplitterPos >= 0 ) + { + SetSplitterPosition( i, nSplitterPos ); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int Splitter::GetPosRange() +{ + int w, h; + GetSize( w, h ); + int nPosRange = (m_Mode == SPLITTER_MODE_HORIZONTAL) ? h : w; + return nPosRange; +} + + +//----------------------------------------------------------------------------- +// Locks the size of a particular child in pixels. +//----------------------------------------------------------------------------- +void Splitter::LockChildSize( int nChildIndex, int nSize ) +{ + Assert( nChildIndex < m_Splitters.Count() ); + SplitterInfo_t &info = m_Splitters[nChildIndex]; + nSize += SPLITTER_HANDLE_WIDTH; + if ( !info.m_bLocked || (info.m_nLockedSize != nSize) ) + { + float flPrevPos = (nChildIndex > 0) ? m_Splitters[nChildIndex-1].m_flPos : 0.0f; + float flOldSize = info.m_flPos - flPrevPos; + float flDelta = nSize - flOldSize; + int nCount = m_Splitters.Count(); + for ( int i = nChildIndex; i < nCount-1; ++i ) + { + m_Splitters[i].m_flPos += flDelta; + } + m_Splitters[nCount-1].m_flPos = GetPosRange(); + + info.m_bLocked = true; + info.m_nLockedSize = nSize; + InvalidateLayout(); + } +} + +void Splitter::UnlockChildSize( int nChildIndex ) +{ + Assert( nChildIndex < m_Splitters.Count() ); + SplitterInfo_t &info = m_Splitters[nChildIndex]; + if ( info.m_bLocked ) + { + info.m_bLocked = false; + + float flPrevPos = (nChildIndex > 0) ? m_Splitters[nChildIndex-1].m_flPos : 0.0f; + float flBelowSize = GetPosRange() - flPrevPos; + + int nLockedSize = ComputeLockedSize( nChildIndex + 1 ); + int nUnlockedCount = 1; + int nCount = m_Splitters.Count(); + for ( int i = nChildIndex + 1; i < nCount; ++i ) + { + if ( !m_Splitters[i].m_bLocked ) + { + ++nUnlockedCount; + } + } + + float flUnlockedSize = ( flBelowSize - nLockedSize ) / nUnlockedCount; + + for ( int i = nChildIndex; i < nCount; ++i ) + { + if ( !m_Splitters[i].m_bLocked ) + { + m_Splitters[i].m_flPos = flPrevPos + flUnlockedSize; + } + else + { + m_Splitters[i].m_flPos = flPrevPos + m_Splitters[i].m_nLockedSize; + } + flPrevPos = m_Splitters[i].m_flPos; + } + InvalidateLayout(); + } +} + + +//----------------------------------------------------------------------------- +// Called when size changes +//----------------------------------------------------------------------------- +void Splitter::OnSizeChanged( int newWide, int newTall ) +{ + BaseClass::OnSizeChanged( newWide, newTall ); + + // Don't resize if it's degenerate and won't show up anyway... + if ( newTall <= 0 || newWide <= 0 ) + return; + + int nLockedSize = 0; + float flUnlockedSize = 0.0f; + int nCount = m_Splitters.Count(); + float flLastPos = 0.0f; + int nUnlockedCount = 0; + for ( int i = 0; i < nCount; ++i ) + { + SplitterInfo_t &info = m_Splitters[i]; + if ( info.m_bLocked ) + { + nLockedSize += info.m_nLockedSize; + } + else + { + ++nUnlockedCount; + flUnlockedSize += info.m_flPos - flLastPos; + } + flLastPos = info.m_flPos; + } + + int nNewTotalSize = (m_Mode == SPLITTER_MODE_HORIZONTAL) ? newTall : newWide; + int nNewUnlockedSize = nNewTotalSize - nLockedSize; + if ( nNewUnlockedSize < nUnlockedCount * SPLITTER_HANDLE_WIDTH ) + { + nNewUnlockedSize = nUnlockedCount * SPLITTER_HANDLE_WIDTH; + } + + float flRatio = nNewUnlockedSize / flUnlockedSize; + float flLastPrevPos = 0.0f; + flLastPos = 0.0f; + for ( int i = 0; i < nCount - 1; ++i ) + { + SplitterInfo_t &info = m_Splitters[i]; + if ( info.m_bLocked ) + { + flLastPrevPos = info.m_flPos; + info.m_flPos = flLastPos + info.m_nLockedSize; + } + else + { + float flNewSize = info.m_flPos - flLastPrevPos; + flNewSize *= flRatio; + flLastPrevPos = info.m_flPos; + info.m_flPos = flLastPos + flNewSize; + } + flLastPos = info.m_flPos; + } + + // Clamp the bottom to 1.0 + m_Splitters[nCount-1].m_flPos = nNewTotalSize; +} + + +//----------------------------------------------------------------------------- +// Splitter position +//----------------------------------------------------------------------------- +int Splitter::GetSplitterPosition( int nIndex ) +{ + return (int)( m_Splitters[nIndex].m_flPos + 0.5f ); +} + +void Splitter::SetSplitterPosition( int nIndex, int nPos ) +{ + int nPosRange = GetPosRange(); + if ( nPosRange == 0 ) + return; + + // If we're locked to a sibling, move the previous sibling first + while ( ( nIndex >= 0 ) && m_Splitters[nIndex].m_bLocked ) + { + nPos -= m_Splitters[nIndex].m_nLockedSize; + --nIndex; + } + if ( nIndex < 0 ) + return; + + // Clamp to the valid positional range + int i; + int nMinPos = 0; + for ( i = 0; i < nIndex; ++i ) + { + if ( !m_Splitters[i].m_bLocked ) + { + nMinPos += SPLITTER_HANDLE_WIDTH; + } + else + { + nMinPos += m_Splitters[i].m_nLockedSize; + } + } + + int nMaxPos = nPosRange - SPLITTER_HANDLE_WIDTH; + int c = GetSplitterCount(); + for ( i = nIndex + 1; i < c; ++i ) + { + if ( !m_Splitters[i].m_bLocked ) + { + nMaxPos -= SPLITTER_HANDLE_WIDTH; + } + else + { + nMaxPos -= m_Splitters[i].m_nLockedSize; + } + } + nPos = clamp( nPos, nMinPos, nMaxPos ); + + m_Splitters[nIndex].m_flPos = nPos; + int p = nPos; + for ( i = nIndex - 1 ; i >= 0; --i ) + { + int nMinPrevPos; + int nMaxPrevPos; + if ( !m_Splitters[i+1].m_bLocked ) + { + nMinPrevPos = -INT_MAX; + nMaxPrevPos = nPos - SPLITTER_HANDLE_WIDTH; + } + else + { + nMinPrevPos = nMaxPrevPos = p - m_Splitters[i+1].m_nLockedSize; + } + + int nCurPos = GetSplitterPosition( i ); + if ( nMaxPrevPos < nCurPos || nMinPrevPos > nCurPos ) + { + m_Splitters[ i ].m_flPos = nMaxPrevPos; + p = nMaxPrevPos; + } + else + { + p = m_Splitters[ i ].m_flPos; + } + } + + for ( i = nIndex + 1 ; i < c; ++i ) + { + int nMinNextPos; + int nMaxNextPos; + if ( !m_Splitters[i].m_bLocked ) + { + nMinNextPos = nPos + SPLITTER_HANDLE_WIDTH; + nMaxNextPos = INT_MAX; + } + else + { + nMinNextPos = nMaxNextPos = nPos + m_Splitters[i].m_nLockedSize; + } + + int nCurPos = GetSplitterPosition( i ); + if ( nMinNextPos > nCurPos || nMaxNextPos < nCurPos ) + { + m_Splitters[ i ].m_flPos = nMinNextPos; + nPos = nMinNextPos; + } + else + { + nPos = m_Splitters[ i ].m_flPos; + } + } + + InvalidateLayout(); +} + + +//----------------------------------------------------------------------------- +// Computes the locked size +//----------------------------------------------------------------------------- +int Splitter::ComputeLockedSize( int nStartingIndex ) +{ + int nLockedSize = 0; + int nCount = m_Splitters.Count(); + for ( int i = nStartingIndex; i < nCount; ++i ) + { + if ( m_Splitters[i].m_bLocked ) + { + nLockedSize += m_Splitters[i].m_nLockedSize; + } + } + return nLockedSize; +} + + +//----------------------------------------------------------------------------- +// Evenly respaces all the splitters +//----------------------------------------------------------------------------- +void Splitter::EvenlyRespaceSplitters( ) +{ + int nSplitterCount = GetSubPanelCount(); + if ( nSplitterCount == 0 ) + return; + + int nLockedSize = ComputeLockedSize( 0 ); + float flUnlockedSize = (float)( GetPosRange() - nLockedSize ); + float flDPos = flUnlockedSize / (float)nSplitterCount; + if ( flDPos < SPLITTER_HANDLE_WIDTH ) + { + flDPos = SPLITTER_HANDLE_WIDTH; + } + float flPos = 0.0f; + for ( int i = 0; i < nSplitterCount; ++i ) + { + if ( !m_Splitters[i].m_bLocked ) + { + flPos += flDPos; + } + else + { + flPos += m_Splitters[i].m_nLockedSize; + } + m_Splitters[i].m_flPos = flPos; + } + + InvalidateLayout(); +} + +void Splitter::RespaceSplitters( float *flFractions ) +{ + int nSplitterCount = GetSubPanelCount(); + if ( nSplitterCount == 0 ) + return; + + float flPos = 0.0f; + int nPosRange = GetPosRange(); + for ( int i = 0; i < nSplitterCount; ++i ) + { + flPos += flFractions[i]; + m_Splitters[i].m_flPos = flPos * nPosRange; + } + + Assert( flPos == 1.0f ); + + InvalidateLayout(); +} + + +//----------------------------------------------------------------------------- +// Purpose: sets user settings +//----------------------------------------------------------------------------- +void Splitter::ApplyUserConfigSettings(KeyValues *userConfig) +{ + BaseClass::ApplyUserConfigSettings( userConfig ); + + // read the splitter sizes + int c = m_Splitters.Count(); + float *pFractions = (float*)_alloca( c * sizeof(float) ); + float flTotalSize = 0.0f; + for ( int i = 0; i < c; i++ ) + { + char name[128]; + _snprintf(name, sizeof(name), "%d_splitter_pos", i); + pFractions[i] = userConfig->GetFloat( name, flTotalSize + SPLITTER_HANDLE_WIDTH + 1 ); + flTotalSize = pFractions[i]; + } + + if ( flTotalSize != 0.0f ) + { + int nPosRange = GetPosRange(); + for ( int i = 0; i < c; ++i ) + { + pFractions[i] /= flTotalSize; + m_Splitters[i].m_flPos = pFractions[i] * nPosRange; + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: returns user config settings for this control +//----------------------------------------------------------------------------- +void Splitter::GetUserConfigSettings(KeyValues *userConfig) +{ + BaseClass::GetUserConfigSettings( userConfig ); + + // save which columns are hidden + int c = m_Splitters.Count(); + for ( int i = 0 ; i < c; i++ ) + { + char name[128]; + _snprintf(name, sizeof(name), "%d_splitter_pos", i); + userConfig->SetFloat( name, m_Splitters[i].m_flPos ); + } +} + + +//----------------------------------------------------------------------------- +// Called to perform layout +//----------------------------------------------------------------------------- +void Splitter::PerformLayout( ) +{ + BaseClass::PerformLayout(); + + int nSplitterCount = GetSubPanelCount(); + if ( nSplitterCount == 0 ) + return; + + int w, h; + GetSize( w, h ); + + int nLastPos = 0; + for ( int i = 0; i < nSplitterCount; ++i ) + { + Panel *pChild = m_Splitters[i].m_pPanel; + SplitterHandle *pHandle = m_Splitters[i].m_pHandle; + int nSplitterPos = (int)( m_Splitters[i].m_flPos + 0.5f ); + + if ( m_Mode == SPLITTER_MODE_HORIZONTAL ) + { + pChild->SetPos( 0, nLastPos ); + pChild->SetSize( w, nSplitterPos - nLastPos ); + if ( pHandle ) + { + pHandle->SetPos( 0, nSplitterPos ); + pHandle->SetSize( w, SPLITTER_HANDLE_WIDTH ); + } + } + else + { + pChild->SetPos( nLastPos, 0 ); + pChild->SetSize( nSplitterPos - nLastPos, h ); + if ( pHandle ) + { + pHandle->SetPos( nSplitterPos, 0 ); + pHandle->SetSize( SPLITTER_HANDLE_WIDTH, h ); + } + } + + nLastPos = nSplitterPos + SPLITTER_HANDLE_WIDTH; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void Splitter::GetSettings( KeyValues *outResourceData ) +{ + BaseClass::GetSettings( outResourceData ); +} -- cgit v1.2.3