286 lines
9.4 KiB
C++
286 lines
9.4 KiB
C++
// Fill out your copyright notice in the Description page of Project Settings.
|
|
|
|
|
|
#include "SPlayListPanel.h"
|
|
|
|
#include "PlayListEdit.h"
|
|
#include "Singleton/PortAudioAPI.h"
|
|
#include "SConstraintCanvas.h"
|
|
#include "SInvalidationPanel.h"
|
|
#include "SlateApplication.h"
|
|
#include "SlateOptMacros.h"
|
|
#include "SScrollBox.h"
|
|
#include "SViewport.h"
|
|
#include "Singleton/MidiSequencer.h"
|
|
#include "Render/UpdatableTexture.h"
|
|
#include "UI/Widget/PlayList/SPlayListTimeLine.h"
|
|
|
|
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
|
|
|
void SPlayListPanel::Construct(const FArguments& InArgs)
|
|
{
|
|
PlayListEdit = MakeShareable(new FPlayListEdit(SharedThis(this)));
|
|
TrackHeightManager.TrackHeight = InArgs._TrackHeight;
|
|
ViewRange = InArgs._FrameRange;
|
|
|
|
FMidiSequencer::Get().OnCreatePatternInstance.AddRaw(this, &SPlayListPanel::OnCreatePatternInstance);
|
|
FMidiSequencer::Get().OnDeletePatternInstance.AddRaw(this, &SPlayListPanel::OnDeletePatternInstance);
|
|
|
|
ChildSlot
|
|
[
|
|
SAssignNew(TrackCanvas, SConstraintCanvas)
|
|
];
|
|
|
|
const TMap<EPatternType, TArray<FPattern*>>& Patterns = FMidiSequencer::Get().GetPattern();
|
|
for (const auto& Tuple : Patterns)
|
|
{
|
|
for (const FPattern* Pattern : Tuple.Value)
|
|
{
|
|
const TArray<FPatternInstance*>& PatternInstances = Pattern->GetInstances();
|
|
for (FPatternInstance* Instance : PatternInstances)
|
|
{
|
|
OnCreatePatternInstance(Instance);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int32 SPlayListPanel::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const
|
|
{
|
|
const auto& Size = AllottedGeometry.Size;
|
|
const TRange<AudioFrame>& Range = ViewRange.Get();
|
|
const double FrameToPixelScaler = Size.X / Range.Size<double>();
|
|
const FMidiSequencer& MidiSequencer = FMidiSequencer::Get();
|
|
const AudioFrame Frame = MidiSequencer.FramePos;
|
|
// const int32 Denominator = MidiSequencer.Denominator;
|
|
// const int32 Numerator = MidiSequencer.Numerator;
|
|
const AudioFrame BarFrameCount = MidiSequencer.GetBar().Frames();
|
|
const AudioFrame BeatFrameCount = MidiSequencer.GetBeat().Frames();
|
|
const AudioFrame RangeBegin = Range.GetLowerBoundValue();
|
|
const AudioFrame RangeEnd = Range.GetUpperBoundValue();
|
|
|
|
++LayerId;
|
|
// 绘制轨道间隔线
|
|
for (int i = 0; i < TrackCount; ++i)
|
|
{
|
|
const float TrackPos = TrackHeightManager.GetTrackPos(i);
|
|
// const float TrackHeight = GetTrackHeight(i);
|
|
TArray<FVector2f> Points;
|
|
Points.Add(FVector2f(0, TrackPos));
|
|
Points.Add(FVector2f(AllottedGeometry.Size.X, TrackPos));
|
|
FSlateDrawElement::MakeLines(
|
|
OutDrawElements,
|
|
LayerId,
|
|
AllottedGeometry.ToPaintGeometry(),
|
|
Points,
|
|
ESlateDrawEffect::None,
|
|
FLinearColor::White,
|
|
true,
|
|
1.f
|
|
);
|
|
}
|
|
|
|
++LayerId;
|
|
// 绘制时间间隔线
|
|
const AudioFrame BarDelta = FMath::Fmod(RangeBegin.Pos, BarFrameCount);
|
|
for (AudioFrame i = RangeBegin - BarDelta; i < RangeEnd; i += BarFrameCount)
|
|
{
|
|
TArray<FVector2f> Points;
|
|
AudioFrame InX = (i - RangeBegin) * FrameToPixelScaler;
|
|
Points.Add(FVector2f(InX, 0));
|
|
Points.Add(FVector2f(InX, Size.Y));
|
|
FSlateDrawElement::MakeLines(
|
|
OutDrawElements,
|
|
++LayerId,
|
|
AllottedGeometry.ToPaintGeometry(),
|
|
Points,
|
|
ESlateDrawEffect::None,
|
|
SpacingLineColor,
|
|
true,
|
|
SpacingLineThickness
|
|
);
|
|
}
|
|
|
|
++LayerId;
|
|
const AudioFrame BeatDelta = FMath::Fmod(RangeBegin, BeatFrameCount);
|
|
for (float i = RangeBegin - BeatDelta; i < RangeEnd; i += BeatFrameCount)
|
|
{
|
|
TArray<FVector2f> Points;
|
|
Points.Add(FVector2f((i - RangeBegin) * FrameToPixelScaler, 0));
|
|
Points.Add(FVector2f((i - RangeBegin) * FrameToPixelScaler, Size.Y));
|
|
FSlateDrawElement::MakeLines(
|
|
OutDrawElements,
|
|
LayerId,
|
|
AllottedGeometry.ToPaintGeometry(),
|
|
Points,
|
|
ESlateDrawEffect::None,
|
|
SpacingLineColor,
|
|
true,
|
|
SpacingLineThickness
|
|
);
|
|
}
|
|
|
|
++LayerId;
|
|
{
|
|
const AudioFrame CurrentFrameX = (Frame - RangeBegin) * FrameToPixelScaler;
|
|
TArray<FVector2f> Points;
|
|
Points.Add(FVector2f(CurrentFrameX, 0));
|
|
Points.Add(FVector2f(CurrentFrameX, Size.Y));
|
|
FSlateDrawElement::MakeLines(
|
|
OutDrawElements,
|
|
++LayerId,
|
|
AllottedGeometry.ToPaintGeometry(),
|
|
Points,
|
|
ESlateDrawEffect::None,
|
|
FLinearColor::Red,
|
|
true,
|
|
1
|
|
);
|
|
}
|
|
|
|
LayerId = PlayListEdit->OnPaint(Args, AllottedGeometry, MyCullingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled);
|
|
|
|
return SCompoundWidget::OnPaint(Args, AllottedGeometry, MyCullingRect, OutDrawElements, LayerId, InWidgetStyle, bParentEnabled);
|
|
}
|
|
|
|
FVector2D SPlayListPanel::ComputeDesiredSize(float LayoutScaleMultiplier) const
|
|
{
|
|
FVector2D Size;
|
|
Size.X = GetTickSpaceGeometry().Size.X;
|
|
Size.Y = TrackHeightManager.GetTrackHeight(TrackCount - 1) + TrackHeightManager.GetTrackPos(TrackCount - 1);
|
|
return Size;
|
|
}
|
|
|
|
FReply SPlayListPanel::OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
|
{
|
|
return PlayListEdit->OnMouseButtonDown(MyGeometry, MouseEvent);
|
|
}
|
|
|
|
FReply SPlayListPanel::OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
|
{
|
|
return PlayListEdit->OnMouseButtonUp(MyGeometry, MouseEvent);
|
|
}
|
|
|
|
FReply SPlayListPanel::OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
|
{
|
|
return PlayListEdit->OnMouseMove(MyGeometry, MouseEvent);
|
|
}
|
|
|
|
FReply SPlayListPanel::OnMouseWheel(const FGeometry& MyGeometry, const FPointerEvent& InMouseEvent)
|
|
{
|
|
return SCompoundWidget::OnMouseWheel(MyGeometry, InMouseEvent);
|
|
}
|
|
|
|
FReply SPlayListPanel::OnKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent)
|
|
{
|
|
return PlayListEdit->OnKeyDown(MyGeometry, InKeyEvent);
|
|
}
|
|
|
|
FReply SPlayListPanel::OnKeyUp(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent)
|
|
{
|
|
return PlayListEdit->OnKeyUp(MyGeometry, InKeyEvent);
|
|
}
|
|
|
|
float SPlayListPanel::MidiTickToPixel(MidiTick InMidiTick) const
|
|
{
|
|
return FrameToPixel(InMidiTick.Frames());
|
|
}
|
|
|
|
float SPlayListPanel::FrameToPixel(AudioFrame InFrame) const
|
|
{
|
|
const TRange<double> WidgetRange(0, GetTickSpaceGeometry().Size.X);
|
|
const TRange<AudioFrame>& CurrentRange = ViewRange.Get();
|
|
const double FrameBegin = CurrentRange.GetLowerBoundValue();
|
|
const double FrameEnd = CurrentRange.GetUpperBoundValue();
|
|
|
|
const TRange<double> TempSampleRange(FrameBegin, FrameEnd);
|
|
return FMath::GetMappedRangeValueClamped<double>(TempSampleRange, WidgetRange, InFrame);
|
|
}
|
|
|
|
AudioFrame SPlayListPanel::GetSampleToPixelScaler() const
|
|
{
|
|
return GetTickSpaceGeometry().Size.X / ViewRange.Get().Size<AudioFrame>();
|
|
}
|
|
|
|
AudioFrame SPlayListPanel::GetPixelToSampleScaler() const
|
|
{
|
|
return ViewRange.Get().Size<AudioFrame>() / static_cast<double>(GetTickSpaceGeometry().Size.X);
|
|
}
|
|
|
|
FMargin SPlayListPanel::CalculatePatternInstancePos(FPatternInstance* PatternInstance) const
|
|
{
|
|
const double Scaler = GetSampleToPixelScaler();
|
|
check(PatternInstance->TrackIndex >= 0)
|
|
|
|
const TRange<AudioFrame>& Range = ViewRange.Get();
|
|
|
|
FMargin Offset;
|
|
const AudioFrame XOffset = PatternInstance->Pos - Range.GetLowerBoundValue();
|
|
|
|
Offset.Left = XOffset.Pos * Scaler; // X
|
|
Offset.Left = FMath::Max(Offset.Left, 0.0f);
|
|
Offset.Top = TrackHeightManager.GetTrackPos(PatternInstance->TrackIndex); // Y
|
|
|
|
Offset.Bottom = TrackHeightManager.GetTrackHeight(PatternInstance->TrackIndex); // Height
|
|
|
|
float LeftWidth = (PatternInstance->GetLength() + PatternInstance->Pos) - Range.GetLowerBoundValue();
|
|
LeftWidth = FMath::Min(PatternInstance->GetLength().Pos, LeftWidth);
|
|
Offset.Right = LeftWidth * Scaler; // Width
|
|
Offset.Right = FMath::Min(Offset.Right, GetTickSpaceGeometry().Size.X - Offset.Left);
|
|
|
|
return Offset;
|
|
}
|
|
|
|
void SPlayListPanel::OnCreatePatternInstance(FPatternInstance* PatternInstance)
|
|
{
|
|
const TAttribute<FMargin> Offset = TAttribute<FMargin>::Create(TAttribute<FMargin>::FGetter::CreateSP(this, &SPlayListPanel::CalculatePatternInstancePos, PatternInstance));
|
|
|
|
TrackCanvas->AddSlot()
|
|
.Anchors(FAnchors(0, 0, 0, 0))
|
|
.Alignment(FVector2D(0, 0))
|
|
.Offset(Offset)
|
|
[
|
|
// SNew(SInvalidationPanel)
|
|
// [
|
|
PlayListEdit->CreatePatternInstanceWidget(PatternInstance)
|
|
// ]
|
|
];
|
|
}
|
|
|
|
void SPlayListPanel::OnDeletePatternInstance(FPatternInstance* PatternInstance)
|
|
{
|
|
TPanelChildren<SConstraintCanvas::FSlot>* Children = (TPanelChildren<SConstraintCanvas::FSlot>*)TrackCanvas->GetChildren();
|
|
for (int i = 0; i < Children->Num(); ++i)
|
|
{
|
|
TSharedRef<SWidget> Widget = Children->GetChildAt(i);
|
|
// TSharedRef<SInvalidationPanel> InvalidationPanel = StaticCastSharedRef<SInvalidationPanel>(Widget);
|
|
// InvalidationPanel->SetCanCache(false);
|
|
// TSharedRef<SWidget> SharedRef = InvalidationPanel->GetChildren()->GetChildAt(0);
|
|
// InvalidationPanel->SetCanCache(true);
|
|
TSharedRef<SPatternInstance> InstanceWidget = StaticCastSharedRef<SPatternInstance>(Widget);
|
|
if (InstanceWidget->GetPatternInstance() == PatternInstance)
|
|
{
|
|
Children->RemoveAt(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SPlayListPanel::UpdateAllPatternInstance()
|
|
{
|
|
TPanelChildren<SConstraintCanvas::FSlot>* Children = (TPanelChildren<SConstraintCanvas::FSlot>*)TrackCanvas->GetChildren();
|
|
for (int i = 0; i < Children->Num(); ++i)
|
|
{
|
|
TSharedRef<SWidget> Widget = Children->GetChildAt(i);
|
|
TSharedRef<SInvalidationPanel> InvalidationPanel = StaticCastSharedRef<SInvalidationPanel>(Widget);
|
|
InvalidationPanel->SetCanCache(false);
|
|
TSharedRef<SWidget> SharedRef = InvalidationPanel->GetChildren()->GetChildAt(0);
|
|
InvalidationPanel->SetCanCache(true);
|
|
TSharedRef<SPatternInstance> InstanceWidget = StaticCastSharedRef<SPatternInstance>(SharedRef);
|
|
InstanceWidget->RequestUpdate();
|
|
// InstanceWidget->Invalidate(EInvalidateWidgetReason::Paint);
|
|
}
|
|
}
|
|
|
|
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
|