Files
sbox-public/engine/Sandbox.Engine/Systems/UI/Styles/Styles.cs
s&box team 71f266059a Open source release
This commit imports the C# engine code and game files, excluding C++ source code.

[Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
2025-11-24 09:05:18 +00:00

297 lines
7.4 KiB
C#

using Sandbox.Engine;
namespace Sandbox.UI;
/// <summary>
/// Represents all supported CSS properties and their currently assigned values.
/// </summary>
[SkipHotload]
public partial class Styles : BaseStyles
{
internal Dictionary<string, IStyleBlock.StyleProperty> RawValues = new Dictionary<string, IStyleBlock.StyleProperty>( StringComparer.OrdinalIgnoreCase );
internal GradientInfo TextGradient;
/// <summary>
/// Whether this style sheet has any transitions that would need to be run.
/// </summary>
public bool HasTransitions => Transitions != null && Transitions.List.Count > 0;
/// <summary>
/// List of transitions this style sheet has.
/// </summary>
public TransitionList Transitions;
//
// Shadows
//
public ShadowList BoxShadow = new ShadowList();
public ShadowList TextShadow = new ShadowList();
public ShadowList FilterDropShadow = new ShadowList();
public Styles()
{
}
public override void Dirty()
{
// Nothing
}
public Length? Padding
{
set
{
PaddingLeft = value;
PaddingTop = value;
PaddingRight = value;
PaddingBottom = value;
}
}
public Length? Margin
{
set
{
MarginLeft = value;
MarginTop = value;
MarginRight = value;
MarginBottom = value;
}
}
public Length? BorderWidth
{
set
{
BorderLeftWidth = value;
BorderRightWidth = value;
BorderTopWidth = value;
BorderBottomWidth = value;
}
}
public Color? BorderColor
{
set
{
BorderLeftColor = value;
BorderRightColor = value;
BorderTopColor = value;
BorderBottomColor = value;
}
}
public bool HasBorder
{
get
{
if ( BorderTopWidth.HasValue && BorderTopWidth.Value.Value > 0 ) return true;
if ( BorderRightWidth.HasValue && BorderRightWidth.Value.Value > 0 ) return true;
if ( BorderBottomWidth.HasValue && BorderBottomWidth.Value.Value > 0 ) return true;
if ( BorderLeftWidth.HasValue && BorderLeftWidth.Value.Value > 0 ) return true;
return false;
}
}
public Margin GetInset( Vector2 size )
{
var border = Sandbox.UI.Margin.GetEdges( size, BorderLeftWidth, BorderTopWidth, BorderRightWidth, BorderBottomWidth );
var padding = Sandbox.UI.Margin.GetEdges( size, PaddingLeft, PaddingTop, PaddingRight, PaddingBottom );
return border + padding;
}
public Margin GetOutset( Vector2 size ) => Sandbox.UI.Margin.GetEdges( size, MarginLeft, MarginTop, MarginRight, MarginBottom );
public readonly static Styles Default = new Styles
{
Padding = 0
};
internal bool SetInternal( string styles, string filename, int lineoffset )
{
bool success = false;
Parse p = new( styles, filename, lineoffset );
while ( !p.IsEnd )
{
p = p.SkipWhitespaceAndNewlines();
var property = p.ReadUntilOrEnd( " :;" );
p = p.SkipWhitespaceAndNewlines();
if ( p.IsEnd )
break;
if ( p.Current != ':' )
throw new System.Exception( $"Error parsing style: {styles}" );
p.Pointer++;
p = p.SkipWhitespaceAndNewlines();
if ( p.IsEnd )
throw new System.Exception( $"Error parsing style: {styles}" );
var line = p.CurrentLine;
var value = p.ReadUntilOrEnd( ";" );
p.Pointer++;
bool wasSuccessful = Set( property, value );
if ( !wasSuccessful )
{
Log.Error( $"{value} is not valid with {property} {p.FileAndLine}" );
}
var prop = new IStyleBlock.StyleProperty
{
Name = property,
Value = value,
OriginalValue = value,
Line = line,
IsValid = wasSuccessful
};
RawValues[property] = prop;
success = wasSuccessful || success;
p = p.SkipWhitespaceAndNewlines();
}
return success;
}
public bool Set( string styles )
{
return SetInternal( styles, null, 0 );
}
/// <summary>
/// Creates a matrix based on this style's "transform" and other related properties
/// </summary>
public Matrix BuildTransformMatrix( Vector2 size )
{
if ( Transform?.IsEmpty() ?? true )
return Matrix.Identity;
var t = Transform.Value;
Vector2 perspectiveCenter = new Vector2( size.x * 0.5f, size.y * 0.5f );
Vector2 perspectiveOrigin = perspectiveCenter;
if ( PerspectiveOriginX.HasValue )
{
perspectiveOrigin.x = PerspectiveOriginX.Value.GetPixels( size.x );
}
if ( PerspectiveOriginY.HasValue )
{
perspectiveOrigin.y = PerspectiveOriginY.Value.GetPixels( size.y );
}
return t.BuildTransform( size.x, size.y, perspectiveOrigin - perspectiveCenter );
}
/// <summary>
/// Try to find all panels using this style and mark them dirty so they'll
/// redraw with the style. This should be called when the style is changed. Which
/// is only technically when done via the editor.
/// </summary>
internal void MarkPanelsDirty()
{
foreach ( var panel in GlobalContext.Current.UISystem.RootPanels )
{
// Dirty all panels that contain this style
panel.DirtyStylesWithStyle( this, true );
}
}
public override void LerpProperty( string name, BaseStyles from, BaseStyles to, float delta )
{
base.LerpProperty( name, from, to, delta );
//
// Properties that aren't auto-generated (i.e. ones not defined in BaseStyles, but are defined in this class)
//
if ( from is Styles a && to is Styles b )
{
switch ( name )
{
case "box-shadow":
if ( a.BoxShadow != b.BoxShadow )
BoxShadow.SetFromLerp( a.BoxShadow, b.BoxShadow, delta );
break;
case "text-shadow":
if ( a.TextShadow != b.TextShadow )
TextShadow.SetFromLerp( a.TextShadow, b.TextShadow, delta );
break;
case "filter-drop-shadow":
if ( a.FilterDropShadow != b.FilterDropShadow )
FilterDropShadow.SetFromLerp( a.FilterDropShadow, b.FilterDropShadow, delta );
break;
case "padding":
LerpProperty( "padding-left", from, to, delta );
LerpProperty( "padding-right", from, to, delta );
LerpProperty( "padding-bottom", from, to, delta );
LerpProperty( "padding-top", from, to, delta );
break;
case "margin":
LerpProperty( "margin-left", from, to, delta );
LerpProperty( "margin-right", from, to, delta );
LerpProperty( "margin-bottom", from, to, delta );
LerpProperty( "margin-top", from, to, delta );
break;
case "border-color":
LerpProperty( "border-color-left", from, to, delta );
LerpProperty( "border-color-right", from, to, delta );
LerpProperty( "border-color-bottom", from, to, delta );
LerpProperty( "border-color-top", from, to, delta );
break;
case "border-width":
LerpProperty( "border-width-left", from, to, delta );
LerpProperty( "border-width-right", from, to, delta );
LerpProperty( "border-width-bottom", from, to, delta );
LerpProperty( "border-width-top", from, to, delta );
break;
case "border-radius":
LerpProperty( "border-top-left-radius", from, to, delta );
LerpProperty( "border-top-right-radius", from, to, delta );
LerpProperty( "border-bottom-right-radius", from, to, delta );
LerpProperty( "border-bottom-left-radius", from, to, delta );
break;
}
}
}
public override void FromLerp( BaseStyles from, BaseStyles to, float delta )
{
base.FromLerp( from, to, delta );
//
// Shadows
//
if ( from is Styles a && to is Styles b )
{
if ( a.BoxShadow != b.BoxShadow )
BoxShadow.SetFromLerp( a.BoxShadow, b.BoxShadow, delta );
if ( a.TextShadow != b.TextShadow )
TextShadow.SetFromLerp( a.TextShadow, b.TextShadow, delta );
if ( a.FilterDropShadow != b.FilterDropShadow )
FilterDropShadow.SetFromLerp( a.FilterDropShadow, b.FilterDropShadow, delta );
}
}
}