// Fill out your copyright notice in the Description page of Project Settings. #include "SPlayList.h" #include "SlateOptMacros.h" #include "SPlayListTimeLine.h" #include "PlayListPanel/SPlayListPanel.h" #include "SScrollBox.h" BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION void SPlayList::Construct(const FArguments& InArgs) { FrameRange = TRange(0, FMidiSequencer::Get().GetBar().Frames() * 16.0); TargetFrameRange = FrameRange; ChildSlot [ SNew(SVerticalBox) +SVerticalBox::Slot() .AutoHeight() .Padding(0, 0, 12, 0) [ SAssignNew(TimeLine, SPlayListTimeLine) .FrameRange(this, &SPlayList::GetFrameRange) ] +SVerticalBox::Slot() .FillHeight(1.f) [ SAssignNew(ScrollBox, SScrollBox) .AnimateWheelScrolling(true) +SScrollBox::Slot() [ SAssignNew(PlayListPanel, SPlayListPanel) .TrackHeight(64) .FrameRange(this, &SPlayList::GetFrameRange) ] ] ]; } void SPlayList::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime) { SCompoundWidget::Tick(AllottedGeometry, InCurrentTime, InDeltaTime); FrameRange.SetLowerBoundValue(FMath::FInterpTo(FrameRange.GetLowerBoundValue(), TargetFrameRange.GetLowerBoundValue(), InDeltaTime, 20.f)); FrameRange.SetUpperBoundValue(FMath::FInterpTo(FrameRange.GetUpperBoundValue(), TargetFrameRange.GetUpperBoundValue(), InDeltaTime, 20.f)); } FReply SPlayList::OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent) { if (!InKeyEvent.IsRepeat() && InKeyEvent.GetKey() == EKeys::SpaceBar) FMidiSequencer::Get().TogglePlay(); if (!InKeyEvent.IsRepeat() && InKeyEvent.GetKey() == EKeys::LeftControl) { Zooming = true; ScrollBox->SetConsumeMouseWheel(EConsumeMouseWheel::Never); } if (!InKeyEvent.IsRepeat() && InKeyEvent.GetKey() == EKeys::LeftShift) { HorizonScrolling = true; ScrollBox->SetConsumeMouseWheel(EConsumeMouseWheel::Never); } return FReply::Handled(); } FReply SPlayList::OnKeyUp(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent) { if (InKeyEvent.GetKey() == EKeys::LeftControl) Zooming = false; if (InKeyEvent.GetKey() == EKeys::LeftShift) HorizonScrolling = false; if (!Zooming && !HorizonScrolling) ScrollBox->SetConsumeMouseWheel(EConsumeMouseWheel::Always); return FReply::Handled(); } FReply SPlayList::OnMouseWheel(const FGeometry& MyGeometry, const FPointerEvent& InMouseEvent) { if (Zooming) { const AudioFrame MinRange = FMidiSequencer::Get().GetBeat() * 4; const TRange& CurrentRange = TargetFrameRange; const AudioFrame WheelDelta = InMouseEvent.GetWheelDelta() * GetScalerLevel() * 2; // 乘以2是因为MousePosPercent if (WheelDelta.Pos > 0 && CurrentRange.Size() <= MinRange) return FReply::Handled(); const FGeometry& PlayListPanelGeometry = PlayListPanel->GetTickSpaceGeometry(); const FVector2f& PanelLocalMouse = PlayListPanelGeometry.AbsoluteToLocal(InMouseEvent.GetScreenSpacePosition()); const double MousePosPercent = PanelLocalMouse.X / PlayListPanelGeometry.Size.X; AudioFrame Low = FMath::Max(0.f, CurrentRange.GetLowerBoundValue() + WheelDelta * MousePosPercent); AudioFrame High = FMath::Max(ExternRange, CurrentRange.GetUpperBoundValue() - WheelDelta * (1 - MousePosPercent)); // High = FMath::Min(High, GetDurationWithExtern()); // 不限制缩放的最大范围 // 限制最小范围 if (WheelDelta.Pos > 0 && High - Low <= MinRange) { const AudioFrame Mid = (Low + High) / 2.0; Low = Mid - MinRange.Pos / 2; High = Mid + MinRange.Pos / 2; } TargetFrameRange = TRange(Low, High); } if (HorizonScrolling) { const TRange& CurrentRange = TargetFrameRange; const AudioFrame WheelDelta = InMouseEvent.GetWheelDelta() * GetScalerLevel(); const AudioFrame Size = CurrentRange.Size(); const AudioFrame Low = FMath::Max(0.f, CurrentRange.GetLowerBoundValue() - WheelDelta); const AudioFrame High = FMath::Max(Low + Size, CurrentRange.GetUpperBoundValue() - WheelDelta); TargetFrameRange = TRange(Low, High); } return SCompoundWidget::OnMouseWheel(MyGeometry, InMouseEvent); } AudioFrame SPlayList::GetScalerLevel() { const MidiTick Scaler = PlayListPanel->GetPixelToSampleScaler().Ticks(); const FMidiSequencer& Sequencer = FMidiSequencer::Get(); const MidiTick Bar = Sequencer.GetBeat(); return Bar; if (Scaler > Bar) return Bar; const MidiTick Beat = Sequencer.GetBeat(); if (Scaler > Beat) return Beat; return Sequencer.GetTicksPerQuarter(); } END_SLATE_FUNCTION_BUILD_OPTIMIZATION