Editor/Panels/SuperShotUI.cs

Editor UI helpers for the SuperShot editor panel. Defines static colors and helper functions to add Banner, Card, ExpandGroup and property sheet widgets, and implements Banner, Card and IconLabel Widget subclasses with custom painting.

Native Interop
using System;
using Sandbox;

namespace Editor.SuperShot;

public static class SuperShotUI
{
	public static Color Accent => new( 0.36f, 0.55f, 0.95f );
	public static Color AccentDim => new( 0.22f, 0.30f, 0.46f );
	public static Color CardBackground => Theme.ControlBackground.Lighten( 0.03f );
	public static Color CardBorder => Theme.ControlBackground.Lighten( 0.12f );
	public static Color Muted => Theme.TextControl.WithAlpha( 0.6f );

	public static Banner AddBanner( Layout layout, string title, string subtitle, string icon )
	{
		var banner = new Banner( title, subtitle, icon );
		layout.Add( banner );
		return banner;
	}

	public static Card AddCard( Layout layout, string title = null, string icon = null )
	{
		var card = new Card( title, icon );
		layout.Add( card );
		return card;
	}

	public static ExpandGroup AddSection( Layout layout, string title, string icon, Widget content, string cookie = null, bool defaultOpen = false )
	{
		var group = new ExpandGroup( null );
		group.Title = title;
		group.Icon = icon;
		group.SetWidget( content );

		if ( !string.IsNullOrEmpty( cookie ) )
			group.StateCookieName = cookie;
		else
			group.SetOpenState( defaultOpen );

		layout.Add( group );
		return group;
	}

	public static Widget SheetWidget( SerializedObject so, Func<SerializedProperty, bool> filter = null )
	{
		var widget = new Widget( null );
		widget.Layout = Layout.Column();

		var sheet = new ControlSheet();
		if ( filter is null )
			sheet.AddObject( so );
		else
			sheet.AddObject( so, filter );

		widget.Layout.Add( sheet );
		return widget;
	}
}

public sealed class Banner : Widget
{
	public string TitleText { get; set; }
	public string SubtitleText { get; set; }
	public string Icon { get; set; }

	public Banner( string title, string subtitle, string icon ) : base( null )
	{
		TitleText = title;
		SubtitleText = subtitle;
		Icon = icon;
		FixedHeight = 56;
		MinimumSize = new Vector2( 0, 56 );
	}

	protected override void OnPaint()
	{
		var rect = LocalRect;

		Paint.ClearPen();
		Paint.SetBrush( SuperShotUI.AccentDim.WithAlpha( 0.35f ) );
		Paint.DrawRect( rect, 6f );

		Paint.ClearBrush();
		Paint.SetBrush( SuperShotUI.Accent );
		Paint.DrawRect( new Rect( rect.Left, rect.Top, 4f, rect.Height ), 2f );

		var content = rect.Shrink( 16, 0 );

		if ( !string.IsNullOrEmpty( Icon ) )
		{
			Paint.SetPen( SuperShotUI.Accent );
			Paint.DrawIcon( new Rect( content.Left, content.Top, 32, content.Height ), Icon, 26, TextFlag.LeftCenter );
			content.Left += 44;
		}

		Paint.SetDefaultFont( 11, 600 );
		Paint.SetPen( Theme.Text );
		Paint.DrawText( new Rect( content.Left, content.Top + 8, content.Width, 22 ), TitleText, TextFlag.LeftTop );

		if ( !string.IsNullOrEmpty( SubtitleText ) )
		{
			Paint.SetDefaultFont( 8, 400 );
			Paint.SetPen( SuperShotUI.Muted );
			Paint.DrawText( new Rect( content.Left, content.Top + 30, content.Width, 18 ), SubtitleText, TextFlag.LeftTop );
		}
	}
}

public sealed class Card : Widget
{
	public Layout Body { get; private set; }

	public Card( string title = null, string icon = null ) : base( null )
	{
		Layout = Layout.Column();
		Layout.Margin = 12;
		Layout.Spacing = 8;

		if ( !string.IsNullOrEmpty( title ) )
		{
			var header = Layout.AddRow();
			header.Spacing = 6;

			if ( !string.IsNullOrEmpty( icon ) )
				header.Add( new IconLabel( icon ) );

			header.Add( new Label.Subtitle( title ) );
			header.AddStretchCell();
		}

		var bodyWidget = new Widget( this );
		bodyWidget.Layout = Layout.Column();
		bodyWidget.Layout.Spacing = 6;
		Body = bodyWidget.Layout;
		Layout.Add( bodyWidget );
	}

	protected override void OnPaint()
	{
		Paint.ClearPen();
		Paint.SetBrush( SuperShotUI.CardBackground );
		Paint.DrawRect( LocalRect, 6f );

		Paint.ClearBrush();
		Paint.SetPen( SuperShotUI.CardBorder, 1f );
		Paint.DrawRect( LocalRect.Shrink( 0.5f ), 6f );
	}
}

public sealed class IconLabel : Widget
{
	public string Icon { get; set; }

	public IconLabel( string icon ) : base( null )
	{
		Icon = icon;
		FixedSize = new Vector2( 20, 20 );
	}

	protected override void OnPaint()
	{
		Paint.SetPen( SuperShotUI.Accent );
		Paint.DrawIcon( LocalRect, Icon, 18, TextFlag.Center );
	}
}