Files
UnrealEngine/Engine/Source/Runtime/Slate/Public/Widgets/Layout/SGridPanel.h
2025-05-18 13:04:45 +08:00

342 lines
10 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;
class FPaintArgs;
class FSlateWindowElementList;
class SGridPanel : public SPanel
{
SLATE_DECLARE_WIDGET_API(SGridPanel, SPanel, SLATE_API)
public:
// Used by the mandatory named parameter in FSlot
class Layer
{
public:
explicit Layer(int32 InLayer)
: TheLayer(InLayer)
{
}
int32 TheLayer;
};
class FSlot : public TBasicLayoutWidgetSlot<FSlot>
{
friend SGridPanel;
public:
/** Default values for a slot. */
FSlot( int32 Column, int32 Row, int32 InLayer )
: TBasicLayoutWidgetSlot<FSlot>(HAlign_Fill, VAlign_Fill)
, ColumnParam( Column )
, ColumnSpanParam( 1 )
, RowParam( Row )
, RowSpanParam( 1 )
, LayerParam( InLayer )
, NudgeParam( FVector2D::ZeroVector )
{
}
SLATE_SLOT_BEGIN_ARGS(FSlot, TBasicLayoutWidgetSlot<FSlot>)
/** Which column in the grid this cell belongs to */
SLATE_ARGUMENT(TOptional<int32>, Column)
/** How many columns this slot spans over */
SLATE_ARGUMENT(TOptional<int32>, ColumnSpan)
/** Which row in the grid this cell belongs to */
SLATE_ARGUMENT(TOptional<int32>, Row)
/** How many rows this this slot spans over */
SLATE_ARGUMENT(TOptional<int32>, RowSpan)
/** Positive values offset this cell to be hit-tested and drawn on top of others. Default is 0; i.e. no offset. */
SLATE_ARGUMENT(TOptional<int32>, Layer)
/** Offset this slot's content by some amount; positive values offset to lower right*/
SLATE_ARGUMENT(TOptional<FVector2D>, Nudge)
SLATE_SLOT_END_ARGS()
SLATE_API void Construct(const FChildren& SlotOwner, FSlotArguments&& InArgs);
public:
/** Which column in the grid this cell belongs to */
int32 GetColumn() const
{
return ColumnParam;
}
void SetColumn(int32 Column)
{
Column = FMath::Max(0, Column);
if (Column != ColumnParam)
{
ColumnParam = Column;
NotifySlotChanged();
}
}
/** How many columns this slot spans over */
int32 GetColumnSpan() const
{
return ColumnSpanParam;
}
void SetColumnSpan(int32 ColumnSpan)
{
// clamp span to a sensible size, otherwise computing slot sizes can slow down dramatically
ColumnSpan = FMath::Clamp(ColumnSpan, 1, 10000);
if (ColumnSpan != ColumnSpanParam)
{
ColumnSpanParam = ColumnSpan;
NotifySlotChanged();
}
}
/** Which row in the grid this cell belongs to */
int32 GetRow() const
{
return RowParam;
}
void SetRow(int32 Row)
{
Row = FMath::Max(0, Row);
if (Row != RowParam)
{
RowParam = Row;
NotifySlotChanged();
}
}
/** How many rows this this slot spans over */
int32 GetRowSpan() const
{
return RowSpanParam;
}
void SetRowSpan(int32 RowSpan)
{
// clamp span to a sensible size, otherwise computing slots sizes can slow down dramatically
RowSpan = FMath::Clamp(RowSpan, 1, 10000);
if (RowSpan != RowSpanParam)
{
RowSpanParam = RowSpan;
NotifySlotChanged();
}
}
/** Positive values offset this cell to be hit-tested and drawn on top of others. Default is 0; i.e. no offset. */
int32 GetLayer() const
{
return LayerParam;
}
void SetLayer(int32 Layer)
{
if (Layer != LayerParam)
{
LayerParam = Layer;
const bool bSlotLayerChanged = true;
NotifySlotChanged(bSlotLayerChanged);
}
}
/** Offset this slot's content by some amount; positive values offset to lower right */
FVector2D GetNudge() const
{
return NudgeParam;
}
void SetNudge(const FVector2D& Nudge)
{
NudgeParam = Nudge;
Invalidate(EInvalidateWidgetReason::Paint);
}
private:
/** The panel that contains this slot */
TWeakPtr<SGridPanel> Panel;
int32 ColumnParam;
int32 ColumnSpanParam;
int32 RowParam;
int32 RowSpanParam;
int32 LayerParam;
FVector2D NudgeParam;
/** Notify that the slot was changed */
FORCEINLINE void NotifySlotChanged(bool bSlotLayerChanged = false)
{
if ( Panel.IsValid() )
{
Panel.Pin()->NotifySlotChanged(this, bSlotLayerChanged);
}
}
};
/**
* Used by declarative syntax to create a Slot in the specified Column, Row and Layer.
*/
static SLATE_API FSlot::FSlotArguments Slot( int32 Column, int32 Row, Layer InLayer = Layer(0) );
using FScopedWidgetSlotArguments = TPanelChildren<FSlot>::FScopedWidgetSlotArguments;
/**
* Dynamically add a new slot to the UI at specified Column and Row. Optionally, specify a layer at which this slot should be added.
*
* @return A reference to the newly-added slot
*/
SLATE_API FScopedWidgetSlotArguments AddSlot( int32 Column, int32 Row, Layer InLayer = Layer(0) );
/**
* Removes a slot from this panel which contains the specified SWidget
*
* @param SlotWidget The widget to match when searching through the slots
* @returns The true if the slot was removed and false if no slot was found matching the widget
*/
SLATE_API bool RemoveSlot(const TSharedRef<SWidget>& SlotWidget);
SLATE_BEGIN_ARGS( SGridPanel )
{
_Visibility = EVisibility::SelfHitTestInvisible;
}
SLATE_SLOT_ARGUMENT( FSlot, Slots )
/** Specify a column to stretch instead of sizing to content. */
FArguments& FillColumn( int32 ColumnId, const TAttribute<float>& Coefficient )
{
while (ColFillCoefficients.Num() <= ColumnId)
{
ColFillCoefficients.Emplace( 0 );
}
ColFillCoefficients[ColumnId] = Coefficient;
return Me();
}
/** Specify a row to stretch instead of sizing to content. */
FArguments& FillRow( int32 RowId, const TAttribute<float>& Coefficient )
{
while (RowFillCoefficients.Num() <= RowId)
{
RowFillCoefficients.Emplace( 0 );
}
RowFillCoefficients[RowId] = Coefficient;
return Me();
}
/** Coefficients for columns that need to stretch instead of size to content */
TArray<TAttribute<float>> ColFillCoefficients;
/** Coefficients for rows that need to stretch instead of size to content */
TArray<TAttribute<float>> RowFillCoefficients;
SLATE_END_ARGS()
SLATE_API SGridPanel();
SLATE_API virtual ~SGridPanel();
/** Removes all slots from the panel */
SLATE_API void ClearChildren();
SLATE_API void Construct( const FArguments& InArgs );
/**
* GetDesiredSize of a subregion in the graph.
*
* @param StartCell The cell (inclusive) in the upper left of the region.
* @param Size Number of cells in the X and Y directions to get the size for.
*
* @return FVector2D The desired size of the region of cells specified.
*/
SLATE_API FVector2D GetDesiredRegionSize( const FIntPoint& StartCell, int32 Width, int32 Height ) const;
/** Specify a column to stretch instead of sizing to content. */
SLATE_API void SetColumnFill( int32 ColumnId, const TAttribute<float>& Coefficient );
/** Specify a row to stretch instead of sizing to content. */
SLATE_API void SetRowFill( int32 RowId, const TAttribute<float>& Coefficient );
/** Clear the row and column fill rules. */
SLATE_API void ClearFill();
public:
// SWidget interface
SLATE_API virtual int32 OnPaint( const FPaintArgs& Args, const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId, const FWidgetStyle& InWidgetStyle, bool bParentEnabled ) const override;
SLATE_API virtual void OnArrangeChildren( const FGeometry& AllottedGeometry, FArrangedChildren& ArrangedChildren ) const override;
SLATE_API virtual void CacheDesiredSize(float) override;
SLATE_API virtual FVector2D ComputeDesiredSize(float) const override;
SLATE_API virtual FChildren* GetChildren() override;
private:
/**
* Given an array of values, re-populate the array such that every contains the partial sums up to that element.
* i.e. Array[N] = Array.Sum(0 .. N-1)
*
* The resulting array is 1-element longer.
*/
static SLATE_API void ComputePartialSums( TArray<float>& TurnMeIntoPartialSums );
/** Given a SizeContribution, distribute it to the elements in DistributeOverMe at indexes from [StartIndex .. UpperBound) */
static SLATE_API void DistributeSizeContributions( float SizeContribution, TArray<float>& DistributeOverMe, int32 StartIndex, int32 UpperBound );
/**
* Find the index where the given slot should be inserted into the list of Slots based on its LayerParam, such that Slots are sorter by layer.
*
* @param The newly-allocated slot to insert.
* @return The index where the slot should be inserted.
*/
SLATE_API int32 FindInsertSlotLocation( const FSlot* InSlot );
/** Compute the sizes of columns and rows needed to fit all the slots in this grid. */
SLATE_API void ComputeDesiredCellSizes( TArray<float>& OutColumns, TArray<float>& OutRows ) const;
/** Draw the debug grid of rows and colummns; useful for inspecting the GridPanel's logic. See OnPaint() for parameter meaning */
SLATE_API int32 LayoutDebugPaint(const FGeometry& AllottedGeometry, const FSlateRect& MyCullingRect, FSlateWindowElementList& OutDrawElements, int32 LayerId ) const;
/**
* Callback used to resize our internal arrays if a slot (or slot span) is changed.
*
* @param InSlot The slot that has just changed.
* @param bSlotLayerChanged Whether the slot layer changed.
*/
SLATE_API void NotifySlotChanged(const FSlot* InSlot, bool bSlotLayerChanged = false);
private:
/** The slots that are placed into various grid locations */
TPanelChildren<FSlot> Slots;
/**
* Offsets of each column from the beginning of the grid.
* Includes a faux value at the end of the array for finding the size of the last cell.
*/
TArray<float> Columns;
/**
* Offsets of each row from the beginning of the grid.
* Includes a faux value at the end of the array for finding the size of the last cell.
*/
TArray<float> Rows;
/** Total desires size along each axis. */
FVector2D TotalDesiredSizes;
protected:
/** Fill coefficients for the columns */
mutable TArray<TAttribute<float>> ColFillCoefficients;
/** Fill coefficients for the rows */
mutable TArray<TAttribute<float>> RowFillCoefficients;
};