VTimeLineControl.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476
  1. //-----------------------------------------------------------------------------
  2. // Verve
  3. // Copyright (C) 2014 - Violent Tulip
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to
  7. // deal in the Software without restriction, including without limitation the
  8. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  9. // sell copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  20. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  21. // IN THE SOFTWARE.
  22. //-----------------------------------------------------------------------------
  23. #include "Verve/GUI/VTimeLineControl.h"
  24. #include "console/consoleTypes.h"
  25. #include "gfx/gfxDrawUtil.h"
  26. #include "gui/core/guiCanvas.h"
  27. //-----------------------------------------------------------------------------
  28. const S32 gUnitsPerSec = 200;
  29. //-----------------------------------------------------------------------------
  30. IMPLEMENT_CONOBJECT( VTimeLineControl );
  31. //-----------------------------------------------------------------------------
  32. VTimeLineControl::VTimeLineControl( void ) :
  33. mIsController( true ),
  34. mController( NULL ),
  35. mDurationOffset( 50 )
  36. {
  37. mSelection.Active = false;
  38. mSelection.StartTime = 0;
  39. mSelection.EndTime = 0;
  40. }
  41. void VTimeLineControl::initPersistFields( void )
  42. {
  43. Parent::initPersistFields();
  44. addField( "IsController", TypeBool, Offset( mIsController, VTimeLineControl ) );
  45. addField( "Controller", TYPEID<VController>(), Offset( mController, VTimeLineControl ) );
  46. addField( "DurationOffset", TypeS32, Offset( mDurationOffset, VTimeLineControl ) );
  47. }
  48. //-----------------------------------------------------------------------------
  49. //
  50. // Mouse Methods.
  51. //
  52. //-----------------------------------------------------------------------------
  53. void VTimeLineControl::onMouseDown( const GuiEvent &pEvent )
  54. {
  55. Parent::onMouseDown( pEvent );
  56. if ( !mIsController || !mController || mController->isPlaying() )
  57. {
  58. return;
  59. }
  60. if ( !isMouseLocked() )
  61. {
  62. GuiCanvas *canvas = getRoot();
  63. if ( canvas->getMouseLockedControl() )
  64. {
  65. GuiEvent event;
  66. canvas->getMouseLockedControl()->onMouseLeave( event );
  67. canvas->mouseUnlock( canvas->getMouseLockedControl() );
  68. }
  69. // Lock.
  70. mouseLock();
  71. }
  72. // Calculate Time.
  73. const Point2I hitPoint = globalToLocalCoord( pEvent.mousePoint );
  74. const S32 time = mClamp( toTime( hitPoint.x ), 0, mController->getDuration() );
  75. // Selection?
  76. if ( pEvent.modifier & SI_SHIFT )
  77. {
  78. if ( !mSelection.Active )
  79. {
  80. // Selection Active.
  81. mSelection.Active = true;
  82. mSelection.StartTime = mController->getTime();
  83. mSelection.EndTime = time;
  84. }
  85. else
  86. {
  87. // Update Selection.
  88. mSelection.EndTime = time;
  89. }
  90. // Callback.
  91. Con::executef( this, "onSelectionUpdate" );
  92. }
  93. else
  94. {
  95. if ( mSelection.Active )
  96. {
  97. // Selection Invalid.
  98. mSelection.Active = false;
  99. // Callback.
  100. Con::executef( this, "onSelectionUpdate" );
  101. }
  102. }
  103. // Set First Responder.
  104. setFirstResponder();
  105. if ( pEvent.modifier & SI_CTRL )
  106. {
  107. // Set Time, No Reset.
  108. mController->setTime( time );
  109. }
  110. else
  111. {
  112. // Reset.
  113. mController->reset( time );
  114. }
  115. }
  116. void VTimeLineControl::onMouseUp( const GuiEvent &pEvent )
  117. {
  118. if ( isMouseLocked() )
  119. {
  120. // Unlock.
  121. mouseUnlock();
  122. }
  123. if ( mIsController && mController && !mController->isPlaying() )
  124. {
  125. // Stop without Reset.
  126. mController->stop( false );
  127. }
  128. }
  129. void VTimeLineControl::onMouseDragged( const GuiEvent &pEvent )
  130. {
  131. Parent::onMouseDragged( pEvent );
  132. if ( !mIsController || !mController || mController->isPlaying() )
  133. {
  134. return;
  135. }
  136. // Calculate Time.
  137. const Point2I hitPoint = globalToLocalCoord( pEvent.mousePoint );
  138. const S32 time = mClamp( toTime( hitPoint.x ), 0, mController->getDuration() );
  139. if ( pEvent.modifier & SI_SHIFT )
  140. {
  141. if ( mSelection.Active )
  142. {
  143. // Update Selection.
  144. mSelection.EndTime = time;
  145. // Callback.
  146. Con::executef( this, "onSelectionUpdate" );
  147. }
  148. }
  149. else
  150. {
  151. if ( mSelection.Active )
  152. {
  153. // Selection Invalid.
  154. mSelection.Active = false;
  155. // Callback.
  156. Con::executef( this, "onSelectionUpdate" );
  157. }
  158. }
  159. if ( pEvent.modifier & SI_CTRL )
  160. {
  161. // Set Time, No Reset.
  162. mController->setTime( time );
  163. }
  164. else if ( !mSelection.Active )
  165. {
  166. // Reset.
  167. mController->reset( time );
  168. }
  169. }
  170. void VTimeLineControl::onRightMouseDown( const GuiEvent &pEvent )
  171. {
  172. Parent::onRightMouseDown( pEvent );
  173. if ( !mIsController || !mController || mController->isPlaying() )
  174. {
  175. return;
  176. }
  177. // Calculate Time.
  178. const Point2I hitPoint = globalToLocalCoord( pEvent.mousePoint );
  179. const S32 time = mClamp( toTime( hitPoint.x ), 0, mController->getDuration() );
  180. // Set First Responder.
  181. setFirstResponder();
  182. if ( mSelection.Active )
  183. {
  184. const S32 minTime = getMin( mSelection.StartTime, mSelection.EndTime );
  185. const S32 maxTime = getMax( mSelection.StartTime, mSelection.EndTime );
  186. if ( time >= minTime && time <= maxTime )
  187. {
  188. // Callback.
  189. onMouseEvent( "onSelectionRightClick", pEvent );
  190. // Don't Update Time.
  191. return;
  192. }
  193. else
  194. {
  195. if ( mSelection.Active )
  196. {
  197. // Selection Invalid.
  198. mSelection.Active = false;
  199. // Callback.
  200. Con::executef( this, "onSelectionUpdate" );
  201. }
  202. }
  203. }
  204. // Reset.
  205. mController->reset( time );
  206. }
  207. void VTimeLineControl::onMouseEvent( const char *pEventName, const GuiEvent &pEvent )
  208. {
  209. // Argument Buffers.
  210. char argBuffer[3][32];
  211. // Format Event-Position Buffer.
  212. dSprintf( argBuffer[0], 32, "%d %d", pEvent.mousePoint.x, pEvent.mousePoint.y );
  213. // Format Event-Modifier Buffer.
  214. dSprintf( argBuffer[1], 32, "%d", pEvent.modifier );
  215. // Format Mouse-Click Count Buffer.
  216. dSprintf( argBuffer[2], 32, "%d", pEvent.mouseClickCount );
  217. // Call Scripts.
  218. Con::executef( this, pEventName, argBuffer[0], argBuffer[1], argBuffer[2] );
  219. }
  220. //-----------------------------------------------------------------------------
  221. //
  222. // Render Methods.
  223. //
  224. //-----------------------------------------------------------------------------
  225. void VTimeLineControl::onPreRender( void )
  226. {
  227. setUpdate();
  228. }
  229. void VTimeLineControl::onRender( Point2I offset, const RectI &updateRect )
  230. {
  231. if ( !mController )
  232. {
  233. // Default Render.
  234. Parent::onRender( offset, updateRect );
  235. // Quit.
  236. return;
  237. }
  238. // Render Properties.
  239. const S32 tickOffset = toPoint( 0 );
  240. const S32 timeLineWidth = toPoint( mController->getDuration() ) - tickOffset;
  241. const F32 tickStep = 0.5f;
  242. const S32 tickInterval = ( mIsController ) ? getWidth() : timeLineWidth;
  243. const S32 tickIntervalCount = ( S32 )mFloor( tickInterval / ( gUnitsPerSec * tickStep ) ) + 1;
  244. // Tick Render Proeprties.
  245. const Point2I tickExtent( 0, getHeight() - 1 );
  246. // Text Render Properties.
  247. const Point2I textExtent( gUnitsPerSec, mProfile->mFontSize );
  248. const Point2I textOffset( 4, -mProfile->mFontSize );
  249. // Render Border.
  250. GFX->getDrawUtil()->drawRectFill( RectI( offset + Point2I( tickOffset + 1, 1 ), Point2I( timeLineWidth - 1, getHeight() - 1 ) ), mProfile->mFillColorHL );
  251. // Font Color.
  252. GFX->getDrawUtil()->setBitmapModulation( mProfile->mFontColor );
  253. for ( S32 i = 0; i < tickIntervalCount; i++ )
  254. {
  255. // Tick Position.
  256. const Point2I tickPosition = offset + Point2I( tickOffset + i * ( gUnitsPerSec * tickStep ), 0 );
  257. // Line Color.
  258. const ColorI lineColor = ( ( i % 2 ) ) ? mProfile->mBorderColorHL : mProfile->mBorderColor;
  259. // Draw Line.
  260. GFX->getDrawUtil()->drawLine( tickPosition, tickPosition + tickExtent, lineColor );
  261. if ( mIsController )
  262. {
  263. // Render Times.
  264. renderJustifiedText( tickPosition + tickExtent + textOffset, textExtent, avar( "%.2f", ( F32 )( i * tickStep ) ) );
  265. }
  266. }
  267. // Render Children
  268. renderChildControls( offset, updateRect );
  269. if ( mSelection.Active )
  270. {
  271. // Selection Width.
  272. const S32 selectionWidth = mCeil( mAbs( toPoint( mSelection.EndTime ) - toPoint( mSelection.StartTime ) ) );
  273. // Selection Position.
  274. const S32 selectionPositionX = toPoint( getMin( mSelection.StartTime, mSelection.EndTime ) );
  275. // Selection Properties.
  276. const Point2I selectionExtent( selectionWidth, getHeight() );
  277. const Point2I selectionPosition = offset + Point2I( selectionPositionX, 0 );
  278. // Render Time Cue.
  279. GFX->getDrawUtil()->drawRectFill( RectI( selectionPosition, selectionExtent ), ColorI( 0, 0, 0, 128 ) );
  280. if ( mIsController )
  281. {
  282. // Buffer.
  283. char buffer[2][128];
  284. dSprintf( buffer[0], 128, "%.2f", ( F32 )( mSelection.StartTime / 1000.f ) );
  285. dSprintf( buffer[1], 128, "%.2f", ( F32 )( mSelection.EndTime / 1000.f ) );
  286. if ( mSelection.StartTime < mSelection.EndTime )
  287. {
  288. // Fetch Width.
  289. const S32 textWidth = mProfile->mFont->getStrWidth( buffer[0] );
  290. // Text Position.
  291. const Point2I startText = Point2I( getMax( ( S32 )( selectionPosition.x - ( textWidth + 2 ) ), updateRect.point.x + 4 ), selectionPosition.y + 2 );
  292. const Point2I endText = Point2I( getMin( ( S32 )( selectionPosition.x + selectionWidth + 4 ), updateRect.point.x + updateRect.extent.x - ( textWidth + 2 ) ), selectionPosition.y + 2 );
  293. // Render Time Text.
  294. renderJustifiedText( startText, textExtent, buffer[0] );
  295. renderJustifiedText( endText, textExtent, buffer[1] );
  296. }
  297. else
  298. {
  299. // Fetch Width.
  300. const S32 textWidth = mProfile->mFont->getStrWidth( buffer[1] );
  301. // Text Position.
  302. const Point2I startText = Point2I( getMax( ( S32 )( selectionPosition.x - ( textWidth + 2 ) ), updateRect.point.x + 4 ), selectionPosition.y + 2 );
  303. const Point2I endText = Point2I( getMin( ( S32 )( selectionPosition.x + selectionWidth + 4 ), updateRect.point.x + updateRect.extent.x - ( textWidth + 2 ) ), selectionPosition.y + 2 );
  304. // Render Time Text.
  305. renderJustifiedText( startText, textExtent, buffer[1] );
  306. renderJustifiedText( endText, textExtent, buffer[0] );
  307. }
  308. }
  309. }
  310. if ( mController && !mSelection.Active )
  311. {
  312. // Time Cue Properties.
  313. const Point2I timeCueExtent( ( mIsController ) ? 4 : 2, getHeight() );
  314. const Point2I timeCuePosition = offset + Point2I( toPoint( mController->getTime() ) - ( timeCueExtent.x / 2 ), 0 );
  315. // Render Time Cue.
  316. GFX->getDrawUtil()->drawRectFill( RectI( timeCuePosition, timeCueExtent ), ColorI( 0,0,0,128 ) );
  317. if ( mIsController )
  318. {
  319. // Buffer.
  320. char buffer[128];
  321. dSprintf( buffer, 128, "%.2f", ( F32 )( mController->getTime() / 1000.f ) );
  322. // Fetch Width.
  323. const S32 textWidth = mProfile->mFont->getStrWidth( buffer );
  324. // Text Position.
  325. const Point2I textPosition( getMin( getMax( timeCuePosition.x + 6, updateRect.point.x + 4 ), updateRect.point.x + updateRect.extent.x - ( textWidth + 2 ) ), timeCuePosition.y + 2 );
  326. // Render Time Text.
  327. renderJustifiedText( textPosition, textExtent, buffer );
  328. }
  329. }
  330. }
  331. //-----------------------------------------------------------------------------
  332. //
  333. // Console Methods.
  334. //
  335. //-----------------------------------------------------------------------------
  336. DefineEngineMethod( VTimeLineControl, toPoint, S32, (S32 time), (0), "( pTime )" )
  337. {
  338. return object->toPoint(time);
  339. }
  340. S32 VTimeLineControl::toTime( const S32 &pPoint )
  341. {
  342. return ( ( S32 )( 1000.f * ( F32 )pPoint / gUnitsPerSec ) - mDurationOffset );
  343. }
  344. DefineEngineMethod( VTimeLineControl, toTime, S32, (S32 point), (0), "( pPoint )" )
  345. {
  346. return object->toTime(point);
  347. }
  348. S32 VTimeLineControl::toPoint( const S32 &pTime )
  349. {
  350. return ( S32 )( gUnitsPerSec * ( ( F32 )( pTime + mDurationOffset ) / 1000.f ) );
  351. }
  352. DefineEngineMethod( VTimeLineControl, getSelection, const char *, (),, "( )" )
  353. {
  354. const S32 minTime = getMin( object->mSelection.StartTime, object->mSelection.EndTime );
  355. const S32 maxTime = getMax( object->mSelection.StartTime, object->mSelection.EndTime );
  356. // Fetch Return Buffer.
  357. char *retBuffer = Con::getReturnBuffer( 256 );
  358. // Write.
  359. dSprintf( retBuffer, 256, "%d %d %d", object->mSelection.Active, minTime, maxTime - minTime );
  360. // Return.
  361. return retBuffer;
  362. }
  363. DefineEngineMethod( VTimeLineControl, setSelection, void, (bool active, S32 time, S32 duration), (true, -1, 1), "( pActive, [pTime, pDuration] )" )
  364. {
  365. object->mSelection.Active = active;
  366. if (time != -1)
  367. {
  368. object->mSelection.StartTime = time;
  369. object->mSelection.EndTime = object->mSelection.StartTime + duration;
  370. }
  371. }
  372. DefineEngineMethod( VTimeLineControl, updateDuration, void, (),, "( )" )
  373. {
  374. object->updateDuration();
  375. }
  376. void VTimeLineControl::updateDuration( void )
  377. {
  378. if ( !mController )
  379. {
  380. // No Controller.
  381. return;
  382. }
  383. // Add 500ms.
  384. const S32 length = toPoint( mController->getDuration() + 500 );
  385. // Set Min Extent.
  386. setMinExtent( Point2I( length, getHeight() ) );
  387. if ( getWidth() < length )
  388. {
  389. // Conform to Min Extent.
  390. setExtent( length, getHeight() );
  391. }
  392. }