// 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 { public: FSlot() : TBasicLayoutWidgetSlot(HAlign_Fill, VAlign_Fill) , SlotFillLineWhenSizeLessThan() , bSlotFillEmptySpace(false) , bSlotForceNewLine(false) { } SLATE_SLOT_BEGIN_ARGS(FSlot, TBasicLayoutWidgetSlot) SLATE_ARGUMENT(TOptional, FillLineWhenSizeLessThan) SLATE_ARGUMENT(TOptional, FillEmptySpace) SLATE_ARGUMENT(TOptional, ForceNewLine) SLATE_SLOT_END_ARGS() void Construct(const FChildren& SlotOwner, FSlotArguments&& InArgs) { TBasicLayoutWidgetSlot::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 InFillLineWhenSizeLessThan) { if (SlotFillLineWhenSizeLessThan != InFillLineWhenSizeLessThan) { SlotFillLineWhenSizeLessThan = InFillLineWhenSizeLessThan; FSlotBase::Invalidate(EInvalidateWidgetReason::Layout); } } TOptional 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 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()); } using FScopedWidgetSlotArguments = TPanelChildren::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& 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 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 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 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 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; };