rj-action-library/Runtime/UI/Styling/UINumber.cs

559 lines
15 KiB
C#

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 TimeLine timeLine;
public bool IsNone => unit == "none";
public UINumber GetMultiplied( float scale )
{
var uiNumber = new UINumber();
uiNumber.CopyFrom( this );
uiNumber.value = value * scale;
return uiNumber;
}
public static UINumber Create( float value, string unit = "" )
{
var uiNumber = new UINumber();
uiNumber.unit = unit;
uiNumber.value = value;
return uiNumber;
}
public static UINumber EM( float em )
{
return Create( em, "em" );
}
public static UINumber PW( float pw )
{
return Create( pw, "pw" );
}
public static UINumber PH( float ph )
{
return Create( ph, "ph" );
}
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, "", container ) );
}
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 ComputeInt( control, property, "", alternative, relative );
}
public static int ComputeInt( Control control, UIStyleNumberProperty property, string shaderPropertyName = "", float alternative = 0, float relative = 100 )
{
return Mathf.RoundToInt( Compute( control, property, shaderPropertyName, 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 CopyFrom( 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 )
{
return Compute( control, property, "", alternative, relative );
}
public static float Compute( Control control, UIStyleNumberProperty property, string shaderPropertyName = "", float alternative = 0, float relative = 100 )
{
var container = control as UIStylePropertyContainer;
// Get selected ui number
var uiNumber = UIStyle.GetUINumberProperty( control as UIStylePropertyContainer, property, shaderPropertyName, container );
var computedNumber = Compute( control, uiNumber, alternative, relative );
var transition = UIStyle.GetTransition( container, property, shaderPropertyName );
var transitionAll = UIStyle.GetTransitionSettingsAll( container );
var usesTransition = transition != null || transitionAll != null && transitionAll.transitionAllProperties;
if ( ! usesTransition )
{
return computedNumber;
}
var activeNumberTransitions = container.GetActiveUINumberTransitions();
var propertyTransition = activeNumberTransitions.Find( t => t != null && t.propertyType.Matches( property, shaderPropertyName ) );
propertyTransition = EnsureTransitionObject( propertyTransition, control, uiNumber, property, shaderPropertyName );
var endValueChanged = uiNumber != propertyTransition.value;
if ( endValueChanged )
{
var transitionSettings = UIStyle.GetTransitionSettings( container, property, shaderPropertyName );
propertyTransition.StartTransition( control, uiNumber, transitionSettings );
}
if ( ! propertyTransition.transitioning )
{
return computedNumber;
}
var computedTransitionValue = 0f;
for ( int i = 0; i < propertyTransition.lastValues.Count; i++ )
{
var lastValue = Compute( control, propertyTransition.lastValues[ i ], 0f );
computedTransitionValue += lastValue * propertyTransition.lastWeights[ i ];
}
var phase = propertyTransition.GetUnclampedTransitionPhase();
var lerpWeight = propertyTransition.ComputeTransitionWeight( phase );
if ( phase >= 1f )
{
propertyTransition.EndTransition();
}
var result = Mathf.Lerp( computedTransitionValue, computedNumber, lerpWeight );
return result;
// if ( propertyTransition == null )
// {
// propertyTransition = new ActiveStyleTransition<UINumber, UIStyleNumberProperty>();
// 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 = UI.GetTimeLine( control, transitionSettings.timeLine );
// propertyTransition.start = propertyTransition.timeLine.position;
// 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 = propertyTransition.timeLine.ComputeRange( 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 );
}
static ActiveStyleTransition<UINumber, UIStyleNumberPropertyAndName> EnsureTransitionObject(
ActiveStyleTransition<UINumber, UIStyleNumberPropertyAndName> propertyTransition,
Control control, UINumber uiNUmber, UIStyleNumberProperty property, string shaderPropertyName )
{
if ( propertyTransition != null )
{
return propertyTransition;
}
var container = control as UIStylePropertyContainer;
propertyTransition = new ActiveStyleTransition<UINumber, UIStyleNumberPropertyAndName>();
propertyTransition.propertyType = UIStyleNumberPropertyAndName.Create( property, shaderPropertyName );
var transitionSettings = UIStyle.GetTransitionSettings( container, property, shaderPropertyName );
propertyTransition.value = uiNUmber;
propertyTransition.timeLine = UI.GetTimeLine( control, transitionSettings.timeLine );
propertyTransition.transitioning = false;
container.GetActiveUINumberTransitions().Add( propertyTransition );
return propertyTransition;
}
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 );
}
public static float em( Control control )
{
var ui = UIHolder.GetUI( control );
return ui == null ? 12 : ui.X_computedFontSizePixels;
}
public static float uiw( Control control )
{
var ui = UIHolder.GetUI( control );
return ui == null ? 1920 : ui.Size.X;
}
public static float uih( Control control )
{
var ui = UIHolder.GetUI( control );
return ui == null ? 1080 : ui.Size.Y;
}
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 timeLine = UI.GetTimeLine( control, number.timeLine );
var phase = timeLine.ComputePhase( number.animationDuration, number.animationOffset );
return number.animationCurve.Sample( phase ) * value;
}
static readonly string[] _variables = new string[]
{
"em",
"vw", "vh",
"pw", "ph",
"cw","ch",
"cx","cy",
"uiw", "uih",
"relative",
"value"
};
static float ComputeWithoutAnimation( Control control, UINumber number, float width, float height, float relative )
{
if ( number == null )
{
return 0;
}
switch ( number.unit )
{
case "em":
{
return number.value * em( control );
}
case "vw":
{
return number.value * width / 100f;
}
case "vh":
{
return number.value * height / 100f;
}
case "pw":
{
var parent = control.GetParent() as Control;
return number.value / 100f * ( parent == null ? width : parent.Size.X );
}
case "cw":
{
var parent = control.GetParent() as Control;
return number.value / 100f * ( parent == null ? width : UILayouting.GetContentSize( parent ).X );
}
case "ch":
{
var parent = control.GetParent() as Control;
return number.value / 100f * ( parent == null ? height : UILayouting.GetContentSize( parent ).Y );
}
case "cx":
{
var parent = control.GetParent() as Control;
return number.value / 100f * ( parent == null ? 0 : UILayouting.GetContentOffset( parent ).X );
}
case "cy":
{
var parent = control.GetParent() as Control;
return number.value / 100f * ( parent == null ? 0 : UILayouting.GetContentOffset( parent ).Y );
}
case "uiw":
{
return number.value * uiw( control );
}
case "uih":
{
return number.value * uih( control );
}
case "ph":
{
var parent = control.GetParent() as Control;
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 " );
if ( ! IsValid( expressionText ) )
{
return 0;
}
var parseResult = expression.Parse( expressionText, _variables );
if ( Error.Ok != parseResult )
{
return 0;
}
var parentControl = control.GetParent() as Control;;
var inputs = new Godot.Collections.Array();
// em
inputs.Add( em( control ) );
// 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 );
// uiw, uih
inputs.Add( uiw( control ) );
inputs.Add( uih( control ) );
// "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() ;
}
static bool IsValid( string expressionText )
{
if ( expressionText == null || expressionText == "" )
{
return false;
}
var lexer = new CSharpLexer();
var list = lexer.LexToList( expressionText );
if ( lexer.hasError )
{
return false;
}
lexer.GrabMatches( list, expressionText );
for ( int i = 0; i < list.Count; i++ )
{
var token = list[ i ];
if ( token.Is( LexerMatcherLibrary.CwordMatcher ) )
{
var value = token.match;
if ( ! Arrays.Contains( _variables, value ) )
{
// RJLog.Log( "Unknown variable:", "'" + value + "'", " in: ", "'" + expressionText + "'" );
return false;
}
}
}
return true;
}
}
}