Editor/Launcher/LauncherWidgets.cs
using Sandbox;
using Editor;
using System;

/// <summary>
/// A project in the launcher's list:
/// <code>
/// [icon]  My Game                       [Game] [Loaded]   2 hours ago  ☆
///         C:/projects/mygame/mygame.sbproj
/// </code>
/// Click to open, right-click for more, the star pins it.
/// </summary>
public class ProjectCard : Widget
{
	const float PinSize = 26;

	private readonly HotCodeEntry _entry;
	private readonly bool _loaded;
	private readonly bool _hasHotCode;

	public Action Opened;
	public Action<bool> PinToggled;
	public Action ContextMenuRequested;

	public ProjectCard( Widget parent, HotCodeEntry entry, bool loaded, bool hasHotCode ) : base( parent )
	{
		_entry = entry;
		_loaded = loaded;
		_hasHotCode = hasHotCode;

		FixedHeight = 58;
		MouseTracking = true;
		Cursor = CursorShape.Finger;
		ToolTip = entry.Path;
	}

	private Rect PinRect => new( Width - PinSize - 10, (Height - PinSize) / 2, PinSize, PinSize );

	protected override void OnMouseEnter() { base.OnMouseEnter(); Update(); }
	protected override void OnMouseLeave() { base.OnMouseLeave(); Update(); }
	protected override void OnMouseMove( MouseEvent e ) { base.OnMouseMove( e ); Update(); }

	protected override void OnMousePress( MouseEvent e )
	{
		base.OnMousePress( e );

		if ( e.RightMouseButton )
		{
			ContextMenuRequested?.Invoke();
			return;
		}

		if ( !e.LeftMouseButton ) return;

		if ( PinRect.IsInside( e.LocalPosition ) ) PinToggled?.Invoke( !_entry.Pinned );
		else Opened?.Invoke();
	}

	protected override void OnPaint()
	{
		var hovered = IsUnderMouse;
		var rect = LocalRect.Shrink( 0, 0, 0, 1 );

		Paint.ClearPen();
		Paint.SetBrush( hovered ? Theme.SurfaceLightBackground : Theme.SurfaceBackground );
		Paint.DrawRect( rect, Theme.ControlRadius );

		// Type icon on a tinted square
		var iconRect = new Rect( 10, (Height - 38) / 2, 38, 38 );
		Paint.SetBrush( _entry.TypeColor.WithAlpha( 0.15f ) );
		Paint.DrawRect( iconRect, Theme.ControlRadius );
		Paint.SetPen( _entry.TypeColor );
		Paint.DrawIcon( iconRect, _entry.Icon, 22, TextFlag.Center );

		// Right side, laid out from the right edge: pin, time, chips
		var right = PinRect.Left - 6;

		var mouse = FromScreen( Editor.Application.CursorPosition );
		var overPin = hovered && PinRect.IsInside( mouse );
		if ( _entry.Pinned || hovered )
		{
			Paint.SetPen( _entry.Pinned ? Theme.Yellow : overPin ? Theme.Text : Theme.TextLight );
			Paint.DrawIcon( PinRect, _entry.Pinned ? "star" : "star_border", 18, TextFlag.Center );
		}

		if ( _entry.LastOpened is { } opened )
		{
			Paint.SetDefaultFont( HotCodeStyle.FontSize );
			var when = RecentEntries.Ago( opened );
			var width = Paint.MeasureText( when ).x;
			Paint.SetPen( Theme.TextLight );
			Paint.DrawText( new Rect( right - width, 0, width, Height ), when, TextFlag.RightCenter );
			right -= width + 14;
		}

		if ( _loaded ) right = DrawChip( right, "Loaded", Theme.Green );
		if ( _hasHotCode ) right = DrawChip( right, "HotCode", HotCodeLogo.Glow );
		right = DrawChip( right, _entry.TypeName, _entry.TypeColor );

		// Title and path
		var textLeft = iconRect.Right + 12;
		var textWidth = right - textLeft - 8;

		Paint.SetDefaultFont( 9, 600 );
		Paint.SetPen( Theme.Text );
		Paint.DrawText( new Rect( textLeft, 10, textWidth, 20 ), Paint.GetElidedText( _entry.Title, textWidth, ElideMode.Right ), TextFlag.LeftCenter );

		Paint.SetDefaultFont( HotCodeStyle.FontSize );
		Paint.SetPen( Theme.TextLight );
		Paint.DrawText( new Rect( textLeft, 30, textWidth, 18 ), Paint.GetElidedText( _entry.Path, textWidth, ElideMode.Middle ), TextFlag.LeftCenter );
	}

