2024-01-25 11:21:15 +08:00

192 lines
5.4 KiB
C++

// Fill out your copyright notice in the Description page of Project Settings.
#include "SPianoRoll.h"
#include "SBackgroundBlur.h"
#include "SGridPanel.h"
#include "SImage.h"
#include "SlateOptMacros.h"
#include "SOverlay.h"
#include "SPianoKey.h"
#include "SScaleBox.h"
#include "SScrollBar.h"
#include "SScrollBox.h"
#include "Midi/MidiMessageSequence.h"
#include "Singleton/MidiSequencer.h"
#include "PianoRollPanel/SPianoRollPanel.h"
#include "UI/Style/AronaStyle.h"
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
void SPianoRoll::Construct(const FArguments& InArgs, FMidiMessageSequence* InMidiMessageSequence, FPluginHost* InPluginHost)
{
MidiMessageSequence = InMidiMessageSequence;
VerticalScrollBar = SNew(SScrollBar)
.Orientation(Orient_Vertical)
.AlwaysShowScrollbar(true)
.AlwaysShowScrollbarTrack(true);
ChildSlot
[
SNew(SGridPanel)
.FillColumn(0, 1)
.FillRow(0, 1)
+SGridPanel::Slot(0, 0)
[
SNew(SOverlay)
+SOverlay::Slot()
[
SNew(SScaleBox)
.Stretch(EStretch::ScaleToFill)
.HAlign(HAlign_Center)
.VAlign(VAlign_Center)
[
SNew(SImage)
.Image(FAronaStyle::GetSlateBrush(PianoRollBackground))
]
]
+SOverlay::Slot()
[
SAssignNew(ScrollBox, SScrollBox)
.ConsumeMouseWheel(EConsumeMouseWheel::Always)
.ExternalScrollbar(VerticalScrollBar)
.AnimateWheelScrolling(false)
.Visibility(EVisibility::SelfHitTestInvisible)
+SScrollBox::Slot()
[
SNew(SHorizontalBox)
+SHorizontalBox::Slot()
.AutoWidth()
[
SNew(SPianoKey, InPluginHost)
.KeyHeight(KeyHeight)
.KeyWidth(100)
]
+SHorizontalBox::Slot()
.FillWidth(1.f)
[
SAssignNew(PianoRollPanel, SPianoRollPanel, MidiMessageSequence)
.FontInfo(FAronaStyle::GetFontInfo(DefaultFontName))
.NoteImage(FAronaStyle::GetSlateBrush(VolumeMeterBar))
.SpacingLineColor(FLinearColor::Green)
.NoteHeight(KeyHeight)
.OnTargetRangeChanged(this, &SPianoRoll::SetFrameRange)
.OnYRequestUpdate(this, &SPianoRoll::AddYDelta)
.OnRequestHScroll(this, &SPianoRoll::OnPanelRequestHScroll)
]
]
]
]
+SGridPanel::Slot(1, 0)
[
VerticalScrollBar.ToSharedRef()
]
+SGridPanel::Slot(0, 1)
[
SAssignNew(HorizontalScrollBar, SScrollBar)
.Orientation(Orient_Horizontal)
.AlwaysShowScrollbar(true)
.AlwaysShowScrollbarTrack(true)
.OnUserScrolled(this, &SPianoRoll::OnHorizontalScrollBarChanged)
]
];
TargetMidiTickRange = PianoRollPanel->TimeRange;
const double EndTime = MidiMessageSequence->getEndTime();
HorizontalScrollBar->SetState(0, TargetMidiTickRange.Size<float>() / EndTime, false);
}
void SPianoRoll::Tick(const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime)
{
SCompoundWidget::Tick(AllottedGeometry, InCurrentTime, InDeltaTime);
if (AllottedGeometry.Size.X == 0)
return;
// const double Lower = FMath::FInterpTo<double>(PianoRollPanel->TimeRange.GetLowerBoundValue(), TargetMidiTickRange.GetLowerBoundValue(), InDeltaTime, 15.f);
// const double Upper = FMath::FInterpTo<double>(PianoRollPanel->TimeRange.GetUpperBoundValue(), TargetMidiTickRange.GetUpperBoundValue(), InDeltaTime, 15.f);
const double Lower = TargetMidiTickRange.GetLowerBoundValue();
const double Upper = TargetMidiTickRange.GetUpperBoundValue();
PianoRollPanel->TimeRange.SetLowerBoundValue(Lower);
PianoRollPanel->TimeRange.SetUpperBoundValue(Upper);
const float MidiTickDuration = PianoRollPanel->TimeRange.Size<float>();
const double EndTime = MidiMessageSequence->getEndTime();
HorizontalScrollBar->SetState(Lower / EndTime, MidiTickDuration / EndTime, false);
}
FReply SPianoRoll::OnMouseWheel(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
{
if (EnableHorizontalScroll)
{
AddFrameRangeDelta(MouseEvent.GetWheelDelta() * -FMidiSequencer::Get().GetTicksPerQuarter());
return FReply::Unhandled();
}
return FReply::Unhandled();
}
void SPianoRoll::AddFrameRangeDelta(AudioFrame InDelta)
{
double Lower = TargetMidiTickRange.GetLowerBoundValue() + InDelta;
double Upper = TargetMidiTickRange.GetUpperBoundValue() + InDelta;
if (Lower < 0)
{
Upper -= Lower;
Lower = 0;
}
TargetMidiTickRange = TRange<AudioFrame>(Lower, Upper);
}
void SPianoRoll::SetFrameRange(TRange<AudioFrame> InMidiRange)
{
TargetMidiTickRange = InMidiRange;
}
void SPianoRoll::AddYDelta(float InDelta)
{
ScrollBox->SetScrollOffset(ScrollBox->GetScrollOffset() + InDelta);
}
void SPianoRoll::InitScrollBar()
{
OnHorizontalScrollBarChanged(0);
}
TSharedPtr<SWidget> SPianoRoll::GetFocusWidget()
{
return PianoRollPanel;
}
void SPianoRoll::OnHorizontalScrollBarChanged(float InScrollOffsetFraction)
{
const auto& PanelSize = PianoRollPanel->GetTickSpaceGeometry().Size;
if (PanelSize.X == 0)
return;
const MidiTick EndTime = MidiMessageSequence->getEndTime();
const MidiTick MidiTickDuration = PianoRollPanel->TimeRange.Size<double>();
const MidiTick StartTick = EndTime * InScrollOffsetFraction;
TargetMidiTickRange = TRange<AudioFrame>(StartTick.Frames(), StartTick.Frames() + MidiTickDuration.Frames());
// HorizontalScrollBar->SetState(InScrollOffsetFraction, MidiTickDuration / EndTime, false);
}
void SPianoRoll::OnPanelRequestHScroll(bool InStatus)
{
if (InStatus)
{
EnableHorizontalScroll = true;
ScrollBox->SetConsumeMouseWheel(EConsumeMouseWheel::Never);
}
else
{
EnableHorizontalScroll = false;
ScrollBox->SetConsumeMouseWheel(EConsumeMouseWheel::Always);
}
}
END_SLATE_FUNCTION_BUILD_OPTIMIZATION