Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

723 lines
14 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include <stdio.h>
  8. #include <windows.h>
  9. #include "mxExpressionSlider.h"
  10. #include "expressiontool.h"
  11. #include "mathlib/mathlib.h"
  12. #include "hlfaceposer.h"
  13. #include "choreowidgetdrawhelper.h"
  14. mxExpressionSlider::mxExpressionSlider (mxWindow *parent, int x, int y, int w, int h, int id )
  15. : mxWindow( parent, x, y, w, h )
  16. {
  17. setId( id );
  18. setType( MX_SLIDER );
  19. FacePoser_AddWindowStyle( this, WS_CLIPCHILDREN | WS_CLIPSIBLINGS );
  20. m_flMin[ 0 ] = 0.0;
  21. m_flMax[ 0 ] = 1.0f;
  22. m_nTicks[ 0 ] = 20;
  23. m_flCurrent[ 0 ] = 0.0f;
  24. m_flMin[ 1 ] = 0.0;
  25. m_flMax[ 1 ] = 1.0f;
  26. m_nTicks[ 1 ] = 20;
  27. m_flCurrent[ 1 ] = 0.5f;
  28. m_flSetting[ 0 ] = 0.0;
  29. m_flSetting[ 1 ] = 0.0;
  30. m_bIsEdited[ 0 ] = false;
  31. m_bIsEdited[ 1 ] = false;
  32. m_bDraggingThumb = false;
  33. m_nCurrentBar = 0;
  34. m_bPaired = false;
  35. m_nTitleWidth = 120;
  36. m_bDrawTitle = true;
  37. m_pInfluence = new mxCheckBox( this, 2, 4, 12, 12, "", IDC_INFLUENCE );
  38. }
  39. mxExpressionSlider::~mxExpressionSlider( void )
  40. {
  41. }
  42. void mxExpressionSlider::SetTitleWidth( int width )
  43. {
  44. m_nTitleWidth = width;
  45. redraw();
  46. }
  47. void mxExpressionSlider::SetDrawTitle( bool drawTitle )
  48. {
  49. m_bDrawTitle = drawTitle;
  50. redraw();
  51. }
  52. void mxExpressionSlider::SetMode( bool paired )
  53. {
  54. if ( m_bPaired != paired )
  55. {
  56. m_bPaired = paired;
  57. redraw();
  58. }
  59. }
  60. void mxExpressionSlider::BoundValue( void )
  61. {
  62. for ( int i = 0; i < NUMBARS; i++ )
  63. {
  64. if ( m_flCurrent[ i ] > m_flMax[ i ] )
  65. {
  66. m_flCurrent[ i ] = m_flMax[ i ];
  67. }
  68. if ( m_flCurrent[ i ] < m_flMin[ i ] )
  69. {
  70. m_flCurrent[ i ] = m_flMin[ i ];
  71. }
  72. }
  73. }
  74. void mxExpressionSlider::setValue( int barnum, float value )
  75. {
  76. if (m_flSetting[ barnum ] == value && m_bIsEdited[ barnum ] == false)
  77. return;
  78. m_flSetting[ barnum ] = value;
  79. m_bIsEdited[ barnum ] = false;
  80. if (m_bPaired)
  81. {
  82. if (m_flSetting[ 0 ] < m_flSetting[ 1 ])
  83. {
  84. m_flCurrent[ 0 ] = m_flSetting[ 1 ];
  85. m_flCurrent[ 1 ] = 1.0 - (m_flSetting[ 0 ] / m_flSetting[ 1 ]) * 0.5;
  86. }
  87. else if (m_flSetting[ 0 ] > m_flSetting[ 1 ])
  88. {
  89. m_flCurrent[ 0 ] = m_flSetting[ 0 ];
  90. m_flCurrent[ 1 ] = (m_flSetting[ 1 ] / m_flSetting[ 0 ]) * 0.5;
  91. }
  92. else
  93. {
  94. m_flCurrent[ 0 ] = m_flSetting[ 0 ];
  95. m_flCurrent[ 1 ] = 0.5;
  96. }
  97. }
  98. else
  99. {
  100. m_flCurrent[ barnum ] = value;
  101. }
  102. BoundValue();
  103. // FIXME: delay this until all sliders are set
  104. if (!m_bPaired || barnum == 1)
  105. redraw();
  106. }
  107. void mxExpressionSlider::setRange( int barnum, float min, float max, int ticks /*= 100*/ )
  108. {
  109. m_flMin[ barnum ] = min;
  110. m_flMax[ barnum ] = max;
  111. Assert( m_flMax[ barnum ] > m_flMin[ barnum ] );
  112. m_nTicks[ barnum ] = ticks;
  113. BoundValue();
  114. redraw();
  115. }
  116. void mxExpressionSlider::setInfluence( float value )
  117. {
  118. bool bWasChecked = m_pInfluence->isChecked( );
  119. bool bNowChecked = value > 0.0f ? true : false;
  120. if (bNowChecked != bWasChecked)
  121. {
  122. m_pInfluence->setChecked( bNowChecked );
  123. redraw();
  124. }
  125. }
  126. float mxExpressionSlider::getRawValue( int barnum ) const
  127. {
  128. return m_flCurrent[ barnum ];
  129. }
  130. float mxExpressionSlider::getValue( int barnum ) const
  131. {
  132. float scale = 1.0;
  133. if (m_bPaired)
  134. {
  135. // if it's paired, this is assuming that m_flCurrent[0] holds the max value,
  136. // and m_flCurrent[1] is a weighting from 0 to 1, with 0.5 being even
  137. if (barnum == 0 && m_flCurrent[ 1 ] > 0.5)
  138. {
  139. scale = (1.0 - m_flCurrent[ 1 ]) / 0.5;
  140. }
  141. else if (barnum == 1 && m_flCurrent[ 1 ] < 0.5)
  142. {
  143. scale = (m_flCurrent[ 1 ] / 0.5);
  144. }
  145. }
  146. return m_flCurrent[ 0 ] * scale;
  147. }
  148. float mxExpressionSlider::getMinValue( int barnum ) const
  149. {
  150. return m_flMin[ barnum ];
  151. }
  152. float mxExpressionSlider::getMaxValue( int barnum ) const
  153. {
  154. return m_flMax[ barnum ];
  155. }
  156. float mxExpressionSlider::getInfluence( ) const
  157. {
  158. return m_pInfluence->isChecked() ? 1.0f : 0.0f;
  159. }
  160. void mxExpressionSlider::setEdited( int barnum, bool isEdited )
  161. {
  162. if (m_bIsEdited[ barnum ] == isEdited)
  163. return;
  164. m_bIsEdited[ barnum ] = isEdited;
  165. redraw();
  166. }
  167. bool mxExpressionSlider::isEdited( int barnum ) const
  168. {
  169. return (m_bIsEdited[ barnum ]);
  170. }
  171. void mxExpressionSlider::GetSliderRect( RECT& rc )
  172. {
  173. HWND wnd = (HWND)getHandle();
  174. if ( !wnd )
  175. return;
  176. GetClientRect( wnd, &rc );
  177. if ( m_bDrawTitle )
  178. {
  179. rc.left += m_nTitleWidth;
  180. }
  181. }
  182. //-----------------------------------------------------------------------------
  183. // Purpose:
  184. // Input : &rc -
  185. //-----------------------------------------------------------------------------
  186. void mxExpressionSlider::GetBarRect( RECT &rcBar )
  187. {
  188. RECT rc;
  189. GetSliderRect( rc );
  190. rcBar = rc;
  191. InflateRect( &rcBar, -10, 0 );
  192. rcBar.top += 5;
  193. rcBar.bottom = rcBar.top + 2;
  194. int midy = ( rcBar.top + rcBar.bottom ) / 2;
  195. rcBar.top = midy - 1;
  196. rcBar.bottom = midy + 1;
  197. }
  198. void mxExpressionSlider::GetThumbRect( int barnum, RECT &rcThumb )
  199. {
  200. RECT rc;
  201. GetSliderRect( rc );
  202. RECT rcBar = rc;
  203. GetBarRect( rcBar );
  204. float frac = 0.0f;
  205. if ( m_flMax[ barnum ] - m_flMin[ barnum ] > 0 )
  206. {
  207. frac = (m_flCurrent[ barnum ] - m_flMin[ barnum ]) / ( m_flMax[ barnum ] - m_flMin[ barnum ] );
  208. }
  209. int tickmark = (int)( frac * m_nTicks[ barnum ] + 0.5 );
  210. tickmark = min( m_nTicks[ barnum ], tickmark );
  211. tickmark = max( 0, tickmark );
  212. int thumbwidth = 20;
  213. int thumbheight = 14;
  214. int xoffset = -thumbwidth / 2;
  215. int yoffset = -thumbheight / 2 + 2;
  216. int leftedge = rcBar.left + (int)( (float)( rcBar.right - rcBar.left ) * (float)(tickmark) / (float)m_nTicks[ barnum ] );
  217. rcThumb.left = leftedge + xoffset;
  218. rcThumb.right = rcThumb.left + thumbwidth;
  219. rcThumb.top = rcBar.top + yoffset;
  220. rcThumb.bottom = rcThumb.top + thumbheight;
  221. }
  222. void mxExpressionSlider::DrawBar( HDC& dc )
  223. {
  224. RECT rcBar;
  225. GetBarRect( rcBar );
  226. HPEN oldPen;
  227. HPEN shadow;
  228. HBRUSH face;
  229. HPEN hilight;
  230. shadow = CreatePen( PS_SOLID, 1, GetSysColor( COLOR_3DSHADOW ) );
  231. hilight = CreatePen( PS_SOLID, 1, GetSysColor( COLOR_3DHIGHLIGHT ) );
  232. face = CreateSolidBrush( GetSysColor( COLOR_3DFACE ) );
  233. oldPen = (HPEN)SelectObject( dc, hilight );
  234. MoveToEx( dc, rcBar.left, rcBar.bottom, NULL );
  235. LineTo( dc, rcBar.left, rcBar.top );
  236. LineTo( dc, rcBar.right, rcBar.top );
  237. SelectObject( dc, shadow );
  238. LineTo( dc, rcBar.right, rcBar.bottom );
  239. LineTo( dc, rcBar.left, rcBar.bottom );
  240. rcBar.left += 1;
  241. //rcBar.right -= 1;
  242. rcBar.top += 1;
  243. rcBar.bottom -= 1;
  244. FillRect( dc, &rcBar, face );
  245. SelectObject( dc, oldPen );
  246. DeleteObject( face );
  247. DeleteObject( shadow );
  248. DeleteObject( hilight );
  249. }
  250. void mxExpressionSlider::DrawThumb( int barnum, HDC& dc )
  251. {
  252. RECT rcThumb;
  253. GetThumbRect( barnum, rcThumb );
  254. // Draw it
  255. HPEN oldPen;
  256. HPEN shadow;
  257. HBRUSH face;
  258. HPEN hilight;
  259. shadow = CreatePen( PS_SOLID, 1, GetSysColor( COLOR_3DDKSHADOW ) );
  260. hilight = CreatePen( PS_SOLID, 1, GetSysColor( COLOR_3DHIGHLIGHT ) );
  261. switch ( barnum )
  262. {
  263. default:
  264. case MAGNITUDE_BAR:
  265. {
  266. float frac;
  267. if (m_flCurrent[ barnum ] < 0)
  268. {
  269. frac = m_flCurrent[ barnum ] / m_flMin[ barnum ];
  270. }
  271. else
  272. {
  273. frac = m_flCurrent[ barnum ] / m_flMax[ barnum ];
  274. }
  275. frac = min( 1.0f, frac );
  276. frac = max( 0.0f, frac );
  277. COLORREF clr = GetSysColor( COLOR_3DFACE );
  278. int r, g, b;
  279. r = GetRValue( clr );
  280. g = GetRValue( clr );
  281. b = GetRValue( clr );
  282. // boost colors
  283. r = (int)( (1-frac) * b );
  284. b = min( 255, (int)(r + ( 255 - r ) * frac ) );
  285. g = (int)( (1-frac) * g );
  286. face = CreateSolidBrush( RGB( r, g, b ) );
  287. }
  288. break;
  289. case BALANCE_BAR:
  290. {
  291. float frac = m_flCurrent[ barnum ];
  292. frac = min( 1.0f, frac );
  293. frac = max( 0.0f, frac );
  294. COLORREF clr = GetSysColor( COLOR_3DFACE );
  295. int r, g, b;
  296. r = GetRValue( clr );
  297. g = GetRValue( clr );
  298. b = GetRValue( clr );
  299. // boost colors toward red if we are not at 0.5
  300. float boost = 2.0 * ( fabs( frac - 0.5f ) );
  301. r = r + ( 255 - r ) * boost;
  302. g = ( 1 - boost ) * g;
  303. b = ( 1 - boost ) * b;
  304. face = CreateSolidBrush( RGB( r, g, b ) );
  305. }
  306. break;
  307. }
  308. //rcThumb.left += 1;
  309. //rcThumb.right -= 1;
  310. //rcThumb.top += 1;
  311. //rcThumb.bottom -= 1;
  312. //FillRect( dc, &rcThumb, face );
  313. POINT region[3];
  314. int cPoints = 3;
  315. InflateRect( &rcThumb, -2, 0 );
  316. // int height = rcThumb.bottom - rcThumb.top;
  317. // int offset = height / 2 + 1;
  318. int offset = 2;
  319. switch ( barnum )
  320. {
  321. case MAGNITUDE_BAR:
  322. default:
  323. {
  324. region[ 0 ].x = rcThumb.left;
  325. region[ 0 ].y = rcThumb.top;
  326. region[ 1 ].x = rcThumb.right;
  327. region[ 1 ].y = rcThumb.top;
  328. region[ 2 ].x = ( rcThumb.left + rcThumb.right ) / 2;
  329. region[ 2 ].y = rcThumb.bottom - offset;
  330. }
  331. break;
  332. case BALANCE_BAR:
  333. {
  334. region[ 0 ].x = ( rcThumb.left + rcThumb.right ) / 2;
  335. region[ 0 ].y = rcThumb.top + offset;
  336. region[ 1 ].x = rcThumb.left;
  337. region[ 1 ].y = rcThumb.bottom;
  338. region[ 2 ].x = rcThumb.right;
  339. region[ 2 ].y = rcThumb.bottom;
  340. }
  341. break;
  342. }
  343. HRGN rgn = CreatePolygonRgn( region, cPoints, ALTERNATE );
  344. int oldPF = SetPolyFillMode( dc, ALTERNATE );
  345. FillRgn( dc, rgn, face );
  346. SetPolyFillMode( dc, oldPF );
  347. DeleteObject( rgn );
  348. oldPen = (HPEN)SelectObject( dc, hilight );
  349. MoveToEx( dc, region[0].x, region[0].y, NULL );
  350. LineTo( dc, region[1].x, region[1].y );
  351. SelectObject( dc, shadow );
  352. LineTo( dc, region[2].x, region[2].y );
  353. SelectObject( dc, hilight );
  354. LineTo( dc, region[0].x, region[0].y );
  355. SelectObject( dc, oldPen );
  356. DeleteObject( face );
  357. DeleteObject( shadow );
  358. DeleteObject( hilight );
  359. }
  360. void mxExpressionSlider::DrawTitle( HDC &dc )
  361. {
  362. if ( !m_bDrawTitle )
  363. return;
  364. HWND wnd = (HWND)getHandle();
  365. if ( !wnd )
  366. return;
  367. RECT rc;
  368. GetClientRect( wnd, &rc );
  369. rc.right = m_nTitleWidth;
  370. rc.left += 16;
  371. InflateRect( &rc, -5, -2 );
  372. char sz[ 128 ];
  373. sprintf( sz, "%s", getLabel() );
  374. HFONT fnt, oldfont;
  375. fnt = CreateFont(
  376. -12 // H
  377. , 0 // W
  378. , 0 // Escapement
  379. , 0 // Orient
  380. , FW_NORMAL // Wt. (BOLD)
  381. , 0 // Ital.
  382. , 0 // Underl.
  383. , 0 // SO
  384. , ANSI_CHARSET // Charset
  385. , OUT_TT_PRECIS // Out prec.
  386. , CLIP_DEFAULT_PRECIS // Clip prec.
  387. , PROOF_QUALITY // Qual.
  388. , VARIABLE_PITCH | FF_DONTCARE // Pitch and Fam.
  389. , "Arial" );
  390. COLORREF oldColor;
  391. if (!isEdited( 0 ))
  392. {
  393. oldColor = SetTextColor( dc, GetSysColor( COLOR_BTNTEXT ) );
  394. }
  395. else
  396. {
  397. oldColor = SetTextColor( dc, RGB( 255, 0, 0 ) );
  398. }
  399. int oldMode = SetBkMode( dc, TRANSPARENT );
  400. oldfont = (HFONT)SelectObject( dc, fnt );
  401. DrawText( dc, sz, -1, &rc, DT_NOPREFIX | DT_VCENTER | DT_SINGLELINE | DT_LEFT | DT_WORD_ELLIPSIS );
  402. SelectObject( dc, oldfont );
  403. DeleteObject( fnt );
  404. SetBkMode( dc, oldMode );
  405. SetTextColor( dc, oldColor );
  406. }
  407. void mxExpressionSlider::redraw()
  408. {
  409. HWND wnd = (HWND)getHandle();
  410. if ( !wnd )
  411. return;
  412. HDC finalDC = GetDC( wnd );
  413. if ( !finalDC )
  414. return;
  415. RECT rc;
  416. GetClientRect( wnd, &rc );
  417. int w = rc.right - rc.left;
  418. int h = rc.bottom - rc.top;
  419. HDC dc = CreateCompatibleDC( finalDC );
  420. HBITMAP oldbm, bm;
  421. bm = CreateCompatibleBitmap( finalDC, w, h );
  422. oldbm = (HBITMAP)SelectObject( dc, bm );
  423. HBRUSH br = CreateSolidBrush( GetSysColor( COLOR_3DFACE ) );
  424. FillRect( dc, &rc, br );
  425. DeleteObject( br );
  426. DrawTitle( dc );
  427. DrawBar( dc );
  428. // Draw slider
  429. for ( int i = ( m_bPaired ? 1 : 0 ); i >= 0 ; i-- )
  430. {
  431. DrawThumb( i, dc );
  432. }
  433. BitBlt( finalDC, 0, 0, w, h, dc, 0, 0, SRCCOPY );
  434. SelectObject( dc, oldbm );
  435. DeleteObject( bm );
  436. DeleteDC( dc );
  437. ReleaseDC( wnd, finalDC );
  438. ValidateRect( wnd, &rc );
  439. }
  440. void mxExpressionSlider::MoveThumb( int barnum, int xpos, bool finish )
  441. {
  442. RECT rcBar;
  443. GetBarRect( rcBar );
  444. if ( xpos < rcBar.left )
  445. {
  446. m_flCurrent[ barnum ] = m_flMin[ barnum ];
  447. }
  448. else if ( xpos > rcBar.right )
  449. {
  450. m_flCurrent[ barnum ] = m_flMax[ barnum ];
  451. }
  452. else
  453. {
  454. float frac = (float)( xpos - rcBar.left ) / (float)( rcBar.right - rcBar.left );
  455. // snap slider to nearest "tick" so that it get drawn in the correct place
  456. int curtick = (int)( frac * m_nTicks[ 0 ] + 0.5);
  457. m_flCurrent[ barnum ] = m_flMin[ barnum ] + ((float)curtick / (float)m_nTicks[ 0 ]) * (m_flMax[ barnum ] - m_flMin[ barnum ]);
  458. }
  459. // update equivalent setting
  460. m_flSetting[ 0 ] = getValue( 0 );
  461. m_flSetting[ 1 ] = getValue( 1 );
  462. m_bIsEdited[ 0 ] = true;
  463. m_bIsEdited[ 1 ] = true;
  464. // Send message to parent
  465. HWND parent = (HWND)( getParent() ? getParent()->getHandle() : NULL );
  466. if ( parent )
  467. {
  468. LPARAM lp;
  469. WPARAM wp;
  470. wp = MAKEWPARAM( finish ? SB_ENDSCROLL : SB_THUMBPOSITION, barnum );
  471. lp = (long)getHandle();
  472. SendMessage( parent, WM_HSCROLL, wp, lp );
  473. }
  474. BoundValue();
  475. redraw();
  476. }
  477. bool mxExpressionSlider::PaintBackground( void )
  478. {
  479. return false;
  480. }
  481. int mxExpressionSlider::handleEvent( mxEvent *event )
  482. {
  483. int iret = 0;
  484. switch ( event->event )
  485. {
  486. case mxEvent::Action:
  487. {
  488. iret = 1;
  489. switch ( event->action )
  490. {
  491. default:
  492. iret = 0;
  493. break;
  494. case IDC_INFLUENCE:
  495. {
  496. SetFocus( (HWND)getHandle() );
  497. setEdited( 0, false );
  498. setEdited( 1, false );
  499. // Send message to parent
  500. HWND parent = (HWND)( getParent() ? getParent()->getHandle() : NULL );
  501. if ( parent )
  502. {
  503. LPARAM lp;
  504. WPARAM wp;
  505. wp = MAKEWPARAM( SB_ENDSCROLL, m_nCurrentBar );
  506. lp = (long)getHandle();
  507. SendMessage( parent, WM_HSCROLL, wp, lp );
  508. }
  509. break;
  510. }
  511. }
  512. }
  513. break;
  514. case mxEvent::MouseDown:
  515. {
  516. SetFocus( (HWND)getHandle() );
  517. if ( !m_bDraggingThumb )
  518. {
  519. RECT rcThumb;
  520. POINT pt;
  521. pt.x = (short)event->x;
  522. pt.y = (short)event->y;
  523. m_nCurrentBar = ( event->buttons & mxEvent::MouseRightButton ) ? BALANCE_BAR : MAGNITUDE_BAR;
  524. GetThumbRect( m_nCurrentBar, rcThumb );
  525. if ( PtInRect( &rcThumb, pt ) )
  526. {
  527. m_bDraggingThumb = true;
  528. }
  529. // Snap position if they didn't click on the thumb itself
  530. #if 0
  531. else
  532. {
  533. m_bDraggingThumb = true;
  534. MoveThumb( m_nCurrentBar, (short)event->x, false );
  535. }
  536. #endif
  537. }
  538. iret = 1;
  539. }
  540. break;
  541. case mxEvent::MouseDrag:
  542. case mxEvent::MouseMove:
  543. {
  544. if ( m_bDraggingThumb )
  545. {
  546. m_pInfluence->setChecked( true );
  547. MoveThumb( m_nCurrentBar, (short)event->x, false );
  548. iret = 1;
  549. }
  550. }
  551. break;
  552. case mxEvent::MouseUp:
  553. {
  554. if ( m_bDraggingThumb )
  555. {
  556. m_pInfluence->setChecked( true );
  557. m_bDraggingThumb = false;
  558. MoveThumb( m_nCurrentBar, (short)event->x, true );
  559. m_nCurrentBar = 0;
  560. }
  561. iret = 1;
  562. }
  563. break;
  564. case mxEvent::KeyDown:
  565. {
  566. if ( event->key == VK_RETURN ||
  567. event->key == 'S' )
  568. {
  569. // See if there is an event in the flex animation window
  570. g_pExpressionTool->OnSetSingleKeyFromFlex( getLabel() );
  571. }
  572. }
  573. break;
  574. }
  575. return iret;
  576. }