mirror of
https://github.com/TorqueGameEngines/Torque3D.git
synced 2026-01-20 20:54:46 +00:00
477 lines
15 KiB
C++
477 lines
15 KiB
C++
//-----------------------------------------------------------------------------
|
|
// 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( void )
|
|
{
|
|
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() );
|
|
}
|
|
}
|