UDocumentation UE5.7 10.02.2026 (Source)
API documentation for Unreal Engine 5.7
LayoutUtils.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#include "CoreMinimal.h"
6#include "ArrangedChildren.h"
7#include "FlowDirection.h"
8#include "Layout/Children.h"
9#include "Margin.h"
10#include "SlateRect.h"
12#include "Types/SlateStructs.h"
13#include "Visibility.h"
14#include "Widgets/SBoxPanel.h"
15
17{
18 AlignmentArrangeResult( float InOffset, float InSize )
19 : Offset(InOffset)
20 , Size(InSize)
21 {
22 }
23
24 float Offset;
25 float Size;
26};
27
33{
34 FSlotProxy() = default;
35
37 template <typename SlotType>
38 explicit FSlotProxy(const int32 InSlotIndex, const SlotType& InSlot)
39 : FSlotProxy(InSlot.GetWidget())
40 {
42 Padding = InSlot.GetPadding();
43 MinSize = InSlot.GetMinSize();
44 MaxSize = InSlot.GetMaxSize();
45 SizeParam.SizeRule = InSlot.GetSizeRule();
46 SizeParam.Value = InSlot.GetSizeValue();
47 SizeParam.ShrinkValue = InSlot.GetShrinkSizeValue();
48 HorizontalAlignment = InSlot.GetHorizontalAlignment();
49 VerticalAlignment = InSlot.GetVerticalAlignment();
50 }
51
54
60 template <typename SlotType, bool CompareArranged = false>
61 bool UpdateFromSlot(const int32 InSlotIndex, const SlotType& InSlot)
62 {
63 bool bAnyValueChanged = UpdateFromSlot(InSlot.GetWidget());
64
65 const FVector2f WidgetArrangedSize = InSlot.GetWidget()->GetTickSpaceGeometry().GetLocalSize();
66 const FVector2f WidgetDesiredSize = InSlot.GetWidget()->GetDesiredSize();
67
68 if constexpr (CompareArranged)
69 {
71 }
72 else
73 {
75 }
76
79
82
83 bAnyValueChanged = bAnyValueChanged || (Padding != InSlot.GetPadding());
84 Padding = InSlot.GetPadding();
85
86 const float SlotMinSize = InSlot.GetMinSize();
89
90 const float SlotMaxSize = InSlot.GetMaxSize();
93
95 SizeParam.SizeRule = InSlot.GetSizeRule();
96
97 const float SlotSizeValue = InSlot.GetSizeValue();
100
101 const float SlotShrinkSizeValue = InSlot.GetShrinkSizeValue();
104
105 bAnyValueChanged = bAnyValueChanged || (HorizontalAlignment != InSlot.GetHorizontalAlignment());
106 HorizontalAlignment = InSlot.GetHorizontalAlignment();
107
108 bAnyValueChanged = bAnyValueChanged || (VerticalAlignment != InSlot.GetVerticalAlignment());
109 VerticalAlignment = InSlot.GetVerticalAlignment();
110
111 return bAnyValueChanged;
112 }
113
115 bool operator==(const int32 InSlotIndex) const
116 {
117 return SlotIndex == InSlotIndex;
118 }
119
122 {
123 return SlotIndex < InOtherSlot.SlotIndex;
124 }
125
126private:
128
131
132public:
135
138
141
144
147
149 float MinSize = 0.0f;
150
152 float MaxSize = 0.0f;
153
156
159
162
165
168
171};
172
174template <typename SlotProxyType
175 UE_REQUIRES(std::is_base_of_v<FSlotProxy, std::decay_t<SlotProxyType>>)>
177{
178public:
185
192
195 {
196 switch (LayoutFlow)
197 {
198 default:
200 ++Index;
201 break;
203 --Index;
204 break;
205 }
206
207 return *this;
208 }
209
212 {
213 switch (LayoutFlow)
214 {
215 default:
217 --Index;
218 break;
220 ++Index;
221 break;
222 }
223
224 return *this;
225 }
226
228 {
229 return Container[Index];
230 }
231
233 {
234 return Container[Index];
235 }
236
238 {
239 return &Container[Index];
240 }
241
243 {
244 return &Container[Index];
245 }
246
248 inline explicit operator bool() const
249 {
250 return Container.IsValidIndex(Index);
251 }
252
255 {
256 return Index;
257 }
258
260 void Reset()
261 {
262 switch (LayoutFlow)
263 {
264 default:
266 Index = 0;
267 break;
269 Index = Container.Num() - 1;
270 break;
271 }
272 }
273
275 void SetToEnd()
276 {
277 switch (LayoutFlow)
278 {
279 default:
281 Index = Container.Num() - 1;
282 break;
284 Index = 0;
285 break;
286 }
287 }
288
289private:
290
291 const TArrayView<SlotProxyType>& Container;
292 int32 Index;
293 EFlowDirection LayoutFlow;
294};
295
296namespace UE::Slate
297{
302 template <typename SlotType, typename = void>
303 struct TSlotAccessor
304 {
306 int32 GetIndex(const SlotType& InSlot, const int32 InDefaultValue = INDEX_NONE) const;
307
309 TSharedRef<SWidget> GetWidget(const SlotType& InSlot) const;
310
313
315 FMargin GetPadding(const SlotType& InSlot, const FMargin& InDefaultValue = FMargin()) const;
316
318 FVector2f GetDesiredSize(const SlotType& InSlot, const FVector2f& InDefaultValue = FVector2f::ZeroVector) const;
319
321 FVector2f GetArrangedSize(const SlotType& InSlot, const FVector2f& InDefaultValue = FVector2f::ZeroVector) const;
322
325
327 float GetSizeValue(const SlotType& InSlot, const float& InDefaultValue = 0.0f) const;
328
330 float GetShrinkSizeValue(const SlotType& InSlot, const float& InDefaultValue = 0.0f) const;
331
333 float GetMinSize(const SlotType& InSlot, const float& InDefaultValue = 0.0f) const;
334
336 float GetMaxSize(const SlotType& InSlot, const float& InDefaultValue = 0.0f) const;
337
339 FArrangedWidget MakeArrangedWidget(const SlotType& InSlot, const FGeometry& InAllottedGeometry, const FVector2f& InLocalOffset, const FVector2f& InLocalSize) const;
340 };
341
342 template <typename SlotType>
343 struct TSlotAccessor<
344 SlotType,
345 std::enable_if_t<
346 std::is_base_of_v<TBasicLayoutWidgetSlot<SlotType>, std::decay_t<SlotType>>
347 && std::is_base_of_v<TResizingWidgetSlotMixin<SlotType>, std::decay_t<SlotType>>>>
348 {
350 int32 GetIndex(const SlotType& InSlot, const int32 InDefaultValue = INDEX_NONE) const
351 {
352 return InDefaultValue;
353 }
354
356 TSharedRef<SWidget> GetWidget(const SlotType& InSlot) const
357 {
358 return InSlot.GetWidget();
359 }
360
363 {
364 return InSlot.GetWidget()->GetVisibility();
365 }
366
368 FMargin GetPadding(const SlotType& InSlot, const FMargin& InDefaultValue = FMargin()) const
369 {
370 return InSlot.GetPadding();
371 }
372
374 FVector2f GetDesiredSize(const SlotType& InSlot, const FVector2f& InDefaultValue = FVector2f::ZeroVector) const
375 {
376 return InSlot.GetWidget()->GetDesiredSize();
377 }
378
380 FVector2f GetArrangedSize(const SlotType& InSlot, const FVector2f& InDefaultValue = FVector2f::ZeroVector) const
381 {
382 return InSlot.GetWidget()->GetTickSpaceGeometry().Size;
383 }
384
387 {
388 return InSlot.GetSizeRule();
389 }
390
392 float GetSizeValue(const SlotType& InSlot, const float& InDefaultValue = 0.0f) const
393 {
394 return InSlot.GetSizeValue();
395 }
396
398 float GetShrinkSizeValue(const SlotType& InSlot, const float& InDefaultValue = 0.0f) const
399 {
400 return InSlot.GetShrinkSizeValue();
401 }
402
404 float GetMinSize(const SlotType& InSlot, const float& InDefaultValue = 0.0f) const
405 {
406 return InSlot.GetMinSize();
407 }
408
410 float GetMaxSize(const SlotType& InSlot, const float& InDefaultValue = 0.0f) const
411 {
412 return InSlot.GetMaxSize();
413 }
414
416 FArrangedWidget MakeArrangedWidget(const SlotType& InSlot, const FGeometry& InAllottedGeometry, const FVector2f& InLocalOffset, const FVector2f& InLocalSize) const
417 {
418 return InAllottedGeometry.MakeChild(
419 // The child widget being arranged
420 GetWidget(InSlot),
421 // Child's local position (i.e. position within parent)
423 // Child's size
425 );
426 }
427 };
428
430 template <typename SlotType>
431 struct TSlotAccessor<
432 SlotType,
433 std::enable_if_t<std::is_base_of_v<FSlotProxy, std::decay_t<SlotType>>>>
434 {
436 int32 GetIndex(const FSlotProxy& InSlot, const int32 InDefaultValue = INDEX_NONE) const
437 {
438 return InSlot.SlotIndex;
439 }
440
442 TSharedRef<SWidget> GetWidget(const FSlotProxy& InSlot) const
443 {
444 return InSlot.Widget.IsValid() ? InSlot.Widget.ToSharedRef() : SNullWidget::NullWidget;
445 }
446
449 {
450 return InSlot.Visibility;
451 }
452
454 FMargin GetPadding(const FSlotProxy& InSlot, const FMargin& InDefaultValue = FMargin()) const
455 {
456 return InSlot.Padding;
457 }
458
460 FVector2f GetDesiredSize(const FSlotProxy& InSlot, const FVector2f& InDefaultValue = FVector2f::ZeroVector) const
461 {
462 return InSlot.DesiredSize;
463 }
464
466 FVector2f GetArrangedSize(const FSlotProxy& InSlot, const FVector2f& InDefaultValue = FVector2f::ZeroVector) const
467 {
468 return InSlot.ArrangedSize;
469 }
470
473 {
474 return InSlot.SizeParam.SizeRule;
475 }
476
478 float GetSizeValue(const FSlotProxy& InSlot, const float& InDefaultValue = 0.0f) const
479 {
480 return InSlot.SizeParam.Value.Get();
481 }
482
484 float GetShrinkSizeValue(const FSlotProxy& InSlot, const float& InDefaultValue = 0.0f) const
485 {
486 return InSlot.SizeParam.ShrinkValue.Get();
487 }
488
490 FVector2f::FReal GetMinSize(const FSlotProxy& InSlot, const FVector2f::FReal& InDefaultValue = 0.0f) const
491 {
492 return InSlot.MinSize;
493 }
494
496 FVector2f::FReal GetMaxSize(const FSlotProxy& InSlot, const FVector2f::FReal& InDefaultValue = 0.0f) const
497 {
498 return InSlot.MaxSize;
499 }
500
502 FArrangedWidget MakeArrangedWidget(const FSlotProxy& InSlot, const FGeometry& InAllottedGeometry, const FVector2f& InLocalOffset, const FVector2f& InLocalSize) const
503 {
504 return InAllottedGeometry.MakeChild(
505 GetWidget(InSlot),
508 }
509 };
510
511 template <typename SlotType, typename = void>
512 struct TSlotIterator
513 {
514 };
515
516 template <typename SlotType>
517 struct TSlotIterator<
518 SlotType,
519 std::enable_if_t<std::is_base_of_v<FSlotBase, std::decay_t<SlotType>>>>
520 {
522 };
523
524 template <typename SlotType>
525 struct TSlotIterator<
526 SlotType,
527 std::enable_if_t<std::is_base_of_v<FSlotProxy, std::decay_t<SlotType>>>>
528 {
530 };
531};
532
534{
536 template<EOrientation Orientation>
538 {
539 template<typename SlotType>
541 };
542
543 template<>
545 {
546 template<typename SlotType>
548 {
549 EHorizontalAlignment HorizontalAlignment;
550 if constexpr (std::is_base_of_v<FSlotProxy, std::decay_t<SlotType>>)
551 {
552 HorizontalAlignment = InSlot.HorizontalAlignment;
553 }
554 else
555 {
556 HorizontalAlignment = InSlot.GetHorizontalAlignment();
557 }
558
559 switch (InFlowDirection)
560 {
561 default:
563 return static_cast<int32>(HorizontalAlignment);
565 switch (HorizontalAlignment)
566 {
567 case HAlign_Left:
568 return static_cast<int32>(HAlign_Right);
569 case HAlign_Right:
570 return static_cast<int32>(HAlign_Left);
571 default:
572 return static_cast<int32>(HorizontalAlignment);
573 }
574 }
575 }
576 };
577
578 template<>
580 {
581 template<typename SlotType>
583 {
584 EVerticalAlignment VerticalAlignment;
585 if constexpr (std::is_base_of_v<FSlotProxy, std::decay_t<SlotType>>)
586 {
587 VerticalAlignment = InSlot.VerticalAlignment;
588 }
589 else
590 {
591 VerticalAlignment = InSlot.GetVerticalAlignment();
592 }
593
594 // InFlowDirection has no effect in vertical orientations.
595 return static_cast<int32>(VerticalAlignment);
596 }
597 };
598
603 template<EOrientation Orientation>
604 static AlignmentArrangeResult AlignFill(float AllottedSize, const FMargin& SlotPadding, const float ContentScale = 1.0f)
605 {
606 const FMargin& Margin = SlotPadding;
607 const float TotalMargin = Margin.GetTotalSpaceAlong<Orientation>();
608 const float MarginPre = (Orientation == Orient_Horizontal) ? Margin.Left : Margin.Top;
610 }
611
616 template<EOrientation Orientation>
617 static AlignmentArrangeResult AlignCenter(float AllottedSize, float ChildDesiredSize, const FMargin& SlotPadding, const float ContentScale = 1.0f, bool bClampToParent = true)
618 {
619 const FMargin& Margin = SlotPadding;
620 const float TotalMargin = Margin.GetTotalSpaceAlong<Orientation>();
621 const float MarginPre = (Orientation == Orient_Horizontal) ? Margin.Left : Margin.Top;
622 const float MarginPost = (Orientation == Orient_Horizontal) ? Margin.Right : Margin.Bottom;
623 const float ChildSize = FMath::Max((bClampToParent ? FMath::Min(ChildDesiredSize, AllottedSize - TotalMargin) : ChildDesiredSize), 0.f);
625 }
626}
627
628static FMargin LayoutPaddingWithFlow(EFlowDirection InLayoutFlow, const FMargin& InPadding);
629
641template<EOrientation Orientation, typename SlotType>
642static AlignmentArrangeResult AlignChild(EFlowDirection InLayoutFlow, float AllottedSize, float ChildDesiredSize, const SlotType& ChildToArrange, const FMargin& SlotPadding, const float& ContentScale = 1.0f, bool bClampToParent = true)
643{
644 const FMargin& Margin = SlotPadding;
645 const float TotalMargin = Margin.GetTotalSpaceAlong<Orientation>();
646 const float MarginPre = ( Orientation == Orient_Horizontal ) ? Margin.Left : Margin.Top;
647 const float MarginPost = ( Orientation == Orient_Horizontal ) ? Margin.Right : Margin.Bottom;
648
650
651 switch (Alignment)
652 {
653 case HAlign_Fill:
655 }
656
657 const float ChildSize = FMath::Max((bClampToParent ? FMath::Min(ChildDesiredSize, AllottedSize - TotalMargin) : ChildDesiredSize), 0.f);
658
659 switch( Alignment )
660 {
661 case HAlign_Left: // same as Align_Top
663 case HAlign_Center:
665 case HAlign_Right: // same as Align_Bottom
667 }
668
669 // Same as Fill
670 return AlignmentArrangeResult(MarginPre, FMath::Max(( AllottedSize - TotalMargin ) * ContentScale, 0.f));
671}
672
673template<EOrientation Orientation, typename SlotType>
674static AlignmentArrangeResult AlignChild(float AllottedSize, float ChildDesiredSize, const SlotType& ChildToArrange, const FMargin& SlotPadding, const float& ContentScale = 1.0f, bool bClampToParent = true)
675{
677}
678
679template<EOrientation Orientation, typename SlotType>
680static AlignmentArrangeResult AlignChild(EFlowDirection InLayoutFlow, float AllottedSize, const SlotType& ChildToArrange, const FMargin& SlotPadding, const float& ContentScale = 1.0f, bool bClampToParent = true)
681{
682 const FMargin& Margin = SlotPadding;
683 const float TotalMargin = Margin.GetTotalSpaceAlong<Orientation>();
684 const float MarginPre = ( Orientation == Orient_Horizontal ) ? Margin.Left : Margin.Top;
685 const float MarginPost = ( Orientation == Orient_Horizontal ) ? Margin.Right : Margin.Bottom;
686
688
689 switch (Alignment)
690 {
691 case HAlign_Fill:
693 }
694
695 float ChildDesiredSize = 0.0f;
696 if constexpr (std::is_base_of_v<FSlotProxy, std::decay_t<SlotType>>)
697 {
699 ? ( ChildToArrange.DesiredSize.X * ContentScale )
700 : ( ChildToArrange.DesiredSize.Y * ContentScale );
701 }
702 else
703 {
705 ? ( ChildToArrange.GetWidget()->GetDesiredSize().X * ContentScale )
706 : ( ChildToArrange.GetWidget()->GetDesiredSize().Y * ContentScale );
707 }
708
709 const float ChildSize = FMath::Max((bClampToParent ? FMath::Min(ChildDesiredSize, AllottedSize - TotalMargin) : ChildDesiredSize), 0.f);
710
711 switch ( Alignment )
712 {
713 case HAlign_Left: // same as Align_Top
715 case HAlign_Center:
717 case HAlign_Right: // same as Align_Bottom
719 }
720
721 // Same as Fill
723}
724
725template<EOrientation Orientation, typename SlotType>
726static AlignmentArrangeResult AlignChild(float AllottedSize, const SlotType& ChildToArrange, const FMargin& SlotPadding, const float& ContentScale = 1.0f, bool bClampToParent = true)
727{
729}
730
731
736template<typename SlotType>
737static void ArrangeSingleChild(const FGeometry& AllottedGeometry, FArrangedChildren& ArrangedChildren, const SlotType& ChildSlot, const TAttribute<FVector2D>& ContentScale)
738{
740}
741
742template<typename SlotType>
743static void ArrangeSingleChild(EFlowDirection InFlowDirection, const FGeometry& AllottedGeometry, FArrangedChildren& ArrangedChildren, const SlotType& ChildSlot, const TAttribute<FVector2D>& ContentScale)
744{
745 const EVisibility ChildVisibility = ChildSlot.GetWidget()->GetVisibility();
746 if ( ArrangedChildren.Accepts(ChildVisibility) )
747 {
749 const FMargin SlotPadding(LayoutPaddingWithFlow(InFlowDirection, ChildSlot.GetPadding()));
750 const AlignmentArrangeResult XResult = AlignChild<Orient_Horizontal>(InFlowDirection, AllottedGeometry.GetLocalSize().X, ChildSlot, SlotPadding, ThisContentScale.X);
751 const AlignmentArrangeResult YResult = AlignChild<Orient_Vertical>(AllottedGeometry.GetLocalSize().Y, ChildSlot, SlotPadding, ThisContentScale.Y);
752
753 ArrangedChildren.AddWidget( ChildVisibility, AllottedGeometry.MakeChild(
754 ChildSlot.GetWidget(),
755 FVector2D(XResult.Offset, YResult.Offset),
756 FVector2D(XResult.Size, YResult.Size)
757 ) );
758 }
759}
760
761template<typename SlotType>
762static void ArrangeSingleChild(const FGeometry& AllottedGeometry, FArrangedChildren& ArrangedChildren, const SlotType& ChildSlot, const FVector2D& ContentScale)
763{
765}
766
767template<typename SlotType>
768static void ArrangeSingleChild(EFlowDirection InFlowDirection, const FGeometry& AllottedGeometry, FArrangedChildren& ArrangedChildren, const SlotType& ChildSlot, const FVector2D& ContentScale)
769{
770 const EVisibility ChildVisibility = ChildSlot.GetWidget()->GetVisibility();
772 {
774 const FMargin SlotPadding(LayoutPaddingWithFlow(InFlowDirection, ChildSlot.GetPadding()));
775 const AlignmentArrangeResult XResult = AlignChild<Orient_Horizontal>(InFlowDirection, AllottedGeometry.GetLocalSize().X, ChildSlot, SlotPadding, ThisContentScale.X);
776 const AlignmentArrangeResult YResult = AlignChild<Orient_Vertical>(AllottedGeometry.GetLocalSize().Y, ChildSlot, SlotPadding, ThisContentScale.Y);
777
778 ArrangedChildren.AddWidget(ChildVisibility, AllottedGeometry.MakeChild(
779 ChildSlot.GetWidget(),
780 FVector2f(XResult.Size, YResult.Size),
782 ));
783 }
784}
785
790template <
792 typename InputChildrenType,
793 typename InputSlotType
795 (std::is_base_of_v<TPanelChildren<InputSlotType>, std::decay_t<InputChildrenType>>
796 || std::is_same_v<TConstArrayView<InputSlotType>, std::decay_t<InputChildrenType>>
797 || std::is_same_v<TArrayView<InputSlotType>, std::decay_t<InputChildrenType>>))>
798static void ArrangeChildrenInStack(EFlowDirection InLayoutFlow, const InputChildrenType& InChildren, const FGeometry& AllottedGeometry, FArrangedChildren& ArrangedChildren, float InOffset, bool bInAllowShrink, FVector2D& OutArrangedSize)
799{
800 if (InChildren.Num() == 0)
801 {
802 return;
803 }
804
805 // Allotted space will be given to fixed-size children first.
806 // Remaining space will be proportionately divided between stretch children (SizeRule_Stretch and SizeRule_StretchContent)
807 // based on their stretch coefficient.
808
809 // Helper function to clamp to max size, if the constraint is set.
810 auto ClampSize = [](const float Size, const float MinSize, const float MaxSize)
811 {
812 return FMath::Clamp(
813 Size,
814 MinSize > 0.0f ? MinSize : 0.0f,
815 MaxSize > 0.0f ? MaxSize : std::numeric_limits<float>::max());
816 };
817
818 float GrowStretchCoefficientTotal = 0.0f;
820 float FixedSizeTotal = 0.0f;
821 float StretchSizeTotal = 0.0f;
822
823 struct FStretchItem
824 {
825 // Size of the item
826 float Size = 0.0f;
827 // Initial size of the item
828 float BasisSize = 0.0f;
829 // Min size constraint of the item.
830 float MinSize = 0.0f;
831 // Max size constraint of the item.
832 float MaxSize = 0.0f;
833 // Stretch coefficient when the items are growing.
834 float GrowStretchValue = 0.0f;
835 // Stretch coefficient when the items are shrinking.
836 float ShrinkStretchValue = 0.0f;
837 // True if the constraints of the item has been satisfied.
838 bool bFrozen = false;
839 // Sizing rule for the item.
841 };
843 StretchItems.Init({}, InChildren.Num());
844
845 bool bAnyChildVisible = false;
846 bool bAnyStretchContentItems = false;
847 bool bAnyStretchItems = false;
848
849 UE::Slate::TSlotAccessor<std::decay_t<InputSlotType>> SlotAccessor;
850
851 // Compute the sum of stretch coefficients (SizeRule_Stretch & SizeRule_StretchContent) and space required by fixed-size widgets (SizeRule_Auto),
852 // as well as the total desired size.
853 for (int32 ChildIndex = 0; ChildIndex < InChildren.Num(); ++ChildIndex)
854 {
855 const InputSlotType& CurChild = InChildren[ChildIndex];
856
857 if (SlotAccessor.GetVisibility(CurChild) != EVisibility::Collapsed)
858 {
859 bAnyChildVisible = true;
860
861 // All widgets contribute their margin to the fixed space requirement
863
865
866 // Auto-sized children contribute their desired size to the fixed space requirement
870
871 const float MinSize = SlotAccessor.GetMinSize(CurChild);
872 const float MaxSize = SlotAccessor.GetMaxSize(CurChild);
873
874 FStretchItem& Item = StretchItems[ChildIndex];
875 Item.MinSize = MinSize;
876 Item.MaxSize = MaxSize;
877 Item.SizeRule = SlotAccessor.GetSizeRule(CurChild);
878
879 // Clamp to the max size if it was specified
880 ChildSize = ClampSize(ChildSize, MinSize, MaxSize);
881
882 if (Item.SizeRule == FSizeParam::SizeRule_Stretch)
883 {
884 // Using same shrink and grow since otherwise the transition would be discontinuous as (reference) basis size is 0.
885 Item.GrowStretchValue = SlotAccessor.GetSizeValue(CurChild);
886 Item.ShrinkStretchValue = Item.GrowStretchValue;
887 Item.Size = 0.0f;
888 Item.BasisSize = 0.0f;
889
890 // For stretch children we save sum up the stretch coefficients
891 GrowStretchCoefficientTotal += Item.GrowStretchValue;
892 ShrinkStretchCoefficientTotal += Item.ShrinkStretchValue;
894
895 bAnyStretchItems = true;
896 }
897 else if (Item.SizeRule == FSizeParam::SizeRule_StretchContent)
898 {
899 // Allow separate values from grow and shrink, as the adjustment is relative to the child size.
900 Item.GrowStretchValue = FMath::Max(0.f, SlotAccessor.GetSizeValue(CurChild));
901 Item.ShrinkStretchValue = FMath::Max(0.f, SlotAccessor.GetShrinkSizeValue(CurChild));
902 Item.Size = ChildSize;
903 Item.BasisSize = ChildSize;
904
905 // For sized stretch we sum to coefficients, but also treat the size as fixed.
906 GrowStretchCoefficientTotal += Item.GrowStretchValue;
907 ShrinkStretchCoefficientTotal += Item.ShrinkStretchValue;
909
911 }
912 else
913 {
915
916 Item.GrowStretchValue = 0.0f;
917 Item.ShrinkStretchValue = 0.0f;
918 Item.Size = ChildSize;
919 Item.BasisSize = ChildSize;
920 }
921 }
922 }
923
924 if (!bAnyChildVisible)
925 {
926 return;
927 }
928
929 // When shrink is not allowed, we'll ensure to use all the space desired by the stretchable widgets.
931
933 ? AllottedGeometry.GetLocalSize().Y
934 : AllottedGeometry.GetLocalSize().X;
935
936 // The space available for SizeRule_Stretch and SizeRule_StretchContent widgets is any space that wasn't taken up by fixed-sized widgets.
938
939 // Apply SizeRule_Stretch.
941 {
942 // Distribute available space amongst the SizeRule_Stretch items proportional to the their stretch coefficient.
943 float UsedSpace = 0.0f;
944 for (FStretchItem& Item : StretchItems)
945 {
946 if (Item.SizeRule == FSizeParam::SizeRule_Stretch)
947 {
948 // Stretch widgets get a fraction of the space remaining after all the fixed-space requirements are met.
949 // Supporting only one stretch value since otherwise the transition would be discontinuous as (reference) basis size is 0.
950 const float Size = AvailableSpace * Item.GrowStretchValue / GrowStretchCoefficientTotal;
951
952 Item.Size = ClampSize(Size, Item.MinSize, Item.MaxSize);
953
954 UsedSpace += Item.Size;
955 }
956 }
957 AvailableSpace -= UsedSpace;
958 }
959
960 // Apply SizeRule_StretchContent.
962
963 const bool bCanStretch = bIsGrowing
966
968 {
969 // Each StretchContent item starts at desired size and shrinks or grows based on available size.
970 // First, consume each items desired size from the available space.
971 // The remainder is corrected by growing ot shrinking the items.
973 for (FStretchItem& Item : StretchItems)
974 {
975 if (Item.SizeRule == FSizeParam::SizeRule_StretchContent)
976 {
977 AvailableSpace -= Item.Size;
979
980 // If the item cannot shrink or grow, mark it already frozen.
981 if (bIsGrowing)
982 {
983 Item.bFrozen |= FMath::IsNearlyZero(Item.GrowStretchValue);
984 }
985 else
986 {
987 Item.bFrozen |= FMath::IsNearlyZero(Item.ShrinkStretchValue);
988 }
989 }
990 }
991
992 // Run number of passes to satisfy the StretchContent constraints.
993 // On each pass distribute the available space to non-frozen items.
994 // An item gets frozen if it's (min/max) constraints are violated.
995 // This makes sure that we distribute all of the available space, event if small items collapse or if items clamp to max size.
996 // Each iteration should solve at least one constraint.
997 // In practice most layouts solve in 2 passes, we're capping to 5 iterations to keep things in fixed budget.
998 const int32 MaxPasses = FMath::Min(NumStretchContentItems, 5);
999 for (int32 Pass = 0; Pass < MaxPasses; Pass++)
1000 {
1001 // If no available space, stop.
1003 {
1004 break;
1005 }
1006
1007 // On each pass calculate the total coefficients for valid items.
1010
1011 for (const FStretchItem& Item : StretchItems)
1012 {
1013 if (Item.SizeRule == FSizeParam::SizeRule_StretchContent
1014 && !Item.bFrozen)
1015 {
1016 // Items are grown proportional to their stretch value.
1017 GrowStretchCoefficientTotal += Item.GrowStretchValue;
1018 // Items are shrank proportional to their stretch value and size. This is to emulate the flexbox behavior.
1019 ShrinkStretchCoefficientTotal += Item.ShrinkStretchValue * Item.BasisSize;
1020 }
1021 }
1022
1026
1027 // If none of the items can stretch, stop.
1029 {
1030 break;
1031 }
1032
1033 float ConsumedSpace = 0.0f;
1034
1035 for (FStretchItem& Item : StretchItems)
1036 {
1037 if (Item.SizeRule == FSizeParam::SizeRule_StretchContent
1038 && !Item.bFrozen)
1039 {
1040 const float SizeAdjust = bIsGrowing
1041 ? (AvailableSpace * (Item.GrowStretchValue / GrowStretchCoefficientTotal))
1043
1044 // If the item cannot be adjusted anymore, mark it frozen.
1046 {
1047 Item.bFrozen = true;
1048 continue;
1049 }
1050
1051 const float MinSize = Item.MinSize;
1052 const float MaxSize = Item.MaxSize;
1053 const bool bHasMaxConstraint = MaxSize > 0.0f;
1054
1055 if ((Item.Size + SizeAdjust) <= MinSize)
1056 {
1057 // Adjustment goes past min constraint, apply what we can and freeze since the item cannot change anymore.
1058 ConsumedSpace += MinSize - Item.Size;
1059 Item.Size = MinSize;
1060 Item.bFrozen = true;
1061 }
1062 else if (bHasMaxConstraint
1063 && (Item.Size + SizeAdjust) >= MaxSize)
1064 {
1065 // Adjustment goes past max constraint, apply what we can and freeze since the item cannot change anymore.
1066 ConsumedSpace += MaxSize - Item.Size;
1067 Item.Size = MaxSize;
1068 Item.bFrozen = true;
1069 }
1070 else
1071 {
1072 // Within constraints, adjust.
1073 ConsumedSpace += SizeAdjust;
1074 Item.Size += SizeAdjust;
1075 }
1076 }
1077 }
1078
1079 AvailableSpace -= ConsumedSpace;
1080 }
1081 }
1082
1083 // Now that we have the satisfied size requirements we can
1084 // arrange widgets top-to-bottom or left-to-right (depending on the orientation).
1085 float PositionSoFar = 0.0f;
1086
1087 ArrangedChildren.Reserve(ArrangedChildren.Num() + InChildren.Num());
1088
1089 using SlotIteratorType = typename UE::Slate::TSlotIterator<InputSlotType>::Type;
1090
1091 // Track the bounds of the arranged widgets.
1094
1096 {
1097 const InputSlotType& CurChild = *It;
1098
1099 const EVisibility ChildVisibility = SlotAccessor.GetVisibility(CurChild);
1100
1101 // Figure out the area allocated to the child in the direction of BoxPanel
1102 // The area allocated to the slot is ChildSize + the associated margin.
1103 const float ChildSize = StretchItems[It.GetIndex()].Size;
1104
1105 const FMargin SlotPadding(LayoutPaddingWithFlow(InLayoutFlow, SlotAccessor.GetPadding(CurChild)));
1106
1107 FVector2f SlotSize = (Orientation == Orient_Vertical)
1108 ? FVector2f(AllottedGeometry.GetLocalSize().X, ChildSize + SlotPadding.template GetTotalSpaceAlong<Orient_Vertical>())
1109 : FVector2f(ChildSize + SlotPadding.template GetTotalSpaceAlong<Orient_Horizontal>(), AllottedGeometry.GetLocalSize().Y);
1110
1111 // Figure out the size and local position of the child within the slot
1114
1115 const FVector2f LocalPosition = (Orientation == Orient_Vertical)
1116 ? FVector2f(XAlignmentResult.Offset, PositionSoFar + YAlignmentResult.Offset + InOffset)
1117 : FVector2f(PositionSoFar + XAlignmentResult.Offset + InOffset, YAlignmentResult.Offset);
1118
1119 const FVector2f LocalSize = FVector2f(XAlignmentResult.Size, YAlignmentResult.Size);
1120
1121 ArrangedWidgetsMin = FVector2D::Min(ArrangedWidgetsMin, static_cast<FVector2D>(LocalPosition));
1122 ArrangedWidgetsMax = FVector2D::Max(ArrangedWidgetsMax, static_cast<FVector2D>(LocalPosition + LocalSize));
1123
1124 ArrangedChildren.AddWidget(
1126 SlotAccessor.MakeArrangedWidget(
1127 CurChild,
1128 AllottedGeometry,
1129 LocalPosition,
1130 LocalSize));
1131
1132 if constexpr (std::is_same_v<TArrayView<InputSlotType>, std::decay_t<InputChildrenType>>
1133 && !std::is_const_v<typename TArrayView<InputSlotType>::ElementType>)
1134 {
1135 InputSlotType& MutableChild = (*It);
1136
1137 // If the FSlotProxy is writable, write it's ArrangedSize.
1138 MutableChild.ArrangedSize = LocalSize;
1139 }
1140
1142 {
1143 // Offset the next child by the size of the current child and any post-child (bottom/right) margin
1144 PositionSoFar += (Orientation == Orient_Vertical) ? SlotSize.Y : SlotSize.X;
1145 }
1146 }
1147
1149}
1150
1151template <
1153 typename SlotType
1154 UE_REQUIRES(std::is_base_of_v<FSlotProxy, std::decay_t<SlotType>>)>
1155static void ArrangeChildrenInStack(EFlowDirection InLayoutFlow, const TConstArrayView<SlotType>& Children, const FGeometry& AllottedGeometry, FArrangedChildren& ArrangedChildren, float InOffset, bool bInAllowShrink, FVector2D& OutArrangedSize)
1156{
1158}
1159
1160template <
1162 typename SlotType
1163 UE_REQUIRES(std::is_base_of_v<FSlotProxy, std::decay_t<SlotType>>)>
1164static void ArrangeChildrenInStack(EFlowDirection InLayoutFlow, const TArrayView<SlotType>& Children, const FGeometry& AllottedGeometry, FArrangedChildren& ArrangedChildren, float InOffset, bool bInAllowShrink, FVector2D& OutArrangedSize)
1165{
1167}
1168
1169template <
1171 typename SlotType
1172 UE_REQUIRES(std::is_base_of_v<FSlotBase, std::decay_t<SlotType>>)>
1173static void ArrangeChildrenInStack(EFlowDirection InLayoutFlow, const TPanelChildren<SlotType>& Children, const FGeometry& AllottedGeometry, FArrangedChildren& ArrangedChildren, float InOffset, bool bInAllowShrink)
1174{
1175 FVector2D Unused;
1177}
1178
1179static FMargin LayoutPaddingWithFlow(EFlowDirection InLayoutFlow, const FMargin& InPadding)
1180{
1183 {
1184 float Temp = ReturnPadding.Left;
1185 ReturnPadding.Left = ReturnPadding.Right;
1186 ReturnPadding.Right = Temp;
1187 }
1188 return ReturnPadding;
1189}
1190
@ INDEX_NONE
Definition CoreMiscDefines.h:150
FPlatformTypes::int32 int32
A 32-bit signed integer.
Definition Platform.h:1125
UE_FORCEINLINE_HINT TSharedRef< CastToType, Mode > StaticCastSharedRef(TSharedRef< CastFromType, Mode > const &InSharedRef)
Definition SharedPointer.h:127
EFlowDirection
Definition FlowDirection.h:17
#define X(Name, Desc)
Definition FormatStringSan.h:47
SLATECORE_API UE::Slate::FDeprecateVector2DResult ComputePopupFitInRect(const FSlateRect &InAnchor, const FSlateRect &PopupRect, const EOrientation &Orientation, const FSlateRect &RectToFit, bool bAllowFlip=true)
Definition LayoutUtils.cpp:61
@ Bottom
Definition MaterialExpressionFunctionInput.h:43
@ Top
Definition MaterialExpressionFunctionInput.h:42
UE::Math::TVector2< float > FVector2f
Definition MathFwd.h:74
UE::Math::TVector2< double > FVector2D
Definition MathFwd.h:48
const bool
Definition NetworkReplayStreaming.h:178
#define UE_REQUIRES(...)
Definition Requires.h:86
USkinnedMeshComponent float
Definition SkinnedMeshComponent.h:60
EHorizontalAlignment
Definition SlateEnums.h:174
EOrientation
Definition SlateEnums.h:261
EVerticalAlignment
Definition SlateEnums.h:194
#define UE_KINDA_SMALL_NUMBER
Definition UnrealMathUtility.h:131
uint32 Size
Definition VulkanMemory.cpp:4034
Definition ArrangedChildren.h:15
Definition ArrangedWidget.h:18
Definition SlateLayoutTransform.h:20
Definition SlateRect.h:26
Definition SlotBase.h:14
static SLATECORE_API TSharedRef< class SWidget > NullWidget
Definition SNullWidget.h:22
Definition ArrayView.h:139
UE_FORCEINLINE_HINT constexpr SizeType Num() const
Definition ArrayView.h:380
UE_FORCEINLINE_HINT constexpr bool IsValidIndex(SizeType Index) const
Definition ArrayView.h:359
Definition Array.h:670
void Init(const ElementType &Element, SizeType Number)
Definition Array.h:3043
Definition Attribute.h:17
const ObjectType & Get() const
Definition Attribute.h:241
Definition Children.h:716
Definition Children.h:461
Definition SharedPointer.h:692
Definition SharedPointer.h:153
Definition LayoutUtils.h:177
void SetToEnd()
Definition LayoutUtils.h:275
TSlotProxyIterator & operator--()
Definition LayoutUtils.h:211
SlotProxyType * operator->()
Definition LayoutUtils.h:242
TSlotProxyIterator(const TArrayView< SlotProxyType > &InContainer, EFlowDirection InLayoutFlow)
Definition LayoutUtils.h:179
int32 GetIndex() const
Definition LayoutUtils.h:254
const SlotProxyType & operator*() const
Definition LayoutUtils.h:227
TSlotProxyIterator & operator++()
Definition LayoutUtils.h:194
TSlotProxyIterator(const TArrayView< SlotProxyType > &InContainer, EOrientation InOrientation, EFlowDirection InLayoutFlow)
Definition LayoutUtils.h:186
void Reset()
Definition LayoutUtils.h:260
const SlotProxyType * operator->() const
Definition LayoutUtils.h:237
Definition LayoutUtils.h:534
Type
Definition PawnAction_Move.h:11
void GetVisibility(const SceneViewOrGizmoViewContext *View, const FVector &ViewDirection, const FVector &GizmoPlaneWorldNormal, const FVector &WorldOrigin, bool &bRenderVisibilityOut, bool &bIsViewPlaneParallelOut)
Definition GizmoCircleComponent.cpp:19
Definition DockingUtilsPrivate.h:8
FDeprecateSlateVector2D FDeprecateVector2DResult
Definition SlateVector2.h:469
U16 Index
Definition radfft.cpp:71
Definition LayoutUtils.h:17
float Size
Definition LayoutUtils.h:25
AlignmentArrangeResult(float InOffset, float InSize)
Definition LayoutUtils.h:18
float Offset
Definition LayoutUtils.h:24
static int32 AsInt(EFlowDirection InFlowDirection, const SlotType &InSlot)
Definition LayoutUtils.h:547
static int32 AsInt(EFlowDirection InFlowDirection, const SlotType &InSlot)
Definition LayoutUtils.h:582
Definition LayoutUtils.h:538
static int32 AsInt(EFlowDirection InFlowDirection, const SlotType &InSlot)
Definition Visibility.h:12
static SLATECORE_API const EVisibility Visible
Definition Visibility.h:14
static SLATECORE_API const EVisibility Collapsed
Definition Visibility.h:17
Definition SlateStructs.h:184
Definition Geometry.h:40
FGeometry MakeChild(const UE::Slate::FDeprecateVector2DParameter &InLocalSize, const FSlateLayoutTransform &LayoutTransform, const FSlateRenderTransform &RenderTransform, const UE::Slate::FDeprecateVector2DParameter &RenderTransformPivot) const
Definition Geometry.h:225
UE::Slate::FDeprecateVector2DResult GetLocalSize() const
Definition Geometry.h:510
Definition Margin.h:17
float Right
Definition Margin.h:30
float Left
Definition Margin.h:22
float GetTotalSpaceAlong() const
Definition Margin.h:210
static UE_FORCEINLINE_HINT bool IsNearlyEqual(float A, float B, float ErrorTolerance=UE_SMALL_NUMBER)
Definition UnrealMathUtility.h:388
static constexpr UE_FORCEINLINE_HINT T Clamp(const T X, const T MinValue, const T MaxValue)
Definition UnrealMathUtility.h:592
static UE_FORCEINLINE_HINT bool IsNearlyZero(float Value, float ErrorTolerance=UE_SMALL_NUMBER)
Definition UnrealMathUtility.h:407
Definition SlateStructs.h:96
ESizeRule SizeRule
Definition SlateStructs.h:105
TAttribute< float > Value
Definition SlateStructs.h:112
ESizeRule
Definition SlateStructs.h:98
@ SizeRule_Stretch
Definition SlateStructs.h:100
@ SizeRule_Auto
Definition SlateStructs.h:99
@ SizeRule_StretchContent
Definition SlateStructs.h:101
TAttribute< float > ShrinkValue
Definition SlateStructs.h:120
Definition LayoutUtils.h:33
TOptional< FSlateRenderTransform > RenderTransform
Definition LayoutUtils.h:164
float MaxSize
Definition LayoutUtils.h:152
TSharedPtr< SWidget > Widget
Definition LayoutUtils.h:170
bool operator==(const int32 InSlotIndex) const
Definition LayoutUtils.h:115
EVerticalAlignment VerticalAlignment
Definition LayoutUtils.h:161
FMargin Padding
Definition LayoutUtils.h:137
EHorizontalAlignment HorizontalAlignment
Definition LayoutUtils.h:158
FSlotProxy()=default
FSizeParam SizeParam
Definition LayoutUtils.h:146
bool operator<(const FSlotProxy &InOtherSlot) const
Definition LayoutUtils.h:121
FVector2f ArrangedSize
Definition LayoutUtils.h:143
FSlotProxy(const int32 InSlotIndex, const SlotType &InSlot)
Definition LayoutUtils.h:38
FVector2f RenderTransformPivot
Definition LayoutUtils.h:167
int32 SlotIndex
Definition LayoutUtils.h:134
float MinSize
Definition LayoutUtils.h:149
FVector2f DesiredSize
Definition LayoutUtils.h:140
EVisibility Visibility
Definition LayoutUtils.h:155
SLATECORE_API FGeometry MakeGeometry(const FGeometry &InParentGeometry, const FVector2f &InChildOffset, const FVector2f &InLocalSize) const
Definition LayoutUtils.cpp:45
bool UpdateFromSlot(const int32 InSlotIndex, const SlotType &InSlot)
Definition LayoutUtils.h:61
Definition Optional.h:131
static UE_FORCEINLINE_HINT TVector2< double > Min(const TVector2< double > &A, const TVector2< double > &B)
Definition Vector2D.h:959
bool Equals(const TVector2< T > &V, T Tolerance=UE_KINDA_SMALL_NUMBER) const
Definition Vector2D.h:1007
T Size() const
Definition Vector2D.h:1111
float FReal
Definition Vector2D.h:42
T Y
Definition Vector2D.h:52
static UE_FORCEINLINE_HINT TVector2< double > Max(const TVector2< double > &A, const TVector2< double > &B)
Definition Vector2D.h:953
T X
Definition Vector2D.h:49
static CORE_API const TVector2< float > ZeroVector
Definition Vector2D.h:63