summaryrefslogtreecommitdiff
path: root/vgui2/dme_controls/attributeslider.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'vgui2/dme_controls/attributeslider.cpp')
-rw-r--r--vgui2/dme_controls/attributeslider.cpp1350
1 files changed, 1350 insertions, 0 deletions
diff --git a/vgui2/dme_controls/attributeslider.cpp b/vgui2/dme_controls/attributeslider.cpp
new file mode 100644
index 0000000..4597e4a
--- /dev/null
+++ b/vgui2/dme_controls/attributeslider.cpp
@@ -0,0 +1,1350 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#include "dme_controls/attributeslider.h"
+#include "materialsystem/imesh.h"
+#include "movieobjects/dmeanimationset.h"
+#include "vgui/IInput.h"
+#include "vgui/ISurface.h"
+#include "vgui_controls/TextEntry.h"
+#include "vgui_controls/TextImage.h"
+#include "vgui_controls/subrectimage.h"
+#include "vgui_controls/CheckButton.h"
+#include "dme_controls/BaseAnimSetAttributeSliderPanel.h"
+#include "dme_controls/BaseAnimationSetEditor.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+using namespace vgui;
+
+
+//-----------------------------------------------------------------------------
+// Enums
+//-----------------------------------------------------------------------------
+#define SLIDER_PIXEL_SPACING 3
+#define CIRCULAR_CONTROL_RADIUS 6.0f
+#define UNDO_CHAIN_MOUSEWHEEL_ATTRIBUTE_SLIDER 9876
+#define FRAC_PER_PIXEL 0.0025f
+#define ANIM_SET_ATTRIBUTE_SLIDER_BALANCE_INSET 30
+#define ANIM_SET_ATTRIBUTE_SLIDER_LEFT_BORDER 5
+#define ANIM_SET_ATTRIBUTE_SLIDER_GRAPH_BUTTON_WIDTH 16
+#define ANIM_SET_ATTRIBUTE_SLIDER_MULTILEVEL_INSET 30
+
+
+static ConVar ifm_attributeslider_sensitivity( "ifm_attributeslider_sensitivity", "3.0", 0 );
+
+
+//-----------------------------------------------------------------------------
+// Globals
+//-----------------------------------------------------------------------------
+static Color s_TextColor( 200, 200, 200, 192 );
+static Color s_TextColorFocus( 208, 143, 40, 192 );
+
+// NOTE: Index with [preview][selected]
+static Color s_BarColor[2][2] =
+{
+ { Color( 45, 45, 45, 255 ), Color( 150, 80, 0, 255 ) },
+ { Color( 30, 255, 255, 80 ), Color( 30, 180, 255, 255 ) }
+};
+
+static Color s_ZeroColor[2][2] =
+{
+ { Color( 33, 33, 33, 255 ), Color( 0, 255, 255, 60 ) },
+ { Color( 100, 80, 0, 255 ), Color( 0, 180, 255, 255 ) }
+};
+
+static Color s_DraggingBarColor( 142, 142, 142, 255 );
+static Color s_PreviewTickColor( 255, 164, 8, 255 );
+static Color s_OldValueTickColor( 100, 100, 100, 63 );
+
+static Color s_MidpointColor( 115, 115, 115, 255 );
+
+//-----------------------------------------------------------------------------
+// Blends flex values in left-right space instead of balance/value space
+//-----------------------------------------------------------------------------
+static void BlendFlexValues( AttributeValue_t *pResult, const AttributeValue_t &src, const AttributeValue_t &dest, float flBlend, float flBalanceFilter = 0.5f )
+{
+ // Apply the left-right balance to the target
+ float flLeftFilter, flRightFilter;
+ ValueBalanceToLeftRight( &flLeftFilter, &flRightFilter, flBlend, flBalanceFilter );
+
+ // Do the math in 'left-right' space because we filter in that space
+ float flSrcLeft, flSrcRight;
+ ValueBalanceToLeftRight( &flSrcLeft, &flSrcRight, src.m_pValue[ANIM_CONTROL_VALUE], src.m_pValue[ANIM_CONTROL_BALANCE] );
+
+ float flDestLeft, flDestRight;
+ ValueBalanceToLeftRight( &flDestLeft, &flDestRight, dest.m_pValue[ANIM_CONTROL_VALUE], dest.m_pValue[ANIM_CONTROL_BALANCE] );
+
+ float flTargetLeft = flSrcLeft + flLeftFilter * ( flDestLeft - flSrcLeft );
+ float flTargetRight = flSrcRight + flRightFilter * ( flDestRight - flSrcRight );
+
+ LeftRightToValueBalance( &pResult->m_pValue[ANIM_CONTROL_VALUE], &pResult->m_pValue[ANIM_CONTROL_BALANCE], flTargetLeft, flTargetRight,
+ ( flBlend <= 0.5f ) ? src.m_pValue[ANIM_CONTROL_BALANCE] : dest.m_pValue[ANIM_CONTROL_BALANCE] );
+
+ pResult->m_pValue[ANIM_CONTROL_MULTILEVEL] = src.m_pValue[ANIM_CONTROL_MULTILEVEL] + ( dest.m_pValue[ANIM_CONTROL_MULTILEVEL] - src.m_pValue[ANIM_CONTROL_MULTILEVEL] ) * flBlend;
+}
+
+//-----------------------------------------------------------------------------
+// The panel used to do text entry when double-clicking in the slider
+//-----------------------------------------------------------------------------
+class CAttributeSliderTextEntry : public TextEntry
+{
+ DECLARE_CLASS_SIMPLE( CAttributeSliderTextEntry, TextEntry );
+
+public:
+ CAttributeSliderTextEntry( CAttributeSlider *slider, const char *panelName ) :
+ BaseClass( (Panel *)slider, panelName ), m_pSlider( slider )
+ {
+ Assert( m_pSlider );
+ }
+
+ MESSAGE_FUNC_PARAMS( OnKillFocus, "KillFocus", kv );
+ virtual void OnMouseWheeled( int delta );
+
+private:
+ CAttributeSlider *m_pSlider;
+};
+
+
+
+//-----------------------------------------------------------------------------
+//
+// CAttributeSlider begins here
+//
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// Constructor, destructor
+//-----------------------------------------------------------------------------
+CAttributeSlider::CAttributeSlider( CBaseAnimSetAttributeSliderPanel *parent, const char *panelName, CDmElement *pControl ) :
+ BaseClass( (Panel *)parent, panelName ),
+ m_pParent( parent ),
+ m_pWhite( NULL ),
+ m_bPreviewEnabled( false ),
+ m_bSimplePreviewOnly( true ),
+ m_bCursorInsidePanel( false ),
+ m_flPreviewGoalTime( -1.0f ),
+ m_bRampUp( false ),
+ m_bFaderBeingDragged( false ),
+ m_flFaderAmount( 1.0f ),
+ m_bIsLogPreviewControl( false ),
+ m_bSelected( false ),
+ m_pRightTextField( 0 )
+{
+ m_SliderMode = SLIDER_MODE_NONE;
+ m_hControl = pControl;
+
+ // Cache off control information since this state should never change
+ // NOTE: If it ever does, just change the implementations of
+ // IsTransform + GetMidpoint to always read these values from the attributes
+ m_bTransform = pControl->GetValue< bool >( "transform" );
+
+ m_nDragStartPosition[ 0 ] = m_nDragStartPosition[ 1 ] = 0;
+ m_nAccum[ 0 ] = m_nAccum[ 1 ] = 0;
+ m_flDragStartValue = 1.0f;
+ m_flDragStartBalance = 0.5f;
+
+ SetPaintBackgroundEnabled( true );
+
+ m_pName = new TextImage( panelName );
+ m_pValues[ 0 ] = new TextImage( "" );
+ m_pValues[ 1 ] = new TextImage( "" );
+ m_pValues[ 2 ] = new TextImage( "" );
+
+ m_pCircleImage = new CSubRectImage( "tools/ifm/icon_balance", false, 7, 8, 19, 15 );
+
+ // Allocate a white material
+ KeyValues *pVMTKeyValues = new KeyValues( "UnlitGeneric" );
+ pVMTKeyValues->SetInt( "$vertexcolor", 1 );
+ pVMTKeyValues->SetInt( "$vertexalpha", 1 );
+ pVMTKeyValues->SetInt( "$ignorez", 1 );
+ pVMTKeyValues->SetInt( "$no_fullbright", 1 );
+ pVMTKeyValues->SetInt( "$nocull", 1 );
+ m_pWhite.Init( "AttributeSlider_White", NULL, pVMTKeyValues );
+
+ SetBgColor( Color( 42, 42, 42, 255 ) );
+
+ m_bIsControlActive[ANIM_CONTROL_VALUE] = true;
+ m_bIsControlActive[ANIM_CONTROL_BALANCE] = false;
+ m_bIsControlActive[ANIM_CONTROL_MULTILEVEL] = false;
+
+ m_pTextField = new CAttributeSliderTextEntry( this, panelName );
+ m_pTextField->SetVisible( false );
+ m_pTextField->SetEnabled( false );
+ m_pTextField->SelectAllOnFocusAlways( true );
+
+ SetPaintBorderEnabled( false );
+}
+
+CAttributeSlider::~CAttributeSlider()
+{
+ m_pWhite.Shutdown();
+ delete m_pCircleImage;
+ delete m_pName;
+ delete m_pValues[ 0 ];
+ delete m_pValues[ 1 ];
+ delete m_pValues[ 2 ];
+}
+
+
+//-----------------------------------------------------------------------------
+// Scheme
+//-----------------------------------------------------------------------------
+void CAttributeSlider::ApplySchemeSettings( IScheme *scheme )
+{
+ BaseClass::ApplySchemeSettings( scheme );
+
+ m_pName->SetFont( scheme->GetFont( "Default" ) );
+ m_pName->SetColor( s_TextColor );
+ m_pName->ResizeImageToContent();
+
+ m_pValues[ 0 ]->SetColor( s_TextColor );
+ m_pValues[ 0 ]->SetFont( scheme->GetFont( "Default" ) );
+ m_pValues[ 1 ]->SetColor( s_TextColorFocus );
+ m_pValues[ 1 ]->SetFont( scheme->GetFont( "Default" ) );
+ m_pValues[ 2 ]->SetColor( s_TextColor );
+ m_pValues[ 2 ]->SetFont( scheme->GetFont( "Default" ) );
+
+ m_pCircleImage->SetColor( Color( 255, 255, 255, 255 ) );
+
+ SetBgColor( Color( 42, 42, 42, 255 ) );
+ SetFgColor( Color( 194, 120, 0, 255 ) );
+}
+
+
+//-----------------------------------------------------------------------------
+// Gets/sets the slider value.
+// NOTE: This may not match the value pushed into the control because of fading
+//-----------------------------------------------------------------------------
+static const char *s_pChangeMessage[ANIM_CONTROL_COUNT] =
+{
+ "SliderMoved",
+ "BalanceChanged",
+ "MultiLevelChanged",
+};
+
+static const char *s_pChangeKeyValue[ANIM_CONTROL_COUNT] =
+{
+ "position",
+ "balance",
+ "level",
+};
+
+void CAttributeSlider::ActivateControl( AnimationControlType_t type, bool bActive )
+{
+ if ( m_bIsControlActive[type] != bActive )
+ {
+ m_bIsControlActive[type] = bActive;
+ if ( bActive )
+ {
+ PostActionSignal( new KeyValues( s_pChangeMessage[type], s_pChangeKeyValue[type], m_Control.m_pValue[type] ) );
+ }
+ }
+}
+
+bool CAttributeSlider::IsControlActive( AnimationControlType_t type )
+{
+ return m_bIsControlActive[type];
+}
+
+void CAttributeSlider::SetValue( AnimationControlType_t type, float flValue )
+{
+ if ( m_Control.m_pValue[type] != flValue )
+ {
+ m_Control.m_pValue[type] = flValue;
+ if ( m_bIsControlActive[type] )
+ {
+ PostActionSignal( new KeyValues( s_pChangeMessage[type], s_pChangeKeyValue[type], flValue ) );
+ }
+ }
+}
+
+void CAttributeSlider::SetValue( const AttributeValue_t& value )
+{
+ for ( int i = 0; i < ANIM_CONTROL_COUNT; ++i )
+ {
+ SetValue( (AnimationControlType_t)i, value.m_pValue[i] );
+ }
+}
+
+float CAttributeSlider::GetValue( AnimationControlType_t type ) const
+{
+ return m_Control.m_pValue[type];
+}
+
+const AttributeValue_t& CAttributeSlider::GetValue() const
+{
+ return m_Control;
+}
+
+
+//-----------------------------------------------------------------------------
+// Returns the default value for the control
+//-----------------------------------------------------------------------------
+float CAttributeSlider::GetControlDefaultValue( AnimationControlType_t type ) const
+{
+ if ( IsTransform() )
+ return 0.0f;
+
+ Assert( m_hControl.Get() );
+ if ( !m_hControl.Get() )
+ return 0.0f;
+
+ switch ( type )
+ {
+ case ANIM_CONTROL_VALUE:
+ return m_hControl->GetValue<float>( "defaultValue" );
+ case ANIM_CONTROL_BALANCE:
+ return m_hControl->GetValue<float>( "defaultBalance" );
+ case ANIM_CONTROL_MULTILEVEL:
+ return m_hControl->GetValue<float>( "defaultMultilevel" );
+ }
+ return 0.0f;
+}
+
+
+//-----------------------------------------------------------------------------
+// Given a mouse position in (x,y) in local coordinates, which animation control is it over?
+//-----------------------------------------------------------------------------
+AnimationControlType_t CAttributeSlider::DetermineControl( int x, int y )
+{
+ if ( IsControlActive( ANIM_CONTROL_MULTILEVEL ) )
+ {
+ Rect_t rect;
+ GetControlRect( &rect, ANIM_CONTROL_MULTILEVEL );
+ if ( x >= rect.x && x < rect.x + rect.width && y >= rect.y && y < rect.y + rect.height )
+ return ANIM_CONTROL_MULTILEVEL;
+ }
+
+ return ANIM_CONTROL_VALUE;
+}
+
+
+void CAttributeSlider::SetSelected( bool state )
+{
+ m_bSelected = state;
+}
+
+bool CAttributeSlider::IsSelected() const
+{
+ return m_bSelected;
+}
+
+void CAttributeSlider::SetIsLogPreviewControl( bool state )
+{
+ m_bIsLogPreviewControl = state;
+}
+
+void CAttributeSlider::OnCursorEntered()
+{
+ BaseClass::OnCursorEntered();
+ m_bCursorInsidePanel = true;
+}
+
+void CAttributeSlider::OnCursorExited()
+{
+ BaseClass::OnCursorExited();
+ m_bCursorInsidePanel = false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Mouse event handlers
+//-----------------------------------------------------------------------------
+void CAttributeSlider::OnMousePressed( MouseCode code )
+{
+ if ( !IsEnabled() || IsInTextEntry() || IsDragging() )
+ return;
+
+ // Deal with transform sliders
+ if ( m_bTransform )
+ {
+ bool bCtrlDown = ( input()->IsKeyDown( KEY_LCONTROL ) || input()->IsKeyDown( KEY_RCONTROL ) );
+ m_pParent->SetLogPreviewControl( m_hControl );
+ if ( !bCtrlDown )
+ {
+ m_pParent->ClearSelectedControls();
+ }
+ m_pParent->SetControlSelected( this, !IsSelected() );
+ return;
+ }
+
+ // Determine which control we clicked on
+ int x,y;
+ input()->GetCursorPosition( x, y );
+ ScreenToLocal( x, y );
+ AnimationControlType_t type = DetermineControl( x, y );
+
+ // Right click sets the value to match the default value
+ if ( code == MOUSE_RIGHT )
+ {
+ SetValue( type, GetControlDefaultValue( type ) );
+
+ CUndoScopeGuard guard( "Set Slider Value To Default" );
+
+ StampValueIntoLogs( type, GetControlDefaultValue( type ) );
+ return;
+ }
+
+ if ( code != MOUSE_LEFT )
+ return;
+
+ // Cache off the value at the click point
+ // in case we end up receiving a double-click
+ m_InitialTextEntryValue = m_Control;
+
+ // Enter drag mode
+ m_SliderMode = (SliderMode_t)( SLIDER_MODE_FIRST_DRAG_MODE + type );
+ m_nDragStartPosition[ 0 ] = x;
+ m_nDragStartPosition[ 1 ] = y;
+ m_nAccum[ 0 ] = m_nAccum[ 1 ] = 0;
+ m_flDragStartValue = GetValue( type );
+ m_flDragStartBalance = GetValue( ANIM_CONTROL_BALANCE );
+ input()->SetMouseCapture( GetVPanel() );
+ SetCursor( dc_blank );
+ m_pParent->RecomputePreview();
+}
+
+
+void CAttributeSlider::OnCursorMoved( int x, int y )
+{
+ if ( !IsEnabled() || !IsDragging() || m_bTransform )
+ return;
+
+ // NOTE: This works because we always slam the mouse to be back at the start position
+ // at the end of this function
+
+ // Accumulate the total mouse movement
+ int dx = x - m_nDragStartPosition[ 0 ];
+ m_nAccum[ 0 ] += dx;
+ float flFactor = FRAC_PER_PIXEL * ifm_attributeslider_sensitivity.GetFloat();
+
+ bool bInRecordMode = m_pParent->GetEditor()->GetRecordingState() == AS_RECORD;
+ float flMinVal = bInRecordMode ? -1.0f : 0.0f;
+ float flMaxVal = bInRecordMode ? 2.0f : 1.0f;
+
+ // Clamp accum so we never generate values < -1 or > 2
+ int nMinVal = floor( ( -m_flDragStartValue + flMinVal ) / flFactor );
+ int nMaxVal = ceil( ( -m_flDragStartValue + flMaxVal ) / flFactor );
+ m_nAccum[ 0 ] = clamp( m_nAccum[ 0 ], nMinVal, nMaxVal );
+
+ float flDelta = flFactor * m_nAccum[ 0 ];
+ if ( GetDragControl() == ANIM_CONTROL_VALUE && IsControlActive( ANIM_CONTROL_BALANCE ) )
+ {
+ // do the hacky conversion from the ui's left/right to the underlying value/balance
+ float flLeftValue, flRightValue;
+ ValueBalanceToLeftRight( &flLeftValue, &flRightValue, m_flDragStartValue, m_flDragStartBalance );
+
+ float flLeftDelta, flRightDelta;
+ ValueBalanceToLeftRight( &flLeftDelta, &flRightDelta, flDelta, m_pParent->GetBalanceSliderValue() );
+
+ flLeftValue = clamp( flLeftValue + flLeftDelta, flMinVal, flMaxVal );
+ flRightValue = clamp( flRightValue + flRightDelta, flMinVal, flMaxVal );
+
+ float flValue, flBalance;
+ LeftRightToValueBalance( &flValue, &flBalance, flLeftValue, flRightValue );
+
+ SetValue( GetDragControl(), flValue );
+ SetValue( ANIM_CONTROL_BALANCE, flBalance ); // TODO - add balance for multi control as well
+ }
+ else
+ {
+ float flValue = clamp( m_flDragStartValue + flDelta, flMinVal, flMaxVal );
+ SetValue( GetDragControl(), flValue );
+ }
+
+ // Slam the cursor back to the drag start point
+ if ( x != m_nDragStartPosition[ 0 ] || y != m_nDragStartPosition[ 1 ] )
+ {
+ x = m_nDragStartPosition[ 0 ];
+ y = m_nDragStartPosition[ 1 ];
+ LocalToScreen( x, y );
+ input()->SetCursorPos( x, y );
+ }
+}
+
+void CAttributeSlider::OnMouseReleased( MouseCode code )
+{
+ if ( !IsEnabled() || !IsDragging() || m_bTransform )
+ return;
+
+ m_SliderMode = SLIDER_MODE_NONE;
+ input()->SetMouseCapture( NULL );
+ SetCursor( dc_arrow );
+ m_pParent->RecomputePreview();
+}
+
+
+
+//-----------------------------------------------------------------------------
+//
+// Methods related to text entry mode
+//
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// Called by the text entry code to enter the value into the logs
+//-----------------------------------------------------------------------------
+void CAttributeSlider::StampValueIntoLogs( AnimationControlType_t type, float flValue )
+{
+ Assert( !m_bTransform );
+ m_pParent->StampValueIntoLogs( m_hControl, type, flValue );
+}
+
+
+//-----------------------------------------------------------------------------
+// Key typed key handler
+//-----------------------------------------------------------------------------
+void CAttributeSlider::OnKeyCodeTyped( KeyCode code )
+{
+ if ( !IsInTextEntry() )
+ {
+ BaseClass::OnKeyCodeTyped( code );
+ return;
+ }
+
+ switch ( code )
+ {
+ default:
+ BaseClass::OnKeyCodeTyped( code );
+ break;
+
+ case KEY_ESCAPE:
+ DiscardTextEntryValue();
+ break;
+
+ case KEY_ENTER:
+ AcceptTextEntryValue();
+ break;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Methods to entry text entry mode
+//-----------------------------------------------------------------------------
+void CAttributeSlider::EnterTextEntryMode( AnimationControlType_t type, bool bRelatchValues )
+{
+ if ( m_bTransform )
+ return;
+
+ m_SliderMode = (SliderMode_t)( SLIDER_MODE_FIRST_TEXT_MODE + type );
+
+ // For double-clicking, ignore the value set by the first single mouse click
+ if ( !bRelatchValues )
+ {
+ SetValue( m_InitialTextEntryValue );
+ }
+
+ m_pTextField->SetVisible( true );
+ m_pTextField->SetEnabled( true );
+
+ if ( type == ANIM_CONTROL_VALUE && IsControlActive( ANIM_CONTROL_BALANCE ) )
+ {
+ if ( !m_pRightTextField )
+ {
+ m_pRightTextField = new CAttributeSliderTextEntry( this, GetName() );
+ m_pRightTextField->SetVisible( false );
+ m_pRightTextField->SetEnabled( false );
+ m_pRightTextField->SelectAllOnFocusAlways( true );
+ InvalidateLayout();
+ }
+ m_pRightTextField->SetVisible( true );
+ m_pRightTextField->SetEnabled( true );
+
+ float flValue = m_InitialTextEntryValue.m_pValue[ ANIM_CONTROL_VALUE ];
+ float flBalance = m_InitialTextEntryValue.m_pValue[ ANIM_CONTROL_BALANCE ];
+ float flLeftValue, flRightValue;
+ ValueBalanceToLeftRight( &flLeftValue, &flRightValue, flValue, flBalance );
+
+ char val[ 64 ];
+ V_snprintf( val, sizeof( val ), "%f", flLeftValue );
+ m_pTextField->SetText( val );
+ V_snprintf( val, sizeof( val ), "%f", flRightValue );
+ m_pRightTextField->SetText( val );
+
+ m_pRightTextField->GotoTextEnd();
+ m_pRightTextField->RequestFocus();
+ }
+ else
+ {
+ char val[ 64 ];
+ Q_snprintf( val, sizeof( val ), "%f", m_InitialTextEntryValue.m_pValue[ type ] );
+ m_pTextField->SetText( val );
+ }
+
+ m_pTextField->GotoTextEnd();
+ m_pTextField->RequestFocus();
+}
+
+
+//-----------------------------------------------------------------------------
+// Methods to accept or discard the value in the text entry field
+//-----------------------------------------------------------------------------
+void CAttributeSlider::AcceptTextEntryValue()
+{
+ if ( !IsInTextEntry() )
+ return;
+
+ Assert( !m_bTransform );
+
+ // Get the value in the text entry field
+ char buf[ 64 ];
+ m_pTextField->GetText( buf, sizeof( buf ) );
+ float flValue = Q_atof( buf );
+
+ // Hide the text entry
+ m_pTextField->SetVisible( false );
+ m_pTextField->SetEnabled( false );
+
+ if ( m_pRightTextField && GetTextEntryControl() == ANIM_CONTROL_VALUE && IsControlActive( ANIM_CONTROL_BALANCE ) )
+ {
+ float flLeftValue = flValue;
+
+ // Get the value in the text entry field
+ buf[0] = 0;
+ m_pRightTextField->GetText( buf, sizeof( buf ) );
+ float flRightValue = Q_atof( buf );
+
+ // Hide the text entry
+ m_pRightTextField->SetVisible( false );
+ m_pRightTextField->SetEnabled( false );
+
+ float flBalance;
+ LeftRightToValueBalance( &flValue, &flBalance, flLeftValue, flRightValue );
+
+ SetValue( ANIM_CONTROL_BALANCE, flBalance );
+ StampValueIntoLogs( ANIM_CONTROL_BALANCE, flBalance );
+ }
+
+ // Apply the change
+ AnimationControlType_t type = GetTextEntryControl();
+ SetValue( type, flValue );
+ StampValueIntoLogs( type, flValue );
+
+ m_SliderMode = SLIDER_MODE_NONE;
+ RequestFocus();
+}
+
+void CAttributeSlider::DiscardTextEntryValue()
+{
+ if ( !IsInTextEntry() )
+ return;
+
+ Assert( !m_bTransform );
+
+ // Hide the text entry
+ m_pTextField->SetVisible( false );
+ m_pTextField->SetEnabled( false );
+
+ if ( m_pRightTextField && GetTextEntryControl() == ANIM_CONTROL_VALUE && IsControlActive( ANIM_CONTROL_BALANCE ) )
+ {
+ m_pRightTextField->SetVisible( false );
+ m_pRightTextField->SetEnabled( false );
+ }
+
+ m_SliderMode = SLIDER_MODE_NONE;
+ RequestFocus();
+}
+
+
+//-----------------------------------------------------------------------------
+// Methods of the text entry widget
+//-----------------------------------------------------------------------------
+void CAttributeSliderTextEntry::OnKillFocus( KeyValues *pParams )
+{
+ Assert( m_pSlider );
+
+ SelectNone();
+
+ VPANEL hPanel = (VPANEL)pParams->GetPtr( "newPanel" );
+ if ( hPanel != INVALID_PANEL && vgui::ipanel()->GetParent( hPanel ) == m_pSlider->GetVPanel() )
+ return;
+
+ m_pSlider->AcceptTextEntryValue();
+}
+
+void CAttributeSliderTextEntry::OnMouseWheeled( int delta )
+{
+ if ( m_pSlider->m_bTransform )
+ return;
+
+ float deltaFactor;
+ if ( input()->IsKeyDown(KEY_LSHIFT) )
+ {
+ deltaFactor = ((float)delta) * 10.0f;
+ }
+ else if ( input()->IsKeyDown(KEY_LCONTROL) )
+ {
+ deltaFactor = ((float)delta) / 100.0;
+ }
+ else
+ {
+ deltaFactor = ((float)delta) / 10.0;
+ }
+
+ char sz[ 64 ];
+ GetText( sz, sizeof( sz ) );
+
+ float val = Q_atof( sz ) + deltaFactor;
+ if ( input()->IsKeyDown(KEY_LALT) )
+ {
+ val = clamp( val, 0.0f, 1.0f );
+ }
+
+ Q_snprintf( sz, sizeof( sz ), "%f", val );
+
+ SetText( sz );
+ m_pSlider->SetValue( ANIM_CONTROL_VALUE, val );
+
+ CUndoScopeGuard guard( UNDO_CHAIN_MOUSEWHEEL_ATTRIBUTE_SLIDER, "Set Slider Value" );
+
+ m_pSlider->StampValueIntoLogs( m_pSlider->GetTextEntryControl(), val );
+}
+
+void CAttributeSlider::OnMouseDoublePressed( MouseCode code )
+{
+ if ( !IsEnabled() || IsDragging() )
+ return;
+
+ if ( code != MOUSE_LEFT )
+ return;
+
+ int x,y;
+ input()->GetCursorPosition( x, y );
+ ScreenToLocal( x, y );
+ AnimationControlType_t type = DetermineControl( x, y );
+ EnterTextEntryMode( type, false );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// Methods related to preview
+//
+//-----------------------------------------------------------------------------
+void CAttributeSlider::EnablePreview( bool state, bool simple, bool faderdrag )
+{
+ m_bPreviewEnabled = state;
+ m_bSimplePreviewOnly = simple;
+ m_bFaderBeingDragged = faderdrag;
+}
+
+bool CAttributeSlider::IsPreviewEnabled() const
+{
+ return m_bPreviewEnabled;
+}
+
+bool CAttributeSlider::IsSimplePreview() const
+{
+ return m_bSimplePreviewOnly;
+}
+
+#define ATTRIBUTE_SLIDER_RAMP_TIME 0.5f
+
+bool CAttributeSlider::IsRampingTowardPreview() const
+{
+ if ( m_flPreviewGoalTime == -1.0f )
+ return false;
+
+ return true;
+}
+
+void CAttributeSlider::RampDown()
+{
+ if ( m_flPreviewGoalTime == -1.0f )
+ return;
+
+ m_Previous.m_Current = GetValue();
+ m_Previous.m_Full = GetValue( );
+ m_bRampUp = false;
+}
+
+void CAttributeSlider::UpdateFaderAmount( float flAmount )
+{
+ m_flFaderAmount = flAmount;
+ AttributeValue_t current = GetValue();
+ if ( m_flPreviewGoalTime == -1.0f )
+ {
+ BlendFlexValues( &m_Preview.m_Current, current, m_Preview.m_Full, flAmount );
+ return;
+ }
+
+ BlendFlexValues( &m_Next.m_Current, current, m_Next.m_Full, flAmount );
+}
+
+void CAttributeSlider::UpdateTime( float dt )
+{
+ if ( m_flPreviewGoalTime == -1.0f )
+ return;
+
+ // Move toward goal
+ if ( m_bRampUp )
+ {
+ if ( m_flPreviewGoalTime < ATTRIBUTE_SLIDER_RAMP_TIME )
+ {
+ m_flPreviewGoalTime += dt;
+ }
+ }
+ else
+ {
+ m_flPreviewGoalTime -= dt;
+ }
+
+ if ( m_flPreviewGoalTime >= ATTRIBUTE_SLIDER_RAMP_TIME )
+ {
+ m_Preview = m_Next;
+ m_flPreviewGoalTime = ATTRIBUTE_SLIDER_RAMP_TIME;
+
+ }
+ else if ( m_flPreviewGoalTime <= 0.0f )
+ {
+ m_flPreviewGoalTime = -1.0f;
+ m_Preview = m_Previous;
+ }
+ else
+ {
+ float frac = m_flPreviewGoalTime / ATTRIBUTE_SLIDER_RAMP_TIME;
+ BlendFlexValues( &m_Preview.m_Current, m_Previous.m_Current, m_Next.m_Current, frac );
+ BlendFlexValues( &m_Preview.m_Full, m_Previous.m_Full, m_Next.m_Full, frac );
+ }
+}
+
+void CAttributeSlider::SetPreview( const AttributeValue_t &value, const AttributeValue_t &full, bool instantaneous, bool startfromcurrent )
+{
+ m_bRampUp = true;
+
+ if ( instantaneous )
+ {
+ m_Next.m_Current = value;
+ m_Next.m_Full = full;
+
+ m_Preview = m_Previous = m_Next;
+ m_flPreviewGoalTime = -1.0f;
+ }
+ else
+ {
+ // Current becomes previous, next becomes goal and preview starts moving toward that goal
+ if ( startfromcurrent )
+ {
+ m_Previous.m_Current = GetValue( );
+ m_Previous.m_Full = GetValue( );
+ }
+ else
+ {
+ m_Previous = m_Preview;
+ }
+
+ m_Next.m_Current = value;
+ m_Next.m_Full = full;
+ m_flPreviewGoalTime = 0.0f;
+ }
+}
+
+const AttributeValue_t &CAttributeSlider::GetPreview() const
+{
+ return m_Preview.m_Current;
+}
+
+float CAttributeSlider::GetPreview( AnimationControlType_t type ) const
+{
+ return m_Preview.m_Current.m_pValue[type];
+}
+
+// Estimates the value of the control given a local coordinate
+float CAttributeSlider::EstimateValueAtPos( int nLocalX, int nLocalY ) const
+{
+ Rect_t rect;
+ GetControlRect( &rect, ANIM_CONTROL_VALUE );
+
+ float flFactor = rect.width > 1 ? (float)( nLocalX - rect.x ) / (float)( rect.width - 1 ) : 0.5f;
+ flFactor = clamp( flFactor, 0.0f, 1.0f );
+ return flFactor;
+}
+
+
+//-----------------------------------------------------------------------------
+// Layout
+//-----------------------------------------------------------------------------
+void CAttributeSlider::PerformLayout()
+{
+ BaseClass::PerformLayout();
+
+ Rect_t rect;
+ GetControlRect( &rect, ANIM_CONTROL_VALUE );
+
+ // Place the text entry along the main attribute track rectangle
+ if ( m_pRightTextField && GetTextEntryControl() == ANIM_CONTROL_VALUE && IsControlActive( ANIM_CONTROL_BALANCE ) )
+ {
+ m_pTextField ->SetBounds( rect.x, rect.y, rect.width / 2, rect.height );
+ m_pRightTextField->SetBounds( rect.x + rect.width / 2, rect.y, rect.width / 2, rect.height );
+ }
+ else
+ {
+ m_pTextField->SetBounds( rect.x, rect.y, rect.width, rect.height );
+ }
+}
+
+void CAttributeSlider::GetControlRect( Rect_t *pRect, AnimationControlType_t type ) const
+{
+ int sw, sh;
+ const_cast<CAttributeSlider*>( this )->GetSize( sw, sh );
+
+ int cw, ch;
+ m_pCircleImage->GetSize( cw, ch );
+
+ switch ( type )
+ {
+ case ANIM_CONTROL_VALUE:
+ pRect->x = 2 * SLIDER_PIXEL_SPACING + cw;
+ pRect->y = SLIDER_PIXEL_SPACING;
+ pRect->width = sw - pRect->x * 2;
+ pRect->height = max( 0, sh - SLIDER_PIXEL_SPACING * 2 );
+ break;
+/*
+ case ANIM_CONTROL_BALANCE:
+ pRect->x = SLIDER_PIXEL_SPACING;
+ pRect->y = max( 0, sh - ch ) / 2;
+ pRect->width = cw;
+ pRect->height = min( ch, sh );
+ break;
+*/
+ case ANIM_CONTROL_MULTILEVEL:
+ pRect->x = sw - SLIDER_PIXEL_SPACING - cw;
+ pRect->y = max( 0, sh - ch ) / 2;
+ pRect->width = cw;
+ pRect->height = min( ch, sh );
+ break;
+ }
+}
+
+bool CAttributeSlider::IsFaderBeingDragged()
+{
+ return IsPreviewEnabled() && m_bFaderBeingDragged;
+}
+
+
+//-----------------------------------------------------------------------------
+//
+// Methods related to painting start here
+//
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// Used to control how fader-driven ticks look
+//-----------------------------------------------------------------------------
+float CAttributeSlider::GetPreviewAlphaScale() const
+{
+ return max( m_flFaderAmount, 0.1f );
+}
+
+
+//-----------------------------------------------------------------------------
+// Draws a tick on the main control
+//-----------------------------------------------------------------------------
+void CAttributeSlider::DrawTick( const Color& clr, float frac, int width, int inset )
+{
+ // Get the control position
+ Rect_t rect;
+ GetControlRect( &rect, ANIM_CONTROL_VALUE );
+
+ // Inset by 1 pixel
+ rect.x++; rect.y++; rect.width -= 2; rect.height -= 2;
+
+ surface()->DrawSetColor( clr );
+
+ int previewx = (int)( frac * (float)rect.width + 0.5f ) + rect.x;
+ int previewtall = rect.height - 2 * inset;
+ int ypos = rect.y + ( rect.height - previewtall ) / 2;
+
+ int xpos = previewx - width / 2;
+ xpos = clamp( xpos, rect.x, rect.x + rect.width - width );
+ surface()->DrawFilledRect( xpos, ypos, xpos + width, ypos + previewtall );
+}
+
+
+//-----------------------------------------------------------------------------
+// Draws a preview tick on the main control
+//-----------------------------------------------------------------------------
+void CAttributeSlider::DrawPreviewTick( bool bMainTick )
+{
+ Color col = s_PreviewTickColor;
+ col[ 3 ] *= bMainTick ? GetPreviewAlphaScale() : 0.5f;
+ DrawTick( col, m_Next.m_Full.m_pValue[ ANIM_CONTROL_VALUE ], 2, 2 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Draws a tick on a circular control
+//-----------------------------------------------------------------------------
+void CAttributeSlider::DrawCircularTick( const Color& clr, float flValue, int nCenterX, int nCenterY, float flRadius )
+{
+ float flFraction = 1.0f;
+ float flAngle = 0.0f;
+ if ( flValue < 0.5f )
+ {
+ flFraction = ( flValue / 0.5f );
+ flAngle = 180.0f + flFraction * 180.0f;
+ }
+ else
+ {
+ flFraction = ( flValue - 0.5f ) * 2.0f;
+ flAngle = flFraction * 180.0f;
+ }
+
+ float flRadians = DEG2RAD( flAngle );
+ float ca = cos( flRadians );
+ float sa = sin( flRadians );
+
+ int nEndX = nCenterX + flRadius * sa;
+ int nEndY = nCenterY - flRadius * ca;
+
+ surface()->DrawSetColor( clr );
+ surface()->DrawLine( nCenterX, nCenterY, nEndX, nEndY );
+}
+
+
+//-----------------------------------------------------------------------------
+// Draws a preview of a circular control
+//-----------------------------------------------------------------------------
+void CAttributeSlider::DrawCircularPreview( AnimationControlType_t type, bool bMainTick, float flRadius )
+{
+ Rect_t rect;
+ GetControlRect( &rect, type );
+
+ // Fill left from top
+ float flPreview = m_Next.m_Full.m_pValue[type];
+ float flCurrent = GetValue( type );
+
+ Color clr = s_PreviewTickColor;
+ clr[ 3 ] *= bMainTick ? GetPreviewAlphaScale() : 0.5f;
+
+ int nCenterX = rect.x + rect.width / 2;
+ int nCenterY = rect.y + rect.height / 2;
+ DrawCircularTick( clr, flPreview, nCenterX, nCenterY, flRadius );
+
+ if ( m_bSimplePreviewOnly && !m_bFaderBeingDragged )
+ return;
+
+ clr = s_OldValueTickColor;
+ if ( !bMainTick )
+ {
+ clr[ 3 ] *= 0.5f;
+ }
+
+ DrawCircularTick( clr, flCurrent, nCenterX, nCenterY, flRadius );
+}
+
+
+//-----------------------------------------------------------------------------
+// Paints ticks
+//-----------------------------------------------------------------------------
+void CAttributeSlider::Paint()
+{
+ DrawTick( s_OldValueTickColor, GetValue( ANIM_CONTROL_VALUE ), 1, 0 );
+ if ( m_bPreviewEnabled )
+ {
+ DrawPreviewTick( true );
+ if ( IsControlActive( ANIM_CONTROL_BALANCE ) )
+ {
+ DrawCircularPreview( ANIM_CONTROL_BALANCE, true, CIRCULAR_CONTROL_RADIUS );
+ }
+ if ( IsControlActive( ANIM_CONTROL_MULTILEVEL ) )
+ {
+ DrawCircularPreview( ANIM_CONTROL_MULTILEVEL, true, CIRCULAR_CONTROL_RADIUS );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Draws the min, current, and max values for the slider
+//-----------------------------------------------------------------------------
+void CAttributeSlider::DrawValueLabel( float flValue )
+{
+ float flMinVal = 0.0f;
+ float flMaxVal = 1.0f;
+ flValue = clamp( flValue, flMinVal, flMaxVal );
+
+ Rect_t rect;
+ GetControlRect( &rect, ANIM_CONTROL_VALUE );
+
+ int cw, ch;
+ char sz[ 32 ];
+ Q_snprintf( sz, sizeof( sz ), "%.1f", flMinVal );
+ m_pValues[ 0 ]->SetText( sz );
+ m_pValues[ 0 ]->ResizeImageToContent();
+ m_pValues[ 0 ]->GetContentSize( cw, ch );
+ m_pValues[ 0 ]->SetPos( rect.x + 5, rect.y + ( rect.height - ch ) * 0.5f );
+ m_pValues[ 0 ]->Paint();
+
+ Q_snprintf( sz, sizeof( sz ), "%.1f", flMaxVal );
+ m_pValues[ 2 ]->SetText( sz );
+ m_pValues[ 2 ]->ResizeImageToContent();
+ m_pValues[ 2 ]->GetContentSize( cw, ch );
+ m_pValues[ 2 ]->SetPos( rect.x + rect.width - cw - 5, rect.y + ( rect.height - ch ) * 0.5f );
+ m_pValues[ 2 ]->Paint();
+
+ Q_snprintf( sz, sizeof( sz ), "%.3f", flValue );
+ m_pValues[ 1 ]->SetText( sz );
+ m_pValues[ 1 ]->ResizeImageToContent();
+ m_pValues[ 1 ]->GetContentSize( cw, ch );
+ m_pValues[ 1 ]->SetPos( rect.x + ( rect.width - cw ) * 0.5f, rect.y + ( rect.height - ch ) * 0.5f );
+ m_pValues[ 1 ]->Paint();
+}
+
+
+//-----------------------------------------------------------------------------
+// Draws the text for the slider. It's either the slider name, or its value if dragging is happening
+//-----------------------------------------------------------------------------
+void CAttributeSlider::DrawNameLabel()
+{
+ if ( IsDragging() )
+ {
+ float flValue = GetValue( GetDragControl() );
+ DrawValueLabel( flValue );
+ return;
+ }
+
+ if ( IsInTextEntry() )
+ return;
+
+ int w, h;
+ GetSize( w, h );
+
+ int cw, ch;
+ Color clr = m_bCursorInsidePanel ? s_TextColorFocus : s_TextColor;
+ m_pName->SetColor( clr );
+ m_pName->GetContentSize( cw, ch );
+
+ Rect_t rect;
+ GetControlRect( &rect, ANIM_CONTROL_VALUE );
+
+ m_pName->SetPos( rect.x + ( rect.width - cw ) * 0.5f, rect.y + ( rect.height - ch ) * 0.5f );
+ m_pName->Paint();
+}
+
+
+//-----------------------------------------------------------------------------
+// Draws the midpoint value for the slider
+//-----------------------------------------------------------------------------
+void CAttributeSlider::DrawMidpoint( int x, int ty, int ttall )
+{
+ surface()->DrawSetColor( s_MidpointColor );
+ surface()->DrawFilledRect( x, ty, x + 1, ty + ttall );
+}
+
+
+//-----------------------------------------------------------------------------
+// Paints circular controls used for balance + multilevel controls
+//-----------------------------------------------------------------------------
+void CAttributeSlider::PaintCircularControl( float flValue, const Rect_t& rect )
+{
+ flValue = clamp( flValue, 0.0f, 1.0f );
+
+ m_pCircleImage->SetPos( rect.x, rect.y );
+ m_pCircleImage->Paint();
+
+ int ofs[ 2 ] = { 0 };
+ LocalToScreen( ofs[ 0 ], ofs[ 1 ] );
+
+ int nCenterX = ofs[ 0 ] + rect.x + rect.width / 2;
+ int nCenterY = ofs[ 1 ] + rect.y + rect.height / 2;
+
+ float maxTrianges = 36.0f;
+
+ float frac = 0.0f;
+ float step = 180.0f / (float)( maxTrianges );
+ float ang = 0.0f;
+ float clamp = 360.0f;
+ int numTriangles = 0;
+ float radius = CIRCULAR_CONTROL_RADIUS;
+
+ float zpos = vgui::surface()->GetZPos();
+
+ Vector centerVert( nCenterX, nCenterY, zpos );
+
+ Vector top;
+ top = centerVert;
+ top.y -= radius;
+
+ // Fill left from top
+ if ( flValue < 0.5f )
+ {
+ frac = 1.0f - ( flValue / 0.5f );
+ numTriangles = (int)( frac * ( maxTrianges ) + 0.5f );
+ clamp = 180.0f;
+ step = -step;
+ ang = 360.0f;
+ }
+ else
+ {
+ frac = ( flValue - 0.5f ) / 0.5f;
+ numTriangles = (int)( frac * ( maxTrianges ) + 0.5f );
+ clamp = 180.0f;
+ }
+
+ if ( numTriangles == 0 )
+ return;
+
+ CMatRenderContextPtr pRenderContext( materials );
+ IMesh *pMesh = pRenderContext->GetDynamicMesh( true, NULL, NULL, m_pWhite );
+
+ Color clr( 102, 102, 102, 255 );
+
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, numTriangles );
+
+ Vector next;
+ next.Init();
+
+ for ( int j = 0; j < numTriangles; j++ )
+ {
+ ang += step;
+ //ang = min( ang, clamp );
+
+ float flRadians = DEG2RAD( ang );
+
+ float ca = cos( flRadians );
+ float sa = sin( flRadians );
+
+ meshBuilder.Position3fv( centerVert.Base() );
+ meshBuilder.Color4ub( clr.r(), clr.g(), clr.b(), clr.a() );
+ meshBuilder.TexCoord2f( 0, 0, 0 );
+ meshBuilder.AdvanceVertex();
+
+ next.Init();
+ next.x = radius * sa;
+ next.y = -radius * ca;
+ next += centerVert;
+
+ if ( step > 0 )
+ {
+ meshBuilder.Position3fv( top.Base() );
+ meshBuilder.Color4ub( clr.r(), clr.g(), clr.b(), clr.a() );
+ meshBuilder.TexCoord2f( 0, 0, 1 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv( next.Base() );
+ meshBuilder.Color4ub( clr.r(), clr.g(), clr.b(), clr.a() );
+ meshBuilder.TexCoord2f( 0, 1, 0 );
+ meshBuilder.AdvanceVertex();
+ }
+ else
+ {
+ meshBuilder.Position3fv( next.Base() );
+ meshBuilder.Color4ub( clr.r(), clr.g(), clr.b(), clr.a() );
+ meshBuilder.TexCoord2f( 0, 0, 1 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv( top.Base() );
+ meshBuilder.Color4ub( clr.r(), clr.g(), clr.b(), clr.a() );
+ meshBuilder.TexCoord2f( 0, 1, 0 );
+ meshBuilder.AdvanceVertex();
+ }
+
+ top = next;
+ }
+
+ meshBuilder.End();
+ pMesh->Draw();
+}
+
+
+//-----------------------------------------------------------------------------
+// Paints the slider
+//-----------------------------------------------------------------------------
+void CAttributeSlider::PaintBackground()
+{
+ Rect_t rect;
+ GetControlRect( &rect, ANIM_CONTROL_VALUE );
+
+ // Paint the border
+ surface()->DrawSetColor( Color( 24, 24, 24, 255 ) );
+ // top and left
+ surface()->DrawOutlinedRect( rect.x, rect.y, rect.x + rect.width, rect.y + 1 );
+ surface()->DrawOutlinedRect( rect.x, rect.y, rect.x + 1, rect.y + rect.height );
+ // right
+ surface()->DrawSetColor( Color( 33, 33, 33, 255 ) );
+ surface()->DrawOutlinedRect( rect.x + rect.width - 1, rect.y, rect.x + rect.width, rect.y + rect.height );
+ // bottom
+ surface()->DrawSetColor( Color( 56, 56, 56, 255 ) );
+ surface()->DrawOutlinedRect( rect.x, rect.y + rect.height - 1, rect.x + rect.width, rect.y + rect.height );
+
+ // Inset the rect by 1 pixel
+ ++rect.x; ++rect.y; rect.width -= 2; rect.height -= 2;
+
+ int y0 = rect.y;
+ int y1 = rect.y + rect.height / 2;
+ int y2 = rect.y + rect.height;
+
+ // Draw the main bar background
+ surface()->DrawSetColor( s_ZeroColor[ m_bIsLogPreviewControl ][ IsSelected() ] );
+ surface()->DrawFilledRect( rect.x, y0, rect.x + rect.width, y2 );
+
+ AnimationControlType_t viewType = ANIM_CONTROL_VALUE;
+ if ( IsDragging() )
+ {
+ viewType = GetDragControl();
+ }
+ else if ( IsInTextEntry() )
+ {
+ viewType = GetTextEntryControl();
+ }
+
+ bool bUsePreview = m_bPreviewEnabled && ( !m_bSimplePreviewOnly || m_bFaderBeingDragged );
+
+ float flMidPoint = GetControlDefaultValue( viewType );
+ int nMidPoint = (int)( (float)rect.width * clamp( flMidPoint, 0.0f, 1.0f ) + 0.5f );
+
+ float flValue = bUsePreview ? m_Preview.m_Current.m_pValue[viewType] : GetValue( viewType );
+ if ( viewType == ANIM_CONTROL_VALUE && IsControlActive( ANIM_CONTROL_BALANCE ) )
+ {
+ float flBalance = bUsePreview ? m_Preview.m_Current.m_pValue[ ANIM_CONTROL_BALANCE ] : GetValue( ANIM_CONTROL_BALANCE );
+ float flLeftValue, flRightValue;
+ ValueBalanceToLeftRight( &flLeftValue, &flRightValue, flValue, flBalance );
+
+ int nLeftValue = (int)( (float)rect.width * clamp( flLeftValue, 0.0f, 1.0f ) + 0.5f );
+ int nRightValue = (int)( (float)rect.width * clamp( flRightValue, 0.0f, 1.0f ) + 0.5f );
+
+ // Draw the current value as a bar from the midpoint
+ surface()->DrawSetColor( IsDragging() ? s_DraggingBarColor : s_BarColor[ m_bIsLogPreviewControl ][ IsSelected() ] );
+ surface()->DrawFilledRect( rect.x + min( nLeftValue, nMidPoint ), y0, rect.x + max( nLeftValue, nMidPoint ), y1 );
+ surface()->DrawFilledRect( rect.x + min( nRightValue, nMidPoint ), y1, rect.x + max( nRightValue, nMidPoint ), y2 );
+ }
+ else
+ {
+ Assert( viewType != ANIM_CONTROL_BALANCE );
+
+ int nValue = (int)( (float)rect.width * clamp( flValue, 0.0f, 1.0f ) + 0.5f );
+
+ // Draw the current value as a bar from the midpoint
+ surface()->DrawSetColor( IsDragging() ? s_DraggingBarColor : s_BarColor[ m_bIsLogPreviewControl ][ IsSelected() ] );
+ surface()->DrawFilledRect( rect.x + min( nValue, nMidPoint ), y0, rect.x + max( nValue, nMidPoint ), y2 );
+ }
+
+ // Draw the midpoint over the top of the current value
+ DrawMidpoint( rect.x + nMidPoint, rect.y, rect.height );
+
+ // Draw the name or value over the top of that
+ DrawNameLabel();
+
+ // Paints the circular controls
+ if ( IsControlActive( ANIM_CONTROL_MULTILEVEL ) )
+ {
+ float flMultiValue = bUsePreview ? m_Preview.m_Current.m_pValue[ANIM_CONTROL_MULTILEVEL] : GetValue( ANIM_CONTROL_MULTILEVEL );
+ GetControlRect( &rect, ANIM_CONTROL_MULTILEVEL );
+ PaintCircularControl( flMultiValue, rect );
+
+ // Draws the midpoint for the circular controls
+ int nCenterX = rect.x + rect.width / 2;
+ int nCenterY = rect.y + rect.height / 2;
+ DrawCircularTick( s_MidpointColor, GetControlDefaultValue( ANIM_CONTROL_MULTILEVEL ), nCenterX, nCenterY, CIRCULAR_CONTROL_RADIUS );
+ }
+} \ No newline at end of file