Torque3D/Engine/source/Verve/GUI/VTimeLineControl.cpp

478 lines
15 KiB
C++
Raw Normal View History

2019-03-07 22:23:41 +00:00
//-----------------------------------------------------------------------------
// Verve
// Copyright (C) 2014 - Violent Tulip
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
// IN THE SOFTWARE.
//-----------------------------------------------------------------------------
#include "Verve/GUI/VTimeLineControl.h"
#include "console/consoleTypes.h"
#include "gfx/gfxDrawUtil.h"
#include "gui/core/guiCanvas.h"
//-----------------------------------------------------------------------------
const S32 gUnitsPerSec = 200;
//-----------------------------------------------------------------------------
IMPLEMENT_CONOBJECT( VTimeLineControl );
//-----------------------------------------------------------------------------
VTimeLineControl::VTimeLineControl( void ) :
mIsController( true ),
mController( NULL ),
mDurationOffset( 50 )
{
mSelection.Active = false;
mSelection.StartTime = 0;
mSelection.EndTime = 0;
}
void VTimeLineControl::initPersistFields()
2019-03-07 22:23:41 +00:00
{
docsURL;
2019-03-07 22:23:41 +00:00
Parent::initPersistFields();
addField( "IsController", TypeBool, Offset( mIsController, VTimeLineControl ) );
addField( "Controller", TYPEID<VController>(), Offset( mController, VTimeLineControl ) );
addField( "DurationOffset", TypeS32, Offset( mDurationOffset, VTimeLineControl ) );
}
//-----------------------------------------------------------------------------
//
// Mouse Methods.
//
//-----------------------------------------------------------------------------
void VTimeLineControl::onMouseDown( const GuiEvent &pEvent )
{
Parent::onMouseDown( pEvent );
if ( !mIsController || !mController || mController->isPlaying() )
{
return;
}
if ( !isMouseLocked() )
{
GuiCanvas *canvas = getRoot();
if ( canvas->getMouseLockedControl() )
{
GuiEvent event;
canvas->getMouseLockedControl()->onMouseLeave( event );
canvas->mouseUnlock( canvas->getMouseLockedControl() );
}
// Lock.
mouseLock();
}
// Calculate Time.
const Point2I hitPoint = globalToLocalCoord( pEvent.mousePoint );
const S32 time = mClamp( toTime( hitPoint.x ), 0, mController->getDuration() );
// Selection?
if ( pEvent.modifier & SI_SHIFT )
{
if ( !mSelection.Active )
{
// Selection Active.
mSelection.Active = true;
mSelection.StartTime = mController->getTime();
mSelection.EndTime = time;
}
else
{
// Update Selection.
mSelection.EndTime = time;
}
// Callback.
Con::executef( this, "onSelectionUpdate" );
}
else
{
if ( mSelection.Active )
{
// Selection Invalid.
mSelection.Active = false;
// Callback.
Con::executef( this, "onSelectionUpdate" );
}
}
// Set First Responder.
setFirstResponder();
if ( pEvent.modifier & SI_CTRL )
{
// Set Time, No Reset.
mController->setTime( time );
}
else
{
// Reset.
mController->reset( time );
}
}
void VTimeLineControl::onMouseUp( const GuiEvent &pEvent )
{
if ( isMouseLocked() )
{
// Unlock.
mouseUnlock();
}
if ( mIsController && mController && !mController->isPlaying() )
{
// Stop without Reset.
mController->stop( false );
}
}
void VTimeLineControl::onMouseDragged( const GuiEvent &pEvent )
{
Parent::onMouseDragged( pEvent );
if ( !mIsController || !mController || mController->isPlaying() )
{
return;
}
// Calculate Time.
const Point2I hitPoint = globalToLocalCoord( pEvent.mousePoint );
const S32 time = mClamp( toTime( hitPoint.x ), 0, mController->getDuration() );
if ( pEvent.modifier & SI_SHIFT )
{
if ( mSelection.Active )
{
// Update Selection.
mSelection.EndTime = time;
// Callback.
Con::executef( this, "onSelectionUpdate" );
}
}
else
{
if ( mSelection.Active )
{
// Selection Invalid.
mSelection.Active = false;
// Callback.
Con::executef( this, "onSelectionUpdate" );
}
}
if ( pEvent.modifier & SI_CTRL )
{
// Set Time, No Reset.
mController->setTime( time );
}
else if ( !mSelection.Active )
{
// Reset.
mController->reset( time );
}
}
void VTimeLineControl::onRightMouseDown( const GuiEvent &pEvent )
{
Parent::onRightMouseDown( pEvent );
if ( !mIsController || !mController || mController->isPlaying() )
{
return;
}
// Calculate Time.
const Point2I hitPoint = globalToLocalCoord( pEvent.mousePoint );
const S32 time = mClamp( toTime( hitPoint.x ), 0, mController->getDuration() );
// Set First Responder.
setFirstResponder();
if ( mSelection.Active )
{
const S32 minTime = getMin( mSelection.StartTime, mSelection.EndTime );
const S32 maxTime = getMax( mSelection.StartTime, mSelection.EndTime );
if ( time >= minTime && time <= maxTime )
{
// Callback.
onMouseEvent( "onSelectionRightClick", pEvent );
// Don't Update Time.
return;
}
else
{
if ( mSelection.Active )
{
// Selection Invalid.
mSelection.Active = false;
// Callback.
Con::executef( this, "onSelectionUpdate" );
}
}
}
// Reset.
mController->reset( time );
}
void VTimeLineControl::onMouseEvent( const char *pEventName, const GuiEvent &pEvent )
{
// Argument Buffers.
char argBuffer[3][32];
// Format Event-Position Buffer.
dSprintf( argBuffer[0], 32, "%d %d", pEvent.mousePoint.x, pEvent.mousePoint.y );
// Format Event-Modifier Buffer.
dSprintf( argBuffer[1], 32, "%d", pEvent.modifier );
// Format Mouse-Click Count Buffer.
dSprintf( argBuffer[2], 32, "%d", pEvent.mouseClickCount );
// Call Scripts.
Con::executef( this, pEventName, argBuffer[0], argBuffer[1], argBuffer[2] );
}
//-----------------------------------------------------------------------------
//
// Render Methods.
//
//-----------------------------------------------------------------------------
void VTimeLineControl::onPreRender( void )
{
setUpdate();
}
void VTimeLineControl::onRender( Point2I offset, const RectI &updateRect )
{
if ( !mController )
{
// Default Render.
Parent::onRender( offset, updateRect );
// Quit.
return;
}
// Render Properties.
const S32 tickOffset = toPoint( 0 );
const S32 timeLineWidth = toPoint( mController->getDuration() ) - tickOffset;
const F32 tickStep = 0.5f;
const S32 tickInterval = ( mIsController ) ? getWidth() : timeLineWidth;
const S32 tickIntervalCount = ( S32 )mFloor( tickInterval / ( gUnitsPerSec * tickStep ) ) + 1;
// Tick Render Proeprties.
const Point2I tickExtent( 0, getHeight() - 1 );
// Text Render Properties.
const Point2I textExtent( gUnitsPerSec, mProfile->mFontSize );
const Point2I textOffset( 4, -mProfile->mFontSize );
// Render Border.
GFX->getDrawUtil()->drawRectFill( RectI( offset + Point2I( tickOffset + 1, 1 ), Point2I( timeLineWidth - 1, getHeight() - 1 ) ), mProfile->mFillColorHL );
// Font Color.
GFX->getDrawUtil()->setBitmapModulation( mProfile->mFontColor );
for ( S32 i = 0; i < tickIntervalCount; i++ )
{
// Tick Position.
const Point2I tickPosition = offset + Point2I( tickOffset + i * ( gUnitsPerSec * tickStep ), 0 );
// Line Color.
const ColorI lineColor = ( ( i % 2 ) ) ? mProfile->mBorderColorHL : mProfile->mBorderColor;
// Draw Line.
GFX->getDrawUtil()->drawLine( tickPosition, tickPosition + tickExtent, lineColor );
if ( mIsController )
{
// Render Times.
renderJustifiedText( tickPosition + tickExtent + textOffset, textExtent, avar( "%.2f", ( F32 )( i * tickStep ) ) );
}
}
// Render Children
renderChildControls( offset, updateRect );
if ( mSelection.Active )
{
// Selection Width.
const S32 selectionWidth = mCeil( mAbs( toPoint( mSelection.EndTime ) - toPoint( mSelection.StartTime ) ) );
// Selection Position.
const S32 selectionPositionX = toPoint( getMin( mSelection.StartTime, mSelection.EndTime ) );
// Selection Properties.
const Point2I selectionExtent( selectionWidth, getHeight() );
const Point2I selectionPosition = offset + Point2I( selectionPositionX, 0 );
// Render Time Cue.
GFX->getDrawUtil()->drawRectFill( RectI( selectionPosition, selectionExtent ), ColorI( 0, 0, 0, 128 ) );
if ( mIsController )
{
// Buffer.
char buffer[2][128];
dSprintf( buffer[0], 128, "%.2f", ( F32 )( mSelection.StartTime / 1000.f ) );
dSprintf( buffer[1], 128, "%.2f", ( F32 )( mSelection.EndTime / 1000.f ) );
if ( mSelection.StartTime < mSelection.EndTime )
{
// Fetch Width.
const S32 textWidth = mProfile->mFont->getStrWidth( buffer[0] );
// Text Position.
const Point2I startText = Point2I( getMax( ( S32 )( selectionPosition.x - ( textWidth + 2 ) ), updateRect.point.x + 4 ), selectionPosition.y + 2 );
const Point2I endText = Point2I( getMin( ( S32 )( selectionPosition.x + selectionWidth + 4 ), updateRect.point.x + updateRect.extent.x - ( textWidth + 2 ) ), selectionPosition.y + 2 );
// Render Time Text.
renderJustifiedText( startText, textExtent, buffer[0] );
renderJustifiedText( endText, textExtent, buffer[1] );
}
else
{
// Fetch Width.
const S32 textWidth = mProfile->mFont->getStrWidth( buffer[1] );
// Text Position.
const Point2I startText = Point2I( getMax( ( S32 )( selectionPosition.x - ( textWidth + 2 ) ), updateRect.point.x + 4 ), selectionPosition.y + 2 );
const Point2I endText = Point2I( getMin( ( S32 )( selectionPosition.x + selectionWidth + 4 ), updateRect.point.x + updateRect.extent.x - ( textWidth + 2 ) ), selectionPosition.y + 2 );
// Render Time Text.
renderJustifiedText( startText, textExtent, buffer[1] );
renderJustifiedText( endText, textExtent, buffer[0] );
}
}
}
if ( mController && !mSelection.Active )
{
// Time Cue Properties.
const Point2I timeCueExtent( ( mIsController ) ? 4 : 2, getHeight() );
const Point2I timeCuePosition = offset + Point2I( toPoint( mController->getTime() ) - ( timeCueExtent.x / 2 ), 0 );
// Render Time Cue.
GFX->getDrawUtil()->drawRectFill( RectI( timeCuePosition, timeCueExtent ), ColorI( 0,0,0,128 ) );
if ( mIsController )
{
// Buffer.
char buffer[128];
dSprintf( buffer, 128, "%.2f", ( F32 )( mController->getTime() / 1000.f ) );
// Fetch Width.
const S32 textWidth = mProfile->mFont->getStrWidth( buffer );
// Text Position.
const Point2I textPosition( getMin( getMax( timeCuePosition.x + 6, updateRect.point.x + 4 ), updateRect.point.x + updateRect.extent.x - ( textWidth + 2 ) ), timeCuePosition.y + 2 );
// Render Time Text.
renderJustifiedText( textPosition, textExtent, buffer );
}
}
}
//-----------------------------------------------------------------------------
//
// Console Methods.
//
//-----------------------------------------------------------------------------
DefineEngineMethod( VTimeLineControl, toPoint, S32, (S32 time), (0), "( pTime )" )
{
return object->toPoint(time);
}
S32 VTimeLineControl::toTime( const S32 &pPoint )
{
return ( ( S32 )( 1000.f * ( F32 )pPoint / gUnitsPerSec ) - mDurationOffset );
}
DefineEngineMethod( VTimeLineControl, toTime, S32, (S32 point), (0), "( pPoint )" )
{
return object->toTime(point);
}
S32 VTimeLineControl::toPoint( const S32 &pTime )
{
return ( S32 )( gUnitsPerSec * ( ( F32 )( pTime + mDurationOffset ) / 1000.f ) );
}
DefineEngineMethod( VTimeLineControl, getSelection, const char *, (),, "( )" )
{
const S32 minTime = getMin( object->mSelection.StartTime, object->mSelection.EndTime );
const S32 maxTime = getMax( object->mSelection.StartTime, object->mSelection.EndTime );
// Fetch Return Buffer.
char *retBuffer = Con::getReturnBuffer( 256 );
// Write.
dSprintf( retBuffer, 256, "%d %d %d", object->mSelection.Active, minTime, maxTime - minTime );
// Return.
return retBuffer;
}
DefineEngineMethod( VTimeLineControl, setSelection, void, (bool active, S32 time, S32 duration), (true, -1, 1), "( pActive, [pTime, pDuration] )" )
{
object->mSelection.Active = active;
if (time != -1)
{
object->mSelection.StartTime = time;
object->mSelection.EndTime = object->mSelection.StartTime + duration;
}
}
DefineEngineMethod( VTimeLineControl, updateDuration, void, (),, "( )" )
{
object->updateDuration();
}
void VTimeLineControl::updateDuration( void )
{
if ( !mController )
{
// No Controller.
return;
}
// Add 500ms.
const S32 length = toPoint( mController->getDuration() + 500 );
// Set Min Extent.
setMinExtent( Point2I( length, getHeight() ) );
if ( getWidth() < length )
{
// Conform to Min Extent.
setExtent( length, getHeight() );
}
}