mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-08-01 00:08:05 -04:00
ADDED css min(), max() and clamp() for lengths css-wide keywords inherit, initial, unset, revert (incl. shorthands) added currentColor keyword to css colors can parse oklch(), lab() and hwb() added css text-align: justify css white-space: pre-wrap and break-spaces added css word-break: break-word parses css image-rendering: crisp-edges (point sampling) added css inset shorthand now parses 1–4 values in the css border-width shorthand added css transition-duration, -delay, -property and -timing-function longhands css :has() now supports descendant selectors added css opacity percentages added css overflow: auto (maps to scroll) added css gap: normal added css filter: none css transform: scale() now takes comma-separated args css animations now accept ms units added more css justify-content, align-* and text-align keywords (start, end, …) parses css dvh/svh/lvh/dvw viewport units (treated as vh/vw) parses css object-fit: scale-down (treated as contain) added css margin-block / margin-inline (+ the logical margin sides) added css padding-block / padding-inline (+ the logical padding sides) added css inset-block / inset-inline (+ the logical inset sides) added the css flex-flow shorthand added the css font shorthand added css font-size keywords (xx-small … xxx-large) css letter-spacing and word-spacing now accept normal added css font-smooth: none css aspect-ratio now accepts the auto form css font-family now maps generic families (serif, sans-serif, monospace) added css flex-flow added css font FIXED fixed css !important dropping the whole declaration css line-height without units is now a multiplier fixed css calc() division and full-expression parsing fixed the css flex shorthand for single-number and three-value forms fixed the css transition shorthand when the property is omitted css font-family now uses the first family in a comma stack colors now parse fully in the css background shorthand an unknown css @-rule no longer drops the whole stylesheet css variables no longer match on partial tokens injected css variables are kept across stylesheet hotloads fixed css selector specificity overflowing when comparing rules css background: none now resets the background (image and colour) css filter: none now overrides a filter set by a base class css transform: none now overrides a transform set by a base class IMPROVED css rule matching is ~3–5× faster on large stylesheets (rules indexed by class) ~2× fewer allocations in css transitions (snapshot styles once, not per property) finished css animations no longer re-layout every frame less GC in css sibling/child selector queries (no list copies or wrappers) less GC diffing active css rules (no LINQ or hashsets) faster, lower-GC Yoga layout wrapper css stylesheet hotload now watches newly imported files
396 lines
16 KiB
Plaintext
396 lines
16 KiB
Plaintext
<#@ template debug="false" hostspecific="false" language="C#" #><#@ assembly name="System.Core" #><#@ import namespace="System.Linq" #><#@ import namespace="System.Text" #><#@ import namespace="System.Text.RegularExpressions" #><#@ import namespace="System.Collections.Generic" #><#@ output extension=".cs" #><#
|
|
|
|
var Members = new Dictionary<string, (string TypeName, string DefaultValue, bool IsInherited)>
|
|
{
|
|
// Name Type Default value Inherit?
|
|
// ---------------------------- ----------------------- ------------------------------- --------
|
|
{ "Content", ("string", "\"\"", false)},
|
|
{ "Width", ("Length?", "Length.Undefined", false)},
|
|
{ "MinWidth", ("Length?", "0", false)},
|
|
{ "MaxWidth", ("Length?", "Length.Undefined", false)},
|
|
{ "Height", ("Length?", "Length.Undefined", false)},
|
|
{ "MinHeight", ("Length?", "0", false)},
|
|
{ "MaxHeight", ("Length?", "Length.Undefined", false)},
|
|
{ "Left", ("Length?", "Length.Undefined", false)},
|
|
{ "Top", ("Length?", "Length.Undefined", false)},
|
|
{ "Right", ("Length?", "Length.Undefined", false)},
|
|
{ "Bottom", ("Length?", "Length.Undefined", false)},
|
|
{ "Opacity", ("float?", "1", false)},
|
|
{ "BackgroundColor", ("Color?", "Color.Transparent", false)},
|
|
{ "PaddingLeft", ("Length?", "0", false)},
|
|
{ "PaddingTop", ("Length?", "0", false)},
|
|
{ "PaddingRight", ("Length?", "0", false)},
|
|
{ "PaddingBottom", ("Length?", "0", false)},
|
|
{ "MarginLeft", ("Length?", "0", false)},
|
|
{ "MarginTop", ("Length?", "0", false)},
|
|
{ "MarginRight", ("Length?", "0", false)},
|
|
{ "MarginBottom", ("Length?", "0", false)},
|
|
{ "BorderTopLeftRadius", ("Length?", "0", false)},
|
|
{ "BorderTopRightRadius", ("Length?", "0", false)},
|
|
{ "BorderBottomRightRadius", ("Length?", "0", false)},
|
|
{ "BorderBottomLeftRadius", ("Length?", "0", false)},
|
|
{ "BorderLeftWidth", ("Length?", "0", false)},
|
|
{ "BorderTopWidth", ("Length?", "0", false)},
|
|
{ "BorderRightWidth", ("Length?", "0", false)},
|
|
{ "BorderBottomWidth", ("Length?", "0", false)},
|
|
{ "BorderLeftColor", ("Color?", "Color.White", false)},
|
|
{ "BorderTopColor", ("Color?", "Color.White", false)},
|
|
{ "BorderRightColor", ("Color?", "Color.White", false)},
|
|
{ "BorderBottomColor", ("Color?", "Color.White", false)},
|
|
{ "FontSize", ("Length?", "Length.Pixels( 13 ).Value", true)},
|
|
{ "FontColor", ("Color?", "Color.Black", true)},
|
|
{ "FontWeight", ("int?", "400", true)},
|
|
{ "FontFamily", ("string", "\"Arial\"", true)},
|
|
{ "CaretColor", ("Color?", "null", false)},
|
|
{ "Cursor", ("string", "\"auto\"", true)},
|
|
{ "PointerEvents", ("PointerEvents?", "UI.PointerEvents.None", true)},
|
|
{ "MixBlendMode", ("string", "\"default\"", true)},
|
|
{ "Position", ("PositionMode?", "UI.PositionMode.Static", false)},
|
|
{ "OverflowX", ("OverflowMode?", "OverflowMode.Visible", false)},
|
|
{ "OverflowY", ("OverflowMode?", "OverflowMode.Visible", false)},
|
|
{ "FlexDirection", ("FlexDirection?", "UI.FlexDirection.Row", false)},
|
|
{ "JustifyContent", ("Justify?", "Justify.FlexStart", false)},
|
|
{ "Display", ("DisplayMode?", "DisplayMode.Flex", false)},
|
|
{ "FlexWrap", ("Wrap?", "Wrap.NoWrap", false)},
|
|
{ "AlignContent", ("Align?", "Align.Auto", false)},
|
|
{ "AlignSelf", ("Align?", "Align.Auto", false)},
|
|
{ "AlignItems", ("Align?", "Align.Stretch", false)},
|
|
{ "FlexBasis", ("Length?", "Length.Auto", false)},
|
|
{ "FlexGrow", ("float?", "0", false)},
|
|
{ "FlexShrink", ("float?", "1", false)},
|
|
{ "RowGap", ("Length?", "Length.Auto", false)},
|
|
{ "ColumnGap", ("Length?", "Length.Auto", false)},
|
|
{ "AspectRatio", ("aspect-ratio", "float.NaN", false)},
|
|
{ "TextAlign", ("TextAlign?", "UI.TextAlign.Left", true)},
|
|
{ "TextOverflow", ("TextOverflow?", "UI.TextOverflow.None", true)},
|
|
{ "TextFilter", ("Rendering.FilterMode?", "Rendering.FilterMode.Bilinear", true)},
|
|
{ "WordBreak", ("WordBreak?", "UI.WordBreak.Normal", true)},
|
|
{ "TextDecorationLine", ("TextDecoration?", "TextDecoration.None", true)},
|
|
{ "TextDecorationColor", ("Color?", "Color.White", true)},
|
|
{ "TextDecorationThickness", ("Length?", "1", true)},
|
|
{ "TextDecorationSkipInk", ("TextSkipInk?", "0", true)},
|
|
{ "TextDecorationStyle", ("TextDecorationStyle?","UI.TextDecorationStyle.Solid", true)},
|
|
{ "TextUnderlineOffset", ("Length?", "0", true)},
|
|
{ "TextOverlineOffset", ("Length?", "0", true)},
|
|
{ "TextLineThroughOffset", ("Length?", "0", true)},
|
|
{ "FontStyle", ("FontStyle?", "UI.FontStyle.None", true)},
|
|
{ "FontVariantNumeric", ("FontVariantNumeric?", "UI.FontVariantNumeric.Normal", true)},
|
|
{ "Transform", ("PanelTransform?", "new UI.PanelTransform()", false)},
|
|
{ "TextTransform", ("TextTransform?", "UI.TextTransform.None", true)},
|
|
{ "TransformOriginX", ("Length?", "Length.Percent( 50 ).Value", false)},
|
|
{ "TransformOriginY", ("Length?", "Length.Percent( 50 ).Value", false)},
|
|
{ "LetterSpacing", ("Length?", "Length.Percent( 0 ).Value", true)},
|
|
{ "LineHeight", ("Length?", "Length.Percent( 100 ).Value", true)},
|
|
{ "WordSpacing", ("Length?", "Length.Percent( 0 ).Value", true)},
|
|
{ "WhiteSpace", ("WhiteSpace?", "UI.WhiteSpace.Normal", true)},
|
|
{ "ZIndex", ("int?", "0", false)},
|
|
{ "Order", ("int?", "0", false)},
|
|
{ "SoundIn", ("string", "\"\"", false)},
|
|
{ "SoundOut", ("string", "\"\"", false)},
|
|
{ "BackdropFilterBlur", ("Length?", "0", false)},
|
|
{ "BackdropFilterBrightness", ("Length?", "1", false)},
|
|
{ "BackdropFilterContrast", ("Length?", "1", false)},
|
|
{ "BackdropFilterSaturate", ("Length?", "1", false)},
|
|
{ "BackdropFilterSepia", ("Length?", "0", false)},
|
|
{ "BackdropFilterInvert", ("Length?", "0", false)},
|
|
{ "BackdropFilterHueRotate", ("Length?", "0", false)},
|
|
{ "FilterBlur", ("Length?", "0", false)},
|
|
{ "FilterSaturate", ("Length?", "1", false)},
|
|
{ "FilterSepia", ("Length?", "0", false)},
|
|
{ "FilterBrightness", ("Length?", "1", false)},
|
|
{ "FilterHueRotate", ("Length?", "0", false)},
|
|
{ "FilterInvert", ("Length?", "0", false)},
|
|
{ "FilterContrast", ("Length?", "1", false)},
|
|
{ "FilterTint", ("Color?", "Color.White", false)},
|
|
{ "FilterBorderWidth", ("Length?", "0", false)},
|
|
{ "FilterBorderColor", ("Color?", "Color.White", false)},
|
|
{ "MaskMode", ("MaskMode?", "UI.MaskMode.MatchSource", false)},
|
|
{ "MaskRepeat", ("BackgroundRepeat?", "UI.BackgroundRepeat.Repeat", false)},
|
|
{ "MaskSizeX", ("Length?", "Length.Undefined", false)},
|
|
{ "MaskSizeY", ("Length?", "Length.Undefined", false)},
|
|
{ "MaskPositionX", ("Length?", "Length.Percent( 0 ).Value", false)},
|
|
{ "MaskPositionY", ("Length?", "Length.Percent( 0 ).Value", false)},
|
|
{ "MaskAngle", ("Length?", "0", false)},
|
|
{ "MaskScope", ("MaskScope?", "UI.MaskScope.Default", false)},
|
|
{ "BackgroundSizeX", ("Length?", "Length.Undefined", false)},
|
|
{ "BackgroundSizeY", ("Length?", "Length.Undefined", false)},
|
|
{ "BackgroundPositionX", ("Length?", "Length.Percent( 0 ).Value", false)},
|
|
{ "BackgroundPositionY", ("Length?", "Length.Percent( 0 ).Value", false)},
|
|
{ "BackgroundRepeat", ("BackgroundRepeat?", "UI.BackgroundRepeat.Repeat", false)},
|
|
{ "BorderImageWidthLeft", ("Length?", "1", false)},
|
|
{ "BorderImageWidthRight", ("Length?", "1", false)},
|
|
{ "BorderImageWidthTop", ("Length?", "1", false)},
|
|
{ "BorderImageWidthBottom", ("Length?", "1", false)},
|
|
{ "BorderImageFill", ("BorderImageFill?", "UI.BorderImageFill.Unfilled", false)},
|
|
{ "BorderImageRepeat", ("BorderImageRepeat?", "UI.BorderImageRepeat.Stretch", false)},
|
|
{ "BorderImageTint", ("Color?", "Color.White", false)},
|
|
{ "BackgroundBlendMode", ("string", "\"normal\"", false)},
|
|
{ "BackgroundTint", ("Color?", "Color.White", false)},
|
|
{ "BackgroundAngle", ("Length?", "0", false)},
|
|
{ "TextBackgroundAngle", ("Length?", "0", false)},
|
|
{ "PerspectiveOriginX", ("Length?", "Length.Percent( 50 ).Value", false)},
|
|
{ "PerspectiveOriginY", ("Length?", "Length.Percent( 50 ).Value", false)},
|
|
{ "TextStrokeColor", ("Color?", "Color.White", true)},
|
|
{ "TextStrokeWidth", ("Length?", "0", true)},
|
|
{ "ImageRendering", ("ImageRendering?", "UI.ImageRendering.Anisotropic",true)},
|
|
{ "AnimationDelay", ("seconds", "0", false)},
|
|
{ "AnimationDirection", ("string", "\"normal\"", false)},
|
|
{ "AnimationDuration", ("seconds", "0", false)},
|
|
{ "AnimationFillMode", ("string", "\"none\"", false)},
|
|
{ "AnimationIterationCount", ("float?", "1", false)},
|
|
{ "AnimationName", ("string", "\"none\"", false)},
|
|
{ "AnimationPlayState", ("string", "\"running\"", false)},
|
|
{ "AnimationTimingFunction", ("string", "\"ease\"", false)},
|
|
{ "FontSmooth", ("FontSmooth?", "UI.FontSmooth.Auto", true)},
|
|
{ "ObjectFit", ("ObjectFit?", "UI.ObjectFit.Cover", false)},
|
|
{ "OutlineWidth", ("Length?", "0", false)},
|
|
{ "OutlineColor", ("Color?", "Color.Transparent", false)},
|
|
{ "OutlineOffset", ("Length?", "0", false)}
|
|
};
|
|
|
|
static bool IsTransitionable( string propertyName )
|
|
{
|
|
var validTransitionTypes = new[]
|
|
{
|
|
"int",
|
|
"float",
|
|
"Length",
|
|
"PanelTransform",
|
|
"Color"
|
|
};
|
|
|
|
return validTransitionTypes.Any( x => x == propertyName || x + "?" == propertyName );
|
|
}
|
|
|
|
#>using System;
|
|
|
|
namespace Sandbox.UI;
|
|
|
|
/// <summary>
|
|
/// Auto generated container class for majority of CSS properties available.
|
|
/// </summary>
|
|
public abstract partial class BaseStyles
|
|
{
|
|
<#
|
|
foreach ( var member in Members)
|
|
{
|
|
var typeName = member.Value.TypeName;
|
|
if ( typeName == "seconds" ) typeName = "float?";
|
|
if ( typeName == "aspect-ratio" ) typeName = "float?";
|
|
#>
|
|
|
|
internal <#=typeName#> _<#=member.Key.ToLowerInvariant()#>;
|
|
|
|
/// <summary>
|
|
<#
|
|
var CSSName = string.Join( "-", Regex.Split( member.Key, @"(?<!^)(?=[A-Z])" ).Select( x => x.ToLowerInvariant() ) );
|
|
WriteLine( $" /// Represents the <c>{CSSName}</c> CSS property." );
|
|
#> /// </summary>
|
|
public <#=typeName#> <#=member.Key#>
|
|
{
|
|
get => _<#=member.Key.ToLowerInvariant()#>;
|
|
set
|
|
{
|
|
if ( _<#=member.Key.ToLowerInvariant()#> == value ) return;
|
|
_<#=member.Key.ToLowerInvariant()#> = value;
|
|
Dirty();
|
|
}
|
|
}
|
|
<#
|
|
}
|
|
#>
|
|
|
|
/// <summary>
|
|
/// Copy over only the styles that are set.
|
|
/// </summary>
|
|
private void AddGenerated( BaseStyles a )
|
|
{
|
|
<#
|
|
foreach ( var member in Members)
|
|
{
|
|
#>
|
|
if ( a._<#=member.Key.ToLowerInvariant()#> != null ) _<#=member.Key.ToLowerInvariant()#> = a._<#=member.Key.ToLowerInvariant()#>;
|
|
<#
|
|
}
|
|
#>
|
|
}
|
|
|
|
/// <summary>
|
|
/// Copy all styles from given style set.
|
|
/// </summary>
|
|
private void FromGenerated( BaseStyles a )
|
|
{
|
|
<#
|
|
foreach ( var member in Members)
|
|
{
|
|
#> _<#=member.Key.ToLowerInvariant()#> = a._<#=member.Key.ToLowerInvariant()#>;
|
|
<#
|
|
}
|
|
#>
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set a CSS property via its string name.
|
|
/// </summary>
|
|
private bool SetGenerated( string property, string value )
|
|
{
|
|
switch ( property )
|
|
{
|
|
<#
|
|
string? GetParsingFunction( string type )
|
|
{
|
|
if ( type.EndsWith( "?" ))
|
|
type = type.Substring( 0, type.Length - 1 );
|
|
|
|
switch ( type )
|
|
{
|
|
case "aspect-ratio":
|
|
return "ParseAspectRatio( value )";
|
|
case "seconds":
|
|
return "ParseSeconds( value )";
|
|
case "int":
|
|
return "ParseInt( value )";
|
|
case "float":
|
|
return "ParseFloat( value )";
|
|
case "string":
|
|
return "value.TrimQuoted( true )";
|
|
case "Length":
|
|
return "Length.Parse( value )";
|
|
case "Color":
|
|
return "Color.Parse( value )";
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
foreach ( var member in Members )
|
|
{
|
|
var cssName = string.Concat( member.Key.Select( x => Char.IsUpper( x ) ? "-" + x : x.ToString() ) ).Trim( '-' ).ToLowerInvariant();
|
|
var parsingFunction = GetParsingFunction(member.Value.TypeName);
|
|
|
|
if ( parsingFunction != null )
|
|
{
|
|
|
|
var returnValue = ( member.Value.TypeName.EndsWith("?") ? $"{member.Key}.HasValue" : "true" );
|
|
#>case "<#= cssName #>":
|
|
<#= member.Key #> = <#= parsingFunction #>;
|
|
return <#= returnValue #>;
|
|
<#}
|
|
}#>default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private int GetHashCodeGenerated()
|
|
{
|
|
var hash = new HashCode();
|
|
|
|
<#
|
|
foreach ( var member in Members )
|
|
{
|
|
#> hash.Add( _<#= member.Key.ToLowerInvariant() #> );
|
|
<#
|
|
}
|
|
#>
|
|
return hash.ToHashCode();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Lerp every property in this stylesheet
|
|
/// </summary>
|
|
public virtual void FromLerp( BaseStyles from, BaseStyles to, float delta )
|
|
{
|
|
<#
|
|
foreach ( var member in Members )
|
|
{
|
|
var CSSName = string.Join( "-", Regex.Split( member.Key, @"(?<!^)(?=[A-Z])" ).Select( x => x.ToLowerInvariant() ) );
|
|
var (key, value) = (member.Key.ToLowerInvariant(), member.Value);
|
|
|
|
if ( IsTransitionable( value.TypeName ) )
|
|
{
|
|
#> LerpProperty( "<#=CSSName#>", from, to, delta );
|
|
<#
|
|
}
|
|
}
|
|
#>
|
|
}
|
|
|
|
/// <summary>
|
|
/// Lerp a specific property by name
|
|
/// </summary>
|
|
public virtual void LerpProperty( string name, BaseStyles from, BaseStyles to, float delta )
|
|
{
|
|
switch ( name )
|
|
{
|
|
<#foreach ( var member in Members ){
|
|
var CSSName = string.Join( "-", Regex.Split( member.Key, @"(?<!^)(?=[A-Z])" ).Select( x => x.ToLowerInvariant() ) );
|
|
var (key, value) = (member.Key.ToLowerInvariant(), member.Value);
|
|
|
|
var defaultValue = $"{member.Value.DefaultValue}";
|
|
if ( value.IsInherited )
|
|
defaultValue = $"from._{key} ?? {defaultValue}";
|
|
|
|
if ( IsTransitionable( value.TypeName ) ) {
|
|
#> case "<#=CSSName#>":
|
|
Lerp( ref _<#=key#>, from._<#=key#>, to._<#=key#>, <#=defaultValue#>, delta );
|
|
break;
|
|
<#}}#>}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Perform a deep copy of this stylesheet
|
|
/// </summary>
|
|
public object Clone()
|
|
{
|
|
var copy = this.MemberwiseClone() as BaseStyles;
|
|
<#foreach ( var member in Members ){
|
|
var (key, value) = (member.Key.ToLowerInvariant(), member.Value);
|
|
#>copy._<#=key#> = _<#=key#>;
|
|
<#}#>copy.CssWide = CssWide == null ? null : new System.Collections.Generic.Dictionary<string, CssWideKeyword>( CssWide );
|
|
return copy;
|
|
}
|
|
|
|
public void ApplyCascading( BaseStyles parent )
|
|
{
|
|
<#
|
|
foreach ( var member in Members )
|
|
{
|
|
if ( !member.Value.IsInherited ) continue;
|
|
var (key, value) = (member.Key.ToLowerInvariant(), member.Value);
|
|
#>
|
|
if ( _<#=key#> == null ) _<#=key#> = parent._<#=key#>;
|
|
<#
|
|
}
|
|
#>
|
|
}
|
|
|
|
private void FillDefaultsGenerated()
|
|
{
|
|
<#
|
|
foreach ( var member in Members )
|
|
{
|
|
if ( member.Value.TypeName.EndsWith( "?" ) )
|
|
{
|
|
var (key, value) = (member.Key.ToLowerInvariant(), member.Value);
|
|
#>
|
|
if ( !_<#=key#>.HasValue ) _<#=key#> = <#=member.Value.DefaultValue#>;
|
|
<#
|
|
}
|
|
}
|
|
#>
|
|
}
|
|
|
|
internal bool IsDefault( string name )
|
|
{
|
|
switch ( name )
|
|
{
|
|
<#foreach ( var member in Members ){
|
|
var CSSName = string.Join( "-", Regex.Split( member.Key, @"(?<!^)(?=[A-Z])" ).Select( x => x.ToLowerInvariant() ) );
|
|
var (key, value) = (member.Key.ToLowerInvariant(), member.Value);
|
|
#> case "<#=CSSName#>": return (_<#=key#> == <#=member.Value.DefaultValue#>);
|
|
<#}#>}
|
|
|
|
throw new Exception( $"Invalid property name '{name}'" );
|
|
}
|
|
}
|