//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // //=============================================================================// #include "cbase.h" #include "ienginevgui.h" #include #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 #include #include #include #include #include #include #include #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( 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 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; iSetPos( 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 ); }