Editor/ThemeEditorWindow.cs
using Editor;
using Sandbox;
using System.Collections.Generic;

[Dock( "Editor", "Theme Editor", "local_fire_department" )]
public class ThemeEditorWidget : Widget
{
	ControlSheet MainSheet;
	EditorTheme theme;

	EditorThemeResource LoadedResource { get; set; }

	Layout ResourceLayout;

	Layout ThemeLayout { get; set; }

	[ResourceType( "etheme" )] string ThemeResourcePath { get; set; } = "";

	public ThemeEditorWidget( Widget parent ) : base( parent, false )
	{

		theme = LoadTheme();

		WindowTitle = "Theme Editor";

		// Create a Column Layout
		Layout = Layout.Column();
		// Give it some Margins/Spacing
		Layout.Margin = 4;
		Layout.Spacing = 4;
		Layout.Alignment = TextFlag.Center;
		Layout.AddStretchCell();

		ResourceLayout = Layout.AddRow();

		TranslucentBackground = true;
		WindowOpacity = 0.99f;

		var themeResourceFinder = new ResourceControlWidget( this.GetSerialized().GetProperty( "LoadedResource" ) );
		themeResourceFinder.Parent = this;
		themeResourceFinder.MaximumWidth = 300;
		ResourceLayout.Add( themeResourceFinder );

		var applyTheme = ResourceLayout.Add( new Button( "Apply Resource", "casino", this ) );
		applyTheme.Clicked += () =>
		{
			if ( LoadedResource.IsValid() )
			{
				theme = EditorTheme.ShallowCopy( LoadedResource.ETheme );
				SaveTheme( theme );
				ApplyToEditor( theme );
				RebuildThemes();
				Refresh();
			}

		};



		var button = Layout.Add( new Button( "Save", "", this ) );
		button.Clicked += () =>
		{
			SaveTheme( theme, LoadedResource );
			ApplyToEditor( theme );
			Refresh();
		};

		var resetButton = Layout.Add( new Button( "Reset", "casino", this ) );
		resetButton.Clicked += () =>
		{
			ResetToDefault( theme );
		};



		Label colorLabel = new Label( "Colors", this );
		colorLabel.Alignment = TextFlag.Center;
		colorLabel.SetStyles( $"font-weight: 600; color: {Editor.Theme.Text}; text-align: center;" );



		Layout.Add( colorLabel );

		ThemeLayout = Layout.Column();

		MainSheet = new ControlSheet();
		RebuildThemes();
		ThemeLayout.SizeConstraint = SizeConstraint.SetFixedSize;


		var scrollArea = Layout.Add( new ScrollArea( this ) { Canvas = new Widget { Layout = Layout.Column() } }, 1 );
		var scrollLayout = scrollArea.Canvas.Layout;

		scrollLayout.Add( MainSheet );
		scrollLayout.Spacing = 4;

		Refresh();

		Layout.Add( ThemeLayout );
	}

	void Refresh()
	{
		SetStyles( $"color: {Editor.Theme.Text.Hex}; color: white; font-weight: 600;" );
	}

	void RebuildThemes()
	{
		MainSheet.Clear( true );
		MainSheet.AddObject( theme.GetSerialized() );
	}

	public static EditorTheme LoadTheme()
	{
		EditorTheme theme = new();
		Dictionary<string, string> dictColors = Editor.FileSystem.Root.ReadJson<Dictionary<string, string>>( "addons/tools/assets/styles/theme.json" );
		foreach ( var col in dictColors )
		{
			if ( col.Key == "RowHeight" || col.Key == "ControlHeight" || col.Key == "ControlRadius" )
			{
				theme.GetType().GetProperty( col.Key )?.SetValue( theme, col.Value.ToInt() );
				continue;
			}
			if ( col.Key == "DefaultFont" || col.Key == "HeadingFont" )
			{
				theme.GetType().GetProperty( col.Key )?.SetValue( theme, col.Value );
				continue;
			}
			var color = Color.Parse( col.Value );
			if ( col.Key == "" || color == null )
			{
				Log.Error( $"Failed to parse color for {col.Key} with value {col.Value}" );
				continue;
			}
			theme.GetType().GetProperty( col.Key )?.SetValue( theme, color.Value );
		}

		return theme;
	}



	public static void SaveTheme( EditorTheme theme, EditorThemeResource loadedResource = null )
	{
		Dictionary<string, string> dictColors = new();
		foreach ( var prop in theme.GetType().GetProperties() )
		{
			if ( prop.Name == "RowHeight" || prop.Name == "ControlHeight" || prop.Name == "ControlRadius" || prop.Name == "DefaultFont" || prop.Name == "HeadingFont" )
			{
				dictColors.Add( prop.Name, prop.GetValue( theme ).ToString() );
				continue;
			}
			if ( prop.PropertyType == typeof( Color ) )
			{
				dictColors.Add( prop.Name, ((Color)prop.GetValue( theme )).Hex );
			}
		}
		Editor.FileSystem.Root.WriteJson( "addons/tools/assets/styles/theme.json", dictColors );

		if ( loadedResource.IsValid() )
		{
			loadedResource.ETheme = theme;
		}
	}

	public static void ApplyToEditor( EditorTheme theme )
	{
		foreach ( var property in theme.GetType().GetProperties() )
		{
			var themeProp = typeof( Editor.Theme ).GetField( property.Name );
			if ( themeProp != null )
			{
				themeProp.SetValue( null, property.GetValue( theme ) );
			}

		}


	}

	public static void ResetToDefault( EditorTheme theme )
	{
		theme = new EditorTheme();
	}
}