//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ //=============================================================================// #include #include #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; }