summaryrefslogtreecommitdiff
path: root/game/client/tf/vgui/tf_controls.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/client/tf/vgui/tf_controls.cpp')
-rw-r--r--game/client/tf/vgui/tf_controls.cpp1292
1 files changed, 1292 insertions, 0 deletions
diff --git a/game/client/tf/vgui/tf_controls.cpp b/game/client/tf/vgui/tf_controls.cpp
new file mode 100644
index 0000000..23f61b2
--- /dev/null
+++ b/game/client/tf/vgui/tf_controls.cpp
@@ -0,0 +1,1292 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+
+#include "cbase.h"
+
+#include "ienginevgui.h"
+#include <vgui_controls/ScrollBarSlider.h>
+#include "vgui/ILocalize.h"
+#include "vgui/ISurface.h"
+#include "vgui/IInput.h"
+#include "tf_controls.h"
+#include "vgui_controls/TextImage.h"
+#include "vgui_controls/PropertyPage.h"
+#include "econ_item_system.h"
+#include "iachievementmgr.h"
+#include <vgui_controls/ListPanel.h>
+#include <vgui_controls/PanelListPanel.h>
+#include <vgui_controls/Label.h>
+#include <vgui_controls/Button.h>
+#include <vgui_controls/MessageBox.h>
+#include <vgui_controls/CheckButton.h>
+#include <vgui_controls/ComboBox.h>
+#include <vgui_controls/TextEntry.h>
+#include <../common/GameUI/cvarslider.h>
+#include "filesystem.h"
+
+using namespace vgui;
+
+wchar_t* LocalizeNumberWithToken( const char* pszLocToken, int nValue )
+{
+ static wchar_t wszOutString[ 128 ];
+ wchar_t wszCount[ 16 ];
+ _snwprintf( wszCount, ARRAYSIZE( wszCount ), L"%d", nValue );
+ const wchar_t *wpszFormat = g_pVGuiLocalize->Find( pszLocToken );
+ g_pVGuiLocalize->ConstructString_safe( wszOutString, wpszFormat, 1, wszCount );
+
+ return wszOutString;
+}
+
+DECLARE_BUILD_FACTORY( CExCheckButton );
+DECLARE_BUILD_FACTORY( CTFFooter );
+
+//-----------------------------------------------------------------------------
+// Purpose: Xbox-specific panel that displays button icons text labels
+//-----------------------------------------------------------------------------
+CTFFooter::CTFFooter( Panel *parent, const char *panelName ) : BaseClass( parent, panelName )
+{
+ SetVisible( true );
+ SetAlpha( 0 );
+
+ m_nButtonGap = 32;
+ m_ButtonPinRight = 100;
+ m_FooterTall = 80;
+
+ m_ButtonOffsetFromTop = 0;
+ m_ButtonSeparator = 4;
+ m_TextAdjust = 0;
+
+ m_bPaintBackground = false;
+ m_bCenterHorizontal = true;
+
+ m_szButtonFont[0] = '\0';
+ m_szTextFont[0] = '\0';
+ m_szFGColor[0] = '\0';
+ m_szBGColor[0] = '\0';
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFFooter::~CTFFooter()
+{
+ ClearButtons();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFooter::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ m_hButtonFont = pScheme->GetFont( ( m_szButtonFont[0] != '\0' ) ? m_szButtonFont : "GameUIButtons" );
+ m_hTextFont = pScheme->GetFont( ( m_szTextFont[0] != '\0' ) ? m_szTextFont : "MenuLarge" );
+
+ SetFgColor( pScheme->GetColor( m_szFGColor, Color( 255, 255, 255, 255 ) ) );
+ SetBgColor( pScheme->GetColor( m_szBGColor, Color( 0, 0, 0, 255 ) ) );
+
+ int x, y, w, h;
+ GetParent()->GetBounds( x, y, w, h );
+ SetBounds( x, h - m_FooterTall, w, m_FooterTall );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFooter::ApplySettings( KeyValues *inResourceData )
+{
+ BaseClass::ApplySettings( inResourceData );
+
+ // gap between hints
+ m_nButtonGap = inResourceData->GetInt( "buttongap", 32 );
+ m_ButtonPinRight = inResourceData->GetInt( "button_pin_right", 100 );
+ m_FooterTall = inResourceData->GetInt( "tall", 80 );
+ m_ButtonOffsetFromTop = inResourceData->GetInt( "buttonoffsety", 0 );
+ m_ButtonSeparator = inResourceData->GetInt( "button_separator", 4 );
+ m_TextAdjust = inResourceData->GetInt( "textadjust", 0 );
+
+ m_bCenterHorizontal = ( inResourceData->GetInt( "center", 1 ) == 1 );
+ m_bPaintBackground = ( inResourceData->GetInt( "paintbackground", 0 ) == 1 );
+
+ // fonts for text and button
+ Q_strncpy( m_szTextFont, inResourceData->GetString( "fonttext", "MenuLarge" ), sizeof( m_szTextFont ) );
+ Q_strncpy( m_szButtonFont, inResourceData->GetString( "fontbutton", "GameUIButtons" ), sizeof( m_szButtonFont ) );
+
+ // fg and bg colors
+ Q_strncpy( m_szFGColor, inResourceData->GetString( "fgcolor", "White" ), sizeof( m_szFGColor ) );
+ Q_strncpy( m_szBGColor, inResourceData->GetString( "bgcolor", "Black" ), sizeof( m_szBGColor ) );
+
+ // clear the buttons because we're going to re-add them here
+ ClearButtons();
+
+ for ( KeyValues *pButton = inResourceData->GetFirstSubKey(); pButton != NULL; pButton = pButton->GetNextKey() )
+ {
+ const char *pNameButton = pButton->GetName();
+
+ if ( !Q_stricmp( pNameButton, "button" ) )
+ {
+ // Add a button to the footer
+ const char *pName = pButton->GetString( "name", "NULL" );
+ const char *pText = pButton->GetString( "text", "NULL" );
+ const char *pIcon = pButton->GetString( "icon", "NULL" );
+ AddNewButtonLabel( pName, pText, pIcon );
+ }
+ }
+
+ InvalidateLayout( false, true ); // force ApplySchemeSettings to run
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFooter::AddNewButtonLabel( const char *name, const char *text, const char *icon )
+{
+ FooterButton_t *button = new FooterButton_t;
+
+ button->bVisible = true;
+ Q_strncpy( button->name, name, sizeof( button->name ) );
+
+ // Button icons are a single character
+ wchar_t *pIcon = g_pVGuiLocalize->Find( icon );
+ if ( pIcon )
+ {
+ button->icon[0] = pIcon[0];
+ button->icon[1] = '\0';
+ }
+ else
+ {
+ button->icon[0] = '\0';
+ }
+
+ // Set the help text
+ wchar_t *pText = g_pVGuiLocalize->Find( text );
+ if ( pText )
+ {
+ wcsncpy( button->text, pText, wcslen( pText ) + 1 );
+ }
+ else
+ {
+ button->text[0] = '\0';
+ }
+
+ m_Buttons.AddToTail( button );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFooter::ShowButtonLabel( const char *name, bool show )
+{
+ for ( int i = 0; i < m_Buttons.Count(); ++i )
+ {
+ if ( !Q_stricmp( m_Buttons[ i ]->name, name ) )
+ {
+ m_Buttons[ i ]->bVisible = show;
+ break;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFooter::PaintBackground( void )
+{
+ if ( !m_bPaintBackground )
+ return;
+
+ BaseClass::PaintBackground();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFooter::Paint( void )
+{
+ // inset from right edge
+ int wide = GetWide();
+
+ // center the text within the button
+ int buttonHeight = vgui::surface()->GetFontTall( m_hButtonFont );
+ int fontHeight = vgui::surface()->GetFontTall( m_hTextFont );
+ int textY = ( buttonHeight - fontHeight )/2 + m_TextAdjust;
+
+ if ( textY < 0 )
+ {
+ textY = 0;
+ }
+
+ int y = m_ButtonOffsetFromTop;
+
+ if ( !m_bCenterHorizontal )
+ {
+ // draw the buttons, right to left
+ int x = wide - m_ButtonPinRight;
+
+ vgui::Label label( this, "temp", L"" );
+ for ( int i = m_Buttons.Count() - 1 ; i >= 0 ; --i )
+ {
+ FooterButton_t *pButton = m_Buttons[i];
+ if ( !pButton->bVisible )
+ continue;
+
+ // Get the string length
+ label.SetFont( m_hTextFont );
+ label.SetText( pButton->text );
+ label.SizeToContents();
+
+ int iTextWidth = label.GetWide();
+
+ if ( iTextWidth == 0 )
+ x += m_nButtonGap; // There's no text, so remove the gap between buttons
+ else
+ x -= iTextWidth;
+
+ // Draw the string
+ vgui::surface()->DrawSetTextFont( m_hTextFont );
+ vgui::surface()->DrawSetTextColor( GetFgColor() );
+ vgui::surface()->DrawSetTextPos( x, y + textY );
+ vgui::surface()->DrawPrintText( pButton->text, wcslen( pButton->text ) );
+
+ // Draw the button
+ // back up button width and a little extra to leave a gap between button and text
+ x -= ( vgui::surface()->GetCharacterWidth( m_hButtonFont, pButton->icon[0] ) + m_ButtonSeparator );
+ vgui::surface()->DrawSetTextFont( m_hButtonFont );
+ vgui::surface()->DrawSetTextColor( 255, 255, 255, 255 );
+ vgui::surface()->DrawSetTextPos( x, y );
+ vgui::surface()->DrawPrintText( pButton->icon, 1 );
+
+ // back up to next string
+ x -= m_nButtonGap;
+ }
+ }
+ else
+ {
+ // center the buttons (as a group)
+ int x = wide / 2;
+ int totalWidth = 0;
+ int i = 0;
+ int nButtonCount = 0;
+
+ vgui::Label label( this, "temp", L"" );
+
+ // need to loop through and figure out how wide our buttons and text are (with gaps between) so we can offset from the center
+ for ( i = 0; i < m_Buttons.Count(); ++i )
+ {
+ FooterButton_t *pButton = m_Buttons[i];
+
+ if ( !pButton->bVisible )
+ continue;
+
+ // Get the string length
+ label.SetFont( m_hTextFont );
+ label.SetText( pButton->text );
+ label.SizeToContents();
+
+ totalWidth += vgui::surface()->GetCharacterWidth( m_hButtonFont, pButton->icon[0] );
+ totalWidth += m_ButtonSeparator;
+ totalWidth += label.GetWide();
+
+ nButtonCount++; // keep track of how many active buttons we'll be drawing
+ }
+
+ totalWidth += ( nButtonCount - 1 ) * m_nButtonGap; // add in the gaps between the buttons
+ x -= ( totalWidth / 2 );
+
+ for ( i = 0; i < m_Buttons.Count(); ++i )
+ {
+ FooterButton_t *pButton = m_Buttons[i];
+
+ if ( !pButton->bVisible )
+ continue;
+
+ // Get the string length
+ label.SetFont( m_hTextFont );
+ label.SetText( pButton->text );
+ label.SizeToContents();
+
+ int iTextWidth = label.GetWide();
+
+ // Draw the icon
+ vgui::surface()->DrawSetTextFont( m_hButtonFont );
+ vgui::surface()->DrawSetTextColor( 255, 255, 255, 255 );
+ vgui::surface()->DrawSetTextPos( x, y );
+ vgui::surface()->DrawPrintText( pButton->icon, 1 );
+ x += vgui::surface()->GetCharacterWidth( m_hButtonFont, pButton->icon[0] ) + m_ButtonSeparator;
+
+ // Draw the string
+ vgui::surface()->DrawSetTextFont( m_hTextFont );
+ vgui::surface()->DrawSetTextColor( GetFgColor() );
+ vgui::surface()->DrawSetTextPos( x, y + textY );
+ vgui::surface()->DrawPrintText( pButton->text, wcslen( pButton->text ) );
+
+ x += iTextWidth + m_nButtonGap;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFFooter::ClearButtons( void )
+{
+ m_Buttons.PurgeAndDeleteElements();
+}
+
+#define OPTIONS_DIR "cfg"
+#define DEFAULT_OPTIONS_FILE OPTIONS_DIR "/user_default.scr"
+#define OPTIONS_FILE OPTIONS_DIR "/user.scr"
+
+//-----------------------------------------------------------------------------
+// Purpose: Constructor
+//-----------------------------------------------------------------------------
+CTFAdvancedOptionsDialog::CTFAdvancedOptionsDialog(vgui::Panel *parent) : BaseClass(NULL, "TFAdvancedOptionsDialog")
+{
+ // Need to use the clientscheme (we're not parented to a clientscheme'd panel)
+ vgui::HScheme scheme = vgui::scheme()->LoadSchemeFromFileEx( enginevgui->GetPanel( PANEL_CLIENTDLL ), "resource/ClientScheme.res", "ClientScheme");
+ SetScheme(scheme);
+ SetProportional( true );
+
+ m_pListPanel = new vgui::PanelListPanel( this, "PanelListPanel" );
+
+ m_pList = NULL;
+
+ m_pToolTip = new CTFTextToolTip( this );
+ m_pToolTipEmbeddedPanel = new vgui::EditablePanel( this, "TooltipPanel" );
+ m_pToolTipEmbeddedPanel->SetKeyBoardInputEnabled( false );
+ m_pToolTipEmbeddedPanel->SetMouseInputEnabled( false );
+ m_pToolTip->SetEmbeddedPanel( m_pToolTipEmbeddedPanel );
+ m_pToolTip->SetTooltipDelay( 0 );
+
+ m_pDescription = new CInfoDescription();
+ m_pDescription->InitFromFile( DEFAULT_OPTIONS_FILE );
+ m_pDescription->InitFromFile( OPTIONS_FILE, false );
+ m_pDescription->TransferCurrentValues( NULL );
+
+// MoveToCenterOfScreen();
+// SetSizeable( false );
+// SetDeleteSelfOnClose( true );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Destructor
+//-----------------------------------------------------------------------------
+CTFAdvancedOptionsDialog::~CTFAdvancedOptionsDialog()
+{
+ delete m_pDescription;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFAdvancedOptionsDialog::ApplySchemeSettings( vgui::IScheme *pScheme )
+{
+ BaseClass::ApplySchemeSettings( pScheme );
+
+ LoadControlSettings("resource/ui/TFAdvancedOptionsDialog.res");
+ m_pListPanel->SetFirstColumnWidth( 0 );
+
+ CreateControls();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFAdvancedOptionsDialog::ApplySettings( KeyValues *inResourceData )
+{
+ BaseClass::ApplySettings( inResourceData );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFAdvancedOptionsDialog::OnClose()
+{
+ BaseClass::OnClose();
+
+ TFModalStack()->PopModal( this );
+ MarkForDeletion();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *command -
+//-----------------------------------------------------------------------------
+void CTFAdvancedOptionsDialog::OnCommand( const char *command )
+{
+ if ( !stricmp( command, "Ok" ) )
+ {
+ // OnApplyChanges();
+ SaveValues();
+ OnClose();
+ return;
+ }
+ else if ( !stricmp( command, "Close" ) )
+ {
+ OnClose();
+ return;
+ }
+
+ BaseClass::OnCommand( command );
+}
+
+void CTFAdvancedOptionsDialog::OnKeyCodeTyped(KeyCode code)
+{
+ // force ourselves to be closed if the escape key it pressed
+ if ( code == KEY_ESCAPE )
+ {
+ OnClose();
+ }
+ else
+ {
+ BaseClass::OnKeyCodeTyped(code);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFAdvancedOptionsDialog::OnKeyCodePressed(KeyCode code)
+{
+ // force ourselves to be closed if the escape key it pressed
+ if ( GetBaseButtonCode( code ) == KEY_XBUTTON_B || GetBaseButtonCode( code ) == STEAMCONTROLLER_B || GetBaseButtonCode( code ) == STEAMCONTROLLER_START )
+ {
+ OnClose();
+ }
+ else
+ {
+ BaseClass::OnKeyCodePressed(code);
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFAdvancedOptionsDialog::GatherCurrentValues()
+{
+ if ( !m_pDescription )
+ return;
+
+ // OK
+ CheckButton *pBox;
+ TextEntry *pEdit;
+ ComboBox *pCombo;
+ CCvarSlider *pSlider;
+
+ mpcontrol_t *pList;
+
+ CScriptObject *pObj;
+ CScriptListItem *pItem;
+
+ char szValue[256];
+ char strValue[ 256 ];
+
+ pList = m_pList;
+ while ( pList )
+ {
+ pObj = pList->pScrObj;
+
+ if ( pObj->type == O_CATEGORY )
+ {
+ pList = pList->next;
+ continue;
+ }
+
+ if ( !pList->pControl )
+ {
+ pObj->SetCurValue( pObj->defValue );
+ pList = pList->next;
+ continue;
+ }
+
+ switch ( pObj->type )
+ {
+ case O_BOOL:
+ pBox = (CheckButton *)pList->pControl;
+ sprintf( szValue, "%s", pBox->IsSelected() ? "1" : "0" );
+ break;
+ case O_NUMBER:
+ pEdit = ( TextEntry * )pList->pControl;
+ pEdit->GetText( strValue, sizeof( strValue ) );
+ sprintf( szValue, "%s", strValue );
+ break;
+ case O_STRING:
+ pEdit = ( TextEntry * )pList->pControl;
+ pEdit->GetText( strValue, sizeof( strValue ) );
+ sprintf( szValue, "%s", strValue );
+ break;
+ case O_LIST:
+ {
+ pCombo = (ComboBox *)pList->pControl;
+ // pCombo->GetText( strValue, sizeof( strValue ) );
+ int activeItem = pCombo->GetActiveItem();
+
+ pItem = pObj->pListItems;
+ // int n = (int)pObj->fdefValue;
+
+ while ( pItem )
+ {
+ if (!activeItem--)
+ break;
+
+ pItem = pItem->pNext;
+ }
+
+ if ( pItem )
+ {
+ sprintf( szValue, "%s", pItem->szValue );
+ }
+ else // Couln't find index
+ {
+ //assert(!("Couldn't find string in list, using default value"));
+ sprintf( szValue, "%s", pObj->defValue );
+ }
+ break;
+ }
+ case O_SLIDER:
+ pSlider = ( CCvarSlider * )pList->pControl;
+ sprintf( szValue, "%.2f", pSlider->GetSliderValue() );
+ break;
+ }
+
+ // Remove double quotes and % characters
+ UTIL_StripInvalidCharacters( szValue, sizeof(szValue) );
+
+ V_strcpy_safe( strValue, szValue );
+
+ pObj->SetCurValue( strValue );
+
+ pList = pList->next;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFAdvancedOptionsDialog::CreateControls()
+{
+ DestroyControls();
+
+ // Go through desciption creating controls
+ CScriptObject *pObj;
+
+ pObj = m_pDescription->pObjList;
+
+ // Build out the clan dropdown
+ CScriptObject *pClanObj = m_pDescription->FindObject( "cl_clanid" );
+ ISteamFriends *pFriends = steamapicontext->SteamFriends();
+ if ( pFriends && pClanObj )
+ {
+ pClanObj->RemoveAndDeleteAllItems();
+ int iGroupCount = pFriends->GetClanCount();
+ pClanObj->AddItem( new CScriptListItem( "#Cstrike_ClanTag_None", "0" ) );
+ for ( int k = 0; k < iGroupCount; ++ k )
+ {
+ CSteamID clanID = pFriends->GetClanByIndex( k );
+ const char *pName = pFriends->GetClanName( clanID );
+ const char *pTag = pFriends->GetClanTag( clanID );
+
+ char id[12];
+ Q_snprintf( id, sizeof( id ), "%d", clanID.GetAccountID() );
+ pClanObj->AddItem( new CScriptListItem( CFmtStr( "%s (%s)", pTag, pName ), id ) );
+ }
+ }
+
+ mpcontrol_t *pCtrl;
+
+ CheckButton *pBox;
+ TextEntry *pEdit;
+ ComboBox *pCombo;
+ CCvarSlider *pSlider;
+ CScriptListItem *pListItem;
+
+ Panel *objParent = m_pListPanel;
+
+ IScheme *pScheme = scheme()->GetIScheme( GetScheme() );
+ vgui::HFont hTextFont = pScheme->GetFont( "HudFontSmallestBold", true );
+ Color tanDark = pScheme->GetColor( "TanDark", Color(255,0,0,255) );
+
+ while ( pObj )
+ {
+ if ( pObj->type == O_OBSOLETE )
+ {
+ pObj = pObj->pNext;
+ continue;
+ }
+
+ pCtrl = new mpcontrol_t( objParent, "mpcontrol_t" );
+ pCtrl->type = pObj->type;
+
+ // Force it to invalidate scheme now, so we can change color afterwards and have it persist
+ pCtrl->InvalidateLayout( true, true );
+
+ switch ( pCtrl->type )
+ {
+ case O_BOOL:
+ pBox = new CheckButton( pCtrl, "DescCheckButton", pObj->prompt );
+ pBox->SetSelected( pObj->fdefValue != 0.0f ? true : false );
+
+ pCtrl->pControl = (Panel *)pBox;
+ pBox->SetFont( hTextFont );
+
+ pBox->InvalidateLayout( true, true );
+
+ // This is utterly fucking retarded.
+ pBox->SetFgColor( tanDark );
+ pBox->SetDefaultColor( tanDark, pBox->GetBgColor() );
+ pBox->SetArmedColor( tanDark, pBox->GetBgColor() );
+ pBox->SetDepressedColor( tanDark, pBox->GetBgColor() );
+ pBox->SetSelectedColor( tanDark, pBox->GetBgColor() );
+ pBox->SetHighlightColor( tanDark );
+ pBox->GetCheckImage()->SetColor( tanDark );
+ break;
+ case O_STRING:
+ case O_NUMBER:
+ pEdit = new TextEntry( pCtrl, "DescTextEntry");
+ pEdit->InsertString(pObj->defValue);
+ pCtrl->pControl = (Panel *)pEdit;
+ pEdit->SetFont( hTextFont );
+
+ pEdit->InvalidateLayout( true, true );
+ pEdit->SetBgColor( Color(0,0,0,255) );
+ break;
+ case O_LIST:
+ {
+ pCombo = new ComboBox( pCtrl, "DescComboBox", 5, false );
+
+ // track which row matches the current value
+ int iRow = -1;
+ int iCount = 0;
+ pListItem = pObj->pListItems;
+ while ( pListItem )
+ {
+ if ( iRow == -1 && !Q_stricmp( pListItem->szValue, pObj->curValue ) )
+ iRow = iCount;
+
+ pCombo->AddItem( pListItem->szItemText, NULL );
+ pListItem = pListItem->pNext;
+ ++iCount;
+ }
+
+
+ pCombo->ActivateItemByRow( iRow );
+
+ pCtrl->pControl = (Panel *)pCombo;
+ pCombo->SetFont( hTextFont );
+ }
+ break;
+ case O_SLIDER:
+ pSlider = new CCvarSlider( pCtrl, "DescSlider", "Test", pObj->fMin, pObj->fMax, pObj->cvarname, false );
+ pCtrl->pControl = (Panel *)pSlider;
+ break;
+ case O_CATEGORY:
+ pCtrl->SetBorder( pScheme->GetBorder("OptionsCategoryBorder") );
+ break;
+ default:
+ break;
+ }
+
+ if ( pCtrl->type != O_BOOL )
+ {
+ pCtrl->pPrompt = new vgui::Label( pCtrl, "DescLabel", "" );
+ pCtrl->pPrompt->SetContentAlignment( vgui::Label::a_west );
+ pCtrl->pPrompt->SetTextInset( 5, 0 );
+ pCtrl->pPrompt->SetText( pObj->prompt );
+ pCtrl->pPrompt->SetFont( hTextFont );
+
+ pCtrl->pPrompt->InvalidateLayout( true, true );
+
+ if ( pCtrl->type == O_CATEGORY )
+ {
+ pCtrl->pPrompt->SetFont( pScheme->GetFont( "HudFontSmallBold", true ) );
+ pCtrl->pPrompt->SetFgColor( pScheme->GetColor( "TanLight", Color(255,0,0,255) ) );
+ }
+ else
+ {
+ pCtrl->pPrompt->SetFgColor( tanDark );
+ }
+ }
+
+ pCtrl->pScrObj = pObj;
+
+ switch ( pCtrl->type )
+ {
+ case O_BOOL:
+ case O_STRING:
+ case O_NUMBER:
+ case O_LIST:
+ case O_CATEGORY:
+ pCtrl->SetSize( m_iControlW, m_iControlH );
+ break;
+ case O_SLIDER:
+ pCtrl->SetSize( m_iSliderW, m_iSliderH );
+ break;
+ default:
+ break;
+ }
+
+ // Hook up the tooltip, if the entry has one
+ if ( pObj->tooltip && pObj->tooltip[0] )
+ {
+ if ( pCtrl->pPrompt )
+ {
+ pCtrl->pPrompt->SetTooltip( m_pToolTip, pObj->tooltip );
+ }
+ else
+ {
+ pCtrl->SetTooltip( m_pToolTip, pObj->tooltip );
+ pCtrl->pControl->SetTooltip( m_pToolTip, pObj->tooltip );
+ }
+ }
+
+ m_pListPanel->AddItem( NULL, pCtrl );
+
+ // Link it in
+ if ( !m_pList )
+ {
+ m_pList = pCtrl;
+ pCtrl->next = NULL;
+ }
+ else
+ {
+ mpcontrol_t *p;
+ p = m_pList;
+ while ( p )
+ {
+ if ( !p->next )
+ {
+ p->next = pCtrl;
+ pCtrl->next = NULL;
+ break;
+ }
+ p = p->next;
+ }
+ }
+
+ pObj = pObj->pNext;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFAdvancedOptionsDialog::DestroyControls()
+{
+ mpcontrol_t *p, *n;
+
+ p = m_pList;
+ while ( p )
+ {
+ n = p->next;
+ //
+ if ( p->pControl )
+ {
+ p->pControl->MarkForDeletion();
+ p->pControl = NULL;
+ }
+ if ( p->pPrompt )
+ {
+ p->pPrompt->MarkForDeletion();
+ p->pPrompt = NULL;
+ }
+ delete p;
+ p = n;
+ }
+
+ m_pList = NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFAdvancedOptionsDialog::SaveValues()
+{
+ // Get the values from the controls:
+ GatherCurrentValues();
+
+ // Create the game.cfg file
+ if ( m_pDescription )
+ {
+ FileHandle_t fp;
+
+ // Add settings to config.cfg
+ m_pDescription->WriteToConfig();
+
+ g_pFullFileSystem->CreateDirHierarchy( OPTIONS_DIR );
+ fp = g_pFullFileSystem->Open( OPTIONS_FILE, "wb" );
+ if ( fp )
+ {
+ m_pDescription->WriteToScriptFile( fp );
+ g_pFullFileSystem->Close( fp );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFAdvancedOptionsDialog::Deploy( void )
+{
+ SetVisible( true );
+ MakePopup();
+ MoveToFront();
+ SetKeyBoardInputEnabled(true);
+ SetMouseInputEnabled(true);
+ TFModalStack()->PushModal( this );
+
+ // Center it, keeping requested size
+ int x, y, ww, wt, wide, tall;
+ vgui::surface()->GetWorkspaceBounds( x, y, ww, wt );
+ GetSize(wide, tall);
+ SetPos(x + ((ww - wide) / 2), y + ((wt - tall) / 2));
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFTextToolTip::PerformLayout()
+{
+ if ( !ShouldLayout() )
+ return;
+
+ _isDirty = false;
+
+ // Resize our text labels to fit.
+ int iW = m_pEmbeddedPanel->GetWide();
+ int iH = 0;
+ for (int i = 0; i < m_pEmbeddedPanel->GetChildCount(); i++)
+ {
+ vgui::Label *pLabel = dynamic_cast<vgui::Label*>( m_pEmbeddedPanel->GetChild(i) );
+ if ( !pLabel )
+ continue;
+
+ // Only checking to see if we have any text
+ char szTmp[2];
+ pLabel->GetText( szTmp, sizeof(szTmp) );
+ if ( !szTmp[0] )
+ continue;
+
+ int iLX, iLY;
+ pLabel->GetPos( iLX, iLY );
+
+ int iMaxWidth = m_pEmbeddedPanel->GetWide() - (iLX * 2);
+ pLabel->GetTextImage()->ResizeImageToContentMaxWidth( iMaxWidth );
+ pLabel->SizeToContents();
+ pLabel->SetWide( iMaxWidth );
+ pLabel->InvalidateLayout(true);
+
+ int iX, iY;
+ pLabel->GetPos( iX, iY );
+ iW = MAX( iW, ( pLabel->GetWide() + (iX * 2) ) );
+
+ if ( iH == 0 )
+ {
+ iH += MAX( iH, pLabel->GetTall() + (iY * 2) );
+ }
+ else
+ {
+ iH += MAX( iH, pLabel->GetTall() );
+ }
+ }
+ m_pEmbeddedPanel->SetSize( m_pEmbeddedPanel->GetWide(), iH );
+
+ m_pEmbeddedPanel->SetVisible(true);
+
+ PositionWindow( m_pEmbeddedPanel );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFTextToolTip::PositionWindow( Panel *pTipPanel )
+{
+ int iTipW, iTipH;
+ pTipPanel->GetSize( iTipW, iTipH );
+
+ int cursorX, cursorY;
+ input()->GetCursorPos(cursorX, cursorY);
+
+ int px, py, wide, tall;
+ ipanel()->GetAbsPos( m_pEmbeddedPanel->GetParent()->GetVPanel(), px, py );
+ m_pEmbeddedPanel->GetParent()->GetSize(wide, tall);
+
+ if ( !m_pEmbeddedPanel->IsPopup() )
+ {
+ // Move the cursor into our parent space
+ cursorX -= px;
+ cursorY -= py;
+ }
+
+ if (wide - iTipW > cursorX)
+ {
+ cursorY += 20;
+ // menu hanging right
+ if (tall - iTipH > cursorY)
+ {
+ // menu hanging down
+ pTipPanel->SetPos(cursorX, cursorY);
+ }
+ else
+ {
+ // menu hanging up
+ pTipPanel->SetPos(cursorX, cursorY - iTipH - 20);
+ }
+ }
+ else
+ {
+ // menu hanging left
+ if (tall - iTipH > cursorY)
+ {
+ // menu hanging down
+ pTipPanel->SetPos( Max( 0, cursorX - iTipW ), cursorY);
+ }
+ else
+ {
+ // menu hanging up
+ pTipPanel->SetPos( Max( 0, cursorX - iTipW ), cursorY - iTipH - 20 );
+ }
+ }
+}
+
+static vgui::DHANDLE<CTFAdvancedOptionsDialog> g_pTFAdvancedOptionsDialog;
+
+//-----------------------------------------------------------------------------
+// Purpose: Callback to open the game menus
+//-----------------------------------------------------------------------------
+void CL_OpenTFAdvancedOptionsDialog( const CCommand &args )
+{
+ if ( g_pTFAdvancedOptionsDialog.Get() == NULL )
+ {
+ g_pTFAdvancedOptionsDialog = vgui::SETUP_PANEL( new CTFAdvancedOptionsDialog( NULL ) );
+ }
+
+ g_pTFAdvancedOptionsDialog->Deploy();
+}
+
+// the console commands
+static ConCommand opentf2options( "opentf2options", &CL_OpenTFAdvancedOptionsDialog, "Displays the TF2 Advanced Options dialog." );
+
+//-----------------------------------------------------------------------------
+// Purpose: A scroll bar that can have specified width
+//-----------------------------------------------------------------------------
+class CExScrollBar : public ScrollBar
+{
+ DECLARE_CLASS_SIMPLE( CExScrollBar, ScrollBar );
+public:
+
+ CExScrollBar( Panel *parent, const char *name, bool bVertical )
+ : ScrollBar( parent, name, bVertical )
+ {}
+
+ virtual void ApplySchemeSettings( IScheme *pScheme ) OVERRIDE
+ {
+ // Deliberately skip ScrollBar
+ Panel::ApplySchemeSettings( pScheme );
+ }
+};
+
+DECLARE_BUILD_FACTORY( CExScrollingEditablePanel );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CExScrollingEditablePanel::CExScrollingEditablePanel( Panel *pParent, const char *pszName )
+ : EditablePanel( pParent, pszName )
+ , m_nLastScrollValue( 0 )
+ , m_bUseMouseWheelToScroll( true )
+{
+ m_pScrollBar = new CExScrollBar( this, "ScrollBar", true );
+ m_pScrollBar->AddActionSignalTarget( this );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CExScrollingEditablePanel::~CExScrollingEditablePanel()
+{}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CExScrollingEditablePanel::ApplySettings( KeyValues *inResourceData )
+{
+ BaseClass::ApplySettings( inResourceData );
+
+ KeyValues *pScrollbarKV = inResourceData->FindKey( "Scrollbar" );
+ if ( pScrollbarKV )
+ {
+ m_pScrollBar->ApplySettings( pScrollbarKV );
+ }
+
+ m_bUseMouseWheelToScroll = inResourceData->GetBool( "allow_mouse_wheel_to_scroll", true );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CExScrollingEditablePanel::OnSizeChanged( int newWide, int newTall )
+{
+ BaseClass::OnSizeChanged( newWide, newTall );
+
+ int nDelta = m_nLastScrollValue;
+ // Go through all our children and move them BACK into position
+ int nNumChildren = GetChildCount();
+ for ( int i=0; i < nNumChildren; ++i )
+ {
+ Panel* pChild = GetChild( i );
+
+ if ( pChild == m_pScrollBar )
+ continue;
+
+ EditablePanel* pEditableChild = dynamic_cast< EditablePanel* >( pChild );
+ if ( pEditableChild && pEditableChild->ShouldSkipAutoResize() )
+ continue;
+
+ int x,y;
+ pChild->GetPos( x, y );
+
+ pChild->SetPos( x, y - nDelta );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CExScrollingEditablePanel::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ int nFurthestY = 0;
+
+ // Go through all our children and find the lowest point on the lowest child
+ // that we'd need to scroll to
+ int nNumChildren = GetChildCount();
+ for ( int i=0; i < nNumChildren; ++i )
+ {
+ Panel* pChild = GetChild( i );
+
+ if ( pChild == m_pScrollBar )
+ continue;
+
+ int x,y,wide,tall;
+ pChild->GetBounds( x, y, wide, tall );
+
+ // Offset by our scroll value
+ y += m_nLastScrollValue;
+
+ if ( m_bRestrictWidth )
+ {
+ int nMaxWide = Min( x + wide, GetWide() - m_pScrollBar->GetWide() );
+ pChild->SetWide( nMaxWide - x );
+ }
+
+ nFurthestY = Max( y + tall, nFurthestY );
+ }
+
+ int nMaxRange = nFurthestY + m_iBottomBuffer;
+ m_pScrollBar->SetRange( 0, nMaxRange );
+ m_pScrollBar->SetRangeWindow( GetTall() );
+
+ OnScrollBarSliderMoved();
+}
+
+//-----------------------------------------------------------------------------
+// Called when the scroll bar moves
+//-----------------------------------------------------------------------------
+void CExScrollingEditablePanel::OnScrollBarSliderMoved()
+{
+ // Figure out how far they just scrolled
+ int nScrollAmount = m_pScrollBar->GetValue();
+ int nDelta = nScrollAmount - m_nLastScrollValue;
+
+ if ( nDelta == 0 )
+ return;
+
+ ShiftChildren( nDelta );
+
+ m_nLastScrollValue = nScrollAmount;
+}
+
+void CExScrollingEditablePanel::ShiftChildren( int nDistance )
+{
+ // Go through all our children and move them
+ int nNumChildren = GetChildCount();
+ for ( int i=0; i < nNumChildren; ++i )
+ {
+ Panel* pChild = GetChild( i );
+
+ if ( pChild == m_pScrollBar )
+ continue;
+
+ int x,y;
+ pChild->GetPos( x, y );
+
+ pChild->SetPos( x, y - nDistance );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// respond to mouse wheel events
+//-----------------------------------------------------------------------------
+void CExScrollingEditablePanel::OnMouseWheeled( int delta )
+{
+ if ( !m_bUseMouseWheelToScroll )
+ {
+ BaseClass::OnMouseWheeled( delta );
+ return;
+ }
+
+ int val = m_pScrollBar->GetValue();
+ val -= ( delta * m_iScrollStep );
+ m_pScrollBar->SetValue( val );
+}
+
+DECLARE_BUILD_FACTORY( CScrollableList );
+//-----------------------------------------------------------------------------
+// Clearnup
+//-----------------------------------------------------------------------------
+CScrollableList::~CScrollableList()
+{
+ ClearAutoLayoutPanels();
+}
+
+void CScrollableList::PerformLayout()
+{
+ int nYpos = -m_nLastScrollValue;
+
+ for( int i=0; i<m_vecAutoLayoutPanels.Count(); ++i )
+ {
+ LayoutInfo_t layout = m_vecAutoLayoutPanels[ i ];
+ nYpos += layout.m_nGap;
+
+ layout.m_pPanel->SetPos( layout.m_pPanel->GetXPos(), nYpos );
+
+ nYpos += layout.m_pPanel->GetTall();
+ }
+
+ BaseClass::PerformLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Add a panel to the bottom
+//-----------------------------------------------------------------------------
+void CScrollableList::AddPanel( Panel* pPanel, int nGap )
+{
+ // We're the captain now
+ pPanel->SetParent( this );
+ pPanel->SetAutoDelete( false );
+
+ auto idx = m_vecAutoLayoutPanels.AddToTail();
+ LayoutInfo_t& layout = m_vecAutoLayoutPanels[ idx ];
+ layout.m_pPanel = pPanel;
+ layout.m_nGap = nGap;
+
+ // Need to do a perform layout so we get sized correctly
+ InvalidateLayout();
+}
+
+//-----------------------------------------------------------------------------
+// Delete any panels we own
+//-----------------------------------------------------------------------------
+void CScrollableList::ClearAutoLayoutPanels()
+{
+ FOR_EACH_VEC( m_vecAutoLayoutPanels, i )
+ {
+ m_vecAutoLayoutPanels[ i ].m_pPanel->MarkForDeletion();
+ }
+
+ m_vecAutoLayoutPanels.Purge();
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+CExpandablePanel::CExpandablePanel( Panel* pParent, const char* pszName )
+ : vgui::EditablePanel( pParent, pszName )
+ , m_bExpanded( false )
+ , m_flAnimEndTime( 0.f )
+ , m_flResizeTime( 0.f )
+{}
+
+
+//-----------------------------------------------------------------------------
+// Set spcific collapsed state
+//-----------------------------------------------------------------------------
+void CExpandablePanel::SetCollapsed( bool bCollapsed )
+{
+ if ( bCollapsed == m_bExpanded )
+ {
+ ToggleCollapse();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Toggle collapsed state
+//-----------------------------------------------------------------------------
+void CExpandablePanel::ToggleCollapse()
+{
+ m_bExpanded = !m_bExpanded;
+ // Allow for quick bounce-back if they click while we're already animating
+ float flEndTime = RemapValClamped( GetPercentAnimated(), 0.f, 1.f, 0.f, m_flResizeTime );
+ m_flAnimEndTime = Plat_FloatTime() + flEndTime;
+
+ OnToggleCollapse( m_bExpanded );
+}
+
+//-----------------------------------------------------------------------------
+// Toggle collapsed state
+//-----------------------------------------------------------------------------
+void CExpandablePanel::OnCommand( const char *command )
+{
+ if ( FStrEq( "toggle_collapse", command ) )
+ {
+ ToggleCollapse();
+
+ return;
+ }
+
+ BaseClass::OnCommand( command );
+}
+
+//-----------------------------------------------------------------------------
+// Do collapsing interpolation
+//-----------------------------------------------------------------------------
+void CExpandablePanel::OnThink()
+{
+ BaseClass::OnThink();
+
+ float flTimeProgress = Gain( GetPercentAnimated(), 0.8f );
+
+ const int& nStartHeight = m_bExpanded ? m_nCollapsedHeight : m_nExpandedHeight;
+ const int& nEndHeight = m_bExpanded ? m_nExpandedHeight : m_nCollapsedHeight;
+ int nCurrentHeight = RemapValClamped( flTimeProgress, 0.f, 1.f, nStartHeight, nEndHeight );
+
+ if ( nCurrentHeight != GetTall() )
+ {
+ SetTall( nCurrentHeight );
+ Panel* pParent = GetParent();
+ if ( pParent )
+ {
+ pParent->InvalidateLayout();
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Where we're at in our interpolation
+//-----------------------------------------------------------------------------
+float CExpandablePanel::GetPercentAnimated() const
+{
+ return RemapValClamped( Plat_FloatTime() - ( m_flAnimEndTime - m_flResizeTime ), 0.f, m_flResizeTime, 0.f, 1.f );
+}
+