using Godot; using Rokojori; namespace Rokojori { [Tool] [GlobalClass] public partial class UINumber : Resource { [Export] public float value = 0; [Export] public string unit = ""; [ExportGroup("Animation")] [Export] public bool isAnimated; [Export] public Curve animationCurve; [Export] public float animationDuration; [Export] public float animationOffset; [Export] public RJTimeLine timeLine; public bool IsNone => unit == "none"; public float ComputeRaw( Control control, float alternative ) { return UINumber.Compute( control, this, alternative ); } public static bool IsNullOrNone( UIStylePropertyContainer container, UIStyleNumberProperty property ) { return IsNullOrNone( UIStyle.GetUINumberProperty( container, property ) ); } public static bool IsNullOrNone( UINumber number ) { if ( number == null ) { return true; } return number.IsNone; } public static int ComputeInt( Control control, UINumber number, float alternative = 0, float relative = 100 ) { return Mathf.RoundToInt( Compute( control, number, alternative, relative ) ); } public static int ComputeInt( Control control, UIStyleNumberProperty property, float alternative = 0, float relative = 100 ) { return Mathf.RoundToInt( Compute( control, property, alternative, relative ) ); } public bool Equals( UINumber other ) { if ( other == this ) { return true; } if ( other == null ) { return false; } if ( other.value != value ) { return false; } if ( other.unit != unit ) { return false; } if ( other.isAnimated != isAnimated ) { return false; } if ( other.animationCurve != animationCurve ) { return false; } if ( other.animationDuration != animationDuration ) { return false; } if ( other.animationOffset != animationOffset ) { return false; } if ( other.timeLine != timeLine ) { return false; } return true; } public void CopyForm( UINumber other ) { if ( other == this ) { return; } value = other.value; unit = other.unit; isAnimated = other.isAnimated; animationCurve = other.animationCurve; animationDuration = other.animationDuration; animationOffset = other.animationOffset; timeLine = other.timeLine; } public static float Compute( Control control, UIStyleNumberProperty property, float alternative = 0, float relative = 100 ) { var container = control as UIStylePropertyContainer; var transition = UIStyle.GetTransition( container, property ); var number = UIStyle.GetUINumberProperty( control as UIStylePropertyContainer, property ); var computedValue = Compute( control, number, alternative, relative ); var allSettings = UIStyle.GetTransitionSettingsAll( container ); var usesTransition = transition != null || allSettings != null && allSettings.transitionAllProperties; if ( ! usesTransition ) { return computedValue; } var activeNumberTransitions = container.GetActiveUINumberTransitions(); var propertyTransition = activeNumberTransitions.Find( t => t != null && t.propertyType == property ); if ( propertyTransition == null ) { propertyTransition = new ActiveStyleTransition(); propertyTransition.propertyType = property; propertyTransition.value = number; propertyTransition.transitioning = false; activeNumberTransitions.Add( propertyTransition ); return computedValue; } else { if ( propertyTransition.value != number && ! propertyTransition.transitioning && UIStyle.GetTransitionSettings( container, property ) != null ) { var transitionSettings = UIStyle.GetTransitionSettings( container, property ); propertyTransition.timeLine = transitionSettings.timeLine; propertyTransition.start = TimeLineManager.GetPosition( transitionSettings.timeLine ); propertyTransition.end = propertyTransition.start + transitionSettings.duration; propertyTransition.transitioning = true; propertyTransition.curve = transitionSettings.curve; } } if ( propertyTransition.value == number ) { propertyTransition.transitioning = false; return computedValue; } var computedTransitionValue = Compute( control, propertyTransition.value, alternative, relative ); var transitionPhase = TimeLineManager.GetRangePhase( propertyTransition.timeLine, propertyTransition.start, propertyTransition.end ); if ( transitionPhase >= 1 ) { activeNumberTransitions.Remove( propertyTransition ); } var amount = MathX.Clamp01( transitionPhase ); var curveAmount = amount; if ( propertyTransition.curve != null ) { curveAmount = propertyTransition.curve.Sample( curveAmount ); } return Mathf.Lerp( computedTransitionValue, computedValue, curveAmount ); } public static float Compute( Control control, UINumber number, float alternative = 0, float relative = 100 ) { if ( number == null || control == null || number.IsNone ) { return alternative; } var width = UI.GetWindowWidth( control ); var height = UI.GetWindowHeight( control ); return Compute( control, number, width, height, relative ); } static UI _ui; public static float em() { return _ui == null ? 12 : _ui.X_computedFontSizePixels; } public static float Compute( Control control, UINumber number, float width, float height, float relative ) { var value = ComputeWithoutAnimation( control, number, width, height, relative ); if ( ! number.isAnimated || number.animationCurve == null ) { return value; } var phase = TimeLineManager.GetPhase( number.timeLine, number.animationDuration, number.animationOffset ); return number.animationCurve.Sample( phase ) * value; } static float ComputeWithoutAnimation( Control control, UINumber number, float width, float height, float relative ) { if ( number == null ) { return 0; } if ( _ui == null ) { _ui = NodesWalker.Get().GetInParents( control, n => n is UI ) as UI; } switch ( number.unit ) { case "em": { return number.value * em(); } case "vw": { return number.value * width / 100f; } case "vh": { return number.value * height / 100f; } case "pw": { var parent = control.GetParent(); return number.value / 100f * ( parent == null ? width : parent.Size.X ); } case "cw": { var parent = control.GetParent(); return number.value / 100f * ( parent == null ? width : UILayouting.GetContentSize( parent ).X ); } case "ch": { var parent = control.GetParent(); return number.value / 100f * ( parent == null ? height : UILayouting.GetContentSize( parent ).Y ); } case "cx": { var parent = control.GetParent(); return number.value / 100f * ( parent == null ? 0 : UILayouting.GetContentOffset( parent ).X ); } case "cy": { var parent = control.GetParent(); return number.value / 100f * ( parent == null ? 0 : UILayouting.GetContentOffset( parent ).Y ); } case "ph": { var parent = control.GetParent(); return number.value / 100f * ( parent == null ? height : parent.Size.Y ); } case "": { return number.value; } case "%": { return number.value * relative / 100f; } } var expression = new Expression(); var expressionText = number.unit == null ? "" : RegexUtility.Replace( number.unit, "%", " * relative " ); var parseResult = expression.Parse( expressionText, new string[] { "em", "vw", "vh", "pw", "ph", "cw","ch", "cx","cy", "relative", "value" } ); if ( Error.Ok != parseResult ) { return 0; } var parentControl = control.GetParent(); var inputs = new Godot.Collections.Array(); inputs.Add( em() ); // vw, vh inputs.Add( width / 100f ); inputs.Add( height / 100f ); // pw, ph inputs.Add( ( parentControl == null ? width : parentControl.Size.X ) / 100f ); inputs.Add( ( parentControl == null ? height : parentControl.Size.Y ) / 100f ); // cw, ch inputs.Add( ( parentControl == null ? width : UILayouting.GetContentSize( parentControl ).X ) / 100f ); inputs.Add( ( parentControl == null ? width : UILayouting.GetContentSize( parentControl ).Y ) / 100f ); // cx, cy inputs.Add( ( parentControl == null ? 0 : UILayouting.GetContentOffset( parentControl ).X ) / 100f ); inputs.Add( ( parentControl == null ? 0 : UILayouting.GetContentOffset( parentControl ).Y ) / 100f ); // "relative" inputs.Add( relative / 100f ); // value if ( number.unit.IndexOf( "value" ) == -1 ) { inputs.Add( 0 ); return number.value * (float) expression.Execute( inputs ).AsDouble() ; } inputs.Add( number.value ); return (float) expression.Execute( inputs ).AsDouble() ; } } }