162 lines
5.6 KiB
C++
162 lines
5.6 KiB
C++
// Fill out your copyright notice in the Description page of Project Settings.
|
|
|
|
|
|
#include "SPianoKey.h"
|
|
|
|
#include "FontMeasure.h"
|
|
#include "SlateApplication.h"
|
|
#include "SlateOptMacros.h"
|
|
#include "Midi/MidiMessage.h"
|
|
#include "PluginHost/PluginHost.h"
|
|
#include "UI/Style/AronaStyle.h"
|
|
|
|
BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION
|
|
|
|
bool IsBetweenBlackKey(int32 NoteNumber)
|
|
{
|
|
return NoteNumber % 12 == 1 || NoteNumber % 12 == 3 || NoteNumber % 12 == 6 || NoteNumber % 12 == 8 || NoteNumber % 12 == 10;
|
|
}
|
|
|
|
void SPianoKey::Construct(const FArguments& InArgs, FPluginHost* InPluginHost)
|
|
{
|
|
PluginHost = InPluginHost;
|
|
KeyHeight = InArgs._KeyHeight;
|
|
KeyWidth = InArgs._KeyWidth;
|
|
}
|
|
|
|
int32 SPianoKey::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled) const
|
|
{
|
|
const FVector2D Size = AllottedGeometry.GetLocalSize();
|
|
const float BlackKeyHeight = KeyHeight.Get(); // 黑键始终为KeyHeight
|
|
const float SemitoneHeight = BlackKeyHeight * 1.5f; // 半音高度
|
|
const float WholeToneHeight = SemitoneHeight * 2; // 全音高度
|
|
const TSharedRef<FSlateFontMeasure> FontMeasureService = FSlateApplication::Get().GetRenderer()->GetFontMeasureService();
|
|
|
|
float CursorY = KeyHeight.Get() * FMidiMessage::MaxNoteNumber - (KeyHeight.Get() * 1.5);
|
|
FSlateFontInfo SlateFontInfo = FAronaStyle::GetFontInfo(DefaultFontName);
|
|
// 画白键
|
|
++LayerId;
|
|
for (int32 i = 0; i < FMidiMessage::MaxNoteNumber + 1; ++i)
|
|
{
|
|
if (FMidiMessage::isMidiNoteBlack(i))
|
|
continue;
|
|
const bool IsBetweenBlack = IsBetweenBlackKey(i);
|
|
const float CurrentKeyHeight = IsBetweenBlack ? SemitoneHeight : WholeToneHeight;
|
|
const FSlateLayoutTransform& InLayoutTransform = FSlateLayoutTransform(FVector2D(0, CursorY));
|
|
|
|
// 画白键
|
|
FSlateDrawElement::MakeBox(
|
|
OutDrawElements,
|
|
LayerId,
|
|
AllottedGeometry.ToPaintGeometry(FVector2f(Size.X, CurrentKeyHeight), InLayoutTransform),
|
|
FAronaStyle::GetSlateBrush(WhiteKey)
|
|
);
|
|
CursorY -= CurrentKeyHeight;
|
|
}
|
|
|
|
// 画黑键
|
|
++LayerId;
|
|
for (int32 i = 0; i < FMidiMessage::MaxNoteNumber + 1; ++i)
|
|
{
|
|
if (FMidiMessage::isMidiNoteWhite(i))
|
|
continue;
|
|
const FSlateLayoutTransform& InLayoutTransform = FSlateLayoutTransform(FVector2D(0, (FMidiMessage::MaxNoteNumber - i) * BlackKeyHeight));
|
|
// AllottedGeometry.ToPaintGeometry(FVector2f(0, (FMidiMessage::MaxNoteNumber - i) * BlackKeyHeight), FVector2f(Size.X * 0.7, BlackKeyHeight))
|
|
// 画黑键
|
|
FSlateDrawElement::MakeBox(
|
|
OutDrawElements,
|
|
LayerId,
|
|
AllottedGeometry.ToPaintGeometry(FVector2f(Size.X * 0.7, BlackKeyHeight), InLayoutTransform),
|
|
FAronaStyle::GetSlateBrush(BlackKey)
|
|
);
|
|
}
|
|
|
|
++LayerId;
|
|
// 画字
|
|
for (int i = 0; i < FMidiMessage::MaxNoteNumber + 1; ++i)
|
|
{
|
|
FString MidiNoteName = FMidiMessage::getMidiNoteName(i, true, true);
|
|
// 计算字体大小
|
|
const float FontSize = FontMeasureService->Measure(MidiNoteName, SlateFontInfo).X;
|
|
const FSlateLayoutTransform& InLayoutTransform = FSlateLayoutTransform(FVector2D(Size.X - FontSize, (FMidiMessage::MaxNoteNumber - i) * BlackKeyHeight));
|
|
|
|
// 画字
|
|
FSlateDrawElement::MakeText(
|
|
OutDrawElements,
|
|
LayerId,
|
|
AllottedGeometry.ToPaintGeometry(FVector2f(Size.X, BlackKeyHeight), InLayoutTransform),
|
|
MidiNoteName,
|
|
SlateFontInfo,
|
|
ESlateDrawEffect::None,
|
|
FLinearColor::Black
|
|
);
|
|
}
|
|
return LayerId;
|
|
}
|
|
|
|
FVector2D SPianoKey::ComputeDesiredSize(float LayoutScaleMultiplier) const
|
|
{
|
|
return FVector2D(KeyWidth.Get(), KeyHeight.Get() * FMidiMessage::MaxNoteNumber);
|
|
}
|
|
|
|
FReply SPianoKey::OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
|
{
|
|
if (MouseEvent.GetEffectingButton() == EKeys::LeftMouseButton)
|
|
{
|
|
const FVector2D LocalPos = MyGeometry.AbsoluteToLocal(MouseEvent.GetScreenSpacePosition());
|
|
const int32 NoteNumber = HeightToNoteNumber(LocalPos.Y);
|
|
if (NoteNumber >= 0 && NoteNumber <= FMidiMessage::MaxNoteNumber)
|
|
{
|
|
PluginHost->IncomingMidi.addEvent(FMidiMessage::noteOn(1, NoteNumber, WidthToVelocity(LocalPos.X)));
|
|
LastNoteNumber = NoteNumber;
|
|
}
|
|
return FReply::Handled().CaptureMouse(AsShared());
|
|
}
|
|
return FReply::Unhandled();
|
|
}
|
|
|
|
FReply SPianoKey::OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
|
{
|
|
if (MouseEvent.GetEffectingButton() == EKeys::LeftMouseButton)
|
|
{
|
|
if (LastNoteNumber >= 0 && LastNoteNumber <= FMidiMessage::MaxNoteNumber)
|
|
{
|
|
PluginHost->IncomingMidi.addEvent(FMidiMessage::noteOff(1, LastNoteNumber, 0.f));
|
|
}
|
|
LastNoteNumber = -1;
|
|
return FReply::Handled().ReleaseMouseCapture();
|
|
}
|
|
return FReply::Unhandled();
|
|
}
|
|
|
|
FReply SPianoKey::OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent)
|
|
{
|
|
if (MouseEvent.IsMouseButtonDown(EKeys::LeftMouseButton))
|
|
{
|
|
const int32 CurrentNoteNumber = HeightToNoteNumber(MyGeometry.AbsoluteToLocal(MouseEvent.GetScreenSpacePosition()).Y);
|
|
|
|
if (LastNoteNumber != CurrentNoteNumber)
|
|
{
|
|
if (LastNoteNumber != -1)
|
|
PluginHost->IncomingMidi.addEvent(FMidiMessage::noteOff(1, LastNoteNumber, 0.f));
|
|
LastNoteNumber = CurrentNoteNumber;
|
|
|
|
const float Velocity = WidthToVelocity(MyGeometry.AbsoluteToLocal(MouseEvent.GetScreenSpacePosition()).X);
|
|
PluginHost->IncomingMidi.addEvent(FMidiMessage::noteOn(1, CurrentNoteNumber, Velocity));
|
|
}
|
|
return FReply::Handled();
|
|
}
|
|
return FReply::Unhandled();
|
|
}
|
|
|
|
int32 SPianoKey::HeightToNoteNumber(float Height) const
|
|
{
|
|
return FMidiMessage::MaxNoteNumber - Height / KeyHeight.Get();
|
|
}
|
|
|
|
float SPianoKey::WidthToVelocity(float Width) const
|
|
{
|
|
return FMath::Min(Width / KeyWidth.Get(), 1.f);
|
|
}
|
|
END_SLATE_FUNCTION_BUILD_OPTIMIZATION
|