//========= Copyright Valve Corporation, All rights reserved. ============// // //=======================================================================================// #include "cbase.h" #include "game_controls/navigationpanel.h" #include "econ/econ_controls.h" #include "vgui_controls/Frame.h" #include "vgui/IInput.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" //----------------------------------------------------------------------------- using namespace vgui; //----------------------------------------------------------------------------- DECLARE_BUILD_FACTORY( CNavigationPanel ); //----------------------------------------------------------------------------- class CNavButton : public CExImageButton { DECLARE_CLASS_SIMPLE( CNavButton, CExImageButton ); public: CNavButton( Panel *parent, const char *name, const char *text = "", Panel *pActionSignalTarget = NULL, const char *cmd = NULL ) : CExImageButton( parent, name, text, pActionSignalTarget, cmd ), m_iUserData( -1 ) {} CNavButton( Panel *parent, const char *name, const wchar_t *wszText = L"", Panel *pActionSignalTarget = NULL, const char *cmd = NULL ) : CExImageButton( parent, name, wszText, pActionSignalTarget, cmd ) , m_iUserData( -1 ) {} virtual void ApplySettings( KeyValues *pInResourceData ) { BaseClass::ApplySettings( pInResourceData ); if ( m_iUserData < 0 ) { m_iUserData = pInResourceData->GetInt( "userdata", -1 ); AssertMsg( m_iUserData != -1, "Any messages sent for this button will have invalid user data!" ); } } int m_iUserData; }; //----------------------------------------------------------------------------- CNavigationPanel::CNavigationPanel( Panel *pParent, const char *pName, bool bAddParentAsActionSignalTarget/*=true*/ ) : BaseClass( pParent, pName ), m_bAutoLayout( false ), m_bAutoScale( true ), m_bDisplayVertical( false ), m_iSelectedButton( 0 ), // m_nAlignment( ALIGN_CENTER ), m_pKVButtonSettings( NULL ) { if ( bAddParentAsActionSignalTarget && pParent ) { AddActionSignalTarget( pParent ); } } CNavigationPanel::~CNavigationPanel() { } void CNavigationPanel::AddButton( int iUserData, const char *pTextToken ) { const int i = m_vecButtons.Count(); CNavButton *pNewButton = new CNavButton( this, CFmtStr( "button_%i", i ).Access(), pTextToken ); pNewButton->m_iUserData = iUserData; pNewButton->InvalidateLayout( true, false ); pNewButton->SetCommand( CFmtStr( "select_%i", i ).Access() ); m_vecButtons.AddToTail( pNewButton ); } CExImageButton *CNavigationPanel::GetButton( int index ) { return m_vecButtons[ index ]; } void CNavigationPanel::ApplySettings( KeyValues *pInResourceData ) { BaseClass::ApplySettings( pInResourceData ); /* const char *pAlignment = pInResourceData->GetString( "align", NULL ); if ( pAlignment ) { if ( !V_strnicmp( pAlignment, "west", 4 ) ) { m_nAlignment = ALIGN_WEST; } else if ( !V_strnicmp( pAlignment, "center", 6 ) ) { m_nAlignment = ALIGN_CENTER; } else if ( !V_strnicmp( pAlignment, "east", 4 ) ) { AssertMsg( 0, "This type of alignment is not supported." ); } } */ m_bAutoLayout = pInResourceData->GetBool( "auto_layout", false ); m_bAutoScale = pInResourceData->GetBool( "auto_scale", true ); m_bDisplayVertical = pInResourceData->GetBool( "display_vertically", false ); KeyValues *pKVButtonSettings = pInResourceData->FindKey( "ButtonSettings" ); AssertMsg( pKVButtonSettings, "This is required" ); if ( !pKVButtonSettings ) { AssertMsg( 0, "No button settings specified. CNavigationPanel is useless without this data." ); return; } // Cache this off for later if ( m_pKVButtonSettings ) { m_pKVButtonSettings->deleteThis(); m_pKVButtonSettings = NULL; } m_pKVButtonSettings = pKVButtonSettings->MakeCopy(); // Get individual button data and apply now KeyValues *pKVButtons = pInResourceData->FindKey( "Buttons" ); if ( !pKVButtons ) return; // Go through each image description and create a button if ( m_vecButtons.Count() ) return; int i = 0; FOR_EACH_SUBKEY( pKVButtons, pKVCurButton ) { CNavButton *pNewButton = new CNavButton( this, pKVCurButton->GetString( "FieldName", pKVCurButton->GetName() ), L"" ); pNewButton->ApplySettings( pKVCurButton ); pNewButton->InvalidateLayout( true, false ); pNewButton->SetCommand( CFmtStr( "select_%i", i ).Access() ); m_vecButtons.AddToTail( pNewButton ); ++i; } if ( m_vecButtons.IsValidIndex( m_iSelectedButtonDefault ) ) { UpdateButtonSelectionStates( m_iSelectedButtonDefault ); m_vecButtons[ m_iSelectedButtonDefault ]->InvalidateLayout( true ); const int iUserData = m_vecButtons[ m_iSelectedButtonDefault ]->m_iUserData; PostActionSignal( new KeyValues( "NavButtonSelected", "userdata", iUserData ) ); } } void CNavigationPanel::ApplySchemeSettings( IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme ); } void CNavigationPanel::PerformLayout() { if ( !m_vecButtons.Count() ) return; if ( !m_pKVButtonSettings ) return; BaseClass::PerformLayout(); // Get from settings int nSettingWidth = m_pKVButtonSettings->GetInt( "wide" ); int nSettingHeight = m_pKVButtonSettings->GetInt( "tall" ); // Button dimensions for setting positions int nButtonWidth = nSettingWidth; int nButtonHeight = nSettingHeight; if ( m_bAutoScale ) { // Get image size int nImageW, nImageH; m_vecButtons[0]->GetImage()->GetSize( nImageW, nImageH ); int nWidth, nHeight; if ( m_bDisplayVertical ) { nWidth = GetWide(); nHeight = nSettingHeight * nWidth / ( nSettingWidth > 0 ? nSettingWidth : 1 ); } else { nHeight = GetTall(); nWidth = nSettingWidth * nHeight / ( nSettingHeight > 0 ? nSettingHeight : 1 ); } // Update button dimensions to scaled versions nButtonWidth = nWidth; nButtonHeight = nHeight; } FOR_EACH_VEC( m_vecButtons, i ) { if ( m_vecButtons[i] && m_pKVButtonSettings ) { // Apply generic settings m_vecButtons[i]->ApplySettings( m_pKVButtonSettings ); } if ( m_bAutoLayout ) { // Display buttons vertically or horizontally? if ( m_bDisplayVertical ) { m_vecButtons[i]->SetPos( 0, i * ( nButtonHeight + m_nVerticalBuffer ) ); } else { const int nStartX = 0;//0.5f * ( GetWide() - NumButtons() * ( nButtonWidth + m_nHorizontalBuffer ) ); m_vecButtons[i]->SetPos( nStartX + i * ( nButtonWidth + m_nHorizontalBuffer ), 0 ); } } if ( m_bAutoScale ) { m_vecButtons[i]->SetSize( nButtonWidth, nButtonHeight ); m_vecButtons[i]->GetImage()->SetSize( nButtonWidth, nButtonHeight ); } m_vecButtons[i]->SetVisible( true ); } } void CNavigationPanel::OnCommand( const char *pCommand ) { if ( !V_strnicmp( pCommand, "select_", 7 ) ) { const int iButton = atoi( pCommand + 7 ); AssertMsg( m_vecButtons.IsValidIndex( iButton ), "Button index out of range!" ); if ( !m_vecButtons.IsValidIndex( iButton ) ) return; UpdateButtonSelectionStates( iButton ); const int iUserData = m_vecButtons[ iButton ]->m_iUserData; PostActionSignal( new KeyValues( "NavButtonSelected", "userdata", iUserData ) ); } else { BaseClass::OnCommand( pCommand ); } } void CNavigationPanel::OnThink() { BaseClass::OnThink(); // Make sure we only ever have one button in the selection state, since it's // possible to do this if you select a button, then click and drag on another // button, and release the mouse elsewhere. if ( !vgui::input()->IsMouseDown( MOUSE_LEFT ) ) { UpdateButtonSelectionStates( m_iSelectedButton ); } } void CNavigationPanel::UpdateButtonSelectionStates( int iButton ) { if ( m_iSelectedButton != iButton ) { m_iSelectedButton = iButton; } // Set the correct button as selected, all other buttons as not selected for ( int i = 0; i < NumButtons(); ++i ) { CNavButton *pCurButton = m_vecButtons[ i ]; if ( !pCurButton ) continue; bool bShouldSelect = iButton == i; pCurButton->SetSelected( bShouldSelect ); pCurButton->InvalidateLayout(); } } //----------------------------------------------------------------------------- #ifdef _DEBUG class CNavPanelTest : public Frame { DECLARE_CLASS_SIMPLE( CNavPanelTest, Frame ); public: CNavPanelTest( vgui::Panel *pParent ) : Frame( pParent, "NavPanelTest" ) { SetProportional( true ); LoadControlSettings( "Resource/UI/NavigationPanelTest.res" ); } }; CON_COMMAND( open_navpanel_test, "" ) { CNavPanelTest *pPanel = SETUP_PANEL( new CNavPanelTest( NULL ) ); pPanel->SetVisible( true ); pPanel->InvalidateLayout( false, true ); engine->ClientCmd_Unrestricted( "gameui_activate" ); const int nWidth = XRES( 300 ); const int nHeight = YRES( 300 ); pPanel->SetBounds( ( ScreenWidth() - nWidth ) / 2, ( ScreenHeight() - nHeight ) / 2, nWidth, nHeight ); pPanel->Activate(); } #endif // _DEBUG