Editor/Inspector/PanelStyleBlockEditor.cs
using Editor.NodeEditor;
using Sandbox;
using Sandbox.UI;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Button = Editor.Button;
using Label = Editor.Label;
namespace Panelize;
/// <summary>
/// Display a CSS rule block
/// </summary>
public class PanelStyleBlockEditor : Widget
{
Panel panel;
IStyleBlock block;
PanelEditorSession session;
bool rawEdit;
public PanelStyleBlockEditor(Panel panel, IStyleBlock block, PanelEditorSession session, bool rawEdit = false)
{
//MinimumHeight = 100f;
Layout = Layout.Column();
Layout.Margin = 4;
Layout.Spacing = 4;
this.panel = panel;
this.block = block;
this.session = session;
this.rawEdit = rawEdit;
Rebuild();
}
public void Rebuild()
{
Layout.Clear(true);
PanelStyleBlockHeader header = new( this, panel, block );
Layout.Add( header );
var entryColumn = Layout.AddColumn();
entryColumn.Margin = new( 8f, 0f, 0f, 0f);
entryColumn.Spacing = 2f;
foreach(var entry in block.GetRawValues())
{
PanelStyleRowEditor row = new( panel, block, entry, session, rawEdit );
if(row.IsValid())
entryColumn.Add( row );
}
}
protected override void OnPaint()
{
base.OnPaint();
Paint.ClearPen();
Paint.SetBrush( Theme.WidgetBackground.Lighten(0.4f) );
Paint.DrawRect( LocalRect );
}
}
file class PanelStyleBlockHeader : Widget
{
private record StyleOption(string Path, PropertyInfo Prop, bool HasGroup = false);
Label text;
Button deleteRuleButton;
Button editRuleButton;
Button addStyleButton;
PanelStyleBlockEditor editor;
Panel panel;
IStyleBlock block;
public PanelStyleBlockHeader( PanelStyleBlockEditor editor, Panel panel, IStyleBlock block )
{
this.editor = editor;
this.panel = panel;
this.block = block;
Layout = Layout.Row();
Rebuild();
}
public void Rebuild()
{
Layout.Clear( true );
text = new( string.Join( ", ", block.SelectorStrings.Select( x => $"<span style=\"color: {Theme.Yellow.Hex}\">{x}</span>" ) ) );
Layout.Add( text, 1 );
deleteRuleButton = new( "Delete", "remove" );
deleteRuleButton.Clicked += DeleteRule;
Layout.Add( deleteRuleButton );
editRuleButton = new( "Edit", "edit" );
editRuleButton.Clicked += EditRule;
Layout.Add( editRuleButton );
addStyleButton = new( "Add Style", "add" );
addStyleButton.Clicked += AddStylePopup;
Layout.Add( addStyleButton, 0 );
}
private void DeleteRule()
{
if ( block is not StyleBlock activeBlock ) return;
foreach(var sheet in panel.AllStyleSheets)
{
if(sheet.Nodes.Contains( activeBlock ) )
{
sheet.Nodes.Remove( activeBlock );
}
}
PanelUtils.DirtyStyles( panel );
editor.Destroy();
}
private void EditRule()
{
throw new NotImplementedException();
}
private void AddStylePopup()
{
ContextMenu menu = new( this );
menu.Layout = Layout.Column();
menu.AddLineEdit( "Filter",
placeholder: "Filter style...",
onChange: (text) => BuildStyleOptions(menu, text) );
BuildStyleOptions( menu );
menu.OpenAtCursor();
}
private void BuildStyleOptions(Menu menu, string filter = "")
{
menu.RemoveMenus();
menu.RemoveOptions();
List<StyleOption> options = new();
foreach ( var prop in PanelUtils.GetStyleProperties() )
{
if ( !prop.Name.Contains( filter, StringComparison.OrdinalIgnoreCase ) )
continue;
if ( !ShowProperty( prop.Name ) )
continue;
string group = GetGroup( prop.Name );
if(!string.IsNullOrEmpty(group))
{
options.Add( new( $"{group}/{prop.Name.ToTitleCase()}", prop, true));
}
else
{
options.Add( new( prop.Name.ToTitleCase(), prop));
}
}
menu.AddOptions( options.OrderBy( o => o.Path + (o.HasGroup ? 10000 : 0) )
, (o) => o.Path, ( o ) => AddStyle( o.Prop ), reduce: false );
menu.AdjustSize();
menu.Update();
}
private void AddStyle(PropertyInfo prop)
{
string property = PanelUtils.GetStyleProperty( prop );
// prop.GetValue(Styles.Default).ToString()
block.SetRawValue( property, PanelUtils.GetStyleDefaultValue(prop) );
editor.Rebuild();
}
private string GetGroup( string property )
{
Dictionary<string, string> propertyGroups = new()
{
{ "Width", "Size" },
{ "MinWidth", "Size" },
{ "MaxWidth", "Size" },
{ "Height", "Size" },
{ "MinHeight", "Size" },
{ "MaxHeight", "Size" },
{ "AspectRatio", "Size" },
{ "WordBreak", "Text" },
{ "WordSpacing", "Text" },
{ "LetterSpacing", "Text" },
{ "RowGap", "Flex" },
{ "ColumnGap", "Flex" },
{ "JustifyContent", "Alignment" },
{ "Left", "Position" },
{ "Top", "Position" },
{ "Right", "Position" },
{ "Bottom", "Position" },
{ "Position", "Position" },
};
Dictionary<string, string> nameStartGroups = new()
{
{ "Background", "Background" },
{ "Mask", "Mask" },
{ "Filter", "Filter" },
{ "Backdrop", "Backdrop" },
{ "Sound", "Sound" },
{ "Transform", "Transform" },
{ "Text", "Text" },
{ "Align", "Alignment" },
{ "Font", "Font" },
{ "Flex", "Flex" },
{ "Border", "Border" },
{ "Animation", "Animation" },
{ "Overflow", "Overflow" },
{ "Margin", "Margin" },
{ "Padding", "Padding" },
};
bool groupSet = false;
foreach ( (string name, string group) in propertyGroups )
{
if ( groupSet ) break;
if ( property == name )
{
return group;
}
}
foreach ( (string group, string value) in nameStartGroups )
{
if ( groupSet ) break;
if ( property.StartsWith( group ) )
{
return value;
}
}
return "";
}
private bool ShowProperty(string property)
{
string[] ignoredProperties = [
"Display",
"Content"
];
return !ignoredProperties.Contains( property );
}
}