	/// <summary>
	/// A small rounded label ending at <paramref name="right"/>; returns where the next one should end.
	/// </summary>
	private float DrawChip( float right, string text, Color color )
	{
		Paint.SetDefaultFont( 7, 600 );
		var width = Paint.MeasureText( text ).x + 14;
		var chip = new Rect( right - width, (Height - 18) / 2, width, 18 );

		Paint.ClearPen();
		Paint.SetBrush( color.WithAlpha( 0.18f ) );
		Paint.DrawRect( chip, 9 );
		Paint.SetPen( color );
		Paint.DrawText( chip, text, TextFlag.Center );

		return chip.Left - 6;
	}
}

/// <summary>
/// A full-width button for the launcher's sidebar: icon, title, and a dim line of description.
/// </summary>
public class SidebarButton : Widget
{
	private readonly string _icon;
	private readonly string _title;
	private readonly string _description;

	public Action Clicked;
	public bool Selected { get; set; }

	public SidebarButton( Widget parent, string icon, string title, string description ) : base( parent )
	{
		_icon = icon;
		_title = title;
		_description = description;

		FixedHeight = 44;
		Cursor = CursorShape.Finger;
		MouseTracking = true;
	}

	protected override void OnMouseEnter() { base.OnMouseEnter(); Update(); }
	protected override void OnMouseLeave() { base.OnMouseLeave(); Update(); }

	protected override void OnMousePress( MouseEvent e )
	{
		base.OnMousePress( e );
		if ( e.LeftMouseButton ) Clicked?.Invoke();
	}

	protected override void OnPaint()
	{
		if ( Selected || IsUnderMouse )
		{
			Paint.ClearPen();
			Paint.SetBrush( Selected ? Theme.SelectedBackground : Theme.SurfaceBackground );
			Paint.DrawRect( LocalRect, Theme.ControlRadius );
		}

		if ( Selected )
		{
			Paint.SetBrush( Theme.Primary );
			Paint.DrawRect( new Rect( 0, 8, 3, Height - 16 ), 1 );
		}

		Paint.SetPen( Selected ? Theme.Primary : Theme.Text );
		Paint.DrawIcon( new Rect( 10, 0, 24, Height ), _icon, 18, TextFlag.Center );

		Paint.SetDefaultFont( HotCodeStyle.FontSize, 600 );
		Paint.SetPen( Theme.Text );
		Paint.DrawText( new Rect( 42, 5, Width - 46, 18 ), _title, TextFlag.LeftCenter );

		Paint.SetDefaultFont( 7 );
		Paint.SetPen( Theme.TextLight );
		Paint.DrawText( new Rect( 42, 22, Width - 46, 16 ), Paint.GetElidedText( _description, Width - 46, ElideMode.Right ), TextFlag.LeftCenter );
	}
}

/// <summary>
/// A selectable tile for picking the kind of project to create.
/// </summary>
public class ChoiceTile : Widget
{
	private readonly string _icon;
	private readonly string _title;
	private readonly string _description;
	private readonly Color _color;

	public Action Clicked;
	public bool Selected { get; set; }

	public ChoiceTile( Widget parent, string icon, string title, string description, Color color ) : base( parent )
	{
		_icon = icon;
		_title = title;
		_description = description;
		_color = color;

		FixedHeight = 70;
		MinimumWidth = 200;
		Cursor = CursorShape.Finger;
		MouseTracking = true;
	}

	protected override void OnMouseEnter() { base.OnMouseEnter(); Update(); }
	protected override void OnMouseLeave() { base.OnMouseLeave(); Update(); }

	protected override void OnMousePress( MouseEvent e )
	{
		base.OnMousePress( e );
		if ( e.LeftMouseButton ) Clicked?.Invoke();
	}

	protected override void OnPaint()
	{
		Paint.SetBrush( IsUnderMouse || Selected ? Theme.SurfaceLightBackground : Theme.SurfaceBackground );
		if ( Selected ) Paint.SetPen( Theme.Primary, 2 );
		else Paint.ClearPen();
		Paint.DrawRect( LocalRect.Shrink( 1 ), Theme.ControlRadius );

		var iconRect = new Rect( 12, (Height - 40) / 2, 40, 40 );
		Paint.ClearPen();
		Paint.SetBrush( _color.WithAlpha( 0.15f ) );
		Paint.DrawRect( iconRect, Theme.ControlRadius );
		Paint.SetPen( _color );
		Paint.DrawIcon( iconRect, _icon, 22, TextFlag.Center );

		Paint.SetDefaultFont( 9, 600 );
		Paint.SetPen( Theme.Text );
		Paint.DrawText( new Rect( 64, 14, Width - 72, 20 ), _title, TextFlag.LeftCenter );

		Paint.SetDefaultFont( HotCodeStyle.FontSize );
		Paint.SetPen( Theme.TextLight );
		Paint.DrawText( new Rect( 64, 34, Width - 72, 20 ), Paint.GetElidedText( _description, Width - 72, ElideMode.Right ), TextFlag.LeftCenter );
	}
}