Files
2025-05-18 13:04:45 +08:00

231 lines
7.0 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Misc/Attribute.h"
#include "Layout/Visibility.h"
#include "Widgets/DeclarativeSyntaxSupport.h"
#include "SlotBase.h"
#include "Widgets/SWidget.h"
#include "Layout/Children.h"
#include "Widgets/SPanel.h"
class FArrangedChildren;
/**
* With EOrientation::Orient_Horizontal
* Arranges widgets left-to-right.
* When the widgets exceed the PreferredSize
* the SWrapBox will place widgets on the next line.
*
* Illustration:
* +-----Preferred Size
* |
* [-----------][-|-]
* [--][------[--]|
* [--------------|]
* [---] |
*/
/**
* With EOrientation::Orient_Vertical
* Arranges widgets top-to-bottom.
* When the widgets exceed the PreferredSize
* the SVerticalWrapBox will place widgets on the next line.
*
* Illustration:
*
* [___] [___]
* [-1-] [-3-]
*
* [___] [___]
* [-2-] [-4-]
*
* [___]
*==============================>--------Preferred Size
* [-3-]
*/
class SWrapBox : public SPanel
{
SLATE_DECLARE_WIDGET_API(SWrapBox, SPanel, SLATE_API)
public:
/** A slot that support alignment of content and padding */
class FSlot : public TBasicLayoutWidgetSlot<FSlot>
{
public:
FSlot()
: TBasicLayoutWidgetSlot<FSlot>(HAlign_Fill, VAlign_Fill)
, SlotFillLineWhenSizeLessThan()
, bSlotFillEmptySpace(false)
, bSlotForceNewLine(false)
{
}
SLATE_SLOT_BEGIN_ARGS(FSlot, TBasicLayoutWidgetSlot<FSlot>)
SLATE_ARGUMENT(TOptional<float>, FillLineWhenSizeLessThan)
SLATE_ARGUMENT(TOptional<bool>, FillEmptySpace)
SLATE_ARGUMENT(TOptional<bool>, ForceNewLine)
SLATE_SLOT_END_ARGS()
void Construct(const FChildren& SlotOwner, FSlotArguments&& InArgs)
{
TBasicLayoutWidgetSlot<FSlot>::Construct(SlotOwner, MoveTemp(InArgs));
if (InArgs._FillLineWhenSizeLessThan.IsSet())
{
SlotFillLineWhenSizeLessThan = InArgs._FillLineWhenSizeLessThan;
}
bSlotFillEmptySpace = InArgs._FillEmptySpace.Get(bSlotFillEmptySpace);
bSlotForceNewLine = InArgs._ForceNewLine.Get(bSlotForceNewLine);
}
/** Dependently of the Orientation, if the total available horizontal or vertical space in the wrap panel drops below this threshold, this slot will attempt to fill an entire line. */
void SetFillLineWhenSizeLessThan(TOptional<float> InFillLineWhenSizeLessThan)
{
if (SlotFillLineWhenSizeLessThan != InFillLineWhenSizeLessThan)
{
SlotFillLineWhenSizeLessThan = InFillLineWhenSizeLessThan;
FSlotBase::Invalidate(EInvalidateWidgetReason::Layout);
}
}
TOptional<float> GetFillLineWhenSizeLessThan() const
{
return SlotFillLineWhenSizeLessThan;
}
/** Should this slot fill the remaining space on the line? */
void SetFillEmptySpace(bool bInFillEmptySpace)
{
if (bSlotFillEmptySpace != bInFillEmptySpace)
{
bSlotFillEmptySpace = bInFillEmptySpace;
FSlotBase::Invalidate(EInvalidateWidgetReason::Layout);
}
}
bool GetFillEmptySpace() const
{
return bSlotFillEmptySpace;
}
void SetForceNewLine(bool bInForceNewLine)
{
if (bSlotForceNewLine != bInForceNewLine)
{
bSlotForceNewLine = bInForceNewLine;
FSlotBase::Invalidate(EInvalidateWidgetReason::Layout);
}
}
bool GetForceNewLine() const
{
return bSlotForceNewLine;
}
private:
TOptional<float> SlotFillLineWhenSizeLessThan;
bool bSlotFillEmptySpace;
bool bSlotForceNewLine;
};
SLATE_BEGIN_ARGS(SWrapBox)
: _PreferredSize(100.f)
, _HAlign(HAlign_Left)
, _InnerSlotPadding(FVector2D::ZeroVector)
, _UseAllottedSize(false)
, _Orientation(EOrientation::Orient_Horizontal)
{
_Visibility = EVisibility::SelfHitTestInvisible;
}
/** The slot supported by this panel */
SLATE_SLOT_ARGUMENT( FSlot, Slots )
/** The preferred size, if not set will fill the space */
SLATE_ATTRIBUTE( float, PreferredSize )
/** How to distribute the elements among any extra space in a given row */
SLATE_ATTRIBUTE(EHorizontalAlignment, HAlign)
/** The inner slot padding goes between slots sharing borders */
SLATE_ARGUMENT( FVector2D, InnerSlotPadding )
/** if true, the PreferredSize will always match the room available to the SWrapBox */
SLATE_ARGUMENT( bool, UseAllottedSize )
/** Determines if the wrap box needs to arrange the slots left-to-right or top-to-bottom.*/
SLATE_ARGUMENT(EOrientation, Orientation);
SLATE_END_ARGS()
SLATE_API SWrapBox();
SLATE_API virtual ~SWrapBox();
static FSlot::FSlotArguments Slot()
{
return FSlot::FSlotArguments(MakeUnique<FSlot>());
}
using FScopedWidgetSlotArguments = TPanelChildren<FSlot>::FScopedWidgetSlotArguments;
SLATE_API FScopedWidgetSlotArguments AddSlot();
/** Removes a slot from this box panel which contains the specified SWidget
*
* @param SlotWidget The widget to match when searching through the slots
* @returns The index in the children array where the slot was removed and -1 if no slot was found matching the widget
*/
SLATE_API int32 RemoveSlot( const TSharedRef<SWidget>& SlotWidget );
SLATE_API void Construct( const FArguments& InArgs );
SLATE_API virtual void Tick( const FGeometry& AllottedGeometry, const double InCurrentTime, const float InDeltaTime ) override;
SLATE_API virtual void OnArrangeChildren( const FGeometry& AllottedGeometry, FArrangedChildren& ArrangedChildren ) const override;
SLATE_API void ClearChildren();
SLATE_API virtual FVector2D ComputeDesiredSize(float) const override;
SLATE_API virtual FChildren* GetChildren() override;
/** See InnerSlotPadding Attribute */
SLATE_API void SetInnerSlotPadding(FVector2D InInnerSlotPadding);
/** Set the size at which the wrap panel should wrap its content. */
SLATE_API void SetWrapSize(TAttribute<float> InWrapSize );
/** When true, use the WrapSize property to determine where to wrap to the next line. */
SLATE_API void SetUseAllottedSize(bool bInUseAllottedSize);
/** Set the Orientation to determine if the wrap box needs to arrange the slots left-to-right or top-to-bottom */
SLATE_API void SetOrientation(EOrientation InOrientation);
/** How to distribute the elements among any extra space in a given row */
SLATE_API void SetHorizontalAlignment(TAttribute<EHorizontalAlignment> InHAlignment);
private:
/** How wide or long, dependently of the orientation, this panel should appear to be. Any widgets past this line will be wrapped onto the next line. */
TSlateAttribute<float> PreferredSize;
/** How to distribute the elements among any extra space in a given row */
TSlateAttribute< EHorizontalAlignment > HAlign;
/** The slots that contain this panel's children. */
TPanelChildren<FSlot> Slots;
/** When two slots end up sharing a border, this will put that much padding between then, but otherwise wont. */
FVector2D InnerSlotPadding;
/** If true the box will have a preferred size equal to its alloted size */
bool bUseAllottedSize;
/** Determines if the wrap box needs to arrange the slots left-to-right or top-to-bottom.*/
EOrientation Orientation;
class FChildArranger;
friend class SWrapBox::FChildArranger;
};