Skip to content

Instantly share code, notes, and snippets.

@neoRiley
Last active June 10, 2024 17:38
Show Gist options
  • Save neoRiley/b24e9d2332e2989833c03e24770d46d9 to your computer and use it in GitHub Desktop.
Save neoRiley/b24e9d2332e2989833c03e24770d46d9 to your computer and use it in GitHub Desktop.
Extends VisualElement and provides the ability to maintain an aspect ratio as well as add a Label who's font size is scaled with the AspectRatioPanel
using System;
using System.Collections.Generic;
using UniRx;
using UnityEngine;
using UnityEngine.UIElements;
namespace N30R1L37
{
[UnityEngine.Scripting.Preserve]
public class AspectRatioButton : Button, IDisposable
{
[UnityEngine.Scripting.Preserve]
public new class UxmlFactory : UxmlFactory<AspectRatioButton, UxmlTraits> {}
[UnityEngine.Scripting.Preserve]
public new class UxmlTraits : VisualElement.UxmlTraits
{
readonly UxmlBoolAttributeDescription maintainAspectRatio = new() { name = "maintain-aspect-ratio", defaultValue = true };
readonly UxmlEnumAttributeDescription<RatioFlexEnum.RatioFlex> ratioFlex = new () { name = "ratio-flex-type", defaultValue = RatioFlexEnum.RatioFlex.Auto};
readonly UxmlFloatAttributeDescription aspectRatio = new() { name = "aspect-ratio", defaultValue = 1.0f };
readonly UxmlFloatAttributeDescription scale = new() { name = "scale", defaultValue = 1.0f };
readonly UxmlStringAttributeDescription label = new() { name = "label", defaultValue = "Button" };
readonly UxmlFloatAttributeDescription fontScale = new() { name = "font-scale", defaultValue = 1.0f };
readonly UxmlIntAttributeDescription maxCharacters = new() { name = "max-characters", defaultValue = 20 };
readonly UxmlFloatAttributeDescription cornerRadius = new() { name = "corner-radius", defaultValue = 0.0f};
public override IEnumerable<UxmlChildElementDescription> uxmlChildElementsDescription
{
get { yield break; }
}
public override void Init( VisualElement visualElement, IUxmlAttributes attributes, CreationContext creationContext )
{
base.Init( visualElement, attributes, creationContext );
AspectRatioButton element = (AspectRatioButton)visualElement;
if (element != null)
{
element.MaintainAspectRatio = maintainAspectRatio.GetValueFromBag(attributes, creationContext);
element.RatioFlexType = ratioFlex.GetValueFromBag(attributes, creationContext);
element.AspectRatio = aspectRatio.GetValueFromBag( attributes, creationContext ) == 0 ? 1 : aspectRatio.GetValueFromBag( attributes, creationContext );
element.Scale = scale.GetValueFromBag( attributes, creationContext );
element.Label = label.GetValueFromBag(attributes, creationContext);
element.FontScale = fontScale.GetValueFromBag( attributes, creationContext );
element.MaxCharacters = maxCharacters.GetValueFromBag( attributes, creationContext );
element.CornerRadius = cornerRadius.GetValueFromBag(attributes, creationContext);
}
}
}
public bool ScaleFont { get; set; }
private Label _labelElement;
public RatioFlexEnum.RatioFlex RatioFlexType { get; set; } = RatioFlexEnum.RatioFlex.Auto;
public float Scale { get; set; } = 1.0f;
private bool _maintainAspectRatio = true;
public bool MaintainAspectRatio
{
get => _maintainAspectRatio;
set
{
_maintainAspectRatio = value;
if (!value) return;
style.width = StyleKeyword.Auto;
style.height = StyleKeyword.Auto;
style.minWidth = StyleKeyword.Auto;
style.minHeight = StyleKeyword.Auto;
style.maxWidth = StyleKeyword.Auto;
style.maxHeight = StyleKeyword.Auto;
}
}
private float _aspectRatio = 1.0f;
public float AspectRatio
{
get => _aspectRatio;
set
{
_aspectRatio = Mathf.Max(value, 0.01f);
}
}
private float _cornerRadius = 0.0f;
public float CornerRadius
{
get => _cornerRadius;
set
{
_cornerRadius = Mathf.Max(value, 0.0f);
}
}
private int _maxCharacters = 20;
public int MaxCharacters
{
get => _maxCharacters;
set
{
_maxCharacters = value;
_isDirty = true;
}
}
private string _label = "";
public string Label
{
get => _label;
set
{
_label = value;
}
}
public float FontScale { get; set; } = 1;
public void ForceRedraw() => _isDirty = true;
public AspectRatioButton()
{
RegisterCallback<AttachToPanelEvent>( OnAttachToPanelEvent );
}
void OnAttachToPanelEvent( AttachToPanelEvent e )
{
parent?.RegisterCallback<GeometryChangedEvent>( OnGeometryChangedEvent );
MonitorIsDirty();
}
private bool _isDirty = false;
private void MonitorIsDirty()
{
Observable.EveryUpdate().ObserveOnMainThread().Subscribe(_ =>
{
if (!_isDirty) return;
Redraw();
});
}
void OnGeometryChangedEvent( GeometryChangedEvent e )
{
_isDirty = true;
}
void Redraw()
{
if (parent == null || !this.IsInitialized()) return;
var newSize = this.RedrawAspectRatio(
parent.localBound.size,
AspectRatio,
Scale,
MaintainAspectRatio,
RatioFlexType);
newSize *= Scale;
SetBorderRadius(CornerRadius * newSize.magnitude * 0.01f);
if (_label.Length > _maxCharacters)
{
text = _label.Substring(0, _maxCharacters - 3) + "...";
}
else if (_label.Length < 4)
{
text = $" {_label} ";
}
else
{
text = _label;
}
if (ScaleFont)
{
this.ResizeFont(newSize, FontScale, Label);
}
else
{
float fontSize = MaxCharacters * FontScale * newSize.magnitude * 0.01f;
style.fontSize = new StyleLength (fontSize);
}
_isDirty = false;
}
private void SetBorderRadius(float scale)
{
style.borderBottomLeftRadius = scale;
style.borderTopLeftRadius = scale;
style.borderTopRightRadius = scale;
style.borderBottomRightRadius = scale;
}
public void Dispose()
{
parent?.UnregisterCallback<GeometryChangedEvent>( OnGeometryChangedEvent );
UnregisterCallback<AttachToPanelEvent>( OnAttachToPanelEvent );
}
}
}
using System;
using System.Collections.Generic;
using EvolveDM.UI.UIToolKit.Base;
using UniRx;
using UnityEngine;
using UnityEngine.UIElements;
using Zenject;
namespace N30R1L37
{
[UnityEngine.Scripting.Preserve]
public class AspectRatioLabel : Label, IDisposable
{
[UnityEngine.Scripting.Preserve]
public new class UxmlFactory : UxmlFactory<AspectRatioLabel, UxmlTraits> {}
[UnityEngine.Scripting.Preserve]
public new class UxmlTraits : VisualElement.UxmlTraits
{
readonly UxmlBoolAttributeDescription maintainAspectRatio = new() { name = "maintain-aspect-ratio", defaultValue = true };
readonly UxmlEnumAttributeDescription<RatioFlexEnum.RatioFlex> ratioFlex = new () { name = "ratio-flex-type", defaultValue = RatioFlexEnum.RatioFlex.Auto};
readonly UxmlFloatAttributeDescription aspectRatio = new() { name = "aspect-ratio", defaultValue = 1.0f };
readonly UxmlFloatAttributeDescription scale = new() { name = "scale", defaultValue = 1.0f };
readonly UxmlStringAttributeDescription label = new() { name = "label", defaultValue = "Button" };
readonly UxmlEnumAttributeDescription<FontResizeTypeEnum.FontResizeType> fontResizeType = new () { name = "font-resize-type", defaultValue = FontResizeTypeEnum.FontResizeType.None};
readonly UxmlFloatAttributeDescription fontScale = new() { name = "font-scale", defaultValue = 1.0f };
readonly UxmlIntAttributeDescription maxCharacters = new() { name = "max-characters", defaultValue = 20 };
//readonly UxmlFloatAttributeDescription grow = new() { name = "label-grow", defaultValue = 1.0f};
readonly UxmlFloatAttributeDescription cornerRadius = new() { name = "corner-radius", defaultValue = 0.0f};
public override IEnumerable<UxmlChildElementDescription> uxmlChildElementsDescription
{
get { yield break; }
}
public override void Init( VisualElement visualElement, IUxmlAttributes attributes, CreationContext creationContext )
{
base.Init( visualElement, attributes, creationContext );
AspectRatioLabel element = (AspectRatioLabel)visualElement;
if (element != null)
{
element.MaintainAspectRatio = maintainAspectRatio.GetValueFromBag(attributes, creationContext);
element.RatioFlexType = ratioFlex.GetValueFromBag(attributes, creationContext);
element.FontResizeType = fontResizeType.GetValueFromBag(attributes, creationContext);
element.AspectRatio = aspectRatio.GetValueFromBag( attributes, creationContext ) == 0 ? 1 : aspectRatio.GetValueFromBag( attributes, creationContext );
element.Scale = scale.GetValueFromBag( attributes, creationContext );
element.Label = label.GetValueFromBag(attributes, creationContext);
element.FontScale = fontScale.GetValueFromBag( attributes, creationContext );
element.MaxCharacters = maxCharacters.GetValueFromBag( attributes, creationContext );
element.CornerRadius = cornerRadius.GetValueFromBag(attributes, creationContext);
}
}
}
public FontResizeTypeEnum.FontResizeType FontResizeType { get; set; } = FontResizeTypeEnum.FontResizeType.None;
public RatioFlexEnum.RatioFlex RatioFlexType { get; set; } = RatioFlexEnum.RatioFlex.Auto;
public float Scale { get; set; } = 1.0f;
private bool _maintainAspectRatio = true;
public bool MaintainAspectRatio
{
get => _maintainAspectRatio;
set
{
_maintainAspectRatio = value;
if (!value) return;
style.width = StyleKeyword.Auto;
style.height = StyleKeyword.Auto;
style.minWidth = StyleKeyword.Auto;
style.minHeight = StyleKeyword.Auto;
style.maxWidth = StyleKeyword.Auto;
style.maxHeight = StyleKeyword.Auto;
}
}
private float _aspectRatio = 1.0f;
public float AspectRatio
{
get => _aspectRatio;
set
{
_aspectRatio = Mathf.Max(value, 0.01f);
}
}
private float _cornerRadius = 0.0f;
public float CornerRadius
{
get => _cornerRadius;
set
{
_cornerRadius = Mathf.Max(value, 0.0f);
}
}
private string _label = "";
public string Label
{
get => _label;
set
{
_label = value;
text = value;
}
}
private int _maxCharacters = 20;
public int MaxCharacters
{
get => _maxCharacters;
set
{
_maxCharacters = value;
_isDirty = true;
}
}
public float FontScale { get; set; } = 1;
public void ForceRedraw() => _isDirty = true;
public AspectRatioLabel()
{
RegisterCallback<AttachToPanelEvent>( OnAttachToPanelEvent );
}
void OnAttachToPanelEvent( AttachToPanelEvent e )
{
parent?.RegisterCallback<GeometryChangedEvent>( OnGeometryChangedEvent );
style.width = Length.Percent(100);
style.height = Length.Percent(100);
style.whiteSpace = WhiteSpace.Normal;
style.overflow = Overflow.Hidden;
MonitorIsDirty();
}
private bool _isDirty = false;
private void MonitorIsDirty()
{
Observable.EveryUpdate().ObserveOnMainThread().Subscribe(_ =>
{
if (!_isDirty) return;
Redraw();
});
}
void OnGeometryChangedEvent( GeometryChangedEvent e )
{
_isDirty = true;
}
void Redraw()
{
if (parent == null || !this.IsInitialized()) return;
var newSize = this.RedrawAspectRatio(
parent.localBound.size,
AspectRatio,
Scale,
MaintainAspectRatio,
RatioFlexType);
newSize *= Scale;
SetBorderRadius(CornerRadius * newSize.magnitude * 0.01f);
switch (FontResizeType)
{
case FontResizeTypeEnum.FontResizeType.None:
break;
case FontResizeTypeEnum.FontResizeType.Scale:
this.ResizeFont(newSize, FontScale);
break;
case FontResizeTypeEnum.FontResizeType.Screen:
float fontSize = MaxCharacters * FontScale * /*(newSize.magnitude * 0.01f) */ ScreenCoordinator.ScreenScale;
style.fontSize = new StyleLength (fontSize);
break;
}
_isDirty = false;
}
private void SetBorderRadius(float scale)
{
style.borderBottomLeftRadius = scale;
style.borderTopLeftRadius = scale;
style.borderTopRightRadius = scale;
style.borderBottomRightRadius = scale;
}
public void Dispose()
{
parent?.UnregisterCallback<GeometryChangedEvent>( OnGeometryChangedEvent );
UnregisterCallback<AttachToPanelEvent>( OnAttachToPanelEvent );
}
}
}
/*
This is free and unencumbered software released into the public
domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the
benefit of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
using System;
using System.Collections.Generic;
using UniRx;
using UnityEngine;
using UnityEngine.UIElements;
namespace N30R1L37
{
[UnityEngine.Scripting.Preserve]
public class AspectRatioPanel : VisualElement, IDisposable
{
[UnityEngine.Scripting.Preserve]
public new class UxmlFactory : UxmlFactory<AspectRatioPanel, UxmlTraits> {}
[UnityEngine.Scripting.Preserve]
public new class UxmlTraits : VisualElement.UxmlTraits
{
readonly UxmlBoolAttributeDescription maintainAspectRatio = new() { name = "maintain-aspect-ratio", defaultValue = true };
readonly UxmlEnumAttributeDescription<RatioFlexEnum.RatioFlex> ratioFlex = new () { name = "ratio-flex-type", defaultValue = RatioFlexEnum.RatioFlex.Auto};
readonly UxmlFloatAttributeDescription aspectRatio = new() { name = "aspect-ratio", defaultValue = 1.0f };
readonly UxmlFloatAttributeDescription scale = new() { name = "scale", defaultValue = 1.0f };
readonly UxmlFloatAttributeDescription cornerRadius = new() { name = "corner-radius", defaultValue = 0.0f};
readonly UxmlBoolAttributeDescription addLabel = new() { name = "add-label", defaultValue = false };
readonly UxmlStringAttributeDescription text = new() { name = "text", defaultValue = "Label" };
readonly UxmlFloatAttributeDescription fontScale = new() { name = "font-scale", defaultValue = 1.0f };
readonly UxmlFloatAttributeDescription lableGrow = new() { name = "label-grow", defaultValue = 1.0f};
public override IEnumerable<UxmlChildElementDescription> uxmlChildElementsDescription
{
get { yield break; }
}
public override void Init( VisualElement visualElement, IUxmlAttributes attributes, CreationContext creationContext )
{
base.Init( visualElement, attributes, creationContext );
AspectRatioPanel element = (AspectRatioPanel)visualElement;
if (element != null)
{
element.MaintainAspectRatio = maintainAspectRatio.GetValueFromBag(attributes, creationContext);
element.RatioFlexType = ratioFlex.GetValueFromBag(attributes, creationContext);
element.AspectRatio = aspectRatio.GetValueFromBag( attributes, creationContext ) == 0 ? 1 : aspectRatio.GetValueFromBag( attributes, creationContext );
element.Scale = scale.GetValueFromBag( attributes, creationContext );
element.Text = text.GetValueFromBag(attributes, creationContext);
element.FontScale = fontScale.GetValueFromBag( attributes, creationContext );
element.AddLabel = addLabel.GetValueFromBag(attributes, creationContext);
element.CornerRadius = cornerRadius.GetValueFromBag(attributes, creationContext);
element.LabelGrow = lableGrow.GetValueFromBag(attributes, creationContext);
element.Redraw();
}
}
}
private Label _label;
public RatioFlexEnum.RatioFlex RatioFlexType { get; set; } = RatioFlexEnum.RatioFlex.Auto;
public float FontScale { get; set; } = 1;
public float Scale { get; set; } = 1.0f;
private bool _maintainAspectRatio = true;
public bool MaintainAspectRatio
{
get => _maintainAspectRatio;
set
{
_maintainAspectRatio = value;
if (!value) return;
style.width = StyleKeyword.Auto;
style.height = StyleKeyword.Auto;
style.minWidth = StyleKeyword.Auto;
style.minHeight = StyleKeyword.Auto;
style.maxWidth = StyleKeyword.Auto;
style.maxHeight = StyleKeyword.Auto;
}
}
private float _labelGrow = 1;
public float LabelGrow
{
get => _labelGrow;
set
{
_labelGrow = value;
if (_label == null) return;
_label.style.flexGrow = _labelGrow;
}
}
private float _aspectRatio = 1.0f;
public float AspectRatio
{
get => _aspectRatio;
set
{
_aspectRatio = Mathf.Max(value, 0.01f);
}
}
private float _cornerRadius = 0.0f;
public float CornerRadius
{
get => _cornerRadius;
set
{
_cornerRadius = Mathf.Max(value, 0.0f);
}
}
private bool _addLabel = false;
public bool AddLabel
{
get => _addLabel;
set
{
_addLabel = value;
if(value && _label == null) AddLabelToView();
else if(!value && _label != null)
{
Remove(_label);
_label = null;
}
}
}
private string _text = "";
public string Text
{
get => _text;
set
{
_text = value;
if (_label != null) _label.text = value;
}
}
public AspectRatioPanel()
{
if(AddLabel) AddLabelToView();
RegisterCallback<AttachToPanelEvent>( OnAttachToPanelEvent );
}
private void AddLabelToView()
{
var length = new StyleLength(0f);
_label = new()
{
style =
{
position = Position.Absolute,
left = length, //StyleInt(0);//StyleKeyword.Auto,
top = length, //StyleKeyword.Auto,
right = length, //StyleKeyword.Auto,
bottom = length, //StyleKeyword.Auto,
flexShrink = 1,
flexGrow = LabelGrow,
whiteSpace = WhiteSpace.Normal
},
text = _text
};
Add(_label);
}
void OnAttachToPanelEvent( AttachToPanelEvent e )
{
parent?.RegisterCallback<GeometryChangedEvent>( OnGeometryChangedEvent );
MonitorIsDirty();
}
private bool _isDirty = false;
private void MonitorIsDirty()
{
// if (Application.isEditor && !Application.isPlaying)
// {
// Redraw();
// return;
// }
Observable.EveryUpdate().ObserveOnMainThread().Subscribe(_ =>
{
if (!_isDirty) return;
Redraw();
});
}
void OnGeometryChangedEvent( GeometryChangedEvent e )
{
_isDirty = true;
}
void Redraw()
{
if (parent == null) return;
var newSize = this.RedrawAspectRatio(
parent.localBound.size,
AspectRatio,
Scale,
MaintainAspectRatio,
RatioFlexType);
newSize *= Scale;
SetBorderRadius(CornerRadius * newSize.magnitude * 0.01f);
if(AddLabel) _label.ResizeFont(newSize, FontScale);
_isDirty = false;
}
private void SetBorderRadius(float scale)
{
style.borderBottomLeftRadius = scale;
style.borderTopLeftRadius = scale;
style.borderTopRightRadius = scale;
style.borderBottomRightRadius = scale;
}
public void Dispose()
{
parent?.UnregisterCallback<GeometryChangedEvent>( OnGeometryChangedEvent );
UnregisterCallback<AttachToPanelEvent>( OnAttachToPanelEvent );
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using DG.Tweening;
using N30R1L37;
using UniRx;
using UnityEngine;
using UnityEngine.UIElements;
using Unit = UniRx.Unit;
public static class UIElementsExtensions
{
//NOTE: if you want to use the click observables, Add UniRX to your project via the Project Manager (it's free)
/*public static IObservable<Unit> OnClickAsObservable(this Button container)
{
Subject<Unit> _subject = new();
container.RegisterCallback<ClickEvent>(evt =>
{
_subject.OnNext(Unit.Default);
});
return _subject.AsUnitObservable();
}
public static IObservable<Unit> OnClickAsObservable(this VisualElement container)
{
Subject<Unit> _subject = new();
container.RegisterCallback<ClickEvent>(evt =>
{
_subject.OnNext(Unit.Default);
});
return _subject.AsUnitObservable();
}
public static IObservable<PointerDownEvent> OnContextClickAsObservable(this VisualElement container)
{
Subject<PointerDownEvent> _subject = new();
container.RegisterCallback<PointerDownEvent>(evt =>
{
if (evt.button == 0) return;
_subject.OnNext(evt);
});
return _subject.AsObservable();
}
public static IObservable<PointerDownEvent> OnMouseDownAsObservable(this VisualElement container)
{
Subject<PointerDownEvent> _subject = new();
container.RegisterCallback<PointerDownEvent>(evt =>
{
_subject.OnNext(evt);
});
return _subject.AsObservable();
}
public static IObservable<PointerMoveEvent> OnMouseMoveAsObservable(this VisualElement container)
{
Subject<PointerMoveEvent> _subject = new();
container.RegisterCallback<PointerMoveEvent>(evt =>
{
_subject.OnNext(evt);
});
return _subject.AsObservable();
}
public static IObservable<PointerUpEvent> OnMouseUpAsObservable(this VisualElement container)
{
Subject<PointerUpEvent> _subject = new();
container.RegisterCallback<PointerUpEvent>(evt =>
{
_subject.OnNext(evt);
});
return _subject.AsObservable();
}
public static IObservable<Unit> OnInitialized(this VisualElement container)
{
return Observable.Create<Unit>(observer =>
{
if (container.IsInitialized())
{
observer.OnNext(Unit.Default);
observer.OnCompleted();
return Disposable.Empty;
}
return container.ObserveEveryValueChanged(x => x.localBound.width).Subscribe(width =>
{
if (!Single.IsNaN(width))
{
observer.OnNext(Unit.Default);
observer.OnCompleted();
}
});
});
}
*/
public static bool IsInitialized(this VisualElement container)
{
return !Single.IsNaN(container.localBound.width);
}
public static void RemoveAllChildren(this VisualElement container)
{
if (container.childCount > 0)
{
List<VisualElement> children = container.Children().ToList();
foreach (var element in children)
{
container.Remove(element);
}
}
}
public static void SetBackgroundImage(this VisualElement container, Sprite image)
{
Background background = container.style.backgroundImage.value;
background.sprite = image;
container.style.backgroundImage = background;
}
public static void SetBackgroundColor(this VisualElement container, Color color, float alpha = -1)
{
StyleColor styleColor = container.style.backgroundColor;
if (alpha > -1)
{
color.a = alpha;
}
styleColor.value = color;
container.style.backgroundColor = styleColor;
}
public static float GetAlpha(this VisualElement container)
{
return container.style.opacity.value;
}
public static void SetAlpha(this VisualElement container, float value)
{
container.style.opacity = value;
}
public static Vector2 GetPosition(this VisualElement container)
{
return new Vector2(container.style.left.value.value, container.style.top.value.value);
}
public static void SetPosition(this VisualElement container, Vector2 position)
{
container.style.left = position.x;
container.style.right = position.y;
}
public static float GetRotation(this VisualElement container)
{
return container.transform.rotation.eulerAngles.z;
}
public static void SetRotation(this VisualElement container, float angle)
{
container.transform.rotation = Quaternion.Euler(0,0,angle);
}
public static void SetDisplay(this VisualElement container, bool visible)
{
var display = container.style.display;
display.value = visible ? DisplayStyle.Flex : DisplayStyle.None;
container.style.display = display;
}
public static void SetVisibility(this VisualElement container, bool visible)
{
var visibility = container.style.visibility;
visibility.value = visible ? Visibility.Visible : Visibility.Hidden;
container.style.visibility = visibility;
}
public static void SetMargin(this VisualElement container, float value)
{
container.style.marginBottom = value;
container.style.marginLeft = value;
container.style.marginRight = value;
container.style.marginTop = value;
}
public static float GetScale(this VisualElement container)
{
return container.style.scale.value.value.x;
}
public static void SetScale(this VisualElement container, float value)
{
var scale = new StyleScale
{
value = new Scale{ value = (Vector2.one * value) }
};
container.style.scale = scale;
}
public static void SetScale(this StyleLength container, float value)
{
container.value = container.value.value * value;
}
public static void SetStyleScale(this StyleLength container, float value)
{
//container.value = container.value.value * value;
container.value = value;
}
public static AspectRatioButton GetButton(this VisualElement container)
{
if (container is Button) return container as AspectRatioButton;
return container.Children().ToList()
.OfType<AspectRatioButton>()
.Select(visualElement => visualElement)
.FirstOrDefault();
}
public static Vector2 RedrawAspectRatio(this VisualElement container,
Vector2 size,
float aspectRatio,
float scale,
bool maintainAspectRatio = true,
RatioFlexEnum.RatioFlex ratioFlexType = RatioFlexEnum.RatioFlex.Auto)
{
var newSize = size;
newSize *= scale;
float targetW = newSize.x;
float targetH = newSize.y;
if (maintainAspectRatio)
{
switch (ratioFlexType)
{
case RatioFlexEnum.RatioFlex.Auto:
targetW = newSize.magnitude / aspectRatio;
targetH = newSize.magnitude * aspectRatio;
break;
case RatioFlexEnum.RatioFlex.ParentWidth:
targetH = targetW / aspectRatio;
break;
case RatioFlexEnum.RatioFlex.ParentHeight:
targetW = targetH / aspectRatio;
break;
}
}
newSize = new Vector2(targetW, targetH);
container.style.width = new StyleLength(newSize.x);
container.style.height = new StyleLength(newSize.y);
return newSize;
}
/// <summary>
/// Pass the object that has text/font - ie: Label, Button
/// </summary>
/// <param name="container"></param>
/// <param name="parentSize">Vector2 - parent.localBound.size</param>
/// <param name="fontScale">float - scale of the font</param>
public static float ResizeFont(this TextElement container,
Vector2 parentSize,
float fontScale,
float fontPadding,
string text = ""
)
{
if (container.resolvedStyle.unityFontDefinition.fontAsset == null) return fontScale;
Vector2 textSize = container.MeasureTextSize(text, 0, VisualElement.MeasureMode.Undefined, 0, VisualElement.MeasureMode.Undefined);
float containerWidthAspectRatio = parentSize.x / textSize.x;
float containerHeightAspectRatio = parentSize.y / textSize.y;
float smallerAspectRatio = Mathf.Min(containerWidthAspectRatio, containerHeightAspectRatio);
fontScale *= smallerAspectRatio * fontPadding;
if (float.IsNaN(fontScale)) return 1.0f;
float fontSize = parentSize.x * fontScale;
container.style.fontSize = new StyleLength(fontSize);
return fontScale;
}
public static void ResizeFont(this VisualElement container, Vector2 parentSize, float fontScale)
{
var ratio = Math.Min(parentSize.x,parentSize.y);
var fontSize = (ratio * 0.01f) * fontScale;
if (float.IsNaN(fontSize) || fontSize == container.style.fontSize) return;
container.style.fontSize = new StyleLength (fontSize);
}
public static void ResizeFont(this TextElement container, Vector2 parentSize, float fontScale, string text)
{
container.style.fontSize = GetCharacterWidth(container, text).x;
Vector2 textSize = container.MeasureTextSize(text, parentSize.x, VisualElement.MeasureMode.Undefined, 0, VisualElement.MeasureMode.Undefined);
float p = 1.0f;
if (textSize.x > parentSize.x)
{
p = (parentSize.x / textSize.x);
}
var ratio = Math.Min(parentSize.x,parentSize.y);
var fontSize = (ratio * 0.1f) * fontScale * p;
if (float.IsNaN(fontSize) || fontSize == container.style.fontSize) return;
container.style.fontSize = new StyleLength (fontSize);
}
public static Vector2 GetCharacterWidth(TextElement textElement, string chr = "SUBMIT")
{
TextElement tempText = new TextElement();
tempText.style.unityFontDefinition = textElement.resolvedStyle.unityFontDefinition;
List<Vector2> list = new();
foreach (var VARIABLE in chr)
{
tempText.text = VARIABLE.ToString();
Vector2 textSize = tempText.MeasureTextSize(VARIABLE.ToString(), 0, VisualElement.MeasureMode.Undefined, 0, VisualElement.MeasureMode.Undefined);
list.Add(textSize);
}
Vector2 averageVector = list.Aggregate(Vector2.zero, (current, vector) => current + vector) / list.Count;
return averageVector;
}
/// <summary>
/// Fades a CanvasGroup to a given value and sets its interactive flags while using a referenced Tweener object for safe tweening from its current alpha value.
/// </summary>
/// <param name="alpha">End Float Value</param>
/// <param name="duration">Optional: duration in seconds. Default is 0.5</param>
/// <param name="isInteractive">Bool of whether or not to be interactive</param>
/// <param name="tweener">Tweener reference. Use this for OnUpdate/OnComplete calls within the caller</param>
public static void FadeToAndSetInteractive(this VisualElement container, float alpha, bool isInteractive, ref Tweener tweener, float duration = 0.5f, Ease easing = Ease.Linear)
{
if (tweener != null && tweener.IsActive())
{
tweener.Pause();
}
tweener = DOTween.To(() => container.GetAlpha(), value =>
{
container.SetAlpha(value);
}, alpha, duration)
.SetEase(easing);
container.pickingMode = isInteractive ? PickingMode.Position : PickingMode.Ignore;
}
/// <summary>
/// Set the size of the canvas and then set it's interactivity
/// </summary>
/// <param name="moveTo">Vector2: representing the final position</param>
/// <param name="interactive">Bool: should this be interactive?</param>
/// <param name="tweener">Tweener reference. Use this for OnUpdate/OnComplete calls within the caller</param>
/// <param name="duration">Optional: duration in seconds. Default is 0.5</param>
/// <param name="easing">Easing to use</param>
public static void MoveToAndSetInteractivity(this VisualElement container, Vector2 moveTo, bool interactive, ref Tweener tweener, float duration = 0.5f, Ease easing = Ease.Linear)
{
if (tweener != null && tweener.IsActive())
{
tweener.Pause();
}
tweener = DOTween.To(container.GetPosition, container.SetPosition, moveTo, duration)
.SetEase(easing);
container.pickingMode = interactive ? PickingMode.Position : PickingMode.Ignore;
}
/// <summary>
/// Set the size of the canvas and then set it's interactivity
/// </summary>
/// <param name="rotateTo">Vector3: representing the final rotation</param>
/// <param name="interactive">Bool: should this be interactive?</param>
/// <param name="tweener">Tweener reference. Use this for OnUpdate/OnComplete calls within the caller</param>
/// <param name="duration">Optional: duration in seconds. Default is 0.5</param>
/// <param name="easing">Easing to use</param>
public static void RotateToAndSetInteractivity(this VisualElement container, float rotateTo, bool interactive, ref Tweener tweener, float duration = 0.5f, Ease easing = Ease.Linear)
{
if (tweener != null && tweener.IsActive())
{
tweener.Pause();
tweener.Complete();
}
float startValue = 0;
float diff = 0;
tweener = DOTween.To(() => startValue, angle =>
{
diff = angle - startValue;
startValue = angle;
container.SetRotation(container.GetRotation() + diff);
}, rotateTo, duration)
.SetEase(easing);
container.pickingMode = interactive ? PickingMode.Position : PickingMode.Ignore;
}
}
@neoRiley
Copy link
Author

Yes! I do apologize - I've been getting my app into Steam (AimDownScreen.com) which is build on UITool Kit and uses these classes. I've many updates for them, but I need to clean them up.

Also, someone had asked about removing UniRx support, and unfortunately, I'm not going to do that. Since this is free, and it's a ton of work I've put into it, anyone is more than welcome to convert the observables into events/Actions in the UIElementExtensions.cs class.

But I would HIGHLY encourage anyone who hasn't used UniRx to add it and try it. There are plenty of examples throughout this code on how to use it, and the pattern is extremely solid with regards to Unity development.

Thanks for your patience, I'll try to get on this this week

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment