Skip to content

Instantly share code, notes, and snippets.

@TrinketBen
Created December 5, 2024 22:42
Show Gist options
  • Save TrinketBen/c4d9a6309db0d2a84b25ff20c5a4fd55 to your computer and use it in GitHub Desktop.
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.
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