diff --git a/Plugins/NaWidgets/Source/NaWidgets/NaWidgets.Build.cs b/Plugins/NaWidgets/Source/NaWidgets/NaWidgets.Build.cs index 81124cc..5e13e06 100644 --- a/Plugins/NaWidgets/Source/NaWidgets/NaWidgets.Build.cs +++ b/Plugins/NaWidgets/Source/NaWidgets/NaWidgets.Build.cs @@ -38,7 +38,8 @@ public NaWidgets(ReadOnlyTargetRules Target) : base(Target) "CoreUObject", "Engine", "Slate", - "SlateCore" + "SlateCore", + "UMG" // ... add private dependencies that you statically link with here ... } ); diff --git a/Plugins/NaWidgets/Source/NaWidgets/Private/Widgets/WindowWidgets/NaBorderedWindow.cpp b/Plugins/NaWidgets/Source/NaWidgets/Private/Widgets/WindowWidgets/NaBorderedWindow.cpp new file mode 100644 index 0000000..7467cad --- /dev/null +++ b/Plugins/NaWidgets/Source/NaWidgets/Private/Widgets/WindowWidgets/NaBorderedWindow.cpp @@ -0,0 +1,62 @@ +// By Sodium + +#include "Widgets/WindowWidgets/NaBorderedWindow.h" + +TSharedRef UNaBorderedWindow::RebuildWidget() +{ + TSharedPtr ContentSlate; + if (Content) + ContentSlate = Content->TakeWidget(); + + SAssignNew(BorderedWindow, SNaBorderedWindow) + .Params(&Params) + .MinBodySize(MinBodySize) + .MaxBodySize(MaxBodySize) + .Content(ContentSlate); + + return BorderedWindow.ToSharedRef(); +} + +void UNaBorderedWindow::ReleaseSlateResources(bool bReleaseChildren) +{ + Super::ReleaseSlateResources(bReleaseChildren); + BorderedWindow.Reset(); +} + +void UNaBorderedWindow::SynchronizeProperties() +{ + Super::SynchronizeProperties(); + if (BorderedWindow.IsValid()) + { + BorderedWindow->UpdateImages(Params); + BorderedWindow->SetBorderWidths(Params.BorderTop, Params.BorderBottom, Params.BorderLeft, Params.BorderRight); + BorderedWindow->SetBodySize(Params.BodySize); + } +} + +const FText UNaBorderedWindow::GetPaletteCategory() +{ + return FText::FromString(TEXT("NaWidgets")); +} + +void UNaBorderedWindow::SetBodySize(FVector2D NewSize) +{ + Params.BodySize = NewSize; + if (BorderedWindow.IsValid()) + BorderedWindow->SetBodySize(NewSize); +} + +void UNaBorderedWindow::SetBorderWidths(float Top, float Bottom, float Left, float Right) +{ + Params.BorderTop = Top; + Params.BorderBottom = Bottom; + Params.BorderLeft = Left; + Params.BorderRight = Right; + if (BorderedWindow.IsValid()) + BorderedWindow->SetBorderWidths(Top, Bottom, Left, Right); +} + +FVector2D UNaBorderedWindow::GetBodySize() const +{ + return Params.BodySize; +} diff --git a/Plugins/NaWidgets/Source/NaWidgets/Private/Widgets/WindowWidgets/SNaBorderedWindow.cpp b/Plugins/NaWidgets/Source/NaWidgets/Private/Widgets/WindowWidgets/SNaBorderedWindow.cpp index e69de29..cb41564 100644 --- a/Plugins/NaWidgets/Source/NaWidgets/Private/Widgets/WindowWidgets/SNaBorderedWindow.cpp +++ b/Plugins/NaWidgets/Source/NaWidgets/Private/Widgets/WindowWidgets/SNaBorderedWindow.cpp @@ -0,0 +1,264 @@ +// By Sodium + +#include "Widgets/WindowWidgets/SNaBorderedWindow.h" +#include "SlateOptMacros.h" +#include "Widgets/Images/SImage.h" +#include "Widgets/SCanvas.h" +#include "Widgets/Layout/SBox.h" +#include "Widgets/SNullWidget.h" +#include "Styling/CoreStyle.h" + +static FSlateBrush MakeBorderedWindowBrush(UObject* ResourceObj, FVector2D Size) +{ + FSlateBrush Brush = *FCoreStyle::Get().GetDefaultBrush(); + Brush.SetResourceObject(ResourceObj); + Brush.SetImageSize(Size); + Brush.Tiling = ESlateBrushTileType::NoTile; + return Brush; +} + +BEGIN_SLATE_FUNCTION_BUILD_OPTIMIZATION +void SNaBorderedWindow::Construct(const FArguments& InArgs) +{ + if (InArgs._Params.Get()) + Params = *InArgs._Params.Get(); + MinBodySize = InArgs._MinBodySize.Get(); + MaxBodySize = InArgs._MaxBodySize.Get(); + ContentWidget = InArgs._Content.Get(); + + // Initialize brushes from params + BrushTopLeft = MakeBorderedWindowBrush(Params.ImageTopLeft, FVector2D(Params.BorderLeft, Params.BorderTop)); + BrushTop = MakeBorderedWindowBrush(Params.ImageTop, FVector2D(Params.BodySize.X, Params.BorderTop)); + BrushTopRight = MakeBorderedWindowBrush(Params.ImageTopRight, FVector2D(Params.BorderRight, Params.BorderTop)); + BrushLeft = MakeBorderedWindowBrush(Params.ImageLeft, FVector2D(Params.BorderLeft, Params.BodySize.Y)); + BrushCenter = MakeBorderedWindowBrush(Params.ImageCenter, Params.BodySize); + BrushRight = MakeBorderedWindowBrush(Params.ImageRight, FVector2D(Params.BorderRight, Params.BodySize.Y)); + BrushBottomLeft = MakeBorderedWindowBrush(Params.ImageBottomLeft, FVector2D(Params.BorderLeft, Params.BorderBottom)); + BrushBottom = MakeBorderedWindowBrush(Params.ImageBottom, FVector2D(Params.BodySize.X, Params.BorderBottom)); + BrushBottomRight = MakeBorderedWindowBrush(Params.ImageBottomRight, FVector2D(Params.BorderRight, Params.BorderBottom)); + + // Create image sub-widgets + SAssignNew(ImgTopLeft, SImage).Image(&BrushTopLeft); + SAssignNew(ImgTop, SImage).Image(&BrushTop); + SAssignNew(ImgTopRight, SImage).Image(&BrushTopRight); + SAssignNew(ImgLeft, SImage).Image(&BrushLeft); + SAssignNew(ImgCenter, SImage).Image(&BrushCenter); + SAssignNew(ImgRight, SImage).Image(&BrushRight); + SAssignNew(ImgBottomLeft, SImage).Image(&BrushBottomLeft); + SAssignNew(ImgBottom, SImage).Image(&BrushBottom); + SAssignNew(ImgBottomRight, SImage).Image(&BrushBottomRight); + + RebuildLayout(); +} +END_SLATE_FUNCTION_BUILD_OPTIMIZATION + +void SNaBorderedWindow::RebuildLayout() +{ + // Update brush sizes to match current params + BrushTopLeft.SetImageSize(FVector2D(Params.BorderLeft, Params.BorderTop)); + BrushTop.SetImageSize(FVector2D(Params.BodySize.X, Params.BorderTop)); + BrushTopRight.SetImageSize(FVector2D(Params.BorderRight, Params.BorderTop)); + BrushLeft.SetImageSize(FVector2D(Params.BorderLeft, Params.BodySize.Y)); + BrushCenter.SetImageSize(Params.BodySize); + BrushRight.SetImageSize(FVector2D(Params.BorderRight, Params.BodySize.Y)); + BrushBottomLeft.SetImageSize(FVector2D(Params.BorderLeft, Params.BorderBottom)); + BrushBottom.SetImageSize(FVector2D(Params.BodySize.X, Params.BorderBottom)); + BrushBottomRight.SetImageSize(FVector2D(Params.BorderRight, Params.BorderBottom)); + + const FVector2D TotalSize( + Params.BorderLeft + Params.BodySize.X + Params.BorderRight, + Params.BorderTop + Params.BodySize.Y + Params.BorderBottom + ); + + // Slot positions + const FVector2D PosTopLeft (0.f, 0.f); + const FVector2D PosTop (Params.BorderLeft, 0.f); + const FVector2D PosTopRight (Params.BorderLeft + Params.BodySize.X, 0.f); + const FVector2D PosLeft (0.f, Params.BorderTop); + const FVector2D PosCenter (Params.BorderLeft, Params.BorderTop); + const FVector2D PosRight (Params.BorderLeft + Params.BodySize.X, Params.BorderTop); + const FVector2D PosBottomLeft (0.f, Params.BorderTop + Params.BodySize.Y); + const FVector2D PosBottom (Params.BorderLeft, Params.BorderTop + Params.BodySize.Y); + const FVector2D PosBottomRight(Params.BorderLeft + Params.BodySize.X, Params.BorderTop + Params.BodySize.Y); + + // Slot sizes + const FVector2D SzCornerTL(Params.BorderLeft, Params.BorderTop); + const FVector2D SzTop (Params.BodySize.X, Params.BorderTop); + const FVector2D SzCornerTR(Params.BorderRight, Params.BorderTop); + const FVector2D SzLeft (Params.BorderLeft, Params.BodySize.Y); + const FVector2D SzCenter (Params.BodySize); + const FVector2D SzRight (Params.BorderRight, Params.BodySize.Y); + const FVector2D SzCornerBL(Params.BorderLeft, Params.BorderBottom); + const FVector2D SzBottom (Params.BodySize.X, Params.BorderBottom); + const FVector2D SzCornerBR(Params.BorderRight, Params.BorderBottom); + + // Optional content widget placed over the center area + const TSharedRef CenterContent = ContentWidget.IsValid() + ? ContentWidget.ToSharedRef() + : SNullWidget::NullWidget; + + // Rebuild canvas with updated absolute positions/sizes + SAssignNew(Canvas, SCanvas) + + SCanvas::Slot().Position(PosTopLeft).Size(SzCornerTL) [ImgTopLeft.ToSharedRef()] + + SCanvas::Slot().Position(PosTop).Size(SzTop) [ImgTop.ToSharedRef()] + + SCanvas::Slot().Position(PosTopRight).Size(SzCornerTR) [ImgTopRight.ToSharedRef()] + + SCanvas::Slot().Position(PosLeft).Size(SzLeft) [ImgLeft.ToSharedRef()] + + SCanvas::Slot().Position(PosCenter).Size(SzCenter) [ImgCenter.ToSharedRef()] + + SCanvas::Slot().Position(PosRight).Size(SzRight) [ImgRight.ToSharedRef()] + + SCanvas::Slot().Position(PosBottomLeft).Size(SzCornerBL) [ImgBottomLeft.ToSharedRef()] + + SCanvas::Slot().Position(PosBottom).Size(SzBottom) [ImgBottom.ToSharedRef()] + + SCanvas::Slot().Position(PosBottomRight).Size(SzCornerBR)[ImgBottomRight.ToSharedRef()] + + SCanvas::Slot().Position(PosCenter).Size(SzCenter) [CenterContent]; + + // Outer canvas: its single slot's position is driven by WindowPosition so that + // dragging changes the real layout position, moving all children with it. + SAssignNew(OuterCanvas, SCanvas) + + SCanvas::Slot() + .Position(TAttribute::CreateSP(this, &SNaBorderedWindow::GetWindowPosition)) + .Size(TotalSize) + [ + SNew(SBox) + .WidthOverride(TotalSize.X) + .HeightOverride(TotalSize.Y) + [ + Canvas.ToSharedRef() + ] + ]; + + ChildSlot + [ + OuterCanvas.ToSharedRef() + ]; +} + +FVector2D SNaBorderedWindow::ClampBodySize(FVector2D Size) const +{ + return FVector2D( + FMath::Clamp(Size.X, MinBodySize.X, MaxBodySize.X), + FMath::Clamp(Size.Y, MinBodySize.Y, MaxBodySize.Y) + ); +} + +void SNaBorderedWindow::SetBodySize(FVector2D NewSize) +{ + Params.BodySize = ClampBodySize(NewSize); + RebuildLayout(); +} + +void SNaBorderedWindow::SetBorderWidths(float Top, float Bottom, float Left, float Right) +{ + Params.BorderTop = Top; + Params.BorderBottom = Bottom; + Params.BorderLeft = Left; + Params.BorderRight = Right; + RebuildLayout(); +} + +void SNaBorderedWindow::UpdateImages(const FNaBorderedWindowParams& NewParams) +{ + Params = NewParams; + BrushTopLeft.SetResourceObject(Params.ImageTopLeft); + BrushTop.SetResourceObject(Params.ImageTop); + BrushTopRight.SetResourceObject(Params.ImageTopRight); + BrushLeft.SetResourceObject(Params.ImageLeft); + BrushCenter.SetResourceObject(Params.ImageCenter); + BrushRight.SetResourceObject(Params.ImageRight); + BrushBottomLeft.SetResourceObject(Params.ImageBottomLeft); + BrushBottom.SetResourceObject(Params.ImageBottom); + BrushBottomRight.SetResourceObject(Params.ImageBottomRight); + RebuildLayout(); +} + +FReply SNaBorderedWindow::OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) +{ + if (MouseEvent.GetEffectingButton() == EKeys::LeftMouseButton) + { + const FVector2D LocalPos = MyGeometry.AbsoluteToLocal(MouseEvent.GetScreenSpacePosition()); + const EWindowRegion Region = GetRegionAtPosition(MyGeometry, LocalPos); + + if (Region == EWindowRegion::TopBorder) + { + bIsDragging = true; + DragStartPosition = MouseEvent.GetScreenSpacePosition(); + DragStartWindowPosition = WindowPosition; + return FReply::Handled().CaptureMouse(SharedThis(this)); + } + else if (Region == EWindowRegion::BottomRightCorner) + { + bIsResizing = true; + DragStartPosition = LocalPos; + DragStartBodySize = Params.BodySize; + return FReply::Handled().CaptureMouse(SharedThis(this)); + } + } + + return FReply::Unhandled(); +} + +FReply SNaBorderedWindow::OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) +{ + if (MouseEvent.GetEffectingButton() == EKeys::LeftMouseButton) + { + if (bIsDragging || bIsResizing) + { + bIsDragging = false; + bIsResizing = false; + return FReply::Handled().ReleaseMouseCapture(); + } + } + + return FReply::Unhandled(); +} + +FReply SNaBorderedWindow::OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) +{ + if (bIsResizing) + { + const FVector2D CurrentPos = MyGeometry.AbsoluteToLocal(MouseEvent.GetScreenSpacePosition()); + const FVector2D Delta = CurrentPos - DragStartPosition; + + FVector2D NewBodySize = DragStartBodySize + Delta; + Params.BodySize = ClampBodySize(NewBodySize); + RebuildLayout(); + return FReply::Handled(); + } + else if (bIsDragging) + { + const FVector2D Delta = MouseEvent.GetScreenSpacePosition() - DragStartPosition; + WindowPosition = DragStartWindowPosition + Delta; + OuterCanvas->Invalidate(EInvalidateWidgetReason::Layout); + return FReply::Handled(); + } + + return FReply::Unhandled(); +} + +SNaBorderedWindow::EWindowRegion SNaBorderedWindow::GetRegionAtPosition( + const FGeometry& MyGeometry, const FVector2D& LocalPosition) const +{ + // Bottom-right corner + if (LocalPosition.X >= Params.BorderLeft + Params.BodySize.X && + LocalPosition.Y >= Params.BorderTop + Params.BodySize.Y) + { + return EWindowRegion::BottomRightCorner; + } + + // Top border (excluding corners) + if (LocalPosition.Y < Params.BorderTop && + LocalPosition.X >= Params.BorderLeft && + LocalPosition.X < Params.BorderLeft + Params.BodySize.X) + { + return EWindowRegion::TopBorder; + } + + // Center + if (LocalPosition.X >= Params.BorderLeft && + LocalPosition.X < Params.BorderLeft + Params.BodySize.X && + LocalPosition.Y >= Params.BorderTop && + LocalPosition.Y < Params.BorderTop + Params.BodySize.Y) + { + return EWindowRegion::Center; + } + + return EWindowRegion::None; +} diff --git a/Plugins/NaWidgets/Source/NaWidgets/Public/Widgets/WindowWidgets/NaBorderedWindow.h b/Plugins/NaWidgets/Source/NaWidgets/Public/Widgets/WindowWidgets/NaBorderedWindow.h new file mode 100644 index 0000000..66385a4 --- /dev/null +++ b/Plugins/NaWidgets/Source/NaWidgets/Public/Widgets/WindowWidgets/NaBorderedWindow.h @@ -0,0 +1,50 @@ +// By Sodium + +#pragma once + +#include "CoreMinimal.h" +#include "Components/Widget.h" +#include "Widgets/WindowWidgets/SNaBorderedWindow.h" +#include "NaBorderedWindow.generated.h" + +/** + * UMG wrapper for SNaBorderedWindow. + * Exposes the 9-part bordered window widget for use in UMG and Blueprints. + */ +UCLASS() +class NAWIDGETS_API UNaBorderedWindow : public UWidget +{ + GENERATED_BODY() + +public: + /** Image and size configuration for all 9 parts. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Appearance") + FNaBorderedWindowParams Params; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Sizing") + FVector2D MinBodySize = FVector2D(50.f, 50.f); + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Sizing") + FVector2D MaxBodySize = FVector2D(1000.f, 1000.f); + + /** Optional widget placed over the center body area. */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Content") + UWidget* Content = nullptr; + + UFUNCTION(BlueprintCallable, Category = "Bordered Window") + void SetBodySize(FVector2D NewSize); + + UFUNCTION(BlueprintCallable, Category = "Bordered Window") + void SetBorderWidths(float Top, float Bottom, float Left, float Right); + + UFUNCTION(BlueprintCallable, Category = "Bordered Window") + FVector2D GetBodySize() const; + + TSharedPtr BorderedWindow; + +protected: + virtual TSharedRef RebuildWidget() override; + virtual void ReleaseSlateResources(bool bReleaseChildren) override; + virtual void SynchronizeProperties() override; + virtual const FText GetPaletteCategory() override; +}; diff --git a/Plugins/NaWidgets/Source/NaWidgets/Public/Widgets/WindowWidgets/SNaBorderedWindow.h b/Plugins/NaWidgets/Source/NaWidgets/Public/Widgets/WindowWidgets/SNaBorderedWindow.h index e69de29..c692bf8 100644 --- a/Plugins/NaWidgets/Source/NaWidgets/Public/Widgets/WindowWidgets/SNaBorderedWindow.h +++ b/Plugins/NaWidgets/Source/NaWidgets/Public/Widgets/WindowWidgets/SNaBorderedWindow.h @@ -0,0 +1,160 @@ +// By Sodium + +#pragma once + +#include "CoreMinimal.h" +#include "Widgets/SCompoundWidget.h" +#include "Widgets/Images/SImage.h" +#include "Widgets/SCanvas.h" +#include "Styling/SlateBrush.h" +#include "SNaBorderedWindow.generated.h" + +/** Configuration struct for all 9 image parts and size parameters of SNaBorderedWindow. */ +USTRUCT(BlueprintType) +struct NAWIDGETS_API FNaBorderedWindowParams +{ + GENERATED_BODY() + + /** Main body size (center rectangle). */ + UPROPERTY(EditAnywhere, BlueprintReadWrite) + FVector2D BodySize = FVector2D(200.f, 150.f); + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + float BorderTop = 8.f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + float BorderBottom = 8.f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + float BorderLeft = 8.f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + float BorderRight = 8.f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + UObject* ImageCenter = nullptr; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + UObject* ImageTop = nullptr; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + UObject* ImageBottom = nullptr; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + UObject* ImageLeft = nullptr; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + UObject* ImageRight = nullptr; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + UObject* ImageTopLeft = nullptr; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + UObject* ImageTopRight = nullptr; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + UObject* ImageBottomLeft = nullptr; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + UObject* ImageBottomRight = nullptr; +}; + +/** + * SNaBorderedWindow is a bordered window widget composed of 9 image parts. + * Supports dragging (top border) and resizing (bottom-right corner). + */ +class NAWIDGETS_API SNaBorderedWindow : public SCompoundWidget +{ +public: + SLATE_BEGIN_ARGS(SNaBorderedWindow) + { + _Params = nullptr; + _MinBodySize = FVector2D(50.f, 50.f); + _MaxBodySize = FVector2D(1000.f, 1000.f); + } + + /** Parameters for images and initial sizes. Only used during construction. */ + SLATE_ATTRIBUTE(const FNaBorderedWindowParams*, Params) + SLATE_ATTRIBUTE(FVector2D, MinBodySize) + SLATE_ATTRIBUTE(FVector2D, MaxBodySize) + /** Optional content widget placed over the center body area. */ + SLATE_ATTRIBUTE(TSharedPtr, Content) + + SLATE_END_ARGS() + + void Construct(const FArguments& InArgs); + + /** Resize the body area, clamped to MinBodySize/MaxBodySize. */ + void SetBodySize(FVector2D NewSize); + + /** Update all four border widths and rebuild layout. */ + void SetBorderWidths(float Top, float Bottom, float Left, float Right); + + /** Replace all image resources and rebuild layout. */ + void UpdateImages(const FNaBorderedWindowParams& NewParams); + + /** Returns the current screen-space position of the window (updated while dragging). */ + FVector2D GetWindowPosition() const { return WindowPosition; } + +protected: + TSharedPtr OuterCanvas; + TSharedPtr Canvas; + + /* The 9 image sub-widgets. */ + TSharedPtr ImgCenter; + TSharedPtr ImgTop; + TSharedPtr ImgBottom; + TSharedPtr ImgLeft; + TSharedPtr ImgRight; + TSharedPtr ImgTopLeft; + TSharedPtr ImgTopRight; + TSharedPtr ImgBottomLeft; + TSharedPtr ImgBottomRight; + + /* Brushes for the 9 image parts. */ + FSlateBrush BrushCenter; + FSlateBrush BrushTop; + FSlateBrush BrushBottom; + FSlateBrush BrushLeft; + FSlateBrush BrushRight; + FSlateBrush BrushTopLeft; + FSlateBrush BrushTopRight; + FSlateBrush BrushBottomLeft; + FSlateBrush BrushBottomRight; + + FNaBorderedWindowParams Params; + FVector2D MinBodySize; + FVector2D MaxBodySize; + + TSharedPtr ContentWidget; + + bool bIsDragging = false; + bool bIsResizing = false; + FVector2D DragStartPosition; + FVector2D DragStartBodySize; + + /** Current layout position of the window within the outer canvas. */ + FVector2D WindowPosition = FVector2D::ZeroVector; + /** Snapshot of WindowPosition taken when a drag begins. */ + FVector2D DragStartWindowPosition = FVector2D::ZeroVector; + + /** Rebuild the canvas layout after any size or image change. */ + void RebuildLayout(); + + /** Clamp a candidate body size to [MinBodySize, MaxBodySize]. */ + FVector2D ClampBodySize(FVector2D Size) const; + + virtual FReply OnMouseButtonDown(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override; + virtual FReply OnMouseButtonUp(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override; + virtual FReply OnMouseMove(const FGeometry& MyGeometry, const FPointerEvent& MouseEvent) override; + + enum class EWindowRegion + { + None, + TopBorder, + BottomRightCorner, + Center + }; + + EWindowRegion GetRegionAtPosition(const FGeometry& MyGeometry, const FVector2D& LocalPosition) const; +};