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 );
}
}