VTimeLineControl.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  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()
  42. {
  43. docsURL;
  44. Parent::initPersistFields();
  45. addField( "IsController", TypeBool, Offset( mIsController, VTimeLineControl ) );
  46. addField( "Controller", TYPEID<VController>(), Offset( mController, VTimeLineControl ) );
  47. addField( "DurationOffset", TypeS32, Offset( mDurationOffset, VTimeLineControl ) );
  48. }
  49. //-----------------------------------------------------------------------------
  50. //
  51. // Mouse Methods.
  52. //
  53. //-----------------------------------------------------------------------------
  54. void VTimeLineControl::onMouseDown( const GuiEvent &pEvent )
  55. {
  56. Parent::onMouseDown( pEvent );
  57. if ( !mIsController || !mController || mController->isPlaying() )
  58. {
  59. return;
  60. }
  61. if ( !isMouseLocked() )
  62. {
  63. GuiCanvas *canvas = getRoot();
  64. if ( canvas->getMouseLockedControl() )
  65. {
  66. GuiEvent event;
  67. canvas->getMouseLockedControl()->onMouseLeave( event );
  68. canvas->mouseUnlock( canvas->getMouseLockedControl() );
  69. }
  70. // Lock.
  71. mouseLock();
  72. }
  73. // Calculate Time.
  74. const Point2I hitPoint = globalToLocalCoord( pEvent.mousePoint );
  75. const S32 time = mClamp( toTime( hitPoint.x ), 0, mController->getDuration() );
  76. // Selection?
  77. if ( pEvent.modifier & SI_SHIFT )
  78. {
  79. if ( !mSelection.Active )
  80. {
  81. // Selection Active.
  82. mSelection.Active = true;
  83. mSelection.StartTime = mController->getTime();
  84. mSelection.EndTime = time;
  85. }
  86. else
  87. {
  88. // Update Selection.
  89. mSelection.EndTime = time;
  90. }
  91. // Callback.
  92. Con::executef( this, "onSelectionUpdate" );
  93. }
  94. else
  95. {
  96. if ( mSelection.Active )
  97. {
  98. // Selection Invalid.
  99. mSelection.Active = false;
  100. // Callback.
  101. Con::executef( this, "onSelectionUpdate" );
  102. }
  103. }
  104. // Set First Responder.
  105. setFirstResponder();
  106. if ( pEvent.modifier & SI_CTRL )
  107. {
  108. // Set Time, No Reset.
  109. mController->setTime( time );
  110. }
  111. else
  112. {
  113. // Reset.
  114. mController->reset( time );
  115. }
  116. }
  117. void VTimeLineControl::onMouseUp( const GuiEvent &pEvent )
  118. {
  119. if ( isMouseLocked() )
  120. {
  121. // Unlock.
  122. mouseUnlock();
  123. }
  124. if ( mIsController && mController && !mController->isPlaying() )
  125. {
  126. // Stop without Reset.
  127. mController->stop( false );
  128. }
  129. }
  130. void VTimeLineControl::onMouseDragged( const GuiEvent &pEvent )
  131. {
  132. Parent::onMouseDragged( pEvent );
  133. if ( !mIsController || !mController || mController->isPlaying() )
  134. {
  135. return;
  136. }
  137. // Calculate Time.
  138. const Point2I hitPoint = globalToLocalCoord( pEvent.mousePoint );
  139. const S32 time = mClamp( toTime( hitPoint.x ), 0, mController->getDuration() );
  140. if ( pEvent.modifier & SI_SHIFT )
  141. {
  142. if ( mSelection.Active )
  143. {
  144. // Update Selection.
  145. mSelection.EndTime = time;
  146. // Callback.
  147. Con::executef( this, "onSelectionUpdate" );
  148. }
  149. }
  150. else
  151. {
  152. if ( mSelection.Active )
  153. {
  154. // Selection Invalid.
  155. mSelection.Active = false;
  156. // Callback.
  157. Con::executef( this, "onSelectionUpdate" );
  158. }
  159. }
  160. if ( pEvent.modifier & SI_CTRL )
  161. {
  162. // Set Time, No Reset.
  163. mController->setTime( time );
  164. }
  165. else if ( !mSelection.Active )
  166. {
  167. // Reset.
  168. mController->reset( time );
  169. }
  170. }
  171. void VTimeLineControl::onRightMouseDown( const GuiEvent &pEvent )
  172. {
  173. Parent::onRightMouseDown( pEvent );
  174. if ( !mIsController || !mController || mController->isPlaying() )
  175. {
  176. return;
  177. }
  178. // Calculate Time.
  179. const Point2I hitPoint = globalToLocalCoord( pEvent.mousePoint );
  180. const S32 time = mClamp( toTime( hitPoint.x ), 0, mController->getDuration() );
  181. // Set First Responder.
  182. setFirstResponder();
  183. if ( mSelection.Active )
  184. {
  185. const S32 minTime = getMin( mSelection.StartTime, mSelection.EndTime );
  186. const S32 maxTime = getMax( mSelection.StartTime, mSelection.EndTime );
  187. if ( time >= minTime && time <= maxTime )
  188. {
  189. // Callback.
  190. onMouseEvent( "onSelectionRightClick", pEvent );
  191. // Don't Update Time.
  192. return;
  193. }
  194. else
  195. {
  196. if ( mSelection.Active )
  197. {
  198. // Selection Invalid.
  199. mSelection.Active = false;
  200. // Callback.
  201. Con::executef( this, "onSelectionUpdate" );
  202. }
  203. }
  204. }
  205. // Reset.
  206. mController->reset( time );
  207. }
  208. void VTimeLineControl::onMouseEvent( const char *pEventName, const GuiEvent &pEvent )
  209. {
  210. // Argument Buffers.
  211. char argBuffer[3][32];
  212. // Format Event-Position Buffer.
  213. dSprintf( argBuffer[0], 32, "%d %d", pEvent.mousePoint.x, pEvent.mousePoint.y );
  214. // Format Event-Modifier Buffer.
  215. dSprintf( argBuffer[1], 32, "%d", pEvent.modifier );
  216. // Format Mouse-Click Count Buffer.
  217. dSprintf( argBuffer[2], 32, "%d", pEvent.mouseClickCount );
  218. // Call Scripts.
  219. Con::executef( this, pEventName, argBuffer[0], argBuffer[1], argBuffer[2] );
  220. }
  221. //-----------------------------------------------------------------------------
  222. //
  223. // Render Methods.
  224. //
  225. //-----------------------------------------------------------------------------
  226. void VTimeLineControl::onPreRender( void )
  227. {
  228. setUpdate();
  229. }
  230. void VTimeLineControl::onRender( Point2I offset, const RectI &updateRect )
  231. {
  232. if ( !mController )
  233. {
  234. // Default Render.
  235. Parent::onRender( offset, updateRect );
  236. // Quit.
  237. return;
  238. }
  239. // Render Properties.
  240. const S32 tickOffset = toPoint( 0 );
  241. const S32 timeLineWidth = toPoint( mController->getDuration() ) - tickOffset;
  242. const F32 tickStep = 0.5f;
  243. const S32 tickInterval = ( mIsController ) ? getWidth() : timeLineWidth;
  244. const S32 tickIntervalCount = ( S32 )mFloor( tickInterval / ( gUnitsPerSec * tickStep ) ) + 1;
  245. // Tick Render Proeprties.
  246. const Point2I tickExtent( 0, getHeight() - 1 );
  247. // Text Render Properties.
  248. const Point2I textExtent( gUnitsPerSec, mProfile->mFontSize );
  249. const Point2I textOffset( 4, -mProfile->mFontSize );
  250. // Render Border.
  251. GFX->getDrawUtil()->drawRectFill( RectI( offset + Point2I( tickOffset + 1, 1 ), Point2I( timeLineWidth - 1, getHeight() - 1 ) ), mProfile->mFillColorHL );
  252. // Font Color.
  253. GFX->getDrawUtil()->setBitmapModulation( mProfile->mFontColor );
  254. for ( S32 i = 0; i < tickIntervalCount; i++ )
  255. {
  256. // Tick Position.
  257. const Point2I tickPosition = offset + Point2I( tickOffset + i * ( gUnitsPerSec * tickStep ), 0 );
  258. // Line Color.
  259. const ColorI lineColor = ( ( i % 2 ) ) ? mProfile->mBorderColorHL : mProfile->mBorderColor;
  260. // Draw Line.
  261. GFX->getDrawUtil()->drawLine( tickPosition, tickPosition + tickExtent, lineColor );
  262. if ( mIsController )
  263. {
  264. // Render Times.
  265. renderJustifiedText( tickPosition + tickExtent + textOffset, textExtent, avar( "%.2f", ( F32 )( i * tickStep ) ) );
  266. }
  267. }
  268. // Render Children
  269. renderChildControls( offset, updateRect );
  270. if ( mSelection.Active )
  271. {
  272. // Selection Width.
  273. const S32 selectionWidth = mCeil( mAbs( toPoint( mSelection.EndTime ) - toPoint( mSelection.StartTime ) ) );
  274. // Selection Position.
  275. const S32 selectionPositionX = toPoint( getMin( mSelection.StartTime, mSelection.EndTime ) );
  276. // Selection Properties.
  277. const Point2I selectionExtent( selectionWidth, getHeight() );
  278. const Point2I selectionPosition = offset + Point2I( selectionPositionX, 0 );
  279. // Render Time Cue.
  280. GFX->getDrawUtil()->drawRectFill( RectI( selectionPosition, selectionExtent ), ColorI( 0, 0, 0, 128 ) );
  281. if ( mIsController )
  282. {
  283. // Buffer.
  284. char buffer[2][128];
  285. dSprintf( buffer[0], 128, "%.2f", ( F32 )( mSelection.StartTime / 1000.f ) );
  286. dSprintf( buffer[1], 128, "%.2f", ( F32 )( mSelection.EndTime / 1000.f ) );
  287. if ( mSelection.StartTime < mSelection.EndTime )
  288. {
  289. // Fetch Width.
  290. const S32 textWidth = mProfile->mFont->getStrWidth( buffer[0] );
  291. // Text Position.
  292. const Point2I startText = Point2I( getMax( ( S32 )( selectionPosition.x - ( textWidth + 2 ) ), updateRect.point.x + 4 ), selectionPosition.y + 2 );
  293. const Point2I endText = Point2I( getMin( ( S32 )( selectionPosition.x + selectionWidth + 4 ), updateRect.point.x + updateRect.extent.x - ( textWidth + 2 ) ), selectionPosition.y + 2 );
  294. // Render Time Text.
  295. renderJustifiedText( startText, textExtent, buffer[0] );
  296. renderJustifiedText( endText, textExtent, buffer[1] );
  297. }
  298. else
  299. {
  300. // Fetch Width.
  301. const S32 textWidth = mProfile->mFont->getStrWidth( buffer[1] );
  302. // Text Position.
  303. const Point2I startText = Point2I( getMax( ( S32 )( selectionPosition.x - ( textWidth + 2 ) ), updateRect.point.x + 4 ), selectionPosition.y + 2 );
  304. const Point2I endText = Point2I( getMin( ( S32 )( selectionPosition.x + selectionWidth + 4 ), updateRect.point.x + updateRect.extent.x - ( textWidth + 2 ) ), selectionPosition.y + 2 );
  305. // Render Time Text.
  306. renderJustifiedText( startText, textExtent, buffer[1] );
  307. renderJustifiedText( endText, textExtent, buffer[0] );
  308. }
  309. }
  310. }
  311. if ( mController && !mSelection.Active )
  312. {
  313. // Time Cue Properties.
  314. const Point2I timeCueExtent( ( mIsController ) ? 4 : 2, getHeight() );
  315. const Point2I timeCuePosition = offset + Point2I( toPoint( mController->getTime() ) - ( timeCueExtent.x / 2 ), 0 );
  316. // Render Time Cue.
  317. GFX->getDrawUtil()->drawRectFill( RectI( timeCuePosition, timeCueExtent ), ColorI( 0,0,0,128 ) );
  318. if ( mIsController )
  319. {
  320. // Buffer.
  321. char buffer[128];
  322. dSprintf( buffer, 128, "%.2f", ( F32 )( mController->getTime() / 1000.f ) );
  323. // Fetch Width.
  324. const S32 textWidth = mProfile->mFont->getStrWidth( buffer );
  325. // Text Position.
  326. const Point2I textPosition( getMin( getMax( timeCuePosition.x + 6, updateRect.point.x + 4 ), updateRect.point.x + updateRect.extent.x - ( textWidth + 2 ) ), timeCuePosition.y + 2 );
  327. // Render Time Text.
  328. renderJustifiedText( textPosition, textExtent, buffer );
  329. }
  330. }
  331. }
  332. //-----------------------------------------------------------------------------
  333. //
  334. // Console Methods.
  335. //
  336. //-----------------------------------------------------------------------------
  337. DefineEngineMethod( VTimeLineControl, toPoint, S32, (S32 time), (0), "( pTime )" )
  338. {
  339. return object->toPoint(time);
  340. }
  341. S32 VTimeLineControl::toTime( const S32 &pPoint )
  342. {
  343. return ( ( S32 )( 1000.f * ( F32 )pPoint / gUnitsPerSec ) - mDurationOffset );
  344. }
  345. DefineEngineMethod( VTimeLineControl, toTime, S32, (S32 point), (0), "( pPoint )" )
  346. {
  347. return object->toTime(point);
  348. }
  349. S32 VTimeLineControl::toPoint( const S32 &pTime )
  350. {
  351. return ( S32 )( gUnitsPerSec * ( ( F32 )( pTime + mDurationOffset ) / 1000.f ) );
  352. }
  353. DefineEngineMethod( VTimeLineControl, getSelection, const char *, (),, "( )" )
  354. {
  355. const S32 minTime = getMin( object->mSelection.StartTime, object->mSelection.EndTime );
  356. const S32 maxTime = getMax( object->mSelection.StartTime, object->mSelection.EndTime );
  357. // Fetch Return Buffer.
  358. char *retBuffer = Con::getReturnBuffer( 256 );
  359. // Write.
  360. dSprintf( retBuffer, 256, "%d %d %d", object->mSelection.Active, minTime, maxTime - minTime );
  361. // Return.
  362. return retBuffer;
  363. }
  364. DefineEngineMethod( VTimeLineControl, setSelection, void, (bool active, S32 time, S32 duration), (true, -1, 1), "( pActive, [pTime, pDuration] )" )
  365. {
  366. object->mSelection.Active = active;
  367. if (time != -1)
  368. {
  369. object->mSelection.StartTime = time;
  370. object->mSelection.EndTime = object->mSelection.StartTime + duration;
  371. }
  372. }
  373. DefineEngineMethod( VTimeLineControl, updateDuration, void, (),, "( )" )
  374. {
  375. object->updateDuration();
  376. }
  377. void VTimeLineControl::updateDuration( void )
  378. {
  379. if ( !mController )
  380. {
  381. // No Controller.
  382. return;
  383. }
  384. // Add 500ms.
  385. const S32 length = toPoint( mController->getDuration() + 500 );
  386. // Set Min Extent.
  387. setMinExtent( Point2I( length, getHeight() ) );
  388. if ( getWidth() < length )
  389. {
  390. // Conform to Min Extent.
  391. setExtent( length, getHeight() );
  392. }
  393. }