// 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() / 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(PianoRollPanel->TimeRange.GetLowerBoundValue(), TargetMidiTickRange.GetLowerBoundValue(), InDeltaTime, 15.f); // const double Upper = FMath::FInterpTo(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(); 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(Lower, Upper); } void SPianoRoll::SetFrameRange(TRange InMidiRange) { TargetMidiTickRange = InMidiRange; } void SPianoRoll::AddYDelta(float InDelta) { ScrollBox->SetScrollOffset(ScrollBox->GetScrollOffset() + InDelta); } void SPianoRoll::InitScrollBar() { OnHorizontalScrollBarChanged(0); } TSharedPtr 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(); const MidiTick StartTick = EndTime * InScrollOffsetFraction; TargetMidiTickRange = TRange(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