Created
July 24, 2024 12:52
-
-
Save starikcetin/19b4f24f664d9ab12952a55ca61bb7cf to your computer and use it in GitHub Desktop.
Modified version of Unity's ContentSizeFitter to allow filling the parent if the content ends up smaller.
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.UI; | |
using UnityEngine; | |
using System.Collections.Generic; | |
[AddComponentMenu("Layout/Parent Aware Content Size Fitter", 141)] | |
[ExecuteAlways] | |
[RequireComponent(typeof(RectTransform))] | |
/// <summary> | |
/// Resizes a RectTransform to fit the size of its content, while also allowing to fill the parent if the content is smaller. | |
/// </summary> | |
/// <remarks> | |
/// The ParentAwareContentSizeFitter can be used on GameObjects that have one or more ILayoutElement components, such as Text, Image, HorizontalLayoutGroup, VerticalLayoutGroup, and GridLayoutGroup. | |
/// </remarks> | |
public class ParentAwareContentSizeFitter : UIBehaviour, ILayoutSelfController | |
{ | |
private static bool SetStruct<T>(ref T currentValue, T newValue) where T : struct | |
{ | |
if (EqualityComparer<T>.Default.Equals(currentValue, newValue)) | |
{ | |
return false; | |
} | |
currentValue = newValue; | |
return true; | |
} | |
/// <summary> | |
/// The size fit modes avaliable to use. | |
/// </summary> | |
public enum FitMode | |
{ | |
/// <summary> | |
/// Don't perform any resizing. | |
/// </summary> | |
Unconstrained, | |
/// <summary> | |
/// Resize to the minimum size of the content. | |
/// </summary> | |
MinSize, | |
/// <summary> | |
/// Resize to the minimum size of the content, but fill the parent if the content is smaller. | |
/// </summary> | |
MinSizeFillParent, | |
/// <summary> | |
/// Resize to the preferred size of the content. | |
/// </summary> | |
PreferredSize, | |
/// <summary> | |
/// Resize to the preferred size of the content, but fill the parent if the content is smaller. | |
/// </summary> | |
PreferredSizeFillParent | |
} | |
[SerializeField] protected FitMode m_HorizontalFit = FitMode.Unconstrained; | |
/// <summary> | |
/// The fit mode to use to determine the width. | |
/// </summary> | |
public FitMode horizontalFit { get { return m_HorizontalFit; } set { if (SetStruct(ref m_HorizontalFit, value)) SetDirty(); } } | |
[SerializeField] protected FitMode m_VerticalFit = FitMode.Unconstrained; | |
/// <summary> | |
/// The fit mode to use to determine the height. | |
/// </summary> | |
public FitMode verticalFit { get { return m_VerticalFit; } set { if (SetStruct(ref m_VerticalFit, value)) SetDirty(); } } | |
[System.NonSerialized] private RectTransform m_Rect; | |
private RectTransform rectTransform | |
{ | |
get | |
{ | |
if (m_Rect == null) | |
m_Rect = GetComponent<RectTransform>(); | |
return m_Rect; | |
} | |
} | |
// field is never assigned warning | |
#pragma warning disable 649 | |
private DrivenRectTransformTracker m_Tracker; | |
#pragma warning restore 649 | |
protected ParentAwareContentSizeFitter() | |
{ } | |
protected override void OnEnable() | |
{ | |
base.OnEnable(); | |
SetDirty(); | |
} | |
protected override void OnDisable() | |
{ | |
m_Tracker.Clear(); | |
LayoutRebuilder.MarkLayoutForRebuild(rectTransform); | |
base.OnDisable(); | |
} | |
protected override void OnRectTransformDimensionsChange() | |
{ | |
SetDirty(); | |
} | |
private void HandleSelfFittingAlongAxis(int axis) | |
{ | |
void _SetSize(bool forMinSize, bool fillParent) | |
{ | |
var size = forMinSize ? LayoutUtility.GetMinSize(m_Rect, axis) : LayoutUtility.GetPreferredSize(m_Rect, axis); | |
if (fillParent) | |
{ | |
var parentSize = (rectTransform.parent as RectTransform).rect; | |
var parentAxisSize = axis == 0 ? parentSize.width : parentSize.height; | |
size = Mathf.Max(size, parentAxisSize); | |
} | |
rectTransform.SetSizeWithCurrentAnchors((RectTransform.Axis)axis, size); | |
} | |
FitMode fitting = (axis == 0 ? horizontalFit : verticalFit); | |
if (fitting == FitMode.Unconstrained) | |
{ | |
// Keep a reference to the tracked transform, but don't control its properties: | |
m_Tracker.Add(this, rectTransform, DrivenTransformProperties.None); | |
return; | |
} | |
m_Tracker.Add(this, rectTransform, (axis == 0 ? DrivenTransformProperties.SizeDeltaX : DrivenTransformProperties.SizeDeltaY)); | |
// Set size to min or preferred size | |
switch (fitting) | |
{ | |
case FitMode.MinSize: | |
_SetSize(true, false); | |
break; | |
case FitMode.MinSizeFillParent: | |
_SetSize(true, true); | |
break; | |
case FitMode.PreferredSize: | |
_SetSize(false, false); | |
break; | |
case FitMode.PreferredSizeFillParent: | |
_SetSize(false, true); | |
break; | |
default: | |
throw new System.ArgumentOutOfRangeException(); | |
} | |
} | |
/// <summary> | |
/// Calculate and apply the horizontal component of the size to the RectTransform | |
/// </summary> | |
public virtual void SetLayoutHorizontal() | |
{ | |
m_Tracker.Clear(); | |
HandleSelfFittingAlongAxis(0); | |
} | |
/// <summary> | |
/// Calculate and apply the vertical component of the size to the RectTransform | |
/// </summary> | |
public virtual void SetLayoutVertical() | |
{ | |
HandleSelfFittingAlongAxis(1); | |
} | |
protected void SetDirty() | |
{ | |
if (!IsActive()) | |
return; | |
LayoutRebuilder.MarkLayoutForRebuild(rectTransform); | |
} | |
#if UNITY_EDITOR | |
protected override void OnValidate() | |
{ | |
SetDirty(); | |
} | |
#endif | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment