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