Created
July 24, 2024 12:19
-
-
Save wappenull/6f09af3d2c2635aa74eda3c6987ea432 to your computer and use it in GitHub Desktop.
This is accompany script for my own StackOverflow answer, see comment below for more info.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine.EventSystems; | |
using UnityEngine; | |
using UnityEngine.UI; | |
namespace Wappen.UI | |
{ | |
/// <summary> | |
/// Similar to LayoutElement but with limiter. | |
/// </summary> | |
[AddComponentMenu( "Layout/Layout Element Filter", 141 )] | |
[ExecuteInEditMode] | |
public class LayoutElementFilter : LayoutElementBehaviour | |
{ | |
public enum FitMode | |
{ | |
Unconstrained, | |
MinSize, | |
PreferredSize | |
} | |
[Tooltip("Must implement ILayoutElement.")] | |
[SerializeField] Component m_SourceElement = default; | |
[SerializeField] protected FitMode m_HorizontalFit = FitMode.Unconstrained; | |
[SerializeField] protected FitMode m_VerticalFit = FitMode.Unconstrained; | |
[Header("Filtering")] | |
[Tooltip( "Max width." )] | |
[SerializeField] protected float m_MaxPreferredWidth = 0; | |
float m_PreferredWidth; | |
float m_PreferredHeight; | |
public override float preferredWidth => m_PreferredWidth; | |
public override float preferredHeight => m_PreferredHeight; | |
#if UNITY_EDITOR | |
protected override void OnValidate( ) | |
{ | |
if( m_SourceElement != null && !(m_SourceElement is ILayoutElement) ) | |
{ | |
// When drag and drop, m_SourceElement could get RectTransform instead | |
// Make sure to reacquire if ILayoutElement is avilable | |
ILayoutElement nextAvailable = m_SourceElement.GetComponent<ILayoutElement>( ); | |
if( nextAvailable != null && nextAvailable is Component c ) | |
{ | |
Helper.RecordUndo( this, "m_SourceElement route" ); | |
m_SourceElement = c; // Change to that instead | |
} | |
else | |
{ | |
Debug.LogWarning( $"{this.GetDebugPath( )} requires m_SourceElement to be component with ILayoutElement.", this ); | |
} | |
} | |
base.OnValidate( ); | |
} | |
#endif | |
private void HandleFittingAlongAxis( int axis ) | |
{ | |
FitMode fitting = (axis == 0 ? m_HorizontalFit : m_VerticalFit); | |
RectTransform.Axis ax = (RectTransform.Axis)axis; | |
ILayoutElement elm = m_SourceElement as ILayoutElement; | |
if( fitting == FitMode.Unconstrained || elm == null ) | |
{ | |
if( ax == RectTransform.Axis.Horizontal ) | |
m_PreferredWidth = -1; | |
else | |
m_PreferredHeight = -1; | |
return; | |
} | |
// Set size to min or preferred size | |
float s; | |
if( fitting == FitMode.MinSize ) | |
{ | |
if( ax == RectTransform.Axis.Horizontal ) | |
s = elm.minWidth; | |
else | |
s = elm.minHeight; | |
//s = LayoutUtility.GetMinSize( r, axis ); | |
} | |
else | |
{ | |
if( ax == RectTransform.Axis.Horizontal ) | |
s = elm.preferredWidth; | |
else | |
s = elm.preferredHeight; | |
//s = LayoutUtility.GetPreferredSize( r, axis ); | |
} | |
// Apply limit | |
if( axis == 0 && m_MaxPreferredWidth > 0 ) // Horz | |
s = Mathf.Clamp( s, 0, m_MaxPreferredWidth ); | |
if( ax == RectTransform.Axis.Horizontal ) | |
m_PreferredWidth = s; | |
else | |
m_PreferredHeight = s; | |
} | |
public override void CalculateLayoutInputHorizontal( ) | |
{ | |
HandleFittingAlongAxis( 0 ); | |
} | |
public override void CalculateLayoutInputVertical( ) | |
{ | |
HandleFittingAlongAxis( 1 ); | |
} | |
} | |
/// <summary> | |
/// Base class for class that want to control ILayoutElement. | |
/// </summary> | |
[RequireComponent( typeof( RectTransform ) )] | |
public abstract class LayoutElementBehaviour : UIBehaviour, ILayoutElement | |
{ | |
/* ILayoutElement //////////////////////////////////*/ | |
public virtual float minWidth => -1; | |
public virtual float preferredWidth => -1; | |
public virtual float minHeight => -1; | |
public virtual float preferredHeight => -1; | |
public virtual float flexibleWidth => -1; | |
public virtual float flexibleHeight => -1; | |
[SerializeField] private int m_LayoutPriority = 2; | |
public int layoutPriority => m_LayoutPriority; | |
public abstract void CalculateLayoutInputHorizontal( ); | |
public abstract void CalculateLayoutInputVertical( ); | |
/* Unity ///////////////////////////////////////*/ | |
#if UNITY_EDITOR | |
protected override void OnValidate( ) | |
{ | |
SetDirty( ); | |
} | |
#endif | |
/* Internal ///////////////////////////////////////////*/ | |
[System.NonSerialized] RectTransform m_Rect; | |
protected RectTransform rectTransform | |
{ | |
get | |
{ | |
if( m_Rect == null ) | |
m_Rect = GetComponent<RectTransform>( ); | |
return m_Rect; | |
} | |
} | |
/* Dirtying stuff //////////////////////*/ | |
protected override void OnEnable() | |
{ | |
base.OnEnable(); | |
SetDirty(); | |
} | |
protected override void OnTransformParentChanged() | |
{ | |
SetDirty(); | |
} | |
protected override void OnDisable() | |
{ | |
SetDirty(); | |
base.OnDisable(); | |
} | |
protected override void OnDidApplyAnimationProperties() | |
{ | |
SetDirty(); | |
} | |
protected override void OnBeforeTransformParentChanged() | |
{ | |
SetDirty(); | |
} | |
protected void SetDirty() | |
{ | |
if (!IsActive()) | |
return; | |
LayoutRebuilder.MarkLayoutForRebuild(transform as RectTransform); | |
} | |
} | |
} |
Note: For readers coming from "flowlayoutgroup" StackOverflow question
The example image in first comment used HorizontalLayoutGroup for simplicity and for demonstrating the script.
To get the real "flowlayoutgroup" you are seeking, don't forget to replace to the real "flowlayoutgroup" controller you have.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is for My own answer in StackOverflow answer
What this script does is allow you to pulls other element's width/height and directly override current element's size.
For example, suppose we have HorizontalLayoutGroup and the traditional "Button - TextMeshPro" UI object where you want button to have dynamic size depending on its TMP_Text child inside you would have to insert LayoutSizeFitter in both TMP_Text (first to have text decide its own size) and in Button (to make it fits from its text inside). BUT oh wait, that doesn't work, because Fitter on TMP_Text will blindly use size reported from its Image instead of TMP_Text we want. So it is another rabbit hole to set this up.
I'm not sure if currently (2024), there is official or more correct way to do this but this script was my own custom resolution to this problem.
So instead of that mess in above paragraph, from the freshly created "Button - TextMeshPro" UI object, just insert this script on Button object and set it to "report" the size from child text object. boom done. (HorizontalLayoutGroup also need some setup from default, as seen from screenshot)