source-engine/utils/hlfaceposer/mxexpressionslider.cpp

724 lines
14 KiB
C++
Raw Normal View History

2020-04-22 16:56:21 +00:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include <stdio.h>
#include <windows.h>
#include "mxExpressionSlider.h"
#include "expressiontool.h"
#include "mathlib/mathlib.h"
#include "hlfaceposer.h"
#include "choreowidgetdrawhelper.h"
mxExpressionSlider::mxExpressionSlider (mxWindow *parent, int x, int y, int w, int h, int id )
: mxWindow( parent, x, y, w, h )
{
setId( id );
setType( MX_SLIDER );
FacePoser_AddWindowStyle( this, WS_CLIPCHILDREN | WS_CLIPSIBLINGS );
m_flMin[ 0 ] = 0.0;
m_flMax[ 0 ] = 1.0f;
m_nTicks[ 0 ] = 20;
m_flCurrent[ 0 ] = 0.0f;
m_flMin[ 1 ] = 0.0;
m_flMax[ 1 ] = 1.0f;
m_nTicks[ 1 ] = 20;
m_flCurrent[ 1 ] = 0.5f;
m_flSetting[ 0 ] = 0.0;
m_flSetting[ 1 ] = 0.0;
m_bIsEdited[ 0 ] = false;
m_bIsEdited[ 1 ] = false;
m_bDraggingThumb = false;
m_nCurrentBar = 0;
m_bPaired = false;
m_nTitleWidth = 120;
m_bDrawTitle = true;
m_pInfluence = new mxCheckBox( this, 2, 4, 12, 12, "", IDC_INFLUENCE );
}
mxExpressionSlider::~mxExpressionSlider( void )
{
}
void mxExpressionSlider::SetTitleWidth( int width )
{
m_nTitleWidth = width;
redraw();
}
void mxExpressionSlider::SetDrawTitle( bool drawTitle )
{
m_bDrawTitle = drawTitle;
redraw();
}
void mxExpressionSlider::SetMode( bool paired )
{
if ( m_bPaired != paired )
{
m_bPaired = paired;
redraw();
}
}
void mxExpressionSlider::BoundValue( void )
{
for ( int i = 0; i < NUMBARS; i++ )
{
if ( m_flCurrent[ i ] > m_flMax[ i ] )
{
m_flCurrent[ i ] = m_flMax[ i ];
}
if ( m_flCurrent[ i ] < m_flMin[ i ] )
{
m_flCurrent[ i ] = m_flMin[ i ];
}
}
}
void mxExpressionSlider::setValue( int barnum, float value )
{
if (m_flSetting[ barnum ] == value && m_bIsEdited[ barnum ] == false)
return;
m_flSetting[ barnum ] = value;
m_bIsEdited[ barnum ] = false;
if (m_bPaired)
{
if (m_flSetting[ 0 ] < m_flSetting[ 1 ])
{
m_flCurrent[ 0 ] = m_flSetting[ 1 ];
m_flCurrent[ 1 ] = 1.0 - (m_flSetting[ 0 ] / m_flSetting[ 1 ]) * 0.5;
}
else if (m_flSetting[ 0 ] > m_flSetting[ 1 ])
{
m_flCurrent[ 0 ] = m_flSetting[ 0 ];
m_flCurrent[ 1 ] = (m_flSetting[ 1 ] / m_flSetting[ 0 ]) * 0.5;
}
else
{
m_flCurrent[ 0 ] = m_flSetting[ 0 ];
m_flCurrent[ 1 ] = 0.5;
}
}
else
{
m_flCurrent[ barnum ] = value;
}
BoundValue();
// FIXME: delay this until all sliders are set
if (!m_bPaired || barnum == 1)
redraw();
}
void mxExpressionSlider::setRange( int barnum, float min, float max, int ticks /*= 100*/ )
{
m_flMin[ barnum ] = min;
m_flMax[ barnum ] = max;
Assert( m_flMax[ barnum ] > m_flMin[ barnum ] );
m_nTicks[ barnum ] = ticks;
BoundValue();
redraw();
}
void mxExpressionSlider::setInfluence( float value )
{
bool bWasChecked = m_pInfluence->isChecked( );
bool bNowChecked = value > 0.0f ? true : false;
if (bNowChecked != bWasChecked)
{
m_pInfluence->setChecked( bNowChecked );
redraw();
}
}
float mxExpressionSlider::getRawValue( int barnum ) const
{
return m_flCurrent[ barnum ];
}
float mxExpressionSlider::getValue( int barnum ) const
{
float scale = 1.0;
if (m_bPaired)
{
// if it's paired, this is assuming that m_flCurrent[0] holds the max value,
// and m_flCurrent[1] is a weighting from 0 to 1, with 0.5 being even
if (barnum == 0 && m_flCurrent[ 1 ] > 0.5)
{
scale = (1.0 - m_flCurrent[ 1 ]) / 0.5;
}
else if (barnum == 1 && m_flCurrent[ 1 ] < 0.5)
{
scale = (m_flCurrent[ 1 ] / 0.5);
}
}
return m_flCurrent[ 0 ] * scale;
}
float mxExpressionSlider::getMinValue( int barnum ) const
{
return m_flMin[ barnum ];
}
float mxExpressionSlider::getMaxValue( int barnum ) const
{
return m_flMax[ barnum ];
}
float mxExpressionSlider::getInfluence( ) const
{
return m_pInfluence->isChecked() ? 1.0f : 0.0f;
}
void mxExpressionSlider::setEdited( int barnum, bool isEdited )
{
if (m_bIsEdited[ barnum ] == isEdited)
return;
m_bIsEdited[ barnum ] = isEdited;
redraw();
}
bool mxExpressionSlider::isEdited( int barnum ) const
{
return (m_bIsEdited[ barnum ]);
}
void mxExpressionSlider::GetSliderRect( RECT& rc )
{
HWND wnd = (HWND)getHandle();
if ( !wnd )
return;
GetClientRect( wnd, &rc );
if ( m_bDrawTitle )
{
rc.left += m_nTitleWidth;
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : &rc -
//-----------------------------------------------------------------------------
void mxExpressionSlider::GetBarRect( RECT &rcBar )
{
RECT rc;
GetSliderRect( rc );
rcBar = rc;
InflateRect( &rcBar, -10, 0 );
rcBar.top += 5;
rcBar.bottom = rcBar.top + 2;
int midy = ( rcBar.top + rcBar.bottom ) / 2;
rcBar.top = midy - 1;
rcBar.bottom = midy + 1;
}
void mxExpressionSlider::GetThumbRect( int barnum, RECT &rcThumb )
{
RECT rc;
GetSliderRect( rc );
RECT rcBar = rc;
GetBarRect( rcBar );
float frac = 0.0f;
if ( m_flMax[ barnum ] - m_flMin[ barnum ] > 0 )
{
frac = (m_flCurrent[ barnum ] - m_flMin[ barnum ]) / ( m_flMax[ barnum ] - m_flMin[ barnum ] );
}
int tickmark = (int)( frac * m_nTicks[ barnum ] + 0.5 );
tickmark = min( m_nTicks[ barnum ], tickmark );
tickmark = max( 0, tickmark );
int thumbwidth = 20;
int thumbheight = 14;
int xoffset = -thumbwidth / 2;
int yoffset = -thumbheight / 2 + 2;
int leftedge = rcBar.left + (int)( (float)( rcBar.right - rcBar.left ) * (float)(tickmark) / (float)m_nTicks[ barnum ] );
rcThumb.left = leftedge + xoffset;
rcThumb.right = rcThumb.left + thumbwidth;
rcThumb.top = rcBar.top + yoffset;
rcThumb.bottom = rcThumb.top + thumbheight;
}
void mxExpressionSlider::DrawBar( HDC& dc )
{
RECT rcBar;
GetBarRect( rcBar );
HPEN oldPen;
HPEN shadow;
HBRUSH face;
HPEN hilight;
shadow = CreatePen( PS_SOLID, 1, GetSysColor( COLOR_3DSHADOW ) );
hilight = CreatePen( PS_SOLID, 1, GetSysColor( COLOR_3DHIGHLIGHT ) );
face = CreateSolidBrush( GetSysColor( COLOR_3DFACE ) );
oldPen = (HPEN)SelectObject( dc, hilight );
MoveToEx( dc, rcBar.left, rcBar.bottom, NULL );
LineTo( dc, rcBar.left, rcBar.top );
LineTo( dc, rcBar.right, rcBar.top );
SelectObject( dc, shadow );
LineTo( dc, rcBar.right, rcBar.bottom );
LineTo( dc, rcBar.left, rcBar.bottom );
rcBar.left += 1;
//rcBar.right -= 1;
rcBar.top += 1;
rcBar.bottom -= 1;
FillRect( dc, &rcBar, face );
SelectObject( dc, oldPen );
DeleteObject( face );
DeleteObject( shadow );
DeleteObject( hilight );
}
void mxExpressionSlider::DrawThumb( int barnum, HDC& dc )
{
RECT rcThumb;
GetThumbRect( barnum, rcThumb );
// Draw it
HPEN oldPen;
HPEN shadow;
HBRUSH face;
HPEN hilight;
shadow = CreatePen( PS_SOLID, 1, GetSysColor( COLOR_3DDKSHADOW ) );
hilight = CreatePen( PS_SOLID, 1, GetSysColor( COLOR_3DHIGHLIGHT ) );
switch ( barnum )
{
default:
case MAGNITUDE_BAR:
{
float frac;
if (m_flCurrent[ barnum ] < 0)
{
frac = m_flCurrent[ barnum ] / m_flMin[ barnum ];
}
else
{
frac = m_flCurrent[ barnum ] / m_flMax[ barnum ];
}
frac = min( 1.0f, frac );
frac = max( 0.0f, frac );
COLORREF clr = GetSysColor( COLOR_3DFACE );
int r, g, b;
r = GetRValue( clr );
g = GetRValue( clr );
b = GetRValue( clr );
// boost colors
r = (int)( (1-frac) * b );
b = min( 255, (int)(r + ( 255 - r ) * frac ) );
g = (int)( (1-frac) * g );
face = CreateSolidBrush( RGB( r, g, b ) );
}
break;
case BALANCE_BAR:
{
float frac = m_flCurrent[ barnum ];
frac = min( 1.0f, frac );
frac = max( 0.0f, frac );
COLORREF clr = GetSysColor( COLOR_3DFACE );
int r, g, b;
r = GetRValue( clr );
g = GetRValue( clr );
b = GetRValue( clr );
// boost colors toward red if we are not at 0.5
float boost = 2.0 * ( fabs( frac - 0.5f ) );
r = r + ( 255 - r ) * boost;
g = ( 1 - boost ) * g;
b = ( 1 - boost ) * b;
face = CreateSolidBrush( RGB( r, g, b ) );
}
break;
}
//rcThumb.left += 1;
//rcThumb.right -= 1;
//rcThumb.top += 1;
//rcThumb.bottom -= 1;
//FillRect( dc, &rcThumb, face );
POINT region[3];
int cPoints = 3;
InflateRect( &rcThumb, -2, 0 );
// int height = rcThumb.bottom - rcThumb.top;
// int offset = height / 2 + 1;
int offset = 2;
switch ( barnum )
{
case MAGNITUDE_BAR:
default:
{
region[ 0 ].x = rcThumb.left;
region[ 0 ].y = rcThumb.top;
region[ 1 ].x = rcThumb.right;
region[ 1 ].y = rcThumb.top;
region[ 2 ].x = ( rcThumb.left + rcThumb.right ) / 2;
region[ 2 ].y = rcThumb.bottom - offset;
}
break;
case BALANCE_BAR:
{
region[ 0 ].x = ( rcThumb.left + rcThumb.right ) / 2;
region[ 0 ].y = rcThumb.top + offset;
region[ 1 ].x = rcThumb.left;
region[ 1 ].y = rcThumb.bottom;
region[ 2 ].x = rcThumb.right;
region[ 2 ].y = rcThumb.bottom;
}
break;
}
HRGN rgn = CreatePolygonRgn( region, cPoints, ALTERNATE );
int oldPF = SetPolyFillMode( dc, ALTERNATE );
FillRgn( dc, rgn, face );
SetPolyFillMode( dc, oldPF );
DeleteObject( rgn );
oldPen = (HPEN)SelectObject( dc, hilight );
MoveToEx( dc, region[0].x, region[0].y, NULL );
LineTo( dc, region[1].x, region[1].y );
SelectObject( dc, shadow );
LineTo( dc, region[2].x, region[2].y );
SelectObject( dc, hilight );
LineTo( dc, region[0].x, region[0].y );
SelectObject( dc, oldPen );
DeleteObject( face );
DeleteObject( shadow );
DeleteObject( hilight );
}
void mxExpressionSlider::DrawTitle( HDC &dc )
{
if ( !m_bDrawTitle )
return;
HWND wnd = (HWND)getHandle();
if ( !wnd )
return;
RECT rc;
GetClientRect( wnd, &rc );
rc.right = m_nTitleWidth;
rc.left += 16;
InflateRect( &rc, -5, -2 );
char sz[ 128 ];
sprintf( sz, "%s", getLabel() );
HFONT fnt, oldfont;
fnt = CreateFont(
-12 // H
, 0 // W
, 0 // Escapement
, 0 // Orient
, FW_NORMAL // Wt. (BOLD)
, 0 // Ital.
, 0 // Underl.
, 0 // SO
, ANSI_CHARSET // Charset
, OUT_TT_PRECIS // Out prec.
, CLIP_DEFAULT_PRECIS // Clip prec.
, PROOF_QUALITY // Qual.
, VARIABLE_PITCH | FF_DONTCARE // Pitch and Fam.
, "Arial" );
COLORREF oldColor;
if (!isEdited( 0 ))
{
oldColor = SetTextColor( dc, GetSysColor( COLOR_BTNTEXT ) );
}
else
{
oldColor = SetTextColor( dc, RGB( 255, 0, 0 ) );
}
int oldMode = SetBkMode( dc, TRANSPARENT );
oldfont = (HFONT)SelectObject( dc, fnt );
DrawText( dc, sz, -1, &rc, DT_NOPREFIX | DT_VCENTER | DT_SINGLELINE | DT_LEFT | DT_WORD_ELLIPSIS );
SelectObject( dc, oldfont );
DeleteObject( fnt );
SetBkMode( dc, oldMode );
SetTextColor( dc, oldColor );
}
void mxExpressionSlider::redraw()
{
HWND wnd = (HWND)getHandle();
if ( !wnd )
return;
HDC finalDC = GetDC( wnd );
if ( !finalDC )
return;
RECT rc;
GetClientRect( wnd, &rc );
int w = rc.right - rc.left;
int h = rc.bottom - rc.top;
HDC dc = CreateCompatibleDC( finalDC );
HBITMAP oldbm, bm;
bm = CreateCompatibleBitmap( finalDC, w, h );
oldbm = (HBITMAP)SelectObject( dc, bm );
HBRUSH br = CreateSolidBrush( GetSysColor( COLOR_3DFACE ) );
FillRect( dc, &rc, br );
DeleteObject( br );
DrawTitle( dc );
DrawBar( dc );
// Draw slider
for ( int i = ( m_bPaired ? 1 : 0 ); i >= 0 ; i-- )
{
DrawThumb( i, dc );
}
BitBlt( finalDC, 0, 0, w, h, dc, 0, 0, SRCCOPY );
SelectObject( dc, oldbm );
DeleteObject( bm );
DeleteDC( dc );
ReleaseDC( wnd, finalDC );
ValidateRect( wnd, &rc );
}
void mxExpressionSlider::MoveThumb( int barnum, int xpos, bool finish )
{
RECT rcBar;
GetBarRect( rcBar );
if ( xpos < rcBar.left )
{
m_flCurrent[ barnum ] = m_flMin[ barnum ];
}
else if ( xpos > rcBar.right )
{
m_flCurrent[ barnum ] = m_flMax[ barnum ];
}
else
{
float frac = (float)( xpos - rcBar.left ) / (float)( rcBar.right - rcBar.left );
// snap slider to nearest "tick" so that it get drawn in the correct place
int curtick = (int)( frac * m_nTicks[ 0 ] + 0.5);
m_flCurrent[ barnum ] = m_flMin[ barnum ] + ((float)curtick / (float)m_nTicks[ 0 ]) * (m_flMax[ barnum ] - m_flMin[ barnum ]);
}
// update equivalent setting
m_flSetting[ 0 ] = getValue( 0 );
m_flSetting[ 1 ] = getValue( 1 );
m_bIsEdited[ 0 ] = true;
m_bIsEdited[ 1 ] = true;
// Send message to parent
HWND parent = (HWND)( getParent() ? getParent()->getHandle() : NULL );
if ( parent )
{
LPARAM lp;
WPARAM wp;
wp = MAKEWPARAM( finish ? SB_ENDSCROLL : SB_THUMBPOSITION, barnum );
lp = (long)getHandle();
SendMessage( parent, WM_HSCROLL, wp, lp );
}
BoundValue();
redraw();
}
bool mxExpressionSlider::PaintBackground( void )
{
return false;
}
int mxExpressionSlider::handleEvent( mxEvent *event )
{
int iret = 0;
switch ( event->event )
{
case mxEvent::Action:
{
iret = 1;
switch ( event->action )
{
default:
iret = 0;
break;
case IDC_INFLUENCE:
{
SetFocus( (HWND)getHandle() );
setEdited( 0, false );
setEdited( 1, false );
// Send message to parent
HWND parent = (HWND)( getParent() ? getParent()->getHandle() : NULL );
if ( parent )
{
LPARAM lp;
WPARAM wp;
wp = MAKEWPARAM( SB_ENDSCROLL, m_nCurrentBar );
lp = (long)getHandle();
SendMessage( parent, WM_HSCROLL, wp, lp );
}
break;
}
}
}
break;
case mxEvent::MouseDown:
{
SetFocus( (HWND)getHandle() );
if ( !m_bDraggingThumb )
{
RECT rcThumb;
POINT pt;
pt.x = (short)event->x;
pt.y = (short)event->y;
m_nCurrentBar = ( event->buttons & mxEvent::MouseRightButton ) ? BALANCE_BAR : MAGNITUDE_BAR;
GetThumbRect( m_nCurrentBar, rcThumb );
if ( PtInRect( &rcThumb, pt ) )
{
m_bDraggingThumb = true;
}
// Snap position if they didn't click on the thumb itself
#if 0
else
{
m_bDraggingThumb = true;
MoveThumb( m_nCurrentBar, (short)event->x, false );
}
#endif
}
iret = 1;
}
break;
case mxEvent::MouseDrag:
case mxEvent::MouseMove:
{
if ( m_bDraggingThumb )
{
m_pInfluence->setChecked( true );
MoveThumb( m_nCurrentBar, (short)event->x, false );
iret = 1;
}
}
break;
case mxEvent::MouseUp:
{
if ( m_bDraggingThumb )
{
m_pInfluence->setChecked( true );
m_bDraggingThumb = false;
MoveThumb( m_nCurrentBar, (short)event->x, true );
m_nCurrentBar = 0;
}
iret = 1;
}
break;
case mxEvent::KeyDown:
{
if ( event->key == VK_RETURN ||
event->key == 'S' )
{
// See if there is an event in the flex animation window
g_pExpressionTool->OnSetSingleKeyFromFlex( getLabel() );
}
}
break;
}
return iret;
}