mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-01-17 18:59:27 -05:00
This commit imports the C# engine code and game files, excluding C++ source code. [Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
216 lines
4.8 KiB
C#
216 lines
4.8 KiB
C#
using Sandbox.Utility;
|
|
|
|
namespace Sandbox.UI;
|
|
|
|
/// <summary>
|
|
/// Handles the storage, progression and application of CSS transitions for a single <see cref="Panel"/>.
|
|
/// </summary>
|
|
public sealed class Transitions
|
|
{
|
|
public delegate void TransitionFunction( Styles style, float delta );
|
|
|
|
public struct Entry
|
|
{
|
|
public string Property { get; init; }
|
|
public float StartTime { get; init; }
|
|
public float Length { get; init; }
|
|
public int Target { get; init; }
|
|
public Utility.Easing.Function EasingFunction { get; init; }
|
|
public bool IsKilled { get; private set; }
|
|
|
|
public TransitionFunction Action { get; init; }
|
|
|
|
public Entry( string property, float startTime, float length, int target, TransitionFunction action, Easing.Function easingFunction ) : this()
|
|
{
|
|
Property = property;
|
|
StartTime = startTime;
|
|
Length = length;
|
|
Target = target;
|
|
Action = action;
|
|
EasingFunction = easingFunction;
|
|
|
|
IsKilled = false;
|
|
}
|
|
|
|
internal void Kill()
|
|
{
|
|
IsKilled = true;
|
|
}
|
|
|
|
internal void Restore()
|
|
{
|
|
IsKilled = false;
|
|
}
|
|
|
|
public float Ease( float delta ) => EasingFunction.Invoke( delta );
|
|
public void Invoke( Styles style, float delta ) => Action.Invoke( style, delta );
|
|
|
|
public override string ToString()
|
|
{
|
|
return $"Entry( '{Property}', {StartTime}s, {Length}s, '{Target}', {Action}, {EasingFunction} )";
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Active CSS transitions.
|
|
/// </summary>
|
|
public List<Entry> Entries = null;
|
|
|
|
/// <summary>
|
|
/// Whether there are any active CSS transitions.
|
|
/// </summary>
|
|
public bool HasAny => Entries?.Count > 0;
|
|
|
|
private Panel panel;
|
|
|
|
internal Transitions( Panel panel )
|
|
{
|
|
this.panel = panel;
|
|
}
|
|
|
|
internal void Kill( Styles from )
|
|
{
|
|
if ( !from.HasTransitions ) return;
|
|
if ( Entries == null ) return;
|
|
|
|
for ( int i = 0; i < Entries.Count; i++ )
|
|
{
|
|
if ( !from.Transitions.List.Any( x => x.Property == Entries[i].Property ) )
|
|
continue;
|
|
|
|
var transition = Entries[i];
|
|
transition.Kill();
|
|
Entries[i] = transition;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clear all transitions. This will immediately remove transitions, leaving styles wherever they are.
|
|
/// </summary>
|
|
internal void Clear()
|
|
{
|
|
Entries?.Clear();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Immediately snaps all transitions to the end point, at which point they're removed.
|
|
/// </summary>
|
|
internal void Kill()
|
|
{
|
|
if ( Entries == null ) return;
|
|
|
|
for ( int i = 0; i < Entries.Count; i++ )
|
|
{
|
|
var transition = Entries[i];
|
|
transition.Kill();
|
|
Entries[i] = transition;
|
|
}
|
|
}
|
|
|
|
internal void Add( Styles from, Styles to, float startTime )
|
|
{
|
|
if ( !to.HasTransitions ) return;
|
|
|
|
foreach ( var desc in to.Transitions.List )
|
|
{
|
|
var fromCopy = (Styles)from.Clone();
|
|
var toCopy = (Styles)to.Clone();
|
|
|
|
TransitionFunction action = (desc.Property == "all")
|
|
? ( style, delta ) => style.FromLerp( fromCopy, toCopy, delta )
|
|
: ( style, delta ) => style.LerpProperty( desc.Property, fromCopy, toCopy, delta );
|
|
|
|
Transition( desc, from, to, action, startTime );
|
|
}
|
|
}
|
|
|
|
void Transition( in TransitionDesc desc, BaseStyles from, BaseStyles to, TransitionFunction action, float startTime )
|
|
{
|
|
if ( from == to ) return;
|
|
|
|
var target = HashCode.Combine( to?.GetHashCode(), desc.Property );
|
|
if ( TryRestoreTransition( target ) ) return;
|
|
|
|
Add( desc, target, action, startTime );
|
|
}
|
|
|
|
void Add( in TransitionDesc desc, int target, TransitionFunction action, float startTime )
|
|
{
|
|
var length = 1.0f;
|
|
var property = desc.Property;
|
|
|
|
if ( desc.Duration.HasValue )
|
|
length = desc.Duration.Value / 1000.0f;
|
|
|
|
if ( length <= 0 && !desc.Delay.HasValue )
|
|
return;
|
|
|
|
if ( desc.Delay.HasValue )
|
|
{
|
|
startTime += desc.Delay.Value / 1000.0f;
|
|
}
|
|
|
|
Entries ??= new List<Entry>();
|
|
var easingFunction = Easing.GetFunction( desc.TimingFunction );
|
|
|
|
var entry = new Entry( property, startTime, length, target, action, easingFunction );
|
|
Entries.Add( entry );
|
|
}
|
|
|
|
internal bool Run( Styles style, float now )
|
|
{
|
|
if ( !HasAny )
|
|
return false;
|
|
|
|
if ( Entries.RemoveAll( x => x.StartTime + x.Length < now ) > 0 )
|
|
{
|
|
panel.SetNeedsPreLayout();
|
|
}
|
|
|
|
foreach ( var entry in Entries )
|
|
{
|
|
//
|
|
// Pre-start (delay)
|
|
//
|
|
if ( now < entry.StartTime )
|
|
{
|
|
entry.Invoke( style, entry.Ease( 0 ) );
|
|
continue;
|
|
}
|
|
|
|
var endTime = entry.StartTime + entry.Length;
|
|
if ( now > endTime ) continue;
|
|
|
|
var t = entry.IsKilled ? 1f : (now - entry.StartTime) / entry.Length;
|
|
|
|
entry.Invoke( style, entry.Ease( t ) );
|
|
}
|
|
|
|
if ( Entries.RemoveAll( x => x.IsKilled ) > 0 )
|
|
{
|
|
panel.SetNeedsPreLayout();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
internal bool TryRestoreTransition( int targetValue )
|
|
{
|
|
if ( Entries == null ) return false;
|
|
|
|
for ( int i = Entries.Count - 1; i >= 0; i-- )
|
|
{
|
|
if ( Entries[i].Target != targetValue )
|
|
continue;
|
|
|
|
var entry = Entries[i];
|
|
entry.Restore();
|
|
Entries[i] = entry;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
}
|