139 lines
4.5 KiB
C++

// 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<AudioFrame>(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<double>(FrameRange.GetLowerBoundValue(), TargetFrameRange.GetLowerBoundValue(), InDeltaTime, 20.f));
FrameRange.SetUpperBoundValue(FMath::FInterpTo<double>(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<AudioFrame>& CurrentRange = TargetFrameRange;
const AudioFrame WheelDelta = InMouseEvent.GetWheelDelta() * GetScalerLevel() * 2; // 乘以2是因为MousePosPercent
if (WheelDelta.Pos > 0 && CurrentRange.Size<AudioFrame>() <= 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<AudioFrame>(Low, High);
}
if (HorizonScrolling)
{
const TRange<AudioFrame>& CurrentRange = TargetFrameRange;
const AudioFrame WheelDelta = InMouseEvent.GetWheelDelta() * GetScalerLevel();
const AudioFrame Size = CurrentRange.Size<AudioFrame>();
const AudioFrame Low = FMath::Max(0.f, CurrentRange.GetLowerBoundValue() - WheelDelta);
const AudioFrame High = FMath::Max(Low + Size, CurrentRange.GetUpperBoundValue() - WheelDelta);
TargetFrameRange = TRange<AudioFrame>(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