// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "Containers/Array.h" #include "Containers/Map.h" #include "CoreMinimal.h" #include "Evaluation/MovieSceneTimeTransform.h" #include "HAL/Platform.h" #include "Internationalization/Text.h" #include "Misc/AssertionMacros.h" #include "Misc/DateTime.h" #include "Misc/FrameNumber.h" #include "Misc/FrameRate.h" #include "Misc/FrameTime.h" #include "Misc/InlineValue.h" #include "Misc/Optional.h" #include "MovieSceneTrack.h" #include "Templates/Function.h" #include "Templates/UnrealTemplate.h" #include "Templates/UnrealTypeTraits.h" #include "UObject/NameTypes.h" #include "UObject/Object.h" #include "UObject/UnrealNames.h" #if WITH_EDITOR namespace MovieSceneClipboard { template static FName GetKeyTypeName() { // Use the template type here to stop the compiler generating the code unless it's actually used static_assert(!std::is_same_v, "This function must be specialized to use with the specified type"); return NAME_None; } } class UMovieSceneTrack; /** * A key in the clipboard representing a time and a value of a specific type * Client code must define MovieSceneClipboard::GetKeyTypeName<> in order to use a type with this class. * In general, keys are stored relaative to the minumum key-time in the clipboard, or some arbitrary time. * This cardinal time is stored with the clipboard environment. */ class FMovieSceneClipboardKey { public: /** Templated constructor accepting a specific value type */ template FMovieSceneClipboardKey(FFrameNumber InTime, T InValue) : Time(InTime) , Data(TKey(MoveTemp(InValue))) { } /** Copy construction/assignment */ MOVIESCENE_API FMovieSceneClipboardKey(const FMovieSceneClipboardKey& In); MOVIESCENE_API FMovieSceneClipboardKey& operator=(const FMovieSceneClipboardKey& In); /** Get the time at which this key is defined */ MOVIESCENE_API FFrameNumber GetTime() const; /** Set the time at which this key is defined */ MOVIESCENE_API void SetTime(FFrameNumber InTime); /** * Get the value of this key as the specified type. * * @return the key as the templated type, or a default-constructed T where no conversion is possible */ template T GetValue() const { T Default = T(); TryGetValue(Default); return Default; } /** * Attempt to get the value of this key as the specified type. * * @param InValue Reference to receive the value * @return true if the value was set, or false if no conversion between the source and T was possible */ template bool TryGetValue(T& Value) const { if (!Data.IsValid()) { return false; } // Check for same type if (Data->GetTypeName() == MovieSceneClipboard::GetKeyTypeName()) { Value = static_cast&>(Data.GetValue()).Value; return true; } // Check for conversion possibility FConversionFunction* ConversionFunction = Data->FindConversionTo(MovieSceneClipboard::GetKeyTypeName()); if (ConversionFunction) { FMovieSceneClipboardKey Key = ConversionFunction->operator()(*this); Value = static_cast&>(Key.Data.GetValue()).Value; return true; } return false; } private: typedef TFunction FConversionFunction; /** Abstract base class for all key types */ struct IKey { virtual ~IKey() {} virtual void CopyTo(TInlineValue& OutDest) const = 0; virtual FConversionFunction* FindConversionTo(FName DestinationTypeName) const = 0; virtual FName GetTypeName() const = 0; }; /** Implementation of any key-value type */ template struct TKey : IKey { TKey(T InValue) : Value(MoveTemp(InValue)) {} /** Find a conversion from this type to the specified destination type */ virtual FConversionFunction* FindConversionTo(FName DestinationTypeName) const { TMap* ConversionBucket = ConversionMap.Find(MovieSceneClipboard::GetKeyTypeName()); if (ConversionBucket) { return ConversionBucket->Find(DestinationTypeName); } return nullptr; } /** Get the name of this value type */ virtual FName GetTypeName() const { return MovieSceneClipboard::GetKeyTypeName(); } /** Copy this value to another destination ptr */ virtual void CopyTo(TInlineValue& OutDest) const { OutDest = TKey(Value); } /** The actual value */ T Value; }; /** The time that this key is defined at */ FFrameNumber Time; /** Type-erased bytes containing the key's value */ TInlineValue Data; public: /** Define a conversion from one type to another type */ template static void DefineConversion(TFunction InFunction) { auto Facade = [=](const FMovieSceneClipboardKey& InKey) -> FMovieSceneClipboardKey { const TKey& TypedKey = static_cast&>(InKey.Data.GetValue()); return FMovieSceneClipboardKey(InKey.GetTime(), InFunction(TypedKey.Value)); }; ConversionMap .Add(MovieSceneClipboard::GetKeyTypeName()) .Add(MovieSceneClipboard::GetKeyTypeName(), Facade); } private: /** Static map of conversions between types */ static MOVIESCENE_API TMap> ConversionMap; }; /** Container for a collection of keys arranged in a track. */ class FMovieSceneClipboardKeyTrack { public: /** Create a key track that wraps the specified key type */ template static FMovieSceneClipboardKeyTrack Create(FName InName, FFrameNumber KeyOffset = 0) { FMovieSceneClipboardKeyTrack Track; Track.TypeName = MovieSceneClipboard::GetKeyTypeName(); Track.Name = MoveTemp(InName); Track.KeyOffset = KeyOffset; return Track; } public: /** Move construction/assignment */ MOVIESCENE_API FMovieSceneClipboardKeyTrack(FMovieSceneClipboardKeyTrack&& In); MOVIESCENE_API FMovieSceneClipboardKeyTrack& operator=(FMovieSceneClipboardKeyTrack&& In); /** Copy construction/assignment */ MOVIESCENE_API FMovieSceneClipboardKeyTrack(const FMovieSceneClipboardKeyTrack& In); MOVIESCENE_API FMovieSceneClipboardKeyTrack& operator=(const FMovieSceneClipboardKeyTrack& In); /** Check the type of the keys contained within this track */ template bool IsKeyOfType() const { return TypeName == MovieSceneClipboard::GetKeyTypeName(); } /** Add a key of the specified type to this track. KeyType must match the type this track was constructed with */ template void AddKey(FFrameNumber Time, KeyType Value) { Time -= KeyOffset; checkf(IsKeyOfType(), TEXT("Unable to add a key of a different value type to the track")); Keys.Add(FMovieSceneClipboardKey(Time, MoveTemp(Value))); } /** * Iterate the keys contained within this track * @param Iter Predicate function to call for every key in this track. * return true to continue iteration, false to abort */ bool IterateKeys(TFunctionRef Iter) const { for (const FMovieSceneClipboardKey& Key : Keys) { if (!Iter(Key)) { return false; } } return true; } bool IterateKeys(TFunctionRef Iter) { for (FMovieSceneClipboardKey& Key : Keys) { if (!Iter(Key)) { return false; } } return true; } /** Get the name of this track */ MOVIESCENE_API const FName& GetName() const; private: FMovieSceneClipboardKeyTrack() {} /** Collection of keys contained within this track */ TArray Keys; /** Type name for the value of the keys this track supports */ FName TypeName; /** Generic name of this track (generally the name of a key area within a track e.g. Location.X) */ FName Name; /** Optional key offset that is applied to all keys added to the clipboard */ FFrameNumber KeyOffset; }; /** Structure representing an environment a clipboard applies to */ struct FMovieSceneClipboardEnvironment { FMovieSceneClipboardEnvironment() : CardinalTime(0) , DateTime(FDateTime::UtcNow()) {} /** The cardinal time for a copy-paste operation. */ FFrameTime CardinalTime; /** The time transform for the copy-paste operation. */ FMovieSceneTimeTransform TimeTransform; /** The date/time at which the copy operation was performed */ FDateTime DateTime; /** The tick resolution within which all clipboard data is stored */ FFrameRate TickResolution; }; /** A clipboard representing serializable copied data for a movie scene */ class FMovieSceneClipboard { public: /** Default Constructor */ MOVIESCENE_API FMovieSceneClipboard(); /** Move construction/assignment */ MOVIESCENE_API FMovieSceneClipboard(FMovieSceneClipboard&& In); MOVIESCENE_API FMovieSceneClipboard& operator=(FMovieSceneClipboard&& In); /** Copy construction/assignment */ MOVIESCENE_API FMovieSceneClipboard(const FMovieSceneClipboard& In); MOVIESCENE_API FMovieSceneClipboard& operator=(const FMovieSceneClipboard& In); /** Get the key track groups that were copied */ MOVIESCENE_API const TArray>& GetKeyTrackGroups() const; /** Get a text description of this clipboard for display on UI */ MOVIESCENE_API FText GetDisplayText() const; /** Get the environment to which this clipboard relates */ MOVIESCENE_API const FMovieSceneClipboardEnvironment& GetEnvironment() const; /** Get the environment to which this clipboard relates */ MOVIESCENE_API FMovieSceneClipboardEnvironment& GetEnvironment(); private: friend class FMovieSceneClipboardBuilder; /** The environment to which this clipboard relates */ FMovieSceneClipboardEnvironment Environment; /** Collection of groups of key tracks that have been copied */ TArray> KeyTrackGroups; }; /** Class responsible for building a clipboard for a movie scene */ class FMovieSceneClipboardBuilder { public: /** Optional key offset that is applied to all keys added to the clipboard */ FFrameNumber KeyOffset; /** Generate a clipboard for the current state of this builder, resetting the builder back to its default state */ MOVIESCENE_API FMovieSceneClipboard Commit(TOptional CopyRelativeTo); /** * Find or add a key track. Key tracks are group primarily by MovieSceneTrack instance, then by name * * @param InName The name of this track (e.g. 'R', 'Location.X', etc) * @param ParentTrack The parent track within which this track resides * @return A key track which can be populated with keys */ template FMovieSceneClipboardKeyTrack& FindOrAddKeyTrack(FName InName, UMovieSceneTrack& ParentTrack) { check(IsValid(&ParentTrack)); TArray& Tracks = TrackIndex.FindOrAdd(&ParentTrack); FMovieSceneClipboardKeyTrack* Existing = Tracks.FindByPredicate([&](const FMovieSceneClipboardKeyTrack& In){ return In.GetName() == InName; }); if (Existing) { return *Existing; } int32 Index = Tracks.Num(); Tracks.Add(FMovieSceneClipboardKeyTrack::Create(MoveTemp(InName))); return Tracks.GetData()[Index]; } private: /** Map of key tracks for a given moviescene track ptr */ TMap> TrackIndex; }; /** Helper functions for defining conversions between types */ namespace MovieSceneClipboard { template struct TImplicitConversionFacade { static To Cast(const From& Value) { return Value; } }; template struct TImplicitConversionFacade { static bool Cast(const From& Value) { return !!Value; } }; template void DefineExplicitConversion(TFunction InConversion) { FMovieSceneClipboardKey::DefineConversion(InConversion); } template void DefineImplicitConversion() { DefineExplicitConversion(&TImplicitConversionFacade::Cast); } } #endif