Authoring/GooModernUiValues.cs
using System;
using Sandbox;
using Sandbox.UI;
namespace Goo.Authoring;
public struct GooGradientStop : IEquatable<GooGradientStop>
{
public Color Color { get; set; }
public GooLength? Position { get; set; }
public static implicit operator GradientStop( GooGradientStop stop )
=> new( stop.Color, stop.Position.HasValue ? (Length)stop.Position.Value : null );
public static implicit operator GooGradientStop( GradientStop stop )
=> new()
{
Color = stop.Color,
Position = stop.Position.HasValue ? (GooLength)stop.Position.Value : null,
};
public readonly bool Equals( GooGradientStop other )
=> Color == other.Color && Nullable.Equals( Position, other.Position );
public readonly override bool Equals( object? obj ) => obj is GooGradientStop other && Equals( other );
public readonly override int GetHashCode() => HashCode.Combine( Color, Position );
}
public struct GooGradient : IEquatable<GooGradient>
{
public GradientKind Kind { get; set; }
[ShowIf( nameof( Kind ), GradientKind.Linear )]
public LinearGradientDirection LinearDirection { get; set; }
[HideIf( nameof( Kind ), GradientKind.Radial )]
public float AngleDegrees { get; set; }
[ShowIf( nameof( Kind ), GradientKind.Radial )]
public RadialGradientShape RadialShape { get; set; }
[ShowIf( nameof( Kind ), GradientKind.Radial )]
public RadialGradientExtent RadialExtent { get; set; }
[HideIf( nameof( Kind ), GradientKind.Linear )]
public GooLength CenterX { get; set; }
[HideIf( nameof( Kind ), GradientKind.Linear )]
public GooLength CenterY { get; set; }
public GooGradientStop[] Stops { get; set; }
public GooGradient()
{
Kind = GradientKind.Linear;
LinearDirection = LinearGradientDirection.Angle;
AngleDegrees = 180f;
RadialShape = RadialGradientShape.Ellipse;
RadialExtent = RadialGradientExtent.FarthestCorner;
CenterX = Percent( 50f );
CenterY = Percent( 50f );
Stops =
[
new GooGradientStop { Color = Color.White },
new GooGradientStop { Color = Color.Black },
];
}
public static implicit operator Gradient( GooGradient value )
{
var source = value.Stops ?? [];
var stops = new GradientStop[source.Length];
for ( int i = 0; i < source.Length; i++ ) stops[i] = source[i];
return value.Kind switch
{
GradientKind.Linear => value.LinearDirection == LinearGradientDirection.Angle
? Gradient.Linear( value.AngleDegrees, stops )
: Gradient.LinearTo( value.LinearDirection, stops ),
GradientKind.Radial => (value.RadialShape == RadialGradientShape.Circle
? Gradient.RadialCircle( stops )
: Gradient.Radial( stops ))
.WithExtent( value.RadialExtent )
.At( Center( value.CenterX ), Center( value.CenterY ) ),
GradientKind.Conic => Gradient.Conic( value.AngleDegrees, stops )
.At( Center( value.CenterX ), Center( value.CenterY ) ),
_ => throw new ArgumentOutOfRangeException( nameof( value.Kind ) ),
};
}
public static implicit operator GooGradient( Gradient value )
{
var stops = new GooGradientStop[value.Count];
for ( int i = 0; i < value.Count; i++ ) stops[i] = value[i];
return new GooGradient
{
Kind = value.Kind,
LinearDirection = value.LinearDirection,
AngleDegrees = value.AngleDegrees,
RadialShape = value.RadialShape,
RadialExtent = value.RadialExtent,
CenterX = value.IsEmpty ? Percent( 50f ) : (GooLength)value.CenterX,
CenterY = value.IsEmpty ? Percent( 50f ) : (GooLength)value.CenterY,
Stops = stops,
};
}
static GooLength Percent( float value ) => new() { Value = value, Unit = LengthUnit.Percentage };
static Length Center( GooLength value )
{
var center = (Length)value;
return center.Unit is LengthUnit.Pixels or LengthUnit.Percentage && float.IsFinite( center.Value )
? center
: new Length { Value = 50f, Unit = LengthUnit.Percentage };
}
public readonly bool Equals( GooGradient other )
{
if ( Kind != other.Kind || LinearDirection != other.LinearDirection || AngleDegrees != other.AngleDegrees
|| RadialShape != other.RadialShape || RadialExtent != other.RadialExtent
|| !CenterX.Equals( other.CenterX ) || !CenterY.Equals( other.CenterY ) )
return false;
var left = Stops ?? [];
var right = other.Stops ?? [];
var count = left.Length;
if ( count != right.Length ) return false;
for ( int i = 0; i < count; i++ )
if ( !left[i].Equals( right[i] ) ) return false;
return true;
}
public readonly override bool Equals( object? obj ) => obj is GooGradient other && Equals( other );
public readonly override int GetHashCode()
{
var hash = new HashCode();
hash.Add( Kind );
hash.Add( LinearDirection );
hash.Add( AngleDegrees );
hash.Add( RadialShape );
hash.Add( RadialExtent );
hash.Add( CenterX );
hash.Add( CenterY );
if ( Stops is not null )
foreach ( var stop in Stops ) hash.Add( stop );
return hash.ToHashCode();
}
}
public struct GooPanelShape : IEquatable<GooPanelShape>
{
public PanelShapeKind Kind { get; set; }
[ShowIf( nameof( Kind ), PanelShapeKind.Circle )]
public GooLength? CircleRadius { get; set; }
[ShowIf( nameof( Kind ), PanelShapeKind.Circle )]
public GooLength CircleCenterX { get; set; }
[ShowIf( nameof( Kind ), PanelShapeKind.Circle )]
public GooLength CircleCenterY { get; set; }
[ShowIf( nameof( Kind ), PanelShapeKind.Polygon )]
public GooPanelShapePoint[] Points { get; set; }
public GooPanelShape()
{
Kind = PanelShapeKind.Circle;
CircleRadius = null;
CircleCenterX = Percent( 50f );
CircleCenterY = Percent( 50f );
Points =
[
GooPanelShapePoint.Percent( 50f, 0f ),
GooPanelShapePoint.Percent( 100f, 100f ),
GooPanelShapePoint.Percent( 0f, 100f ),
];
}
public static implicit operator PanelShape( GooPanelShape value )
=> value.Kind switch
{
PanelShapeKind.None => PanelShape.None,
PanelShapeKind.Circle => PanelShape.Circle(
value.CircleRadius.HasValue ? (Length)value.CircleRadius.Value : null,
Center( value.CircleCenterX ),
Center( value.CircleCenterY ) ),
PanelShapeKind.Polygon => Polygon( value.Points ),
_ => throw new ArgumentOutOfRangeException( nameof( value.Kind ) ),
};
public static implicit operator GooPanelShape( PanelShape value )
{
var points = new GooPanelShapePoint[value.Count];
for ( int i = 0; i < value.Count; i++ )
points[i] = value[i];
return new GooPanelShape
{
Kind = value.Kind,
CircleRadius = value.CircleRadius.HasValue ? (GooLength)value.CircleRadius.Value : null,
CircleCenterX = value.Kind == PanelShapeKind.Circle ? (GooLength)value.CircleCenterX : Percent( 50f ),
CircleCenterY = value.Kind == PanelShapeKind.Circle ? (GooLength)value.CircleCenterY : Percent( 50f ),
Points = points,
};
}
static GooLength Percent( float value ) => new() { Value = value, Unit = LengthUnit.Percentage };
static Length Center( GooLength value )
{
var center = (Length)value;
return center.Unit is LengthUnit.Pixels or LengthUnit.Percentage && float.IsFinite( center.Value )
? center
: new Length { Value = 50f, Unit = LengthUnit.Percentage };
}
static PanelShape Polygon( GooPanelShapePoint[] points )
{
var source = points ?? [];
var typed = new PanelShapePoint[source.Length];
for ( int i = 0; i < source.Length; i++ ) typed[i] = source[i];
return PanelShape.Polygon( typed );
}
public readonly bool Equals( GooPanelShape other )
{
if ( Kind != other.Kind || !Nullable.Equals( CircleRadius, other.CircleRadius )
|| !CircleCenterX.Equals( other.CircleCenterX ) || !CircleCenterY.Equals( other.CircleCenterY ) )
return false;
var left = Points ?? [];
var right = other.Points ?? [];
var count = left.Length;
if ( count != right.Length ) return false;
for ( int i = 0; i < count; i++ )
if ( !left[i].Equals( right[i] ) ) return false;
return true;
}
public readonly override bool Equals( object? obj ) => obj is GooPanelShape other && Equals( other );
public readonly override int GetHashCode()
{
var hash = new HashCode();
hash.Add( Kind );
hash.Add( CircleRadius );
hash.Add( CircleCenterX );
hash.Add( CircleCenterY );
if ( Points is not null )
foreach ( var point in Points ) hash.Add( point );
return hash.ToHashCode();
}
}
public struct GooPanelShapePoint : IEquatable<GooPanelShapePoint>
{
public GooLength X { get; set; }
public GooLength Y { get; set; }
public static GooPanelShapePoint Percent( float x, float y )
=> new()
{
X = new GooLength { Value = x, Unit = LengthUnit.Percentage },
Y = new GooLength { Value = y, Unit = LengthUnit.Percentage },
};
public static implicit operator PanelShapePoint( GooPanelShapePoint point )
=> new( point.X, point.Y );
public static implicit operator GooPanelShapePoint( PanelShapePoint point )
=> new() { X = point.X, Y = point.Y };
public readonly bool Equals( GooPanelShapePoint other )
=> X.Equals( other.X ) && Y.Equals( other.Y );
public readonly override bool Equals( object? obj ) => obj is GooPanelShapePoint other && Equals( other );
public readonly override int GetHashCode() => HashCode.Combine( X, Y );
}