Files
UnrealEngine/Engine/Source/Developer/AnimationWidgets/Public/SAdvancedTransformInputBox.h
2025-05-18 13:04:45 +08:00

1672 lines
56 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
// HEADER_UNIT_SKIP - Bad include
#include "SAdvancedRotationInputBox.h"
#include "Widgets/Input/SComboButton.h"
#include "Widgets/Input/SButton.h"
#include "Styling/AppStyle.h"
#include "Textures/SlateIcon.h"
#include "Framework/Application/SlateApplication.h"
#include "Framework/Commands/UIAction.h"
#include "Framework/Commands/UICommandInfo.h"
#if WITH_EDITOR
#include "DetailCategoryBuilder.h"
#include "DetailLayoutBuilder.h"
#include "IDetailChildrenBuilder.h"
#include "DetailWidgetRow.h"
#include "IDetailGroup.h"
#include "Framework/MultiBox/MultiBoxBuilder.h"
#endif
namespace ESlateTransformComponent
{
enum Type : int
{
Location,
Rotation,
Scale,
Max
};
}
/**
* Generic Transform Slate control
*/
template<typename TransformType = FTransform, typename NumericType = FVector::FReal>
class SAdvancedTransformInputBox : public SCompoundWidget
{
public:
typedef SNumericVectorInputBox<NumericType, UE::Math::TVector<NumericType>, 3> SNumericVectorInputBox3;
/** Delegate for notification for a transform value change */
DECLARE_DELEGATE_OneParam(FOnTransformChanged, TransformType);
/** Delegate for notification for a transform value commit */
DECLARE_DELEGATE_TwoParams(FOnTransformCommitted, TransformType, ETextCommit::Type);
/** Delegate to retrieve a numerictype value */
DECLARE_DELEGATE_RetVal_ThreeParams(TOptional<NumericType>, FOnGetNumericValue, ESlateTransformComponent::Type, ESlateRotationRepresentation::Type, ESlateTransformSubComponent::Type);
/** Notification for numerictype value change */
DECLARE_DELEGATE_FourParams(FOnNumericValueChanged, ESlateTransformComponent::Type, ESlateRotationRepresentation::Type, ESlateTransformSubComponent::Type, NumericType);
/** Notification for numerictype value committed */
DECLARE_DELEGATE_FiveParams(FOnNumericValueCommitted, ESlateTransformComponent::Type, ESlateRotationRepresentation::Type, ESlateTransformSubComponent::Type, NumericType, ETextCommit::Type);
/** Notification for begin slider movement */
DECLARE_DELEGATE_ThreeParams(FOnBeginSliderMovement, ESlateTransformComponent::Type, ESlateRotationRepresentation::Type, ESlateTransformSubComponent::Type);
/** Notification for end slider movement */
DECLARE_DELEGATE_FourParams(FOnEndSliderMovement, ESlateTransformComponent::Type, ESlateRotationRepresentation::Type, ESlateTransformSubComponent::Type, NumericType);
/** Delegate to retrieve relative / world state */
DECLARE_DELEGATE_RetVal_OneParam(bool, FOnGetIsComponentRelative, ESlateTransformComponent::Type);
/** Notification for a relative / world state change */
DECLARE_DELEGATE_TwoParams(FOnIsComponentRelativeChanged, ESlateTransformComponent::Type, bool /* is relative? */);
/** Notification for the rotation representation change */
DECLARE_DELEGATE_OneParam(FOnRotationRepresentationChanged, ESlateRotationRepresentation::Type);
/** Notification for the scale lock change */
DECLARE_DELEGATE_OneParam(FOnScaleLockChanged, bool /* is locked? */);
/** Delegate to retrieve toggle checkstate */
DECLARE_DELEGATE_RetVal_ThreeParams(ECheckBoxState, FOnGetToggleChecked, ESlateTransformComponent::Type, ESlateRotationRepresentation::Type, ESlateTransformSubComponent::Type);
/** Notification for toggle checkstate change */
DECLARE_DELEGATE_FourParams(FOnToggleChanged, ESlateTransformComponent::Type, ESlateRotationRepresentation::Type, ESlateTransformSubComponent::Type, ECheckBoxState);
/** Delegate to fire when a row or the whole transform (type == max) is being copied to the clipboard */
DECLARE_DELEGATE_OneParam(FOnCopyToClipboard, ESlateTransformComponent::Type);
/** Delegate to fire when a row or the whole transform (type == max) is being pasted into from the clipboard */
DECLARE_DELEGATE_OneParam(FOnPasteFromClipboard, ESlateTransformComponent::Type);
/** Delegate to fire when a row or the whole transform (type == max) needs to be reset to its default */
DECLARE_DELEGATE_OneParam(FOnResetToDefault, ESlateTransformComponent::Type);
/** Delegate to determine if the row or the whole transform (type == max) differs from its default */
DECLARE_DELEGATE_RetVal_OneParam(bool, FDiffersFromDefault, ESlateTransformComponent::Type);
SLATE_BEGIN_ARGS( SAdvancedTransformInputBox )
: _ConstructLocation(true)
, _ConstructRotation(true)
, _ConstructScale(true)
, _UseQuaternionForRotation(false)
, _RotationRepresentation(TSharedPtr<ESlateRotationRepresentation::Type>( new ESlateRotationRepresentation::Type(ESlateRotationRepresentation::Rotator)))
, _bColorAxisLabels(true)
, _ShowInlineLabels(false)
, _LocationLabel(NSLOCTEXT("SAdvancedTransformInputBox", "Location", "Location"))
, _RotationLabel(NSLOCTEXT("SAdvancedTransformInputBox", "Rotation", "Rotation"))
, _ScaleLabel(NSLOCTEXT("SAdvancedTransformInputBox", "Scale", "Scale"))
, _LabelPadding( FMargin( 0.f,0.f,6.f,0.f ) )
, _Font(FAppStyle::Get().GetFontStyle("NormalFont"))
, _AllowSpin(true)
, _LocationSpinDelta(0.01f)
, _ScaleSpinDelta(0.001f)
, _AllowEditRotationRepresentation(true)
, _DisplayScaleLock(true)
, _IsScaleLocked(TSharedPtr<bool>(new bool(false)))
, _DisplayRelativeWorld(false)
, _DisplayToggle( false )
, _TogglePadding( FMargin( 1.f,0.f,1.f,0.f ) )
, _PreventThrottling(false)
{}
/** Whether or not to construct the location component */
SLATE_ARGUMENT( bool, ConstructLocation )
/** Whether or not to construct the rotation component */
SLATE_ARGUMENT( bool, ConstructRotation )
/** Whether or not to construct the scale component */
SLATE_ARGUMENT( bool, ConstructScale )
/** Whether to use the quaternion is the primary rotation representation or the rotator (default) */
SLATE_ARGUMENT( bool, UseQuaternionForRotation )
/** Optional representation as transform */
SLATE_ATTRIBUTE( TOptional<TransformType>, Transform )
/** Optional delegate to notify a change in the transform */
SLATE_EVENT( FOnTransformChanged, OnTransformChanged )
/** Optional delegate to notify a commit in the transform */
SLATE_EVENT( FOnTransformCommitted, OnTransformCommitted )
/** Representation of the rotation */
SLATE_ARGUMENT( TSharedPtr<ESlateRotationRepresentation::Type>, RotationRepresentation )
/** Delegate to notify a rotation representation change */
SLATE_EVENT( FOnRotationRepresentationChanged, OnRotationRepresentationChanged )
/** Delegate to retrieve a value */
SLATE_EVENT( FOnGetNumericValue, OnGetNumericValue )
/** Delegate to notify a value change */
SLATE_EVENT( FOnNumericValueChanged, OnNumericValueChanged )
/** Delegate to notify a committed value */
SLATE_EVENT( FOnNumericValueCommitted, OnNumericValueCommitted )
/** Delegate to notify a begin slider movement */
SLATE_EVENT(FOnBeginSliderMovement, OnBeginSliderMovement)
/** Delegate to notify a end slider movement */
SLATE_EVENT(FOnEndSliderMovement, OnEndSliderMovement)
/** Should the axis labels be colored */
SLATE_ARGUMENT( bool, bColorAxisLabels )
/** Should we show labels inline next to all input boxes */
SLATE_ARGUMENT( bool, ShowInlineLabels )
/** The label to use for the location component */
SLATE_ARGUMENT( FText, LocationLabel )
/** The label to use for the rotation component */
SLATE_ARGUMENT( FText, RotationLabel )
/** The label to use for the scale component */
SLATE_ARGUMENT( FText, ScaleLabel )
/** Padding around the label checkbox */
SLATE_ARGUMENT( FMargin, LabelPadding )
/** Font to use for the text in this box */
SLATE_ATTRIBUTE( FSlateFontInfo, Font )
/** Whether or not values can be spun or if they should be typed in */
SLATE_ARGUMENT( bool, AllowSpin )
/** The delta amount to apply, per pixel, when a location spinner is dragged. */
SLATE_ATTRIBUTE( NumericType, LocationSpinDelta )
/** The delta amount to apply, per pixel, when a scale spinner is dragged. */
SLATE_ATTRIBUTE( NumericType, ScaleSpinDelta )
/** Whether or not to display the rotation representation picker in the label */
SLATE_ARGUMENT( bool, AllowEditRotationRepresentation )
/** Whether or not to display the lock scale button */
SLATE_ARGUMENT( bool, DisplayScaleLock )
/** The state of the scale lock */
SLATE_ARGUMENT( TSharedPtr<bool>, IsScaleLocked )
/** Delegate to notify a scale lock change */
SLATE_EVENT( FOnScaleLockChanged, OnScaleLockChanged )
/** Whether or not to display the per component buttons to switch between relative and world */
SLATE_ARGUMENT( bool, DisplayRelativeWorld )
/** Delegate to retrieve a relative / world state per component */
SLATE_EVENT( FOnGetIsComponentRelative, OnGetIsComponentRelative )
/** Delegate to notify a relative / world state change */
SLATE_EVENT( FOnIsComponentRelativeChanged, OnIsComponentRelativeChanged )
/** Whether or not to include a toggle checkbox to the left of the widget */
SLATE_ARGUMENT( bool, DisplayToggle )
/** Delegate to retrieve a toggle state */
SLATE_EVENT( FOnGetToggleChecked, OnGetToggleChecked )
/** Delegate to notify a toggle state change */
SLATE_EVENT( FOnToggleChanged, OnToggleChanged )
/** Padding around the toggle checkbox */
SLATE_ARGUMENT( FMargin, TogglePadding )
/** Delegate to react to copy to clipboard */
SLATE_EVENT( FOnCopyToClipboard, OnCopyToClipboard )
/** Delegate to react to paste from clipboard */
SLATE_EVENT( FOnPasteFromClipboard, OnPasteFromClipboard )
/** Delegate to react to reset to default */
SLATE_EVENT( FOnResetToDefault, OnResetToDefault )
/** Delegate to determine if a row (or the whole transform) differs from its default */
SLATE_EVENT( FDiffersFromDefault, DiffersFromDefault )
/** If refresh requests for the viewport should happen for all value changes **/
SLATE_ARGUMENT(bool, PreventThrottling)
SLATE_END_ARGS()
/**
* Construct this widget
*
* @param InArgs The declaration data for this widget
*/
void Construct( const FArguments& InArgs )
{
TSharedRef<SVerticalBox> VerticalBox = SNew(SVerticalBox);
if(InArgs._ConstructLocation)
{
VerticalBox->AddSlot()
[
ConstructWidget(InArgs, ESlateTransformComponent::Location)
];
}
if(InArgs._ConstructRotation)
{
VerticalBox->AddSlot()
[
ConstructWidget(InArgs, ESlateTransformComponent::Rotation)
];
}
if(InArgs._ConstructScale)
{
VerticalBox->AddSlot()
[
ConstructWidget(InArgs, ESlateTransformComponent::Scale)
];
}
ChildSlot
[
VerticalBox
];
}
/**
* Construct an input widget
*/
static TSharedRef<SWidget> ConstructWidget(const FArguments& InArgs, ESlateTransformComponent::Type InComponent)
{
SHorizontalBox::FArguments BoxArgs;
((FSlateBaseNamedArgs&)BoxArgs) = (FSlateBaseNamedArgs)InArgs;
TSharedRef<SHorizontalBox> HorizontalBox = SArgumentNew(BoxArgs, SHorizontalBox);
if(InArgs._ShowInlineLabels)
{
HorizontalBox->AddSlot()
.HAlign(HAlign_Left)
.VAlign(VAlign_Center)
.Padding(InArgs._LabelPadding)
.AutoWidth()
[
ConstructLabel(InArgs, InComponent)
];
}
TSharedRef<SWidget> InputWidget = SNullWidget::NullWidget;
TAttribute<TOptional<TransformType>> Transform = InArgs._Transform;
FOnTransformChanged OnTransformChanged = InArgs._OnTransformChanged;
FOnTransformCommitted OnTransformCommitted = InArgs._OnTransformCommitted;
FOnGetNumericValue OnGetNumericValue = InArgs._OnGetNumericValue;
FOnNumericValueChanged OnNumericValueChanged = InArgs._OnNumericValueChanged;
FOnNumericValueCommitted OnNumericValueCommitted = InArgs._OnNumericValueCommitted;
FOnBeginSliderMovement OnBeginSliderMovement = InArgs._OnBeginSliderMovement;
FOnEndSliderMovement OnEndSliderMovement = InArgs._OnEndSliderMovement;
FOnGetToggleChecked OnGetToggleChecked = InArgs._OnGetToggleChecked;
FOnToggleChanged OnToggleChanged = InArgs._OnToggleChanged;
FOnRotationRepresentationChanged OnRotationRepresentationChanged = InArgs._OnRotationRepresentationChanged;
const bool UseQuaternionForRotation = InArgs._UseQuaternionForRotation;
auto OnGetLocation = [Transform, OnGetNumericValue]() -> TOptional<FVector>
{
if(Transform.IsBound())
{
TOptional<TransformType> Xfo = Transform.Get();
if(Xfo.IsSet())
{
return Xfo->GetLocation();
}
}
if(OnGetNumericValue.IsBound())
{
FVector Location = FVector::OneVector;
Location.X = OnGetNumericValue.Execute(ESlateTransformComponent::Location, ESlateRotationRepresentation::EulerXYZ, ESlateTransformSubComponent::X).Get(Location.X);
Location.Y = OnGetNumericValue.Execute(ESlateTransformComponent::Location, ESlateRotationRepresentation::EulerXYZ, ESlateTransformSubComponent::Y).Get(Location.Y);
Location.Z = OnGetNumericValue.Execute(ESlateTransformComponent::Location, ESlateRotationRepresentation::EulerXYZ, ESlateTransformSubComponent::Z).Get(Location.Z);
return Location;
}
return TOptional<FVector>();
};
auto OnGetRotator = [Transform, OnGetNumericValue]() -> TOptional<FRotator>
{
if(Transform.IsBound())
{
TOptional<TransformType> Xfo = Transform.Get();
if(Xfo.IsSet())
{
return Xfo->Rotator();
}
}
if(OnGetNumericValue.IsBound())
{
FRotator Rotator = FRotator::ZeroRotator;
Rotator.Roll = OnGetNumericValue.Execute(ESlateTransformComponent::Rotation, ESlateRotationRepresentation::Rotator, ESlateTransformSubComponent::Roll).Get(Rotator.Roll);
Rotator.Pitch = OnGetNumericValue.Execute(ESlateTransformComponent::Rotation, ESlateRotationRepresentation::Rotator, ESlateTransformSubComponent::Pitch).Get(Rotator.Pitch);
Rotator.Yaw = OnGetNumericValue.Execute(ESlateTransformComponent::Rotation, ESlateRotationRepresentation::Rotator, ESlateTransformSubComponent::Yaw).Get(Rotator.Yaw);
return Rotator;
}
return TOptional<FRotator>();
};
auto OnGetQuaternion = [Transform, OnGetNumericValue]() -> TOptional<FQuat>
{
if(Transform.IsBound())
{
TOptional<TransformType> Xfo = Transform.Get();
if(Xfo.IsSet())
{
return Xfo->GetRotation().GetNormalized();
}
}
if(OnGetNumericValue.IsBound())
{
FQuat Quat = FQuat::Identity;
Quat.X = OnGetNumericValue.Execute(ESlateTransformComponent::Rotation, ESlateRotationRepresentation::Quaternion, ESlateTransformSubComponent::X).Get(Quat.X);
Quat.Y = OnGetNumericValue.Execute(ESlateTransformComponent::Rotation, ESlateRotationRepresentation::Quaternion, ESlateTransformSubComponent::Y).Get(Quat.Y);
Quat.Z = OnGetNumericValue.Execute(ESlateTransformComponent::Rotation, ESlateRotationRepresentation::Quaternion, ESlateTransformSubComponent::Z).Get(Quat.Z);
Quat.W = OnGetNumericValue.Execute(ESlateTransformComponent::Rotation, ESlateRotationRepresentation::Quaternion, ESlateTransformSubComponent::W).Get(Quat.W);
return Quat.GetNormalized();
}
return TOptional<FQuat>();
};
auto OnGetScale = [Transform, OnGetNumericValue]() -> TOptional<FVector>
{
if(Transform.IsBound())
{
TOptional<TransformType> Xfo = Transform.Get();
if(Xfo.IsSet())
{
return Xfo->GetScale3D();
}
}
if(OnGetNumericValue.IsBound())
{
FVector Scale = FVector::OneVector;
Scale.X = OnGetNumericValue.Execute(ESlateTransformComponent::Scale, ESlateRotationRepresentation::EulerXYZ, ESlateTransformSubComponent::X).Get(Scale.X);
Scale.Y = OnGetNumericValue.Execute(ESlateTransformComponent::Scale, ESlateRotationRepresentation::EulerXYZ, ESlateTransformSubComponent::Y).Get(Scale.Y);
Scale.Z = OnGetNumericValue.Execute(ESlateTransformComponent::Scale, ESlateRotationRepresentation::EulerXYZ, ESlateTransformSubComponent::Z).Get(Scale.Z);
return Scale;
}
return TOptional<FVector>();
};
auto OnGetVector = [OnGetLocation, OnGetScale](ESlateTransformComponent::Type InComponent) -> TOptional<FVector>
{
return (InComponent == ESlateTransformComponent::Location) ? OnGetLocation() : OnGetScale();
};
auto OnGetTransform = [OnGetLocation, OnGetQuaternion, OnGetRotator, OnGetScale, UseQuaternionForRotation]() -> TransformType
{
TransformType Result = TransformType::Identity;
Result.SetLocation(OnGetLocation().Get(Result.GetLocation()));
Result.SetScale3D(OnGetScale().Get(Result.GetScale3D()));
if(UseQuaternionForRotation)
{
Result.SetRotation(OnGetQuaternion().Get(Result.GetRotation()));
}
else
{
const FRotator Rotator = OnGetRotator().Get(Result.Rotator());
Result = TransformType(Rotator, Result.GetLocation(), Result.GetScale3D());
}
Result.NormalizeRotation();
return Result;
};
switch(InComponent)
{
case ESlateTransformComponent::Location:
case ESlateTransformComponent::Scale:
{
TAttribute<TOptional<NumericType>> XAttribute, YAttribute, ZAttribute;
TAttribute<TOptional<FVector>> Vector3Attribute;
if(InArgs._OnGetNumericValue.IsBound())
{
XAttribute = TAttribute<TOptional<NumericType>>::CreateLambda(
[OnGetNumericValue, OnGetVector, InComponent]() -> TOptional<NumericType>
{
return OnGetNumericValue.Execute(InComponent, ESlateRotationRepresentation::EulerXYZ, ESlateTransformSubComponent::X);
});
YAttribute = TAttribute<TOptional<NumericType>>::CreateLambda(
[OnGetNumericValue, OnGetVector, InComponent]() -> TOptional<NumericType>
{
return OnGetNumericValue.Execute(InComponent, ESlateRotationRepresentation::EulerXYZ, ESlateTransformSubComponent::Y);
});
ZAttribute = TAttribute<TOptional<NumericType>>::CreateLambda(
[OnGetNumericValue, OnGetVector, InComponent]() -> TOptional<NumericType>
{
return OnGetNumericValue.Execute(InComponent, ESlateRotationRepresentation::EulerXYZ, ESlateTransformSubComponent::Z);
});
}
if(InArgs._Transform.IsBound() || InArgs._Transform.IsSet())
{
Vector3Attribute = TAttribute<TOptional<FVector>>::CreateLambda(
[OnGetVector, InComponent]() -> TOptional<FVector>
{
return OnGetVector(InComponent);
});
}
typename SNumericVectorInputBox3::FOnNumericValueChanged XChanged, YChanged, ZChanged;
if(OnNumericValueChanged.IsBound())
{
XChanged = SNumericVectorInputBox3::FOnNumericValueChanged::CreateLambda(
[OnNumericValueChanged, InComponent](NumericType InValue)
{
OnNumericValueChanged.Execute(InComponent, ESlateRotationRepresentation::EulerXYZ, ESlateTransformSubComponent::X, InValue);
}
);
YChanged = SNumericVectorInputBox3::FOnNumericValueChanged::CreateLambda(
[OnNumericValueChanged, InComponent](NumericType InValue)
{
OnNumericValueChanged.Execute(InComponent, ESlateRotationRepresentation::EulerXYZ, ESlateTransformSubComponent::Y, InValue);
}
);
ZChanged = SNumericVectorInputBox3::FOnNumericValueChanged::CreateLambda(
[OnNumericValueChanged, InComponent](NumericType InValue)
{
OnNumericValueChanged.Execute(InComponent, ESlateRotationRepresentation::EulerXYZ, ESlateTransformSubComponent::Z, InValue);
}
);
}
FSimpleDelegate XBeginSlide, YBeginSlide, ZBeginSlide;
if(OnBeginSliderMovement.IsBound())
{
XBeginSlide = FSimpleDelegate::CreateLambda(
[OnBeginSliderMovement, InComponent]()
{
OnBeginSliderMovement.Execute(InComponent, ESlateRotationRepresentation::EulerXYZ, ESlateTransformSubComponent::X);
}
);
YBeginSlide = FSimpleDelegate::CreateLambda(
[OnBeginSliderMovement, InComponent]()
{
OnBeginSliderMovement.Execute(InComponent, ESlateRotationRepresentation::EulerXYZ, ESlateTransformSubComponent::Y);
}
);
ZBeginSlide = FSimpleDelegate::CreateLambda(
[OnBeginSliderMovement, InComponent]()
{
OnBeginSliderMovement.Execute(InComponent, ESlateRotationRepresentation::EulerXYZ, ESlateTransformSubComponent::Z);
}
);
}
typename SNumericVectorInputBox3::FOnNumericValueChanged XEndSlide, YEndSlide, ZEndSlide;
if(OnEndSliderMovement.IsBound())
{
XEndSlide = SNumericVectorInputBox3::FOnNumericValueChanged::CreateLambda(
[OnEndSliderMovement, InComponent](NumericType InValue)
{
OnEndSliderMovement.Execute(InComponent, ESlateRotationRepresentation::EulerXYZ, ESlateTransformSubComponent::X, InValue);
}
);
YEndSlide = SNumericVectorInputBox3::FOnNumericValueChanged::CreateLambda(
[OnEndSliderMovement, InComponent](NumericType InValue)
{
OnEndSliderMovement.Execute(InComponent, ESlateRotationRepresentation::EulerXYZ, ESlateTransformSubComponent::Y, InValue);
}
);
ZEndSlide = SNumericVectorInputBox3::FOnNumericValueChanged::CreateLambda(
[OnEndSliderMovement, InComponent](NumericType InValue)
{
OnEndSliderMovement.Execute(InComponent, ESlateRotationRepresentation::EulerXYZ, ESlateTransformSubComponent::Z, InValue);
}
);
}
typename SNumericVectorInputBox3::FOnVectorValueChanged VectorChanged;
if(OnTransformChanged.IsBound())
{
VectorChanged = SNumericVectorInputBox3::FOnVectorValueChanged::CreateLambda(
[OnTransformChanged, OnGetTransform, InComponent](FVector InValue)
{
if(OnTransformChanged.IsBound())
{
TransformType Transform = OnGetTransform();
if(InComponent == ESlateTransformComponent::Location)
{
Transform.SetLocation(InValue);
}
else
{
Transform.SetScale3D(InValue);
}
OnTransformChanged.Execute(Transform);
}
}
);
}
typename SNumericVectorInputBox3::FOnNumericValueCommitted XCommitted, YCommitted, ZCommitted;
if(OnNumericValueCommitted.IsBound())
{
XCommitted = SNumericVectorInputBox3::FOnNumericValueCommitted::CreateLambda(
[OnNumericValueCommitted, InComponent](NumericType InValue, ETextCommit::Type InCommitType)
{
OnNumericValueCommitted.Execute(InComponent, ESlateRotationRepresentation::EulerXYZ, ESlateTransformSubComponent::X, InValue, InCommitType);
}
);
YCommitted = SNumericVectorInputBox3::FOnNumericValueCommitted::CreateLambda(
[OnNumericValueCommitted, InComponent](NumericType InValue, ETextCommit::Type InCommitType)
{
OnNumericValueCommitted.Execute(InComponent, ESlateRotationRepresentation::EulerXYZ, ESlateTransformSubComponent::Y, InValue, InCommitType);
}
);
ZCommitted = SNumericVectorInputBox3::FOnNumericValueCommitted::CreateLambda(
[OnNumericValueCommitted, InComponent](NumericType InValue, ETextCommit::Type InCommitType)
{
OnNumericValueCommitted.Execute(InComponent, ESlateRotationRepresentation::EulerXYZ, ESlateTransformSubComponent::Z, InValue, InCommitType);
}
);
}
typename SNumericVectorInputBox3::FOnVectorValueCommitted VectorCommitted;
if(OnTransformCommitted.IsBound())
{
VectorCommitted = SNumericVectorInputBox3::FOnVectorValueCommitted::CreateLambda(
[OnTransformCommitted, OnGetTransform, InComponent](FVector InValue, ETextCommit::Type InCommitType)
{
if(OnTransformCommitted.IsBound())
{
TransformType Transform = OnGetTransform();
if(InComponent == ESlateTransformComponent::Location)
{
Transform.SetLocation(InValue);
}
else
{
Transform.SetScale3D(InValue);
}
OnTransformCommitted.Execute(Transform, InCommitType);
}
}
);
}
TSharedPtr<bool> ScaleLockState = InArgs._IsScaleLocked;
typename SNumericVectorInputBox3::FOnConstrainVector ConstrainComponents;
if(InComponent == ESlateTransformComponent::Scale)
{
if(ScaleLockState.IsValid())
{
ConstrainComponents = SNumericVectorInputBox3::FOnConstrainVector::CreateStatic(
&SAdvancedTransformInputBox::ConstrainScale, TAttribute<bool>::CreateLambda([ScaleLockState]()
{
return *ScaleLockState.Get();
})
);
}
}
InputWidget = SNew(SNumericVectorInputBox3)
.Font(InArgs._Font)
.AllowSpin(InArgs._AllowSpin)
.SpinDelta(InComponent == ESlateTransformComponent::Location ? InArgs._LocationSpinDelta : InArgs._ScaleSpinDelta)
.bColorAxisLabels(InArgs._bColorAxisLabels)
.X(XAttribute)
.Y(YAttribute)
.Z(ZAttribute)
.Vector(Vector3Attribute)
.OnXChanged(XChanged)
.OnYChanged(YChanged)
.OnZChanged(ZChanged)
.OnVectorChanged(VectorChanged)
.OnXCommitted(XCommitted)
.OnYCommitted(YCommitted)
.OnZCommitted(ZCommitted)
.OnVectorCommitted(VectorCommitted)
.ConstrainVector(ConstrainComponents)
.DisplayToggle(InArgs._DisplayToggle)
.TogglePadding(InArgs._TogglePadding)
.PreventThrottling(InArgs._PreventThrottling)
.OnXBeginSliderMovement(XBeginSlide)
.OnYBeginSliderMovement(YBeginSlide)
.OnZBeginSliderMovement(ZBeginSlide)
.OnXEndSliderMovement(XEndSlide)
.OnYEndSliderMovement(YEndSlide)
.OnZEndSliderMovement(ZEndSlide)
.ToggleXChecked_Lambda([OnGetToggleChecked, InComponent]() -> ECheckBoxState
{
if(OnGetToggleChecked.IsBound())
{
return OnGetToggleChecked.Execute(InComponent, ESlateRotationRepresentation::EulerXYZ, ESlateTransformSubComponent::X);
}
return ECheckBoxState::Checked;
})
.ToggleYChecked_Lambda([OnGetToggleChecked, InComponent]() -> ECheckBoxState
{
if(OnGetToggleChecked.IsBound())
{
return OnGetToggleChecked.Execute(InComponent, ESlateRotationRepresentation::EulerXYZ, ESlateTransformSubComponent::Y);
}
return ECheckBoxState::Checked;
})
.ToggleZChecked_Lambda([OnGetToggleChecked, InComponent]() -> ECheckBoxState
{
if(OnGetToggleChecked.IsBound())
{
return OnGetToggleChecked.Execute(InComponent, ESlateRotationRepresentation::EulerXYZ, ESlateTransformSubComponent::Z);
}
return ECheckBoxState::Checked;
})
.OnToggleXChanged_Lambda([OnToggleChanged, InComponent](ECheckBoxState InState)
{
if(OnToggleChanged.IsBound())
{
OnToggleChanged.Execute(InComponent, ESlateRotationRepresentation::EulerXYZ, ESlateTransformSubComponent::X, InState);
}
})
.OnToggleYChanged_Lambda([OnToggleChanged, InComponent](ECheckBoxState InState)
{
if(OnToggleChanged.IsBound())
{
OnToggleChanged.Execute(InComponent, ESlateRotationRepresentation::EulerXYZ, ESlateTransformSubComponent::Y, InState);
}
})
.OnToggleZChanged_Lambda([OnToggleChanged, InComponent](ECheckBoxState InState)
{
if(OnToggleChanged.IsBound())
{
OnToggleChanged.Execute(InComponent, ESlateRotationRepresentation::EulerXYZ, ESlateTransformSubComponent::Z, InState);
}
});
break;
}
case ESlateTransformComponent::Rotation:
{
typename SAdvancedRotationInputBox<NumericType>::FOnGetNumericValue RotationGetNumericValue;
if(OnGetNumericValue.IsBound())
{
RotationGetNumericValue = SAdvancedRotationInputBox<NumericType>::FOnGetNumericValue::CreateLambda(
[OnGetNumericValue, InComponent](ESlateRotationRepresentation::Type InRepr, ESlateTransformSubComponent::Type InSubComponent) -> TOptional<NumericType>
{
return OnGetNumericValue.Execute(InComponent, InRepr, InSubComponent);
});
}
typename SAdvancedRotationInputBox<NumericType>::FOnNumericValueChanged RotationValueChanged;
if(OnNumericValueChanged.IsBound())
{
RotationValueChanged = SAdvancedRotationInputBox<NumericType>::FOnNumericValueChanged::CreateLambda(
[OnNumericValueChanged, InComponent](ESlateRotationRepresentation::Type InRepr, ESlateTransformSubComponent::Type InSubComponent, NumericType InValue)
{
OnNumericValueChanged.Execute(InComponent, InRepr, InSubComponent, InValue);
});
}
typename SAdvancedRotationInputBox<NumericType>::FOnNumericValueCommitted RotationValueCommitted;
if(OnNumericValueCommitted.IsBound())
{
RotationValueCommitted = SAdvancedRotationInputBox<NumericType>::FOnNumericValueCommitted::CreateLambda(
[OnNumericValueCommitted, InComponent](ESlateRotationRepresentation::Type InRepr, ESlateTransformSubComponent::Type InSubComponent, NumericType InValue, ETextCommit::Type InCommitType)
{
OnNumericValueCommitted.Execute(InComponent, InRepr, InSubComponent, InValue, InCommitType);
});
}
typename SAdvancedRotationInputBox<NumericType>::FOnBeginSliderMovement RotationBeginSliderMovement;
if(OnBeginSliderMovement.IsBound())
{
RotationBeginSliderMovement = SAdvancedRotationInputBox<NumericType>::FOnBeginSliderMovement::CreateLambda(
[OnBeginSliderMovement, InComponent](ESlateRotationRepresentation::Type InRepr, ESlateTransformSubComponent::Type InSubComponent)
{
OnBeginSliderMovement.Execute(InComponent, InRepr, InSubComponent);
});
}
typename SAdvancedRotationInputBox<NumericType>::FOnEndSliderMovement RotationEndSliderMovement;
if(OnEndSliderMovement.IsBound())
{
RotationEndSliderMovement = SAdvancedRotationInputBox<NumericType>::FOnEndSliderMovement::CreateLambda(
[OnEndSliderMovement, InComponent](ESlateRotationRepresentation::Type InRepr, ESlateTransformSubComponent::Type InSubComponent, NumericType InValue)
{
OnEndSliderMovement.Execute(InComponent, InRepr, InSubComponent, InValue);
});
}
TAttribute<TOptional<FQuat>> QuaternionAttribute;
TAttribute<TOptional<FRotator>> RotatorAttribute;
if(InArgs._Transform.IsBound() || InArgs._Transform.IsSet())
{
if(InArgs._UseQuaternionForRotation)
{
QuaternionAttribute = TAttribute<TOptional<FQuat>>::CreateLambda(
[OnGetQuaternion]() -> TOptional<FQuat>
{
return OnGetQuaternion();
});
}
else
{
RotatorAttribute = TAttribute<TOptional<FRotator>>::CreateLambda(
[OnGetRotator]() -> TOptional<FRotator>
{
return OnGetRotator();
});
}
}
typename SNumericVectorInputBox3::FOnNumericValueChanged XChanged, YChanged, ZChanged;
if(OnNumericValueChanged.IsBound())
{
XChanged = SNumericVectorInputBox3::FOnNumericValueChanged::CreateLambda(
[OnNumericValueChanged, InComponent](NumericType InValue)
{
OnNumericValueChanged.Execute(InComponent, ESlateRotationRepresentation::EulerXYZ, ESlateTransformSubComponent::X, InValue);
}
);
YChanged = SNumericVectorInputBox3::FOnNumericValueChanged::CreateLambda(
[OnNumericValueChanged, InComponent](NumericType InValue)
{
OnNumericValueChanged.Execute(InComponent, ESlateRotationRepresentation::EulerXYZ, ESlateTransformSubComponent::Y, InValue);
}
);
ZChanged = SNumericVectorInputBox3::FOnNumericValueChanged::CreateLambda(
[OnNumericValueChanged, InComponent](NumericType InValue)
{
OnNumericValueChanged.Execute(InComponent, ESlateRotationRepresentation::EulerXYZ, ESlateTransformSubComponent::Z, InValue);
}
);
}
typename SAdvancedRotationInputBox<NumericType>::FOnRotatorChanged RotatorChanged;
typename SAdvancedRotationInputBox<NumericType>::FOnQuaternionChanged QuaternionChanged;
if(OnTransformChanged.IsBound())
{
if(InArgs._UseQuaternionForRotation)
{
QuaternionChanged = SAdvancedRotationInputBox<NumericType>::FOnQuaternionChanged::CreateLambda(
[OnTransformChanged, OnGetTransform](FQuat InQuaternion)
{
TransformType Xfo = OnGetTransform();
Xfo.SetRotation(InQuaternion);
OnTransformChanged.Execute(Xfo);
}
);
}
else
{
RotatorChanged = SAdvancedRotationInputBox<NumericType>::FOnRotatorChanged::CreateLambda(
[OnTransformChanged, OnGetTransform](FRotator InRotator)
{
TransformType Xfo = OnGetTransform();
Xfo = TransformType(InRotator, Xfo.GetLocation(), Xfo.GetScale3D());
OnTransformChanged.Execute(Xfo);
}
);
}
}
typename SAdvancedRotationInputBox<NumericType>::FOnRotatorCommitted RotatorCommitted;
typename SAdvancedRotationInputBox<NumericType>::FOnQuaternionCommitted QuaternionCommitted;
if(OnTransformCommitted.IsBound())
{
if(InArgs._UseQuaternionForRotation)
{
QuaternionCommitted = SAdvancedRotationInputBox<NumericType>::FOnQuaternionCommitted::CreateLambda(
[OnTransformCommitted, OnGetTransform](FQuat InQuaternion, ETextCommit::Type InCommitType)
{
TransformType Xfo = OnGetTransform();
Xfo.SetRotation(InQuaternion);
OnTransformCommitted.Execute(Xfo, InCommitType);
}
);
}
else
{
RotatorCommitted = SAdvancedRotationInputBox<NumericType>::FOnRotatorCommitted::CreateLambda(
[OnTransformCommitted, OnGetTransform](FRotator InRotator, ETextCommit::Type InCommitType)
{
TransformType Xfo = OnGetTransform();
Xfo = TransformType(InRotator, Xfo.GetLocation(), Xfo.GetScale3D());
OnTransformCommitted.Execute(Xfo, InCommitType);
}
);
}
}
TSharedPtr<ESlateRotationRepresentation::Type> RotationRepresentationPtr = InArgs._RotationRepresentation;
InputWidget = SNew(SAdvancedRotationInputBox<NumericType>)
.Font(InArgs._Font)
.AllowSpin(InArgs._AllowSpin)
.bColorAxisLabels(InArgs._bColorAxisLabels)
.Representation_Lambda([RotationRepresentationPtr]() -> ESlateRotationRepresentation::Type
{
if(RotationRepresentationPtr.IsValid())
{
return *RotationRepresentationPtr.Get();
}
return ESlateRotationRepresentation::Rotator;
})
.OnGetNumericValue(RotationGetNumericValue)
.OnNumericValueChanged(RotationValueChanged)
.OnNumericValueCommitted(RotationValueCommitted)
.Rotator(RotatorAttribute)
.OnRotatorChanged(RotatorChanged)
.OnRotatorCommitted(RotatorCommitted)
.Quaternion(QuaternionAttribute)
.OnQuaternionChanged(QuaternionChanged)
.OnQuaternionCommitted(QuaternionCommitted)
.DisplayToggle(InArgs._DisplayToggle)
.TogglePadding(InArgs._TogglePadding)
.PreventThrottling(InArgs._PreventThrottling)
.OnBeginSliderMovement(RotationBeginSliderMovement)
.OnEndSliderMovement(RotationEndSliderMovement)
.OnGetToggleChecked(SAdvancedRotationInputBox<NumericType>::FOnGetToggleChecked::CreateLambda(
[OnGetToggleChecked, InComponent](ESlateRotationRepresentation::Type InRepr, ESlateTransformSubComponent::Type InSubComponent) -> ECheckBoxState
{
if(OnGetToggleChecked.IsBound())
{
return OnGetToggleChecked.Execute(InComponent, InRepr, InSubComponent);
}
return ECheckBoxState::Checked;
}))
.OnToggleChanged(SAdvancedRotationInputBox<NumericType>::FOnToggleChanged::CreateLambda(
[OnToggleChanged, InComponent](ESlateRotationRepresentation::Type InRepr, ESlateTransformSubComponent::Type InSubComponent, ECheckBoxState InState)
{
if(OnToggleChanged.IsBound())
{
return OnToggleChanged.Execute(InComponent, InRepr, InSubComponent, InState);
}
}));
break;
}
}
if(InputWidget != SNullWidget::NullWidget)
{
HorizontalBox->AddSlot()
.HAlign(HAlign_Fill)
.VAlign(VAlign_Center)
.FillWidth(1.f)
[
InputWidget
];
}
return HorizontalBox;
}
/**
* Constructs a label widget
*/
static TSharedRef<SWidget> ConstructLabel(const FArguments& InArgs, ESlateTransformComponent::Type InComponent)
{
const TAttribute<bool> EnabledAttribute = InArgs._IsEnabled;
TSharedRef<SWidget> LabelWidget = SNullWidget::NullWidget;
if(InComponent == ESlateTransformComponent::Rotation && InArgs._AllowEditRotationRepresentation)
{
const TArray<FText>& Labels = GetRotationRepresentationLabels();
TSharedPtr<ESlateRotationRepresentation::Type> RotationRepresentationPtr = InArgs._RotationRepresentation;
FOnRotationRepresentationChanged OnRotationRepresentationChanged = InArgs._OnRotationRepresentationChanged;
LabelWidget = SNew(SComboButton)
.IsEnabled(true)
.ContentPadding(0.f)
.OnGetMenuContent_Lambda([Labels, RotationRepresentationPtr, OnRotationRepresentationChanged]
{
FMenuBuilder MenuBuilder(true, nullptr);
for (int32 LabelIndex = 0; LabelIndex < Labels.Num(); LabelIndex++)
{
MenuBuilder.AddMenuEntry(
Labels[LabelIndex],
FText(),
FSlateIcon(),
FUIAction
(
FExecuteAction::CreateLambda([LabelIndex, RotationRepresentationPtr, OnRotationRepresentationChanged]()
{
if(OnRotationRepresentationChanged.IsBound())
{
OnRotationRepresentationChanged.Execute((ESlateRotationRepresentation::Type)LabelIndex);
}
if(RotationRepresentationPtr.IsValid())
{
*RotationRepresentationPtr.Get() = (ESlateRotationRepresentation::Type)LabelIndex;
}
}),
FCanExecuteAction(),
FIsActionChecked::CreateLambda([LabelIndex, RotationRepresentationPtr]() -> bool
{
if(RotationRepresentationPtr.IsValid())
{
return LabelIndex == (int32)*RotationRepresentationPtr.Get();
}
return false;
})
),
NAME_None,
EUserInterfaceActionType::Check);
}
return MenuBuilder.MakeWidget();
})
.ButtonContent()
[
SNew(STextBlock)
.Font(InArgs._Font)
.Text_Lambda([RotationRepresentationPtr, Labels]() -> FText
{
if(RotationRepresentationPtr.IsValid())
{
return Labels[(int32)*RotationRepresentationPtr.Get()];
}
return Labels[0];
})
];
}
else
{
const FText& LabelText =
(InComponent == ESlateTransformComponent::Rotation) ?
InArgs._RotationLabel : (
(InComponent == ESlateTransformComponent::Location) ?
InArgs._LocationLabel :
InArgs._ScaleLabel);
LabelWidget = SNew(STextBlock)
.IsEnabled(EnabledAttribute)
.Font(InArgs._Font)
.Text(LabelText);
}
SHorizontalBox::FArguments BoxArgs;
((FSlateBaseNamedArgs&)BoxArgs) = (FSlateBaseNamedArgs)InArgs;
BoxArgs.IsEnabled(true);
TSharedRef<SHorizontalBox> HorizontalBox = SArgumentNew(BoxArgs, SHorizontalBox);
HorizontalBox->AddSlot()
.HAlign(HAlign_Left)
.VAlign(VAlign_Center)
.AutoWidth()
[
LabelWidget
];
const TSharedRef<SWidget> ScaleLockWidget = ConstructScaleLockWidget(InArgs, InComponent);
if(ScaleLockWidget != SNullWidget::NullWidget)
{
ScaleLockWidget->SetEnabled(EnabledAttribute);
HorizontalBox->AddSlot()
.HAlign(HAlign_Left)
.VAlign(VAlign_Center)
.Padding(FMargin(4.f, 0.f, 0.f, 0.f))
[
ScaleLockWidget
];
}
const TSharedRef<SWidget> RelativeWorldWidget = ConstructRelativeWorldWidget(InArgs, InComponent);
if(RelativeWorldWidget != SNullWidget::NullWidget)
{
// the relative to world will be always enabled...
// (unless the outer scope if not enabled)
HorizontalBox->AddSlot()
.HAlign(HAlign_Right)
.VAlign(VAlign_Center)
.Padding(FMargin(4.f, 0.f, 0.f, 0.f))
[
RelativeWorldWidget
];
}
return HorizontalBox;
}
/**
* Constructs a relative / world switcher button
*/
static TSharedRef<SWidget> ConstructScaleLockWidget(const FArguments& InArgs, ESlateTransformComponent::Type InComponent )
{
if(InArgs._DisplayScaleLock && InArgs._ConstructScale && InComponent == ESlateTransformComponent::Scale && InArgs._IsScaleLocked.IsValid())
{
TSharedPtr<bool> LockState = InArgs._IsScaleLocked;
FOnScaleLockChanged OnScaleLockChanged = InArgs._OnScaleLockChanged;
static const FText TooltipText = NSLOCTEXT("SAdvancedTransformInputBox", "PreserveScaleToolTip",
"When locked, all axis values scale together so the object maintains its proportions in all directions.");
return SNew(SButton)
.ContentPadding(0.f)
.ButtonStyle(FAppStyle::Get(), "SimpleButton")
.OnClicked_Lambda([OnScaleLockChanged, LockState]() -> FReply
{
*LockState.Get() = !*LockState.Get();
if(OnScaleLockChanged.IsBound())
{
OnScaleLockChanged.Execute(*LockState.Get());
}
return FReply::Handled();
})
.ToolTipText(TooltipText)
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
.ForegroundColor(FSlateColor::UseStyle())
.Content()
[
SNew(SImage)
.Image_Lambda([LockState]() -> const FSlateBrush*
{
return GetScaleLockIcon(*LockState.Get());
})
.ColorAndOpacity(FSlateColor::UseForeground())
];
}
return SNullWidget::NullWidget;
}
/**
* Gather the icon for scale locked / unlocked
*/
static const FSlateBrush* GetScaleLockIcon(bool bIsLocked)
{
return bIsLocked ? FAppStyle::Get().GetBrush( TEXT("Icons.Lock") ) : FAppStyle::Get().GetBrush( TEXT("Icons.Unlock") ) ;
}
/**
* Constructs a relative / world switcher button
*/
static TSharedRef<SWidget> ConstructRelativeWorldWidget(const FArguments& InArgs, ESlateTransformComponent::Type InComponent)
{
if(InArgs._DisplayRelativeWorld)
{
FOnGetIsComponentRelative OnGetIsComponentRelative = InArgs._OnGetIsComponentRelative;;
FOnIsComponentRelativeChanged OnIsComponentRelativeChanged = InArgs._OnIsComponentRelativeChanged;
static const FText TooltipFormat = NSLOCTEXT("SAdvancedTransformInputBox", "RelativeWorldTooltip",
"Cycles the Transform {0} coordinate system between world and local (relative) space.\nShift clicking this button affects the whole transform.");
static const FText LocationTooltip = FText::Format(TooltipFormat, NSLOCTEXT("SAdvancedTransformInputBox", "Location", "Location"));
static const FText RotationTooltip = FText::Format(TooltipFormat, NSLOCTEXT("SAdvancedTransformInputBox", "Rotation", "Rotation"));
static const FText ScaleTooltip = FText::Format(TooltipFormat, NSLOCTEXT("SAdvancedTransformInputBox", "Scale", "Scale"));
FText TooltipText;
switch(InComponent)
{
case ESlateTransformComponent::Location:
{
TooltipText = LocationTooltip;
break;
}
case ESlateTransformComponent::Rotation:
{
TooltipText = RotationTooltip;
break;
}
case ESlateTransformComponent::Scale:
{
TooltipText = ScaleTooltip;
break;
}
}
return SNew(SButton)
.IsEnabled(true)
.ContentPadding(0.f)
.ButtonStyle(FAppStyle::Get(), "SimpleButton")
.OnClicked_Lambda([OnIsComponentRelativeChanged, OnGetIsComponentRelative, InComponent]() -> FReply
{
if(OnIsComponentRelativeChanged.IsBound())
{
bool bIsRelative = true;
if(OnGetIsComponentRelative.IsBound())
{
bIsRelative = OnGetIsComponentRelative.Execute(InComponent);
}
if(FSlateApplication::Get().GetModifierKeys().IsShiftDown() ||
FSlateApplication::Get().GetModifierKeys().IsControlDown() ||
FSlateApplication::Get().GetModifierKeys().IsAltDown())
{
OnIsComponentRelativeChanged.Execute(ESlateTransformComponent::Location, !bIsRelative);
OnIsComponentRelativeChanged.Execute(ESlateTransformComponent::Rotation, !bIsRelative);
OnIsComponentRelativeChanged.Execute(ESlateTransformComponent::Scale, !bIsRelative);
}
else
{
OnIsComponentRelativeChanged.Execute(InComponent, !bIsRelative);
}
}
return FReply::Handled();
})
.ToolTipText(TooltipText)
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
.ForegroundColor(FSlateColor::UseStyle())
.Content()
[
SNew(SImage)
.Image_Lambda([OnGetIsComponentRelative, InComponent]() -> const FSlateBrush*
{
bool bIsRelative = true;
if(OnGetIsComponentRelative.IsBound())
{
bIsRelative = OnGetIsComponentRelative.Execute(InComponent);
}
return GetRelativeWorldIcon(bIsRelative).GetIcon();
})
.ColorAndOpacity(FSlateColor::UseForeground())
];
}
return SNullWidget::NullWidget;
}
/**
* Gather the icon for relative world
*/
static const FSlateIcon& GetRelativeWorldIcon(bool bRelative)
{
if (bRelative)
{
static FName LocalIconName("Icons.Transform");
static FSlateIcon LocalIcon = FSlateIcon(FAppStyle::GetAppStyleSetName(), LocalIconName);
return LocalIcon;
}
static FName WorldIconName("EditorViewport.RelativeCoordinateSystem_World");
static FSlateIcon WorldIcon = FSlateIcon(FAppStyle::GetAppStyleSetName(), WorldIconName);
return WorldIcon;
}
#if WITH_EDITOR
static IDetailGroup& ConstructDetailGroup(IDetailCategoryBuilder& InBuilder, FName GroupName, const FText& LocalizedDisplayName)
{
return InBuilder.AddGroup(GroupName, LocalizedDisplayName, false, true);
}
static IDetailGroup& ConstructDetailGroup(IDetailChildrenBuilder& InBuilder, FName GroupName, const FText& LocalizedDisplayName)
{
return InBuilder.AddGroup(GroupName, LocalizedDisplayName);
}
static void ConfigureComponentWidgetRow(
FDetailWidgetRow& WidgetRow,
ESlateTransformComponent::Type InComponent,
typename SAdvancedTransformInputBox<TransformType>::FArguments WidgetArgs)
{
if(WidgetArgs._OnCopyToClipboard.IsBound() && WidgetArgs._OnPasteFromClipboard.IsBound())
{
WidgetRow
.CopyAction(FUIAction(
FExecuteAction::CreateLambda([WidgetArgs, InComponent]()
{
WidgetArgs._OnCopyToClipboard.ExecuteIfBound(InComponent);
}),
FCanExecuteAction())
);
WidgetRow.PasteAction(FUIAction(
FExecuteAction::CreateLambda([WidgetArgs, InComponent]()
{
WidgetArgs._OnPasteFromClipboard.ExecuteIfBound(InComponent);
}),
FCanExecuteAction::CreateLambda([WidgetArgs]() -> bool
{
return WidgetArgs._IsEnabled.Get();
})));
}
if(WidgetArgs._OnResetToDefault.IsBound() && WidgetArgs._DiffersFromDefault.IsBound())
{
WidgetRow.OverrideResetToDefault(FResetToDefaultOverride::Create(
TAttribute<bool>::CreateLambda([WidgetArgs, InComponent]() -> bool
{
if(!WidgetArgs._IsEnabled.Get())
{
return false;
}
return WidgetArgs._DiffersFromDefault.Execute(InComponent);
}),
FSimpleDelegate::CreateLambda([WidgetArgs, InComponent]()
{
WidgetArgs._OnResetToDefault.Execute(InComponent);
})
));
}
if(InComponent != ESlateTransformComponent::Max)
{
WidgetRow
.NameContent()
.HAlign(HAlign_Fill)
[
SAdvancedTransformInputBox<TransformType>::ConstructLabel(WidgetArgs, InComponent)
]
.ValueContent()
.MinDesiredWidth(375.f)
.MaxDesiredWidth(375.f)
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
[
SAdvancedTransformInputBox<TransformType>::ConstructWidget(WidgetArgs, InComponent)
]
];
}
};
static void ConfigureHeader(
FDetailWidgetRow& HeaderRow,
const FText& InLabel,
const FText& InTooltip,
typename SAdvancedTransformInputBox<TransformType>::FArguments WidgetArgs,
TSharedPtr<SWidget> NameContent = TSharedPtr<SWidget>()
)
{
ConfigureComponentWidgetRow(HeaderRow, ESlateTransformComponent::Max, WidgetArgs);
if(!NameContent.IsValid())
{
SAssignNew(NameContent, STextBlock)
.Font(IDetailLayoutBuilder::GetDetailFont())
.Text(InLabel)
.ToolTipText(InTooltip);
}
HeaderRow
.Visibility(WidgetArgs._Visibility)
.NameContent()
[
NameContent.ToSharedRef()
];
}
template<typename BuilderType = IDetailCategoryBuilder>
static FDetailWidgetRow& ConstructGroupedTransformRows(
BuilderType& InBuilder,
const FText& InLabel,
const FText& InTooltip,
typename SAdvancedTransformInputBox<TransformType>::FArguments WidgetArgs,
TSharedPtr<SWidget> NameContent = TSharedPtr<SWidget>()
)
{
IDetailGroup& Group = ConstructDetailGroup(InBuilder, *InLabel.ToString(), InLabel);
FDetailWidgetRow& HeaderRow = Group.HeaderRow();
ConfigureHeader(HeaderRow, InLabel, InTooltip, WidgetArgs, NameContent);
if(WidgetArgs._ConstructLocation)
{
FDetailWidgetRow& WidgetRow = Group.AddWidgetRow();
ConfigureComponentWidgetRow(WidgetRow, ESlateTransformComponent::Location, WidgetArgs);
}
if(WidgetArgs._ConstructRotation)
{
FDetailWidgetRow& WidgetRow = Group.AddWidgetRow();
ConfigureComponentWidgetRow(WidgetRow, ESlateTransformComponent::Rotation, WidgetArgs);
}
if(WidgetArgs._ConstructScale)
{
FDetailWidgetRow& WidgetRow = Group.AddWidgetRow();
ConfigureComponentWidgetRow(WidgetRow, ESlateTransformComponent::Scale, WidgetArgs);
}
return HeaderRow;
}
#endif
static const TArray<FText>& GetRotationRepresentationLabels()
{
static TArray<FText> Labels;
if(Labels.IsEmpty())
{
Labels.Add(NSLOCTEXT("SAdvancedTransformInputBox", "EulerXYZ", "Euler XYZ"));
Labels.Add(NSLOCTEXT("SAdvancedTransformInputBox", "EulerXZY", "Euler XZY"));
Labels.Add(NSLOCTEXT("SAdvancedTransformInputBox", "EulerYXZ", "Euler YXZ"));
Labels.Add(NSLOCTEXT("SAdvancedTransformInputBox", "EulerYZX", "Euler YZX"));
Labels.Add(NSLOCTEXT("SAdvancedTransformInputBox", "EulerZXY", "Euler ZXY"));
Labels.Add(NSLOCTEXT("SAdvancedTransformInputBox", "EulerZYX", "Euler ZYX"));
Labels.Add(NSLOCTEXT("SAdvancedTransformInputBox", "Rotator", "Rotator"));
Labels.Add(NSLOCTEXT("SAdvancedTransformInputBox", "Quaternion", "Quaternion"));
Labels.Add(NSLOCTEXT("SAdvancedTransformInputBox", "AxisAndAngle", "Axis And Angle"));
}
return Labels;
}
static void ConstrainScale(
int32 ComponentIndex,
UE::Math::TVector<NumericType> OldValue,
UE::Math::TVector<NumericType>& NewValue,
TAttribute<bool> ConstrainComponents)
{
if(!ConstrainComponents.Get())
{
return;
}
if(FMath::IsNearlyZero(OldValue[ComponentIndex]) || FMath::IsNearlyZero(NewValue[ComponentIndex]))
{
return;
}
const NumericType Ratio = NewValue[ComponentIndex] / OldValue[ComponentIndex];
NewValue = OldValue * Ratio;
}
static TOptional<NumericType> GetNumericValueFromTransform(
const TransformType& Transform,
ESlateTransformComponent::Type Component,
ESlateRotationRepresentation::Type Representation,
ESlateTransformSubComponent::Type SubComponent)
{
switch(Component)
{
case ESlateTransformComponent::Location:
{
switch(SubComponent)
{
case ESlateTransformSubComponent::X:
return Transform.GetLocation().X;
case ESlateTransformSubComponent::Y:
return Transform.GetLocation().Y;
case ESlateTransformSubComponent::Z:
return Transform.GetLocation().Z;
}
break;
}
case ESlateTransformComponent::Rotation:
{
switch(Representation)
{
case ESlateRotationRepresentation::EulerXYZ:
case ESlateRotationRepresentation::EulerXZY:
case ESlateRotationRepresentation::EulerYXZ:
case ESlateRotationRepresentation::EulerYZX:
case ESlateRotationRepresentation::EulerZXY:
case ESlateRotationRepresentation::EulerZYX:
{
const int32 RotationOrder = int32(Representation) - int32(ESlateRotationRepresentation::EulerXYZ);
const FVector Euler = AnimationCore::EulerFromQuat(Transform.GetRotation(), EEulerRotationOrder(RotationOrder), true);
switch(SubComponent)
{
case ESlateTransformSubComponent::X:
{
return Euler.X;
}
case ESlateTransformSubComponent::Y:
{
return Euler.Y;
}
case ESlateTransformSubComponent::Z:
{
return Euler.Z;
}
}
break;
}
case ESlateRotationRepresentation::Rotator:
{
switch(SubComponent)
{
case ESlateTransformSubComponent::Roll:
return Transform.Rotator().Roll;
case ESlateTransformSubComponent::Pitch:
return Transform.Rotator().Pitch;
case ESlateTransformSubComponent::Yaw:
return Transform.Rotator().Yaw;
}
break;
}
case ESlateRotationRepresentation::Quaternion:
{
switch(SubComponent)
{
case ESlateTransformSubComponent::X:
return Transform.GetRotation().X;
case ESlateTransformSubComponent::Y:
return Transform.GetRotation().Y;
case ESlateTransformSubComponent::Z:
return Transform.GetRotation().Z;
case ESlateTransformSubComponent::W:
return Transform.GetRotation().W;
}
break;
}
case ESlateRotationRepresentation::AxisAndAngle:
{
FVector Axis;
NumericType Angle;
Transform.GetRotation().ToAxisAndAngle(Axis, Angle);
switch(SubComponent)
{
case ESlateTransformSubComponent::X:
return Axis.X;
case ESlateTransformSubComponent::Y:
return Axis.Y;
case ESlateTransformSubComponent::Z:
return Axis.Z;
case ESlateTransformSubComponent::Angle:
return Angle;
}
break;
}
}
break;
}
case ESlateTransformComponent::Scale:
{
switch(SubComponent)
{
case ESlateTransformSubComponent::X:
return Transform.GetScale3D().X;
case ESlateTransformSubComponent::Y:
return Transform.GetScale3D().Y;
case ESlateTransformSubComponent::Z:
return Transform.GetScale3D().Z;
}
break;
}
}
return TOptional<NumericType>();
}
static void ApplyNumericValueChange(
TransformType& Transform,
NumericType Value,
ESlateTransformComponent::Type Component,
ESlateRotationRepresentation::Type Representation,
ESlateTransformSubComponent::Type SubComponent)
{
switch(Component)
{
case ESlateTransformComponent::Location:
{
UE::Math::TVector<NumericType> Location = Transform.GetLocation();
switch(SubComponent)
{
case ESlateTransformSubComponent::X:
{
Location.X = Value;
break;
}
case ESlateTransformSubComponent::Y:
{
Location.Y = Value;
break;
}
case ESlateTransformSubComponent::Z:
{
Location.Z = Value;
break;
}
}
Transform.SetLocation(Location);
break;
}
case ESlateTransformComponent::Rotation:
{
switch(Representation)
{
case ESlateRotationRepresentation::EulerXYZ:
case ESlateRotationRepresentation::EulerXZY:
case ESlateRotationRepresentation::EulerYXZ:
case ESlateRotationRepresentation::EulerYZX:
case ESlateRotationRepresentation::EulerZXY:
case ESlateRotationRepresentation::EulerZYX:
{
const int32 RotationOrder = int32(Representation) - int32(ESlateRotationRepresentation::EulerXYZ);
FVector Euler = AnimationCore::EulerFromQuat(Transform.GetRotation(), EEulerRotationOrder(RotationOrder));
switch(SubComponent)
{
case ESlateTransformSubComponent::X:
{
Euler.X = Value;
break;
}
case ESlateTransformSubComponent::Y:
{
Euler.Y = Value;
break;
}
case ESlateTransformSubComponent::Z:
{
Euler.Z = Value;
break;
}
}
Transform.SetRotation(AnimationCore::QuatFromEuler(Euler, EEulerRotationOrder(RotationOrder), true));
break;
}
case ESlateRotationRepresentation::Rotator:
{
FRotator Rotator = Transform.Rotator();
switch(SubComponent)
{
case ESlateTransformSubComponent::Roll:
{
Rotator.Roll = Value;
break;
}
case ESlateTransformSubComponent::Pitch:
{
Rotator.Pitch = Value;
break;
}
case ESlateTransformSubComponent::Yaw:
{
Rotator.Yaw = Value;
break;
}
}
Transform = TransformType(Rotator, Transform.GetLocation(), Transform.GetScale3D());
break;
}
case ESlateRotationRepresentation::Quaternion:
{
FQuat Rotation = Transform.GetRotation();
switch(SubComponent)
{
case ESlateTransformSubComponent::X:
{
Rotation.X = Value;
break;
}
case ESlateTransformSubComponent::Y:
{
Rotation.Y = Value;
break;
}
case ESlateTransformSubComponent::Z:
{
Rotation.Z = Value;
break;
}
case ESlateTransformSubComponent::W:
{
Rotation.W = Value;
break;
}
}
Rotation.Normalize();
Transform.SetRotation(Rotation);
break;
}
case ESlateRotationRepresentation::AxisAndAngle:
{
FVector Axis;
NumericType Angle;
Transform.GetRotation().ToAxisAndAngle(Axis, Angle);
switch(SubComponent)
{
case ESlateTransformSubComponent::X:
{
Axis.X = Value;
break;
}
case ESlateTransformSubComponent::Y:
{
Axis.Y = Value;
break;
}
case ESlateTransformSubComponent::Z:
{
Axis.Z = Value;
break;
}
case ESlateTransformSubComponent::Angle:
{
Angle = Value;
break;
}
}
Transform.SetRotation(FQuat(Axis, Angle));
break;
}
}
break;
}
case ESlateTransformComponent::Scale:
{
UE::Math::TVector<NumericType> Scale3D = Transform.GetScale3D();
switch(SubComponent)
{
case ESlateTransformSubComponent::X:
{
Scale3D.X = Value;
break;
}
case ESlateTransformSubComponent::Y:
{
Scale3D.Y = Value;
break;
}
case ESlateTransformSubComponent::Z:
{
Scale3D.Z = Value;
break;
}
}
Transform.SetScale3D(Scale3D);
break;
}
}
}
};