Created
December 5, 2024 22:42
-
-
Save TrinketBen/c4d9a6309db0d2a84b25ff20c5a4fd55 to your computer and use it in GitHub Desktop.
Tries to calculate a size for a parent RectTransform (e.g. a window) based on the child RectTransforms relative to a target Transform, Content.
This file contains hidden or 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 System; | |
using System.Collections; | |
using UnityEngine; | |
using UnityEngine.UI; | |
using Sirenix.OdinInspector; | |
namespace Trinket.UtilityComponents { | |
[ExecuteAlways] | |
public class SizeToFitContents : MonoBehaviour { | |
public RectTransform Content; | |
public Vector2 MaxDimensions = new(-1, -1); | |
public bool ExecuteWhenChildrenChange; | |
public AxisFlags AxisFlags = AxisFlags.XY; | |
Coroutine _coroutine; | |
bool _executing; | |
RectTransform Rect => transform as RectTransform; | |
ScrollRect ScrollRect => transform.GetComponentInChildren<ScrollRect>(true); | |
float ComputeSize(RectTransform inContent, RectTransform.Axis inAxis) { | |
if(inContent == null) { | |
return 0f; | |
} | |
float SumOffsets(float inSum, RectTransform inTarget) { | |
if((inTarget.GetComponent<LayoutElement>()?.ignoreLayout).GetValueOrDefault()) { | |
return inSum; | |
} | |
var contentSizeFitter = inTarget.GetComponent<ContentSizeFitter>(); | |
var layoutGroup = inTarget.GetComponent<LayoutGroup>(); | |
switch(inAxis) { | |
case RectTransform.Axis.Vertical: | |
if(contentSizeFitter == null && inTarget.anchorMin.y <= 0f && inTarget.anchorMax.y >= 1f) { | |
if(!Mathf.Approximately(inTarget.offsetMin.y, 0f)) { | |
inSum += inTarget.offsetMin.y; | |
} | |
if(!Mathf.Approximately(inTarget.offsetMax.y, 0f)) { | |
inSum -= inTarget.offsetMax.y; | |
} | |
} | |
if(layoutGroup != null) { | |
inSum += layoutGroup.padding.top; | |
inSum += layoutGroup.padding.bottom; | |
} | |
break; | |
case RectTransform.Axis.Horizontal: | |
if(contentSizeFitter == null && inTarget.anchorMin.x <= 0f && inTarget.anchorMax.x >= 1f) { | |
if(!Mathf.Approximately(inTarget.offsetMin.x, 0f)) { | |
inSum += inTarget.offsetMin.x; | |
} | |
if(!Mathf.Approximately(inTarget.offsetMax.x, 0f)) { | |
inSum -= inTarget.offsetMax.x; | |
} | |
} | |
if(layoutGroup != null) { | |
inSum += layoutGroup.padding.left; | |
inSum += layoutGroup.padding.right; | |
} | |
break; | |
} | |
return inSum; | |
} | |
var sum = 0f; var targetTransform = inContent; | |
while(targetTransform != null) { | |
if(targetTransform == transform) { | |
break; | |
} | |
sum = SumOffsets(sum, targetTransform); | |
targetTransform = targetTransform.parent as RectTransform; | |
} | |
var contentSizeFitter = inContent.GetComponent<ContentSizeFitter>(); | |
switch(inAxis) { | |
case RectTransform.Axis.Horizontal: | |
if(Mathf.Approximately(inContent.anchorMin.x, inContent.anchorMax.x) || contentSizeFitter != null) { | |
sum += inContent.rect.width; | |
} | |
break; | |
case RectTransform.Axis.Vertical: | |
if(Mathf.Approximately(inContent.anchorMin.y, inContent.anchorMax.y) || contentSizeFitter != null) { | |
sum += inContent.rect.height; | |
} | |
break; | |
} | |
return sum; | |
} | |
public void Execute() { | |
if(!Application.isPlaying && !gameObject.activeInHierarchy) { | |
return; | |
} | |
if(_executing) { | |
return; | |
} | |
_executing = true; | |
if(!gameObject.activeInHierarchy) { | |
Execute_Impl(); | |
} else { | |
IEnumerator Delay() { | |
yield return null; | |
Execute_Impl(); | |
_coroutine = null; | |
} | |
if(_coroutine == null) { | |
_coroutine = StartCoroutine(Delay()); | |
} | |
} | |
_executing = false; | |
} | |
[Button("Execute")] | |
void Execute_Impl() { | |
var content = Content != null ? Content : ScrollRect?.content; | |
if(content == null) { | |
return; | |
} | |
void ApplyMaxSize(RectTransform.Axis inAxis, float inMaxValue, float inCurrentSize) { | |
var newValue = ComputeSize(content, inAxis); | |
if(inMaxValue > 0f) { | |
newValue = Mathf.Min(newValue, inMaxValue); | |
} | |
newValue = Mathf.Max(newValue, 0f); | |
if(Mathf.Approximately(newValue, inCurrentSize)) { | |
return; | |
} | |
Rect.SetSizeWithCurrentAnchors(inAxis, newValue); | |
if(ScrollRect != null) { | |
switch(inAxis) { | |
case RectTransform.Axis.Vertical: | |
ScrollRect.verticalNormalizedPosition = 1f; | |
return; | |
} | |
} | |
} | |
if(AxisFlags.HasFlag(AxisFlags.X)) { | |
ApplyMaxSize(RectTransform.Axis.Horizontal, MaxDimensions.x, Rect.GetSize().x); | |
} | |
if(AxisFlags.HasFlag(AxisFlags.Y)) { | |
ApplyMaxSize(RectTransform.Axis.Vertical, MaxDimensions.y, Rect.GetSize().y); | |
} | |
} | |
void Awake() { | |
if(ExecuteWhenChildrenChange) { | |
Execute(); | |
} | |
} | |
void OnEnable() { | |
if(ExecuteWhenChildrenChange) { | |
Execute(); | |
} | |
} | |
void OnDisable() { | |
_coroutine = null; | |
} | |
void OnRectTransformDimensionsChange() { | |
if(ExecuteWhenChildrenChange) { | |
Execute(); | |
} | |
} | |
void OnTransformChildrenChanged() { | |
if(ExecuteWhenChildrenChange) { | |
Execute(); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment