2016-01-07 08:17:16 -05:00
|
|
|
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
|
2014-11-19 07:43:07 -05:00
|
|
|
|
|
|
|
|
#include "LogVisualizer.h"
|
Copying //UE4/Dev-Sequencer to //UE4/Main
==========================
MAJOR FEATURES + CHANGES
==========================
Change 2859626 on 2016/02/08 by Max.Preussner
Editor: Added SaveAs functionality to content asset editors
Change 2859666 on 2016/02/08 by Max.Chen
Sequencer: Fix crash in CheckForWorldGCLeaks when loading a new map because spawnables are left behind.
#jira UE-25616
Change 2859685 on 2016/02/08 by Max.Chen
Sequencer: Add prompt to save sub level sequences if they are dirty
#jira UE-26510
Change 2859715 on 2016/02/08 by Thomas.Sarkanen
Adding actor spawning recording
Actors are queued for record on spawn then added to the list like manually-specifed ones.
Changed almost everything about UActorRecording. We now record on a per-component basis, with property tracks encapsulated in each actor recording. Much effort is expended to make sure that the correct components are owned by their respective actors, as we can add and remove components at runtime, but they must be created up-front in the UMovieScene Blueprints. We go as far as to add our own SCS nodes to make sure components are correctly spawned.
Fixed infinite loop in FSequencer::AddSpawnable.
Fixed visibility track instance to work with scene components as well as actors.
Fixed particle track instance to work on UParticleSystemComponent rather than just AEmitters.
Added particle recorder.
Moved animation recording into an animation property recorder rather than having it as a special case. This still uses the animation recorder under the hood.
Moved old-style Matinee animation control into FMovieSceneSkeletalAnimationTrackInstance & made this work on USkeletalMeshComponents directly, rather than via the old interface.
Exposed SetMatineeAnimPositionInner and PreviewMatineeSetAnimPositionInner in FAnimMontageInstance so those utility functions can be used externally to Engine.
Added a predicate version of UMovieScene::FindPossessable.
Exposed UMovieSceneParticleSection::AddKey externally via MOVIESCENETRACKS_API so I can programmatically add keys.
Fixed a crash in FScalableFloatDetails::CustomizeHeader when selecting PIE projectiles in Orion.
Moved all recorders over to recording Actors or Components & store UObjects instead of AActors.
Allowed skeletal animation tracks on components as well as actors.
Change 2862675 on 2016/02/10 by Max.Chen
Sequencer: Add option to link the sequencer curve editor with the sequencer timeline.
Under General Options->Link Curve Editor Time Range. The default is false, so the sequencer and curve editor have separate time ranges.
#jira UE-25933
Change 2862699 on 2016/02/11 by Max.Chen
Sequencer: Added a playback status of jumping which the AudioTrack and Skeletal Mesh Track (anim notifies) ignores for updates. This is used to updating thumbnail at certain times.
#jira UE-26447, UE-26671
Change 2862712 on 2016/02/11 by Max.Chen
Sequencer: Fix spawnables firing off their particles. Disable auto activate on spawnable components
#jira UE-26390
Change 2862719 on 2016/02/11 by Max.Preussner
Editor: Refactored detail customizations for colors, rotators, vectors
- broke color and rotator customizations out into their own files
- added vector customizations (placeholder)
- cleaned up localization namespaces, forward declarations
Change 2866454 on 2016/02/14 by Max.Preussner
Sequencer: Removed ULevelEditorSequencerSettings; moved default settings into INI
Change 2866455 on 2016/02/14 by Thomas.Sarkanen
Sequence recorder can now record replays
Added extra edtior-only UI to the replay playback controls to record sequences. Curretnly very placeholder: only records the entire sequence and provides no feedback in the UI if it is recording.
Fixed bindings to recorded objects not working in various circumstances. Added the ability to manually create a binding.
Recompiled actor blueprints post-record if we added components.
Fixed a null ptr dereference in FOrionTeamUIInfo::Update.
Removed tolerances when reducing tracks - they are now 'very small'.
Added actor filter so actors of certain classes can be recorded.
Change 2866458 on 2016/02/14 by Max.Chen
Sequencer: Fix anim notifies that fire at shot cuts. Anim notifies are fired from the last position to the current position. When jumping cuts, we want the delta to be 0 so that the anim notifies before the shot are not fired off.
#jira UE-26390, UE-26671
Change 2866459 on 2016/02/14 by Max.Chen
Sequencer: Add option to toggle visibility of combined keys
Change 2866466 on 2016/02/14 by Frank.Fella
Sequencer - Add a track for controlling streamed level visibilty and remove visibility code from the master level blueprint.
Change 2866470 on 2016/02/14 by Max.Chen
Sequencer: Add return value to indicate data has changed when a section has been added. This fixes a bug where creating a new section doesn't seem to add a key.
#jira UE-26837
Change 2866481 on 2016/02/14 by Max.Preussner
Sequencer: Implemented Presets for adding tracks automatically based on actor type (UE-24513)
#Jira: UE-24513
Change 2866482 on 2016/02/14 by Max.Chen
Sequencer: Allow for any actor that has a camera component to be a camera cut.
#jira UE-26777
Change 2866484 on 2016/02/14 by Thomas.Sarkanen
Added in/out times to sequence recording
Also added the optional ability to record different actor types (heroes, projectiles, minions).
Change 2866495 on 2016/02/14 by Max.Chen
Sequencer: Need to limit camera control to the section bounds of the camera cut otherwise, control won't be relinquished back to player at the end of the playback.
#jira UE-26886
[CL 2874647 by Max Chen in Main branch]
2016-02-19 21:36:27 -05:00
|
|
|
|
|
|
|
|
|
2014-11-19 07:43:07 -05:00
|
|
|
#define LOCTEXT_NAMESPACE "TimeSlider"
|
|
|
|
|
|
Copying //UE4/Dev-Sequencer to //UE4/Main
==========================
MAJOR FEATURES + CHANGES
==========================
Change 2859626 on 2016/02/08 by Max.Preussner
Editor: Added SaveAs functionality to content asset editors
Change 2859666 on 2016/02/08 by Max.Chen
Sequencer: Fix crash in CheckForWorldGCLeaks when loading a new map because spawnables are left behind.
#jira UE-25616
Change 2859685 on 2016/02/08 by Max.Chen
Sequencer: Add prompt to save sub level sequences if they are dirty
#jira UE-26510
Change 2859715 on 2016/02/08 by Thomas.Sarkanen
Adding actor spawning recording
Actors are queued for record on spawn then added to the list like manually-specifed ones.
Changed almost everything about UActorRecording. We now record on a per-component basis, with property tracks encapsulated in each actor recording. Much effort is expended to make sure that the correct components are owned by their respective actors, as we can add and remove components at runtime, but they must be created up-front in the UMovieScene Blueprints. We go as far as to add our own SCS nodes to make sure components are correctly spawned.
Fixed infinite loop in FSequencer::AddSpawnable.
Fixed visibility track instance to work with scene components as well as actors.
Fixed particle track instance to work on UParticleSystemComponent rather than just AEmitters.
Added particle recorder.
Moved animation recording into an animation property recorder rather than having it as a special case. This still uses the animation recorder under the hood.
Moved old-style Matinee animation control into FMovieSceneSkeletalAnimationTrackInstance & made this work on USkeletalMeshComponents directly, rather than via the old interface.
Exposed SetMatineeAnimPositionInner and PreviewMatineeSetAnimPositionInner in FAnimMontageInstance so those utility functions can be used externally to Engine.
Added a predicate version of UMovieScene::FindPossessable.
Exposed UMovieSceneParticleSection::AddKey externally via MOVIESCENETRACKS_API so I can programmatically add keys.
Fixed a crash in FScalableFloatDetails::CustomizeHeader when selecting PIE projectiles in Orion.
Moved all recorders over to recording Actors or Components & store UObjects instead of AActors.
Allowed skeletal animation tracks on components as well as actors.
Change 2862675 on 2016/02/10 by Max.Chen
Sequencer: Add option to link the sequencer curve editor with the sequencer timeline.
Under General Options->Link Curve Editor Time Range. The default is false, so the sequencer and curve editor have separate time ranges.
#jira UE-25933
Change 2862699 on 2016/02/11 by Max.Chen
Sequencer: Added a playback status of jumping which the AudioTrack and Skeletal Mesh Track (anim notifies) ignores for updates. This is used to updating thumbnail at certain times.
#jira UE-26447, UE-26671
Change 2862712 on 2016/02/11 by Max.Chen
Sequencer: Fix spawnables firing off their particles. Disable auto activate on spawnable components
#jira UE-26390
Change 2862719 on 2016/02/11 by Max.Preussner
Editor: Refactored detail customizations for colors, rotators, vectors
- broke color and rotator customizations out into their own files
- added vector customizations (placeholder)
- cleaned up localization namespaces, forward declarations
Change 2866454 on 2016/02/14 by Max.Preussner
Sequencer: Removed ULevelEditorSequencerSettings; moved default settings into INI
Change 2866455 on 2016/02/14 by Thomas.Sarkanen
Sequence recorder can now record replays
Added extra edtior-only UI to the replay playback controls to record sequences. Curretnly very placeholder: only records the entire sequence and provides no feedback in the UI if it is recording.
Fixed bindings to recorded objects not working in various circumstances. Added the ability to manually create a binding.
Recompiled actor blueprints post-record if we added components.
Fixed a null ptr dereference in FOrionTeamUIInfo::Update.
Removed tolerances when reducing tracks - they are now 'very small'.
Added actor filter so actors of certain classes can be recorded.
Change 2866458 on 2016/02/14 by Max.Chen
Sequencer: Fix anim notifies that fire at shot cuts. Anim notifies are fired from the last position to the current position. When jumping cuts, we want the delta to be 0 so that the anim notifies before the shot are not fired off.
#jira UE-26390, UE-26671
Change 2866459 on 2016/02/14 by Max.Chen
Sequencer: Add option to toggle visibility of combined keys
Change 2866466 on 2016/02/14 by Frank.Fella
Sequencer - Add a track for controlling streamed level visibilty and remove visibility code from the master level blueprint.
Change 2866470 on 2016/02/14 by Max.Chen
Sequencer: Add return value to indicate data has changed when a section has been added. This fixes a bug where creating a new section doesn't seem to add a key.
#jira UE-26837
Change 2866481 on 2016/02/14 by Max.Preussner
Sequencer: Implemented Presets for adding tracks automatically based on actor type (UE-24513)
#Jira: UE-24513
Change 2866482 on 2016/02/14 by Max.Chen
Sequencer: Allow for any actor that has a camera component to be a camera cut.
#jira UE-26777
Change 2866484 on 2016/02/14 by Thomas.Sarkanen
Added in/out times to sequence recording
Also added the optional ability to record different actor types (heroes, projectiles, minions).
Change 2866495 on 2016/02/14 by Max.Chen
Sequencer: Need to limit camera control to the section bounds of the camera cut otherwise, control won't be relinquished back to player at the end of the playback.
#jira UE-26886
[CL 2874647 by Max Chen in Main branch]
2016-02-19 21:36:27 -05:00
|
|
|
|
2014-11-19 07:43:07 -05:00
|
|
|
namespace ScrubConstants
|
|
|
|
|
{
|
|
|
|
|
/** The minimum amount of pixels between each major ticks on the widget */
|
|
|
|
|
const int32 MinPixelsPerDisplayTick = 5;
|
|
|
|
|
|
|
|
|
|
/**The smallest number of units between between major tick marks */
|
|
|
|
|
const float MinDisplayTickSpacing = 0.001f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** Utility struct for converting between scrub range space and local/absolute screen space */
|
|
|
|
|
struct FScrubRangeToScreen
|
|
|
|
|
{
|
|
|
|
|
FVector2D WidgetSize;
|
|
|
|
|
|
|
|
|
|
TRange<float> ViewInput;
|
|
|
|
|
float ViewInputRange;
|
|
|
|
|
float PixelsPerInput;
|
|
|
|
|
|
|
|
|
|
FScrubRangeToScreen(TRange<float> InViewInput, const FVector2D& InWidgetSize )
|
|
|
|
|
{
|
|
|
|
|
WidgetSize = InWidgetSize;
|
|
|
|
|
|
|
|
|
|
ViewInput = InViewInput;
|
|
|
|
|
ViewInputRange = ViewInput.Size<float>();
|
|
|
|
|
PixelsPerInput = ViewInputRange > 0 ? ( WidgetSize.X / ViewInputRange ) : 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Local Widget Space -> Curve Input domain. */
|
|
|
|
|
float LocalXToInput(float ScreenX) const
|
|
|
|
|
{
|
|
|
|
|
float LocalX = ScreenX;
|
|
|
|
|
return (LocalX/PixelsPerInput) + ViewInput.GetLowerBoundValue();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Curve Input domain -> local Widget Space */
|
|
|
|
|
float InputToLocalX(float Input) const
|
|
|
|
|
{
|
|
|
|
|
return (Input - ViewInput.GetLowerBoundValue()) * PixelsPerInput;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Gets the the next spacing value in the series
|
|
|
|
|
* to determine a good spacing value
|
|
|
|
|
* E.g, .001,.005,.010,.050,.100,.500,1.000,etc
|
|
|
|
|
*/
|
|
|
|
|
static float GetNextSpacing( uint32 CurrentStep )
|
|
|
|
|
{
|
|
|
|
|
if(CurrentStep & 0x01)
|
|
|
|
|
{
|
|
|
|
|
// Odd numbers
|
|
|
|
|
return FMath::Pow( 10.f, 0.5f*((float)(CurrentStep-1)) + 1.f );
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Even numbers
|
|
|
|
|
return 0.5f * FMath::Pow( 10.f, 0.5f*((float)(CurrentStep)) + 1.f );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Copying //UE4/Dev-Build to //UE4/Dev-Main (Source: //UE4/Dev-Build @ 3104200)
#lockdown Nick.Penwarden
#rb none
==========================
MAJOR FEATURES + CHANGES
==========================
Change 3096076 on 2016/08/22 by Matthew.Griffin
Fixes for building with -nosharedPCH
Added nodes for building with -nosharedPCH and -noPCH but not using them by default as they take so much longer than NonUnity and there are still errors with -noPCH
Change 3097064 on 2016/08/22 by Ben.Marsh
Core: Refactoring OutputDevice.h, OutputDevices.h, FeedbackDevice.h, CallbackDevice.h into one-class per file, and files named after their classes.
Change 3097102 on 2016/08/22 by Ben.Marsh
Core: Move log macros and related functionality into the Logging folder.
Change 3097240 on 2016/08/22 by Ben.Marsh
Core: Move FCommandLine into its own header.
Change 3097258 on 2016/08/22 by Ben.Marsh
Core: Move FFileHelper into its own header.
Change 3097279 on 2016/08/22 by Ben.Marsh
Core: Split FArchive derived classes into separate headers per class.
Change 3097742 on 2016/08/23 by Ben.Marsh
UnrealGameSync: Add the -r5 parameter to P4 sync command lines, to retry syncs on poor connections before failing.
Change 3099283 on 2016/08/24 by Ben.Marsh
Core: Move math functions which are dependent on multiple class definitions into individual class headers. This forces an include order, but allows us to include individual math classes separately. Would ideally split out the FMath class into a Math namespace and declare/implement things like intersection tests and interpolation routines in separate headers.
Change 3099417 on 2016/08/24 by Ben.Marsh
CoreUObject: Separating out UObject archive classes into separate headers.
Change 3099544 on 2016/08/24 by Ben.Marsh
CoreUObject: Rename UTextProperty.h/cpp to TextProperty.h/cpp
Change 3099678 on 2016/08/24 by Ben.Marsh
CoreUObject: Rename ObjectBase.h to ObjectMacros.h to avoid ambiguiity with UObjectBase.h, and move Interface.h/cpp into the UObject folder.
Change 3099951 on 2016/08/24 by Ben.Marsh
Core: Split out ThreadingBase into class-per-file headers.
Change 3102385 on 2016/08/26 by Matthew.Griffin
First round of fixes from attempting to compile Editor in Monolithic mode
Changed multiple DetermineOptimalSpacing free functions to be members of their respective classes
Changed duplicate FScopedTempFile classes to be named for their respective source control types (Git & SVN)
Export Declaration of LogUnrealAudioDevice extern instead of defining it in multiple modules
Changed duplicate FDiffResultItem struct to FTreeDiffResultItem
Added FORCEINLINE to FFeaturedClasses functions in .inl file
Change 3102728 on 2016/08/26 by Ben.Marsh
Core: Move threading classes from ThreadingBase.h into separate headers, and globals into CoreGlobals.h.
Change 3102850 on 2016/08/26 by Ben.Marsh
Core: Move FCriticalSection into its own file (CriticalSection.h and platform-specific versions).
Change 3102877 on 2016/08/26 by Ben.Marsh
Core: Move definitions of GLog, GError, GWarn into CoreGlobals.h
Change 3102898 on 2016/08/26 by Ben.Marsh
Core: Move supporting assert functionality (the FDebug class) into AssertionMacros.h to remove circular header dependencies.
Change 3102956 on 2016/08/26 by Ben.Marsh
Core: Move FExec into its own header (Misc/Exec.h)
Change 3102961 on 2016/08/26 by Ben.Marsh
Core: Fix matrix functions referencing the forceinline'd FMatrix constructor before it's declared.
Change 3102982 on 2016/08/26 by Ben.Marsh
Core: Move FMessageDialog into its own header (Misc/MessageDialog.h)
Change 3103008 on 2016/08/26 by Ben.Marsh
Core: Move definition of LowLevelFatalError into AssertionMacros.h.
Change 3103040 on 2016/08/26 by Ben.Marsh
Core: Move FError::Throwf (only enabled for UHT) into its own header in CoreUObject.
Change 3103057 on 2016/08/26 by Ben.Marsh
Core: Move FMsg function definitions into the LogMacros.h header, where it's primarily used from.
Change 3103102 on 2016/08/26 by Ben.Marsh
Core: Make the LogMacros.h header standalone. Untangles a few header dependencies necessary to include log macros early on. Global Core log channels are now defined in CoreGlobals.h
Change 3103160 on 2016/08/26 by Ben.Marsh
CoreUObject: Separate Linker.h into LinkerLoad.h, LinkerSave.h, PackageFileSummary.h, ObjectResource.h (matching existing cpp files).
Change 3103265 on 2016/08/26 by Ben.Marsh
Core: Split FScriptArray, TMRUArray, TTransArray, TIndirectArray into separate headers.
Change 3103642 on 2016/08/26 by Ben.Marsh
Core: Remove reference to FMath function from allocator, to avoid unnecessarily bringing in math library.
[CL 3104315 by Ben Marsh in Main branch]
2016-08-29 10:27:24 -04:00
|
|
|
FVisualLoggerTimeSliderController::FVisualLoggerTimeSliderController(const FVisualLoggerTimeSliderArgs& InArgs)
|
|
|
|
|
: TimeSliderArgs( InArgs )
|
|
|
|
|
, DistanceDragged( 0.0f )
|
|
|
|
|
, bDraggingScrubber( false )
|
|
|
|
|
, bPanning( false )
|
|
|
|
|
{
|
|
|
|
|
ScrubHandleUp = FEditorStyle::GetBrush( TEXT( "Sequencer.Timeline.ScrubHandleUp" ) );
|
|
|
|
|
ScrubHandleDown = FEditorStyle::GetBrush( TEXT( "Sequencer.Timeline.ScrubHandleDown" ) );
|
|
|
|
|
CursorBackground = FEditorStyle::GetBrush("Sequencer.SectionArea.Background");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float FVisualLoggerTimeSliderController::DetermineOptimalSpacing(float InPixelsPerInput, uint32 MinTick, float MinTickSpacing) const
|
2014-11-19 07:43:07 -05:00
|
|
|
{
|
|
|
|
|
uint32 CurStep = 0;
|
|
|
|
|
|
|
|
|
|
// Start with the smallest spacing
|
|
|
|
|
float Spacing = MinTickSpacing;
|
|
|
|
|
|
|
|
|
|
while( Spacing * InPixelsPerInput < MinTick )
|
|
|
|
|
{
|
|
|
|
|
Spacing = MinTickSpacing * GetNextSpacing( CurStep );
|
|
|
|
|
CurStep++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Spacing;
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-29 19:21:24 -05:00
|
|
|
void FVisualLoggerTimeSliderController::SetTimesliderArgs(const FVisualLoggerTimeSliderArgs& InArgs)
|
2015-01-15 06:19:24 -05:00
|
|
|
{
|
|
|
|
|
TimeSliderArgs = InArgs;
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-19 07:43:07 -05:00
|
|
|
struct FDrawTickArgs
|
|
|
|
|
{
|
|
|
|
|
/** Geometry of the area */
|
|
|
|
|
FGeometry AllottedGeometry;
|
|
|
|
|
/** Clipping rect of the area */
|
|
|
|
|
FSlateRect ClippingRect;
|
|
|
|
|
/** Color of each tick */
|
|
|
|
|
FLinearColor TickColor;
|
|
|
|
|
/** Offset in Y where to start the tick */
|
|
|
|
|
float TickOffset;
|
|
|
|
|
/** Height in of major ticks */
|
|
|
|
|
float MajorTickHeight;
|
|
|
|
|
/** Start layer for elements */
|
|
|
|
|
int32 StartLayer;
|
|
|
|
|
/** Draw effects to apply */
|
|
|
|
|
ESlateDrawEffect::Type DrawEffects;
|
|
|
|
|
/** Whether or not to only draw major ticks */
|
|
|
|
|
bool bOnlyDrawMajorTicks;
|
|
|
|
|
/** Whether or not to mirror labels */
|
|
|
|
|
bool bMirrorLabels;
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
2015-01-29 19:21:24 -05:00
|
|
|
void FVisualLoggerTimeSliderController::DrawTicks( FSlateWindowElementList& OutDrawElements, const struct FScrubRangeToScreen& RangeToScreen, FDrawTickArgs& InArgs ) const
|
2014-11-19 07:43:07 -05:00
|
|
|
{
|
|
|
|
|
const float Spacing = DetermineOptimalSpacing( RangeToScreen.PixelsPerInput, ScrubConstants::MinPixelsPerDisplayTick, ScrubConstants::MinDisplayTickSpacing );
|
|
|
|
|
|
|
|
|
|
// Sub divisions
|
|
|
|
|
// @todo Sequencer may need more robust calculation
|
|
|
|
|
const int32 Divider = 10;
|
|
|
|
|
// For slightly larger halfway tick mark
|
|
|
|
|
const int32 HalfDivider = Divider / 2;
|
|
|
|
|
// Find out where to start from
|
|
|
|
|
int32 OffsetNum = FMath::FloorToInt(RangeToScreen.ViewInput.GetLowerBoundValue() / Spacing);
|
|
|
|
|
|
|
|
|
|
FSlateFontInfo SmallLayoutFont( FPaths::EngineContentDir() / TEXT("Slate/Fonts/Roboto-Regular.ttf"), 8 );
|
|
|
|
|
|
|
|
|
|
TArray<FVector2D> LinePoints;
|
|
|
|
|
LinePoints.AddUninitialized(2);
|
|
|
|
|
|
|
|
|
|
float Seconds = 0;
|
|
|
|
|
while( (Seconds = OffsetNum*Spacing) < RangeToScreen.ViewInput.GetUpperBoundValue() )
|
|
|
|
|
{
|
|
|
|
|
// X position local to start of the widget area
|
|
|
|
|
float XPos = RangeToScreen.InputToLocalX( Seconds );
|
|
|
|
|
uint32 AbsOffsetNum = FMath::Abs(OffsetNum);
|
|
|
|
|
|
|
|
|
|
if ( AbsOffsetNum % Divider == 0 )
|
|
|
|
|
{
|
|
|
|
|
FVector2D Offset( XPos, InArgs.TickOffset );
|
|
|
|
|
FVector2D TickSize( 1.0f, InArgs.MajorTickHeight );
|
|
|
|
|
|
|
|
|
|
LinePoints[0] = FVector2D(1.0f,1.0f);
|
|
|
|
|
LinePoints[1] = TickSize;
|
|
|
|
|
|
|
|
|
|
// lines should not need anti-aliasing
|
|
|
|
|
const bool bAntiAliasLines = false;
|
|
|
|
|
|
|
|
|
|
// Draw each tick mark
|
|
|
|
|
FSlateDrawElement::MakeLines(
|
|
|
|
|
OutDrawElements,
|
|
|
|
|
InArgs.StartLayer,
|
|
|
|
|
InArgs.AllottedGeometry.ToPaintGeometry( Offset, TickSize ),
|
|
|
|
|
LinePoints,
|
|
|
|
|
InArgs.ClippingRect,
|
|
|
|
|
InArgs.DrawEffects,
|
|
|
|
|
InArgs.TickColor,
|
|
|
|
|
false
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if( !InArgs.bOnlyDrawMajorTicks )
|
|
|
|
|
{
|
|
|
|
|
FString FrameString = Spacing == ScrubConstants::MinDisplayTickSpacing ? FString::Printf( TEXT("%.3f"), Seconds ) : FString::Printf( TEXT("%.2f"), Seconds );
|
|
|
|
|
|
|
|
|
|
// Space the text between the tick mark but slightly above
|
|
|
|
|
const TSharedRef< FSlateFontMeasure > FontMeasureService = FSlateApplication::Get().GetRenderer()->GetFontMeasureService();
|
|
|
|
|
FVector2D TextSize = FontMeasureService->Measure(FrameString, SmallLayoutFont);
|
|
|
|
|
FVector2D TextOffset( XPos-(TextSize.X*0.5f), InArgs.bMirrorLabels ? TextSize.Y : FMath::Abs( InArgs.AllottedGeometry.Size.Y - (InArgs.MajorTickHeight+TextSize.Y) ) );
|
|
|
|
|
|
|
|
|
|
FSlateDrawElement::MakeText(
|
|
|
|
|
OutDrawElements,
|
|
|
|
|
InArgs.StartLayer+1,
|
|
|
|
|
InArgs.AllottedGeometry.ToPaintGeometry( TextOffset, TextSize ),
|
|
|
|
|
FrameString,
|
|
|
|
|
SmallLayoutFont,
|
|
|
|
|
InArgs.ClippingRect,
|
|
|
|
|
InArgs.DrawEffects,
|
|
|
|
|
InArgs.TickColor
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if( !InArgs.bOnlyDrawMajorTicks )
|
|
|
|
|
{
|
|
|
|
|
// Compute the size of each tick mark. If we are half way between to visible values display a slightly larger tick mark
|
|
|
|
|
const float MinorTickHeight = AbsOffsetNum % HalfDivider == 0 ? 7.0f : 4.0f;
|
|
|
|
|
|
|
|
|
|
FVector2D Offset(XPos, InArgs.bMirrorLabels ? 0.0f : FMath::Abs( InArgs.AllottedGeometry.Size.Y - MinorTickHeight ) );
|
|
|
|
|
FVector2D TickSize(1, MinorTickHeight);
|
|
|
|
|
|
|
|
|
|
LinePoints[0] = FVector2D(1.0f,1.0f);
|
|
|
|
|
LinePoints[1] = TickSize;
|
|
|
|
|
|
|
|
|
|
const bool bAntiAlias = false;
|
|
|
|
|
// Draw each sub mark
|
|
|
|
|
FSlateDrawElement::MakeLines(
|
|
|
|
|
OutDrawElements,
|
|
|
|
|
InArgs.StartLayer,
|
|
|
|
|
InArgs.AllottedGeometry.ToPaintGeometry( Offset, TickSize ),
|
|
|
|
|
LinePoints,
|
|
|
|
|
InArgs.ClippingRect,
|
|
|
|
|
InArgs.DrawEffects,
|
|
|
|
|
InArgs.TickColor,
|
|
|
|
|
bAntiAlias
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
// Advance to next tick mark
|
|
|
|
|
++OffsetNum;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2015-01-29 19:21:24 -05:00
|
|
|
int32 FVisualLoggerTimeSliderController::OnPaintTimeSlider( bool bMirrorLabels, const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const
|
2014-11-19 07:43:07 -05:00
|
|
|
{
|
|
|
|
|
const bool bEnabled = bParentEnabled;
|
|
|
|
|
const ESlateDrawEffect::Type DrawEffects = bEnabled ? ESlateDrawEffect::None : ESlateDrawEffect::DisabledEffect;
|
|
|
|
|
|
|
|
|
|
TRange<float> LocalViewRange = TimeSliderArgs.ViewRange.Get();
|
|
|
|
|
const float LocalViewRangeMin = LocalViewRange.GetLowerBoundValue();
|
|
|
|
|
const float LocalViewRangeMax = LocalViewRange.GetUpperBoundValue();
|
|
|
|
|
const float LocalSequenceLength = LocalViewRangeMax-LocalViewRangeMin;
|
|
|
|
|
|
|
|
|
|
FVector2D Scale = FVector2D(1.0f,1.0f);
|
|
|
|
|
if ( LocalSequenceLength > 0)
|
|
|
|
|
{
|
|
|
|
|
FScrubRangeToScreen RangeToScreen( LocalViewRange, AllottedGeometry.Size );
|
|
|
|
|
|
|
|
|
|
const float MajorTickHeight = 9.0f;
|
|
|
|
|
|
|
|
|
|
FDrawTickArgs Args;
|
|
|
|
|
Args.AllottedGeometry = AllottedGeometry;
|
|
|
|
|
Args.bMirrorLabels = bMirrorLabels;
|
|
|
|
|
Args.bOnlyDrawMajorTicks = false;
|
|
|
|
|
Args.TickColor = FLinearColor::White;
|
|
|
|
|
Args.ClippingRect = MyClippingRect;
|
|
|
|
|
Args.DrawEffects = DrawEffects;
|
|
|
|
|
Args.StartLayer = LayerId;
|
|
|
|
|
Args.TickOffset = bMirrorLabels ? 0.0f : FMath::Abs( AllottedGeometry.Size.Y - MajorTickHeight );
|
|
|
|
|
Args.MajorTickHeight = MajorTickHeight;
|
|
|
|
|
|
|
|
|
|
DrawTicks( OutDrawElements, RangeToScreen, Args );
|
|
|
|
|
|
|
|
|
|
const float HandleSize = 13.0f;
|
|
|
|
|
float HalfSize = FMath::TruncToFloat(HandleSize/2.0f);
|
|
|
|
|
|
|
|
|
|
// Draw the scrub handle
|
|
|
|
|
const float XPos = RangeToScreen.InputToLocalX( TimeSliderArgs.ScrubPosition.Get() );
|
|
|
|
|
|
|
|
|
|
// Draw cursor size
|
|
|
|
|
const float CursorHalfSize = TimeSliderArgs.CursorSize.Get() * 0.5f;
|
|
|
|
|
const int32 CursorLayer = LayerId + 2;
|
|
|
|
|
const float CursorHalfLength = AllottedGeometry.Size.X * CursorHalfSize;
|
|
|
|
|
FPaintGeometry CursorGeometry = AllottedGeometry.ToPaintGeometry(FVector2D(XPos - CursorHalfLength, 0), FVector2D(2 * CursorHalfLength, AllottedGeometry.Size.Y));
|
|
|
|
|
|
|
|
|
|
FLinearColor CursorColor = InWidgetStyle.GetColorAndOpacityTint();
|
|
|
|
|
CursorColor.A = CursorColor.A*0.08f;
|
|
|
|
|
CursorColor.B *= 0.1f;
|
|
|
|
|
CursorColor.G *= 0.2f;
|
|
|
|
|
FSlateDrawElement::MakeBox(
|
|
|
|
|
OutDrawElements,
|
|
|
|
|
CursorLayer,
|
|
|
|
|
CursorGeometry,
|
|
|
|
|
CursorBackground,
|
|
|
|
|
MyClippingRect,
|
|
|
|
|
DrawEffects,
|
|
|
|
|
CursorColor
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Should draw above the text
|
|
|
|
|
const int32 ArrowLayer = LayerId + 3;
|
|
|
|
|
FPaintGeometry MyGeometry = AllottedGeometry.ToPaintGeometry( FVector2D( XPos-HalfSize, 0 ), FVector2D( HandleSize, AllottedGeometry.Size.Y ) );
|
|
|
|
|
FLinearColor ScrubColor = InWidgetStyle.GetColorAndOpacityTint();
|
|
|
|
|
|
|
|
|
|
// @todo Sequencer this color should be specified in the style
|
|
|
|
|
ScrubColor.A = ScrubColor.A*0.5f;
|
|
|
|
|
ScrubColor.B *= 0.1f;
|
|
|
|
|
ScrubColor.G *= 0.2f;
|
|
|
|
|
FSlateDrawElement::MakeBox(
|
|
|
|
|
OutDrawElements,
|
|
|
|
|
ArrowLayer,
|
|
|
|
|
MyGeometry,
|
|
|
|
|
bMirrorLabels ? ScrubHandleUp : ScrubHandleDown,
|
|
|
|
|
MyClippingRect,
|
|
|
|
|
DrawEffects,
|
|
|
|
|
ScrubColor
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return ArrowLayer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return LayerId;
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-09 19:35:15 -05:00
|
|
|
FReply FVisualLoggerTimeSliderController::OnMouseButtonDown( SWidget& WidgetOwner, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent )
|
2014-11-19 07:43:07 -05:00
|
|
|
{
|
|
|
|
|
bool bHandleLeftMouseButton = MouseEvent.GetEffectingButton() == EKeys::LeftMouseButton;
|
|
|
|
|
bool bHandleRightMouseButton = MouseEvent.GetEffectingButton() == EKeys::RightMouseButton && TimeSliderArgs.AllowZoom;
|
|
|
|
|
|
|
|
|
|
DistanceDragged = 0;
|
|
|
|
|
|
|
|
|
|
if ( bHandleLeftMouseButton )
|
|
|
|
|
{
|
|
|
|
|
// Always capture mouse if we left or right click on the widget
|
|
|
|
|
FScrubRangeToScreen RangeToScreen(TimeSliderArgs.ViewRange.Get(), MyGeometry.Size);
|
|
|
|
|
FVector2D CursorPos = MyGeometry.AbsoluteToLocal(MouseEvent.GetLastScreenSpacePosition());
|
|
|
|
|
float NewValue = RangeToScreen.LocalXToInput(CursorPos.X);
|
|
|
|
|
|
|
|
|
|
CommitScrubPosition(NewValue, /*bIsScrubbing=*/false);
|
2015-11-09 19:35:15 -05:00
|
|
|
return FReply::Handled().CaptureMouse( WidgetOwner.AsShared() ).PreventThrottling();
|
2014-11-19 07:43:07 -05:00
|
|
|
}
|
|
|
|
|
else if ( bHandleRightMouseButton )
|
|
|
|
|
{
|
2015-11-09 19:35:15 -05:00
|
|
|
return FReply::Handled().CaptureMouse(WidgetOwner.AsShared());
|
2014-11-19 07:43:07 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return FReply::Unhandled();
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-09 19:35:15 -05:00
|
|
|
FReply FVisualLoggerTimeSliderController::OnMouseButtonUp( SWidget& WidgetOwner, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent )
|
2014-11-19 07:43:07 -05:00
|
|
|
{
|
2015-11-09 19:35:15 -05:00
|
|
|
bool bHandleLeftMouseButton = MouseEvent.GetEffectingButton() == EKeys::LeftMouseButton && WidgetOwner.HasMouseCapture();
|
|
|
|
|
bool bHandleRightMouseButton = MouseEvent.GetEffectingButton() == EKeys::RightMouseButton && WidgetOwner.HasMouseCapture() && TimeSliderArgs.AllowZoom ;
|
2014-11-19 07:43:07 -05:00
|
|
|
|
|
|
|
|
if ( bHandleRightMouseButton )
|
|
|
|
|
{
|
|
|
|
|
if (!bPanning)
|
|
|
|
|
{
|
|
|
|
|
// return unhandled in case our parent wants to use our right mouse button to open a context menu
|
|
|
|
|
return FReply::Unhandled().ReleaseMouseCapture();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bPanning = false;
|
2015-11-09 19:35:15 -05:00
|
|
|
FReply::Handled().CaptureMouse(WidgetOwner.AsShared()).UseHighPrecisionMouseMovement(WidgetOwner.AsShared());
|
2014-12-10 09:22:32 -05:00
|
|
|
|
2014-11-19 07:43:07 -05:00
|
|
|
return FReply::Handled().ReleaseMouseCapture();
|
|
|
|
|
}
|
|
|
|
|
else if ( bHandleLeftMouseButton )
|
|
|
|
|
{
|
|
|
|
|
if( bDraggingScrubber )
|
|
|
|
|
{
|
|
|
|
|
TimeSliderArgs.OnEndScrubberMovement.ExecuteIfBound();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
FScrubRangeToScreen RangeToScreen( TimeSliderArgs.ViewRange.Get(), MyGeometry.Size );
|
|
|
|
|
FVector2D CursorPos = MyGeometry.AbsoluteToLocal(MouseEvent.GetLastScreenSpacePosition());
|
|
|
|
|
float NewValue = RangeToScreen.LocalXToInput(CursorPos.X);
|
|
|
|
|
|
|
|
|
|
CommitScrubPosition( NewValue, /*bIsScrubbing=*/false );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bDraggingScrubber = false;
|
|
|
|
|
return FReply::Handled().ReleaseMouseCapture();
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return FReply::Unhandled();
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-09 19:35:15 -05:00
|
|
|
FReply FVisualLoggerTimeSliderController::OnMouseMove( SWidget& WidgetOwner, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent )
|
2014-11-19 07:43:07 -05:00
|
|
|
{
|
2015-11-09 19:35:15 -05:00
|
|
|
if ( WidgetOwner.HasMouseCapture() )
|
2014-11-19 07:43:07 -05:00
|
|
|
{
|
2014-12-10 09:22:32 -05:00
|
|
|
if (MouseEvent.IsMouseButtonDown(EKeys::RightMouseButton))
|
2014-11-19 07:43:07 -05:00
|
|
|
{
|
|
|
|
|
if (!bPanning)
|
|
|
|
|
{
|
|
|
|
|
DistanceDragged += FMath::Abs( MouseEvent.GetCursorDelta().X );
|
---- Merging with SlateDev branch ----
Introduces the concept of "Active Ticking" to allow Slate to go to sleep when there is no need to update the UI.
While asleep, Slate will skip the Tick & Paint pass for that frame entirely.
- There are TWO ways to "wake" Slate and cause a Tick/Paint pass:
1. Provide some sort of input (mouse movement, clicks, and key presses). Slate will always tick when the user is active.
- Therefore, if the logic in a given widget's Tick is only relevant in response to user action, there is no need to register an active tick.
2. Register an Active Tick. Currently this is an all-or-nothing situation, so if a single active tick needs to execute, all of Slate will be ticked.
- The purpose of an Active Tick is to allow a widget to "drive" Slate and guarantee a Tick/Paint pass in the absence of any user action.
- Examples include animation, async operations that update periodically, progress updates, loading bars, etc.
- An empty active tick is registered for viewports when they are real-time, so game project widgets are unaffected by this change and should continue to work as before.
- An Active Tick is registered by creating an FWidgetActiveTickDelegate and passing it to SWidget::RegisterActiveTick()
- There are THREE ways to unregister an active tick:
1. Return EActiveTickReturnType::StopTicking from the active tick function
2. Pass the FActiveTickHandle returned by RegisterActiveTick() to SWidget::UnregisterActiveTick()
3. Destroy the widget responsible for the active tick
- Sleeping is currently disabled, can be enabled with Slate.AllowSlateToSleep cvar
- There is currently a little buffer time during which Slate continues to tick following any input. Long-term, this is planned to be removed.
- The duration of the buffer can be adjusted using Slate.SleepBufferPostInput cvar (defaults to 1.0f)
- The FCurveSequence API has been updated to work with the active tick system
- Playing a curve sequence now requires that you pass the widget being animated by the sequence
- The active tick will automatically be registered on behalf of the widget and unregister when the sequence is complete
- GetLerpLooping() has been removed. Instead, pass true as the second param to Play() to indicate that the animation will loop. This causes the active tick to be registered indefinitely until paused or jumped to the start/end.
[CL 2391669 by Dan Hertzka in Main branch]
2014-12-17 16:07:57 -05:00
|
|
|
if ( DistanceDragged > FSlateApplication::Get().GetDragTriggerDistance() )
|
2014-11-19 07:43:07 -05:00
|
|
|
{
|
2015-11-09 19:35:15 -05:00
|
|
|
FReply::Handled().CaptureMouse(WidgetOwner.AsShared()).UseHighPrecisionMouseMovement(WidgetOwner.AsShared());
|
2014-12-10 09:22:32 -05:00
|
|
|
SoftwareCursorPosition = MyGeometry.AbsoluteToLocal(MouseEvent.GetLastScreenSpacePosition());
|
2014-11-19 07:43:07 -05:00
|
|
|
bPanning = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2014-12-10 09:22:32 -05:00
|
|
|
SoftwareCursorPosition = MyGeometry.AbsoluteToLocal(MouseEvent.GetLastScreenSpacePosition());
|
|
|
|
|
|
2014-11-19 07:43:07 -05:00
|
|
|
TRange<float> LocalViewRange = TimeSliderArgs.ViewRange.Get();
|
|
|
|
|
float LocalViewRangeMin = LocalViewRange.GetLowerBoundValue();
|
|
|
|
|
float LocalViewRangeMax = LocalViewRange.GetUpperBoundValue();
|
|
|
|
|
|
|
|
|
|
FScrubRangeToScreen ScaleInfo( LocalViewRange, MyGeometry.Size );
|
|
|
|
|
FVector2D ScreenDelta = MouseEvent.GetCursorDelta();
|
|
|
|
|
FVector2D InputDelta;
|
|
|
|
|
InputDelta.X = ScreenDelta.X/ScaleInfo.PixelsPerInput;
|
|
|
|
|
|
|
|
|
|
float NewViewOutputMin = LocalViewRangeMin - InputDelta.X;
|
|
|
|
|
float NewViewOutputMax = LocalViewRangeMax - InputDelta.X;
|
|
|
|
|
|
2015-07-27 11:14:09 -04:00
|
|
|
float LocalClampMin = TimeSliderArgs.ClampRange.Get().GetLowerBoundValue();
|
|
|
|
|
float LocalClampMax = TimeSliderArgs.ClampRange.Get().GetUpperBoundValue();
|
2014-11-19 07:43:07 -05:00
|
|
|
|
2015-07-27 11:14:09 -04:00
|
|
|
// Clamp the range
|
|
|
|
|
if ( NewViewOutputMin < LocalClampMin )
|
2014-11-19 07:43:07 -05:00
|
|
|
{
|
2015-07-27 11:14:09 -04:00
|
|
|
NewViewOutputMin = LocalClampMin;
|
2014-11-19 07:43:07 -05:00
|
|
|
}
|
|
|
|
|
|
2015-07-27 11:14:09 -04:00
|
|
|
if ( NewViewOutputMax > LocalClampMax )
|
2014-11-19 07:43:07 -05:00
|
|
|
{
|
2015-07-27 11:14:09 -04:00
|
|
|
NewViewOutputMax = LocalClampMax;
|
2014-11-19 07:43:07 -05:00
|
|
|
}
|
|
|
|
|
|
2015-10-31 10:55:13 -04:00
|
|
|
TimeSliderArgs.OnViewRangeChanged.ExecuteIfBound(TRange<float>(NewViewOutputMin, NewViewOutputMax), EViewRangeInterpolation::Immediate);
|
2014-12-10 09:22:32 -05:00
|
|
|
if (Scrollbar.IsValid())
|
|
|
|
|
{
|
2015-07-27 11:14:09 -04:00
|
|
|
float InOffsetFraction = (NewViewOutputMin - LocalClampMin) / (LocalClampMax - LocalClampMin);
|
|
|
|
|
float InThumbSizeFraction = (NewViewOutputMax - NewViewOutputMin) / (LocalClampMax - LocalClampMin);
|
2014-12-10 09:22:32 -05:00
|
|
|
Scrollbar->SetState(InOffsetFraction, InThumbSizeFraction);
|
|
|
|
|
}
|
2014-11-19 07:43:07 -05:00
|
|
|
|
|
|
|
|
if( !TimeSliderArgs.ViewRange.IsBound() )
|
|
|
|
|
{
|
|
|
|
|
// The output is not bound to a delegate so we'll manage the value ourselves
|
|
|
|
|
TimeSliderArgs.ViewRange.Set( TRange<float>( NewViewOutputMin, NewViewOutputMax ) );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (MouseEvent.IsMouseButtonDown( EKeys::LeftMouseButton ))
|
|
|
|
|
{
|
|
|
|
|
if ( !bDraggingScrubber )
|
|
|
|
|
{
|
|
|
|
|
DistanceDragged += FMath::Abs( MouseEvent.GetCursorDelta().X );
|
---- Merging with SlateDev branch ----
Introduces the concept of "Active Ticking" to allow Slate to go to sleep when there is no need to update the UI.
While asleep, Slate will skip the Tick & Paint pass for that frame entirely.
- There are TWO ways to "wake" Slate and cause a Tick/Paint pass:
1. Provide some sort of input (mouse movement, clicks, and key presses). Slate will always tick when the user is active.
- Therefore, if the logic in a given widget's Tick is only relevant in response to user action, there is no need to register an active tick.
2. Register an Active Tick. Currently this is an all-or-nothing situation, so if a single active tick needs to execute, all of Slate will be ticked.
- The purpose of an Active Tick is to allow a widget to "drive" Slate and guarantee a Tick/Paint pass in the absence of any user action.
- Examples include animation, async operations that update periodically, progress updates, loading bars, etc.
- An empty active tick is registered for viewports when they are real-time, so game project widgets are unaffected by this change and should continue to work as before.
- An Active Tick is registered by creating an FWidgetActiveTickDelegate and passing it to SWidget::RegisterActiveTick()
- There are THREE ways to unregister an active tick:
1. Return EActiveTickReturnType::StopTicking from the active tick function
2. Pass the FActiveTickHandle returned by RegisterActiveTick() to SWidget::UnregisterActiveTick()
3. Destroy the widget responsible for the active tick
- Sleeping is currently disabled, can be enabled with Slate.AllowSlateToSleep cvar
- There is currently a little buffer time during which Slate continues to tick following any input. Long-term, this is planned to be removed.
- The duration of the buffer can be adjusted using Slate.SleepBufferPostInput cvar (defaults to 1.0f)
- The FCurveSequence API has been updated to work with the active tick system
- Playing a curve sequence now requires that you pass the widget being animated by the sequence
- The active tick will automatically be registered on behalf of the widget and unregister when the sequence is complete
- GetLerpLooping() has been removed. Instead, pass true as the second param to Play() to indicate that the animation will loop. This causes the active tick to be registered indefinitely until paused or jumped to the start/end.
[CL 2391669 by Dan Hertzka in Main branch]
2014-12-17 16:07:57 -05:00
|
|
|
if ( DistanceDragged > 0/*FSlateApplication::Get().GetDragTriggerDistance()*/ )
|
2014-11-19 07:43:07 -05:00
|
|
|
{
|
|
|
|
|
bDraggingScrubber = true;
|
|
|
|
|
TimeSliderArgs.OnBeginScrubberMovement.ExecuteIfBound();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
FScrubRangeToScreen RangeToScreen( TimeSliderArgs.ViewRange.Get(), MyGeometry.Size );
|
|
|
|
|
FVector2D CursorPos = MyGeometry.AbsoluteToLocal( MouseEvent.GetLastScreenSpacePosition() );
|
|
|
|
|
float NewValue = RangeToScreen.LocalXToInput( CursorPos.X );
|
2015-07-27 11:14:09 -04:00
|
|
|
|
|
|
|
|
float LocalClampMin = TimeSliderArgs.ClampRange.Get().GetLowerBoundValue();
|
|
|
|
|
float LocalClampMax = TimeSliderArgs.ClampRange.Get().GetUpperBoundValue();
|
|
|
|
|
|
|
|
|
|
if (NewValue < LocalClampMin)
|
2015-01-16 07:07:42 -05:00
|
|
|
{
|
2015-07-27 11:14:09 -04:00
|
|
|
NewValue = LocalClampMin;
|
2015-01-16 07:07:42 -05:00
|
|
|
}
|
2014-11-19 07:43:07 -05:00
|
|
|
|
2015-07-27 11:14:09 -04:00
|
|
|
if (NewValue > LocalClampMax)
|
2015-01-16 07:07:42 -05:00
|
|
|
{
|
2015-07-27 11:14:09 -04:00
|
|
|
NewValue = LocalClampMax;
|
2015-01-16 07:07:42 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CommitScrubPosition(NewValue, /*bIsScrubbing=*/true);
|
2014-11-19 07:43:07 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return FReply::Handled();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return FReply::Unhandled();
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-29 19:21:24 -05:00
|
|
|
void FVisualLoggerTimeSliderController::CommitScrubPosition( float NewValue, bool bIsScrubbing )
|
2014-11-19 07:43:07 -05:00
|
|
|
{
|
|
|
|
|
// Manage the scrub position ourselves if its not bound to a delegate
|
|
|
|
|
if ( !TimeSliderArgs.ScrubPosition.IsBound() )
|
|
|
|
|
{
|
|
|
|
|
TimeSliderArgs.ScrubPosition.Set( NewValue );
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-07 08:06:36 -05:00
|
|
|
TRange<float> LocalViewRange = TimeSliderArgs.ViewRange.Get();
|
|
|
|
|
const float RangeSize = LocalViewRange.Size<float>();
|
|
|
|
|
if (NewValue < LocalViewRange.GetLowerBoundValue())
|
|
|
|
|
{
|
|
|
|
|
SetTimeRange(NewValue, NewValue + RangeSize);
|
|
|
|
|
}
|
|
|
|
|
else if (NewValue > LocalViewRange.GetUpperBoundValue())
|
|
|
|
|
{
|
|
|
|
|
SetTimeRange(NewValue - RangeSize, NewValue);
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-19 07:43:07 -05:00
|
|
|
TimeSliderArgs.OnScrubPositionChanged.ExecuteIfBound( NewValue, bIsScrubbing );
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-29 19:21:24 -05:00
|
|
|
void FVisualLoggerTimeSliderController::SetExternalScrollbar(TSharedRef<SScrollBar> InScrollbar)
|
2014-12-10 09:22:32 -05:00
|
|
|
{
|
|
|
|
|
Scrollbar = InScrollbar;
|
2015-01-29 19:21:24 -05:00
|
|
|
Scrollbar->SetOnUserScrolled(FOnUserScrolled::CreateRaw(this, &FVisualLoggerTimeSliderController::HorizontalScrollBar_OnUserScrolled));
|
2014-12-10 09:22:32 -05:00
|
|
|
};
|
|
|
|
|
|
2015-01-29 19:21:24 -05:00
|
|
|
void FVisualLoggerTimeSliderController::HorizontalScrollBar_OnUserScrolled(float ScrollOffset)
|
2014-12-10 09:22:32 -05:00
|
|
|
{
|
|
|
|
|
if (!TimeSliderArgs.ViewRange.IsBound())
|
|
|
|
|
{
|
|
|
|
|
TRange<float> LocalViewRange = TimeSliderArgs.ViewRange.Get();
|
|
|
|
|
float LocalViewRangeMin = LocalViewRange.GetLowerBoundValue();
|
|
|
|
|
float LocalViewRangeMax = LocalViewRange.GetUpperBoundValue();
|
2015-07-27 11:14:09 -04:00
|
|
|
float LocalClampMin = TimeSliderArgs.ClampRange.Get().GetLowerBoundValue();
|
|
|
|
|
float LocalClampMax = TimeSliderArgs.ClampRange.Get().GetUpperBoundValue();
|
2014-12-10 09:22:32 -05:00
|
|
|
|
2015-07-27 11:14:09 -04:00
|
|
|
float InThumbSizeFraction = (LocalViewRangeMax - LocalViewRangeMin) / (LocalClampMax - LocalClampMin);
|
2014-12-10 09:22:32 -05:00
|
|
|
|
2015-07-27 11:14:09 -04:00
|
|
|
float NewViewOutputMin = LocalClampMin + ScrollOffset * (LocalClampMax - LocalClampMin);
|
2015-01-07 08:06:36 -05:00
|
|
|
// The output is not bound to a delegate so we'll manage the value ourselves
|
2015-07-27 11:14:09 -04:00
|
|
|
float NewViewOutputMax = FMath::Min<float>(NewViewOutputMin + (LocalViewRangeMax - LocalViewRangeMin), LocalClampMax);
|
2015-01-07 08:06:36 -05:00
|
|
|
NewViewOutputMin = NewViewOutputMax - (LocalViewRangeMax - LocalViewRangeMin);
|
|
|
|
|
|
2015-07-27 11:14:09 -04:00
|
|
|
float InOffsetFraction = (NewViewOutputMin - LocalClampMin) / (LocalClampMax - LocalClampMin);
|
2015-01-07 08:06:36 -05:00
|
|
|
//if (InOffsetFraction + InThumbSizeFraction <= 1)
|
|
|
|
|
{
|
|
|
|
|
TimeSliderArgs.ViewRange.Set(TRange<float>(NewViewOutputMin, NewViewOutputMax));
|
|
|
|
|
Scrollbar->SetState(InOffsetFraction, InThumbSizeFraction);
|
|
|
|
|
}
|
2014-12-10 09:22:32 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-29 19:21:24 -05:00
|
|
|
void FVisualLoggerTimeSliderController::SetTimeRange(float NewViewOutputMin, float NewViewOutputMax)
|
2014-12-10 09:22:32 -05:00
|
|
|
{
|
|
|
|
|
TimeSliderArgs.ViewRange.Set(TRange<float>(NewViewOutputMin, NewViewOutputMax));
|
|
|
|
|
|
2015-07-27 11:14:09 -04:00
|
|
|
float LocalClampMin = TimeSliderArgs.ClampRange.Get().GetLowerBoundValue();
|
|
|
|
|
float LocalClampMax = TimeSliderArgs.ClampRange.Get().GetUpperBoundValue();
|
2014-12-10 09:22:32 -05:00
|
|
|
|
2015-07-27 11:14:09 -04:00
|
|
|
const float InOffsetFraction = (NewViewOutputMin - LocalClampMin) / (LocalClampMax - LocalClampMin);
|
|
|
|
|
const float InThumbSizeFraction = (NewViewOutputMax - NewViewOutputMin) / (LocalClampMax - LocalClampMin);
|
2014-12-10 09:22:32 -05:00
|
|
|
Scrollbar->SetState(InOffsetFraction, InThumbSizeFraction);
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-29 19:21:24 -05:00
|
|
|
void FVisualLoggerTimeSliderController::SetClampRange(float MinValue, float MaxValue)
|
2015-01-07 08:06:36 -05:00
|
|
|
{
|
|
|
|
|
TRange<float> LocalViewRange = TimeSliderArgs.ViewRange.Get();
|
2015-07-27 11:14:09 -04:00
|
|
|
float LocalClampMin = TimeSliderArgs.ClampRange.Get().GetLowerBoundValue();
|
|
|
|
|
float LocalClampMax = TimeSliderArgs.ClampRange.Get().GetUpperBoundValue();
|
2015-01-07 08:06:36 -05:00
|
|
|
const float CurrentDistance = LocalClampMax - LocalClampMin;
|
|
|
|
|
const float ZoomDelta = (LocalViewRange.GetUpperBoundValue() - LocalViewRange.GetLowerBoundValue()) / CurrentDistance;
|
|
|
|
|
|
|
|
|
|
MaxValue = MinValue + (MaxValue - MinValue < 2 ? CurrentDistance : MaxValue - MinValue);
|
|
|
|
|
|
2015-07-27 11:14:09 -04:00
|
|
|
TimeSliderArgs.ClampRange = TRange<float>(MinValue, MaxValue);
|
2015-01-07 08:06:36 -05:00
|
|
|
|
|
|
|
|
const float LocalViewRangeMin = FMath::Clamp(LocalViewRange.GetLowerBoundValue(), MinValue, MaxValue);
|
|
|
|
|
const float LocalViewRangeMax = FMath::Clamp(LocalViewRange.GetUpperBoundValue(), MinValue, MaxValue);
|
|
|
|
|
SetTimeRange(ZoomDelta >= 1 ? MinValue : LocalViewRangeMin, ZoomDelta >= 1 ? MaxValue : LocalViewRangeMax);
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-09 19:35:15 -05:00
|
|
|
FReply FVisualLoggerTimeSliderController::OnMouseWheel( SWidget& WidgetOwner, const FGeometry& MyGeometry, const FPointerEvent& MouseEvent )
|
2014-11-19 07:43:07 -05:00
|
|
|
{
|
|
|
|
|
FReply ReturnValue = FReply::Unhandled();;
|
|
|
|
|
|
2014-11-20 07:30:36 -05:00
|
|
|
if (MouseEvent.IsLeftShiftDown())
|
2014-11-19 07:43:07 -05:00
|
|
|
{
|
2015-05-20 10:04:17 -04:00
|
|
|
const float ZoomDelta = 0.025f * MouseEvent.GetWheelDelta();
|
2014-11-19 07:43:07 -05:00
|
|
|
TimeSliderArgs.CursorSize.Set(FMath::Clamp(TimeSliderArgs.CursorSize.Get() + ZoomDelta, 0.0f, 1.0f));
|
|
|
|
|
|
|
|
|
|
ReturnValue = FReply::Handled();
|
|
|
|
|
}
|
|
|
|
|
else if ( TimeSliderArgs.AllowZoom )
|
|
|
|
|
{
|
|
|
|
|
const float ZoomDelta = -0.1f * MouseEvent.GetWheelDelta();
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
TRange<float> LocalViewRange = TimeSliderArgs.ViewRange.Get();
|
|
|
|
|
float LocalViewRangeMax = LocalViewRange.GetUpperBoundValue();
|
|
|
|
|
float LocalViewRangeMin = LocalViewRange.GetLowerBoundValue();
|
|
|
|
|
const float OutputViewSize = LocalViewRangeMax - LocalViewRangeMin;
|
|
|
|
|
const float OutputChange = OutputViewSize * ZoomDelta;
|
|
|
|
|
|
|
|
|
|
float NewViewOutputMin = LocalViewRangeMin - (OutputChange * 0.5f);
|
|
|
|
|
float NewViewOutputMax = LocalViewRangeMax + (OutputChange * 0.5f);
|
|
|
|
|
|
|
|
|
|
if( FMath::Abs( OutputChange ) > 0.01f && NewViewOutputMin < NewViewOutputMax )
|
|
|
|
|
{
|
2015-07-27 11:14:09 -04:00
|
|
|
float LocalClampMin = TimeSliderArgs.ClampRange.Get().GetLowerBoundValue();
|
|
|
|
|
float LocalClampMax = TimeSliderArgs.ClampRange.Get().GetUpperBoundValue();
|
2014-11-19 07:43:07 -05:00
|
|
|
|
|
|
|
|
// Clamp the range if clamp values are set
|
2015-07-27 11:14:09 -04:00
|
|
|
if ( NewViewOutputMin < LocalClampMin )
|
2014-11-19 07:43:07 -05:00
|
|
|
{
|
2015-07-27 11:14:09 -04:00
|
|
|
NewViewOutputMin = LocalClampMin;
|
2014-11-19 07:43:07 -05:00
|
|
|
}
|
|
|
|
|
|
2015-07-27 11:14:09 -04:00
|
|
|
if ( NewViewOutputMax > LocalClampMax )
|
2014-11-19 07:43:07 -05:00
|
|
|
{
|
2015-07-27 11:14:09 -04:00
|
|
|
NewViewOutputMax = LocalClampMax;
|
2014-11-19 07:43:07 -05:00
|
|
|
}
|
|
|
|
|
|
2015-10-31 10:55:13 -04:00
|
|
|
TimeSliderArgs.OnViewRangeChanged.ExecuteIfBound(TRange<float>(NewViewOutputMin, NewViewOutputMax), EViewRangeInterpolation::Immediate);
|
2014-12-10 09:22:32 -05:00
|
|
|
if (Scrollbar.IsValid())
|
|
|
|
|
{
|
2015-07-27 11:14:09 -04:00
|
|
|
float InOffsetFraction = (NewViewOutputMin - LocalClampMin) / (LocalClampMax - LocalClampMin);
|
|
|
|
|
float InThumbSizeFraction = (NewViewOutputMax - NewViewOutputMin) / (LocalClampMax - LocalClampMin);
|
2014-12-10 09:22:32 -05:00
|
|
|
Scrollbar->SetState(InOffsetFraction, InThumbSizeFraction);
|
|
|
|
|
}
|
2014-11-19 07:43:07 -05:00
|
|
|
if( !TimeSliderArgs.ViewRange.IsBound() )
|
|
|
|
|
{
|
|
|
|
|
// The output is not bound to a delegate so we'll manage the value ourselves
|
|
|
|
|
TimeSliderArgs.ViewRange.Set( TRange<float>( NewViewOutputMin, NewViewOutputMax ) );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ReturnValue = FReply::Handled();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ReturnValue;
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-29 19:21:24 -05:00
|
|
|
int32 FVisualLoggerTimeSliderController::OnPaintSectionView( const FGeometry& AllottedGeometry, const FSlateRect& MyClippingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, bool bEnabled, bool bDisplayTickLines, bool bDisplayScrubPosition ) const
|
2014-11-19 07:43:07 -05:00
|
|
|
{
|
|
|
|
|
const ESlateDrawEffect::Type DrawEffects = bEnabled ? ESlateDrawEffect::None : ESlateDrawEffect::DisabledEffect;
|
|
|
|
|
|
|
|
|
|
TRange<float> LocalViewRange = TimeSliderArgs.ViewRange.Get();
|
|
|
|
|
float LocalScrubPosition = TimeSliderArgs.ScrubPosition.Get();
|
|
|
|
|
|
|
|
|
|
float ViewRange = LocalViewRange.Size<float>();
|
|
|
|
|
float PixelsPerInput = ViewRange > 0 ? AllottedGeometry.Size.X / ViewRange : 0;
|
|
|
|
|
float LinePos = (LocalScrubPosition - LocalViewRange.GetLowerBoundValue()) * PixelsPerInput;
|
|
|
|
|
|
|
|
|
|
FScrubRangeToScreen RangeToScreen( LocalViewRange, AllottedGeometry.Size );
|
|
|
|
|
|
|
|
|
|
if( bDisplayTickLines )
|
|
|
|
|
{
|
|
|
|
|
// Draw major tick lines in the section area
|
|
|
|
|
FDrawTickArgs Args;
|
|
|
|
|
Args.AllottedGeometry = AllottedGeometry;
|
|
|
|
|
Args.bMirrorLabels = false;
|
|
|
|
|
Args.bOnlyDrawMajorTicks = true;
|
|
|
|
|
Args.TickColor = FLinearColor( 0.3f, 0.3f, 0.3f, 0.3f );
|
|
|
|
|
Args.ClippingRect = MyClippingRect;
|
|
|
|
|
Args.DrawEffects = DrawEffects;
|
|
|
|
|
// Draw major ticks under sections
|
|
|
|
|
Args.StartLayer = LayerId-1;
|
|
|
|
|
// Draw the tick the entire height of the section area
|
|
|
|
|
Args.TickOffset = 0.0f;
|
|
|
|
|
Args.MajorTickHeight = AllottedGeometry.Size.Y;
|
|
|
|
|
|
|
|
|
|
DrawTicks( OutDrawElements, RangeToScreen, Args );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if( bDisplayScrubPosition )
|
|
|
|
|
{
|
|
|
|
|
// Draw cursor size
|
|
|
|
|
const float CursorHalfSize = TimeSliderArgs.CursorSize.Get() * 0.5f;
|
|
|
|
|
const float CursorHalfLength = AllottedGeometry.Size.X * CursorHalfSize;
|
|
|
|
|
FPaintGeometry CursorGeometry = AllottedGeometry.ToPaintGeometry(FVector2D(LinePos - CursorHalfLength, 0), FVector2D(2 * CursorHalfLength, AllottedGeometry.Size.Y));
|
|
|
|
|
|
|
|
|
|
FSlateDrawElement::MakeBox(
|
|
|
|
|
OutDrawElements,
|
2014-12-10 09:22:32 -05:00
|
|
|
++LayerId,
|
2014-11-19 07:43:07 -05:00
|
|
|
CursorGeometry,
|
|
|
|
|
CursorBackground,
|
|
|
|
|
MyClippingRect,
|
|
|
|
|
DrawEffects,
|
More Gamma Correction - Didn't catch this on the previous pass, apparently there was an implicit constructor allowing FLinearColor to FColor that was doing pow(2.2) gamma conversion inversion. Rather than leave the implicit constructor, I'm making it private and making people use ToFColor(true). Which is slightly more expensive, but performs the proper sRGB conversion. While fixing this, I found several terrible uses of the implicit constructor, when Hashing FLinearColors we were converting them to FColors first, when clearing FCanvas we were manually gamma correcting but leaving it as an FLinearColor, then implicitly converting to FColor, double gamma corrrecting. Neither of which should even be required as the RHI Clear command expects an FLinearColor. Additionally fixing a myriad of Slate widgets that were all doing FColor conversions needlessly only to convert back to FLinearColor when queuing slate draw commands.
#codereview nick.penwarden, martin.mittring, andrew.brown, gareth.martin
[CL 2593605 by Nick Darnell in Main branch]
2015-06-19 11:17:11 -04:00
|
|
|
FLinearColor::White.CopyWithNewOpacity(0.08f)
|
2014-11-19 07:43:07 -05:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Draw a line for the scrub position
|
|
|
|
|
TArray<FVector2D> LinePoints;
|
|
|
|
|
LinePoints.AddUninitialized(2);
|
|
|
|
|
LinePoints[0] = FVector2D( 1.0f, 0.0f );
|
|
|
|
|
LinePoints[1] = FVector2D( 1.0f, FMath::RoundToFloat( AllottedGeometry.Size.Y ) );
|
|
|
|
|
|
|
|
|
|
FSlateDrawElement::MakeLines(
|
|
|
|
|
OutDrawElements,
|
2014-12-10 09:22:32 -05:00
|
|
|
++LayerId,
|
2014-11-19 07:43:07 -05:00
|
|
|
AllottedGeometry.ToPaintGeometry( FVector2D(LinePos, 0.0f ), FVector2D(1.0f,1.0f) ),
|
|
|
|
|
LinePoints,
|
|
|
|
|
MyClippingRect,
|
|
|
|
|
DrawEffects,
|
More Gamma Correction - Didn't catch this on the previous pass, apparently there was an implicit constructor allowing FLinearColor to FColor that was doing pow(2.2) gamma conversion inversion. Rather than leave the implicit constructor, I'm making it private and making people use ToFColor(true). Which is slightly more expensive, but performs the proper sRGB conversion. While fixing this, I found several terrible uses of the implicit constructor, when Hashing FLinearColors we were converting them to FColors first, when clearing FCanvas we were manually gamma correcting but leaving it as an FLinearColor, then implicitly converting to FColor, double gamma corrrecting. Neither of which should even be required as the RHI Clear command expects an FLinearColor. Additionally fixing a myriad of Slate widgets that were all doing FColor conversions needlessly only to convert back to FLinearColor when queuing slate draw commands.
#codereview nick.penwarden, martin.mittring, andrew.brown, gareth.martin
[CL 2593605 by Nick Darnell in Main branch]
2015-06-19 11:17:11 -04:00
|
|
|
FLinearColor::White.CopyWithNewOpacity(0.39f),
|
2014-11-19 07:43:07 -05:00
|
|
|
false
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return LayerId;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#undef LOCTEXT_NAMESPACE
|