// Copyright Epic Games, Inc. All Rights Reserved. #include "STimeRange.h" #include "AnimatedRange.h" #include "HAL/Platform.h" #include "HAL/PlatformCrt.h" #include "Internationalization/Internationalization.h" #include "Layout/Children.h" #include "Layout/Margin.h" #include "Layout/Visibility.h" #include "Math/Color.h" #include "Math/Range.h" #include "Misc/Attribute.h" #include "Misc/FrameNumber.h" #include "Misc/FrameRate.h" #include "Misc/FrameTime.h" #include "Misc/Optional.h" #include "MovieSceneTimeHelpers.h" #include "SlotBase.h" #include "Styling/AppStyle.h" #include "Styling/ISlateStyle.h" #include "Styling/SlateColor.h" #include "Styling/SlateTypes.h" #include "Types/SlateStructs.h" #include "Widgets/Input/SSpinBox.h" #include "Widgets/Layout/SBorder.h" #include "Widgets/Layout/SBox.h" #include "Widgets/SBoxPanel.h" #include "Widgets/SCompoundWidget.h" #include "Widgets/SNullWidget.h" class SWidget; template struct INumericTypeInterface; #define LOCTEXT_NAMESPACE "STimeRange" void STimeRange::Construct( const STimeRange::FArguments& InArgs, TSharedRef InTimeSliderController ) { TimeSliderController = InTimeSliderController; PlaybackRangeStartColor = InArgs._PlaybackRangeStartColor; PlaybackRangeEndColor = InArgs._PlaybackRangeEndColor; TSharedRef WorkingRangeStart = SNullWidget::NullWidget, WorkingRangeEnd = SNullWidget::NullWidget; if (InArgs._ShowWorkingRange) { WorkingRangeStart = SNew(SSpinBox) .IsEnabled(InArgs._EnableWorkingRange) .Value(this, &STimeRange::WorkingStartTime) .ToolTipText(LOCTEXT("WorkingRangeStart", "Working Range Start")) .OnValueCommitted(this, &STimeRange::OnWorkingStartTimeCommitted) .OnValueChanged(this, &STimeRange::OnWorkingStartTimeChanged) .MinValue(TOptional()) .MaxValue(TOptional()) .Style(&FAppStyle::Get().GetWidgetStyle("Sequencer.HyperlinkSpinBox")) .TypeInterface(InArgs._NumericTypeInterface) .ClearKeyboardFocusOnCommit(true) .Delta(this, &STimeRange::GetSpinboxDelta) .LinearDeltaSensitivity(25); WorkingRangeEnd = SNew(SSpinBox) .IsEnabled(InArgs._EnableWorkingRange) .Value(this, &STimeRange::WorkingEndTime) .ToolTipText(LOCTEXT("WorkingRangeEnd", "Working Range End")) .OnValueCommitted( this, &STimeRange::OnWorkingEndTimeCommitted ) .OnValueChanged( this, &STimeRange::OnWorkingEndTimeChanged ) .MinValue(TOptional()) .MaxValue(TOptional()) .Style(&FAppStyle::Get().GetWidgetStyle("Sequencer.HyperlinkSpinBox")) .TypeInterface(InArgs._NumericTypeInterface) .ClearKeyboardFocusOnCommit(true) .Delta(this, &STimeRange::GetSpinboxDelta) .LinearDeltaSensitivity(25); } TSharedRef ViewRangeStart = SNullWidget::NullWidget, ViewRangeEnd = SNullWidget::NullWidget; if (InArgs._ShowViewRange) { ViewRangeStart = SNew(SSpinBox) .IsEnabled(InArgs._EnableViewRange) .Value(this, &STimeRange::ViewStartTime) .ToolTipText(LOCTEXT("ViewStartTimeTooltip", "View Range Start Time")) .OnValueCommitted( this, &STimeRange::OnViewStartTimeCommitted ) .OnValueChanged( this, &STimeRange::OnViewStartTimeChanged ) .MinValue(TOptional()) .MaxValue(TOptional()) .Style(&FAppStyle::Get().GetWidgetStyle("Sequencer.HyperlinkSpinBox")) .TypeInterface(InArgs._NumericTypeInterface) .ClearKeyboardFocusOnCommit(true) .Delta(this, &STimeRange::GetSpinboxDelta) .LinearDeltaSensitivity(25); ViewRangeEnd = SNew(SSpinBox) .IsEnabled(InArgs._EnableViewRange) .Value(this, &STimeRange::ViewEndTime) .ToolTipText(LOCTEXT("ViewEndTimeTooltip", "View Range End Time")) .OnValueCommitted( this, &STimeRange::OnViewEndTimeCommitted ) .OnValueChanged( this, &STimeRange::OnViewEndTimeChanged ) .MinValue(TOptional()) .MaxValue(TOptional()) .Style(&FAppStyle::Get().GetWidgetStyle("Sequencer.HyperlinkSpinBox")) .TypeInterface(InArgs._NumericTypeInterface) .ClearKeyboardFocusOnCommit(true) .Delta(this, &STimeRange::GetSpinboxDelta) .LinearDeltaSensitivity(25); } TSharedRef PlaybackRangeStart = SNullWidget::NullWidget, PlaybackRangeEnd = SNullWidget::NullWidget; if (InArgs._ShowPlaybackRange) { PlaybackRangeStart = SNew(SSpinBox) .IsEnabled(InArgs._EnablePlaybackRange) .Value(this, &STimeRange::PlayStartTime) .ToolTipText(LOCTEXT("PlayStartTimeTooltip", "Playback Range Start Time")) .OnValueCommitted(this, &STimeRange::OnPlayStartTimeCommitted) .OnValueChanged(this, &STimeRange::OnPlayStartTimeChanged) .MinValue(TOptional()) .MaxValue(TOptional()) .Style(&FAppStyle::Get().GetWidgetStyle("Sequencer.HyperlinkSpinBox")) .TypeInterface(InArgs._NumericTypeInterface) .ClearKeyboardFocusOnCommit(true) .Delta(this, &STimeRange::GetSpinboxDelta) .LinearDeltaSensitivity(25); PlaybackRangeEnd = SNew(SSpinBox) .IsEnabled(InArgs._EnablePlaybackRange) .Value(this, &STimeRange::PlayEndTime) .ToolTipText(LOCTEXT("PlayEndTimeTooltip", "Playback Range Stop Time")) .OnValueCommitted( this, &STimeRange::OnPlayEndTimeCommitted ) .OnValueChanged( this, &STimeRange::OnPlayEndTimeChanged ) .MinValue(TOptional()) .MaxValue(TOptional()) .Style(&FAppStyle::Get().GetWidgetStyle("Sequencer.HyperlinkSpinBox")) .TypeInterface(InArgs._NumericTypeInterface) .ClearKeyboardFocusOnCommit(true) .Delta(this, &STimeRange::GetSpinboxDelta) .LinearDeltaSensitivity(25); } this->ChildSlot .HAlign(HAlign_Fill) [ SNew(SHorizontalBox) +SHorizontalBox::Slot() .VAlign(VAlign_Center) .AutoWidth() .Padding(2.f) [ SNew(SBox) .Visibility(InArgs._ShowWorkingRange ? EVisibility::Visible : EVisibility::Collapsed) .MinDesiredWidth(64.f) .HAlign(HAlign_Center) [ WorkingRangeStart ] ] +SHorizontalBox::Slot() .VAlign(VAlign_Center) .AutoWidth() .Padding(2.f) [ SNew(SBox) .MinDesiredWidth(64.f) .HAlign(HAlign_Center) .Visibility(InArgs._ShowPlaybackRange ? EVisibility::Visible : EVisibility::Collapsed) [ SNew(SBorder) .Padding(0.f) .BorderImage(nullptr) .ForegroundColor(PlaybackRangeStartColor) [ PlaybackRangeStart ] ] ] +SHorizontalBox::Slot() .VAlign(VAlign_Center) .AutoWidth() .Padding(2.f) [ SNew(SBox) .Visibility(InArgs._ShowViewRange ? EVisibility::Visible : EVisibility::Collapsed) .MinDesiredWidth(64.f) .HAlign(HAlign_Center) [ ViewRangeStart ] ] +SHorizontalBox::Slot() .FillWidth(1.0f) .Padding(2.0f, 4.0f) .VAlign(VAlign_Center) [ InArgs._CenterContent.Widget ] +SHorizontalBox::Slot() .VAlign(VAlign_Center) .AutoWidth() .Padding(2.f) [ SNew(SBox) .Visibility(InArgs._ShowViewRange ? EVisibility::Visible : EVisibility::Collapsed) .MinDesiredWidth(64.f) .HAlign(HAlign_Center) [ ViewRangeEnd ] ] +SHorizontalBox::Slot() .VAlign(VAlign_Center) .AutoWidth() .Padding(2.f) [ SNew(SBox) .MinDesiredWidth(64.f) .HAlign(HAlign_Center) .Visibility(InArgs._ShowPlaybackRange ? EVisibility::Visible : EVisibility::Collapsed) [ SNew(SBorder) .Padding(0.f) .BorderImage(nullptr) .ForegroundColor(PlaybackRangeEndColor) [ PlaybackRangeEnd ] ] ] +SHorizontalBox::Slot() .VAlign(VAlign_Center) .AutoWidth() .Padding(2.f) [ SNew(SBox) .MinDesiredWidth(64.f) .HAlign(HAlign_Center) .Visibility(InArgs._ShowWorkingRange ? EVisibility::Visible : EVisibility::Collapsed) [ WorkingRangeEnd ] ] ]; } double STimeRange::WorkingStartTime() const { FFrameRate Rate = TimeSliderController->GetTickResolution(); FFrameTime Time = TimeSliderController->GetClampRange().GetLowerBoundValue() * Rate; return Time.GetFrame().Value; } double STimeRange::WorkingEndTime() const { FFrameRate Rate = TimeSliderController->GetTickResolution(); FFrameTime Time = TimeSliderController->GetClampRange().GetUpperBoundValue() * Rate; return Time.GetFrame().Value; } double STimeRange::ViewStartTime() const { FFrameRate TickResolution = TimeSliderController->GetTickResolution(); // View range is in seconds so we convert it to tick resolution FFrameTime Time = TimeSliderController->GetViewRange().GetLowerBoundValue() * TickResolution; return Time.GetFrame().Value; } double STimeRange::ViewEndTime() const { FFrameRate TickResolution = TimeSliderController->GetTickResolution(); // View range is in seconds so we convert it to tick resolution FFrameTime Time = TimeSliderController->GetViewRange().GetUpperBoundValue() * TickResolution; return Time.GetFrame().Value; } double STimeRange::GetSpinboxDelta() const { return TimeSliderController->GetTickResolution().AsDecimal() * TimeSliderController->GetDisplayRate().AsInterval(); } double STimeRange::PlayStartTime() const { FFrameNumber LowerBound = UE::MovieScene::DiscreteInclusiveLower(TimeSliderController->GetPlayRange()); return LowerBound.Value; } double STimeRange::PlayEndTime() const { FFrameNumber UpperBound = UE::MovieScene::DiscreteExclusiveUpper(TimeSliderController->GetPlayRange()); return UpperBound.Value; } void STimeRange::OnWorkingStartTimeCommitted(double NewValue, ETextCommit::Type InTextCommit) { OnWorkingStartTimeChanged(NewValue); } void STimeRange::OnWorkingEndTimeCommitted(double NewValue, ETextCommit::Type InTextCommit) { OnWorkingEndTimeChanged(NewValue); } void STimeRange::OnViewStartTimeCommitted(double NewValue, ETextCommit::Type InTextCommit) { OnViewStartTimeChanged(NewValue); } void STimeRange::OnViewEndTimeCommitted(double NewValue, ETextCommit::Type InTextCommit) { OnViewEndTimeChanged(NewValue); } void STimeRange::OnPlayStartTimeCommitted(double NewValue, ETextCommit::Type InTextCommit) { OnPlayStartTimeChanged(NewValue); } void STimeRange::OnPlayEndTimeCommitted(double NewValue, ETextCommit::Type InTextCommit) { OnPlayEndTimeChanged(NewValue); } void STimeRange::OnWorkingStartTimeChanged(double NewValue) { FFrameRate TickResolution = TimeSliderController->GetTickResolution(); double Time = TickResolution.AsSeconds(FFrameTime::FromDecimal(NewValue)); // Clamp range is in seconds TimeSliderController->SetClampRange(Time, TimeSliderController->GetClampRange().GetUpperBoundValue()); if (Time > TimeSliderController->GetViewRange().GetLowerBoundValue()) { OnViewStartTimeChanged(NewValue); } } void STimeRange::OnWorkingEndTimeChanged(double NewValue) { FFrameRate TickResolution = TimeSliderController->GetTickResolution(); double Time = TickResolution.AsSeconds(FFrameTime::FromDecimal(NewValue)); // Clamp range is in seconds TimeSliderController->SetClampRange(TimeSliderController->GetClampRange().GetLowerBoundValue(), Time); if (Time < TimeSliderController->GetViewRange().GetUpperBoundValue()) { OnViewEndTimeChanged(NewValue); } } void STimeRange::OnViewStartTimeChanged(double NewValue) { FFrameRate TickResolution = TimeSliderController->GetTickResolution(); double Time = TickResolution.AsSeconds(FFrameTime::FromDecimal(NewValue)); double ViewStartTime = TimeSliderController->GetViewRange().GetLowerBoundValue(); double ViewEndTime = TimeSliderController->GetViewRange().GetUpperBoundValue(); double ClampStartTime = TimeSliderController.Get()->GetClampRange().GetLowerBoundValue(); double ClampEndTime = TimeSliderController.Get()->GetClampRange().GetUpperBoundValue(); if (Time >= ViewEndTime) { double ViewDuration = ViewEndTime - ViewStartTime; ViewEndTime = Time + ViewDuration; if (ViewEndTime > TimeSliderController.Get()->GetClampRange().GetUpperBoundValue()) { TimeSliderController->SetClampRange(TimeSliderController->GetClampRange().GetLowerBoundValue(), ViewEndTime); } } if (Time < TimeSliderController.Get()->GetClampRange().GetLowerBoundValue()) { TimeSliderController->SetClampRange(Time, TimeSliderController->GetClampRange().GetUpperBoundValue()); } TimeSliderController->SetViewRange(Time, ViewEndTime, EViewRangeInterpolation::Immediate); } void STimeRange::OnViewEndTimeChanged(double NewValue) { FFrameRate TickResolution = TimeSliderController->GetTickResolution(); double Time = TickResolution.AsSeconds(FFrameTime::FromDecimal(NewValue)); double ViewStartTime = TimeSliderController->GetViewRange().GetLowerBoundValue(); double ViewEndTime = TimeSliderController->GetViewRange().GetUpperBoundValue(); if (Time <= ViewStartTime) { double ViewDuration = ViewEndTime - ViewStartTime; ViewStartTime = Time - ViewDuration; if (ViewStartTime < TimeSliderController.Get()->GetClampRange().GetLowerBoundValue()) { TimeSliderController->SetClampRange(ViewStartTime, TimeSliderController->GetClampRange().GetUpperBoundValue()); } } if (Time > TimeSliderController->GetClampRange().GetUpperBoundValue()) { TimeSliderController->SetClampRange(TimeSliderController->GetClampRange().GetLowerBoundValue(), Time); } TimeSliderController->SetViewRange(ViewStartTime, Time, EViewRangeInterpolation::Immediate); } void STimeRange::OnPlayStartTimeChanged(double NewValue) { FFrameRate TickResolution = TimeSliderController->GetTickResolution(); FFrameTime Time = FFrameTime::FromDecimal(NewValue); double TimeInSeconds = TickResolution.AsSeconds(Time); TRange PlayRange = TimeSliderController->GetPlayRange(); FFrameNumber PlayDuration; if (Time.FrameNumber >= UE::MovieScene::DiscreteExclusiveUpper(PlayRange)) { PlayDuration = UE::MovieScene::DiscreteExclusiveUpper(PlayRange) - UE::MovieScene::DiscreteInclusiveLower(PlayRange); } else { PlayDuration = UE::MovieScene::DiscreteExclusiveUpper(PlayRange) - Time.FrameNumber; } TimeSliderController->SetPlayRange(Time.FrameNumber, PlayDuration.Value); // Expand view ranges if outside of play range if (TimeInSeconds < TimeSliderController.Get()->GetClampRange().GetLowerBoundValue()) { OnViewStartTimeChanged(NewValue); } FFrameNumber PlayEnd = TimeSliderController->GetPlayRange().GetUpperBoundValue(); double PlayEndSeconds = PlayEnd / TickResolution; if (PlayEndSeconds > TimeSliderController.Get()->GetClampRange().GetUpperBoundValue()) { OnViewEndTimeChanged(TickResolution.AsFrameNumber(PlayEndSeconds).Value); } } void STimeRange::OnPlayEndTimeChanged(double NewValue) { FFrameRate TickResolution = TimeSliderController->GetTickResolution(); FFrameTime Time = FFrameTime::FromDecimal(NewValue); double TimeInSeconds = TickResolution.AsSeconds(Time); TRange PlayRange = TimeSliderController->GetPlayRange(); FFrameNumber PlayDuration; FFrameNumber StartFrame = UE::MovieScene::DiscreteInclusiveLower(PlayRange); if (Time.FrameNumber <= StartFrame) { PlayDuration = UE::MovieScene::DiscreteExclusiveUpper(PlayRange) - StartFrame; StartFrame = Time.FrameNumber - PlayDuration; } else { PlayDuration = Time.FrameNumber - StartFrame; } TimeSliderController->SetPlayRange(StartFrame, PlayDuration.Value); // Expand view ranges if outside of play range if (TimeInSeconds > TimeSliderController->GetClampRange().GetUpperBoundValue()) { OnViewEndTimeChanged(NewValue); } FFrameNumber PlayStart = TimeSliderController->GetPlayRange().GetLowerBoundValue(); double PlayStartSeconds = PlayStart / TickResolution; if (PlayStartSeconds < TimeSliderController.Get()->GetClampRange().GetLowerBoundValue()) { OnViewStartTimeChanged(TickResolution.AsFrameNumber(PlayStartSeconds).Value); } } #undef LOCTEXT_NAMESPACE