Editor/TextToAnimation/UI/StartPage.cs

An editor UI widget for the TextToAnimation tool's start page. It draws a centered drop area with labels, buttons to choose or import models, options to create a new scene from stock characters, drag-and-drop handling for model files, and a visual hover/drag state.

using System;
using System.Collections.Generic;
using Editor;
using Sandbox;
using TextToAnimation.Editor.Session;

namespace TextToAnimation.Editor.UI;

/// <summary>
/// First-load surface (the Weapon Importer's drop area): a dark-gray rounded box with a centered prompt,
/// a blue choose button and buttons to start from a stock character. Accepts .vmdl files from the OS and
/// models from the asset browser.
/// </summary>
public sealed class StartPage : Widget
{
	readonly Action<IReadOnlyList<string>> _open;
	int _hover;

	public StartPage( Widget parent, Action<IReadOnlyList<string>> open, Action chooseFile, Action chooseFromDisk,
		Action newFromCitizen, Action newFromCitizenHuman, GenerationFlow flow ) : base( parent )
	{
		_open = open;
		AcceptDrops = true;
		Layout = Layout.Column();
		Layout.Margin = 12;
		Layout.Spacing = 8;
		Layout.AddStretchCell();
		var row = Layout.AddRow();
		row.AddStretchCell();
		var center = row.AddColumn();
		center.Spacing = 12;
		center.Add( new BigIcon( this ) );
		center.Add( new Label.Subtitle( "Drop a model", this ) { Alignment = TextFlag.Center } );
		center.Add( TaStyle.Muted( new Label( "VMDL or rigged FBX", this ) { Alignment = TextFlag.Center } ) );
		center.Add( TaStyle.Muted( new Label( "Drop a rigged model to animate it, or start fresh from a copy of an s&box character.", this )
			{ Alignment = TextFlag.Center, WordWrap = true, MaximumWidth = 440 }, small: true ) );
		center.AddSpacingCell( 4 );
		var choice = center.AddRow();
		choice.Spacing = 8;
		choice.AddStretchCell();
		choice.Add( new Button.Primary( "Choose VMDL" ) { Icon = "folder_open", Tint = TaStyle.Accent, MinimumWidth = 140, FixedHeight = 32, Clicked = chooseFile, ToolTip = "Pick a model from the asset browser" } );
		choice.Add( new TaButton( this, "From disk…", "file_open", chooseFromDisk, "Pick a .vmdl or a rigged .fbx (copied into the project; an FBX gets its textures, materials and a vmdl)", 32 ) );
		choice.AddStretchCell();
		var fresh = center.AddRow();
		fresh.Spacing = 8;
		fresh.AddStretchCell();
		fresh.Add( new TaButton( this, "New from Citizen", "person_add", newFromCitizen, "Start fresh: copies citizen.vmdl into your project", 32 ) );
		fresh.Add( new TaButton( this, "New from Citizen Human", "person_add", newFromCitizenHuman, "Start fresh: copies citizen_human_male.vmdl into your project", 32 ) );
		fresh.AddStretchCell();
		center.AddSpacingCell( 8 );
		center.Add( new DownloadStrip( this, flow ) { FixedWidth = 560 } );
		row.AddStretchCell();
		Layout.AddStretchCell();
	}

	public override void OnDragHover( DragEvent e )
	{
		var valid = TaDrop.Paths( e.Data, TaDrop.ModelExtensions ).Count > 0;
		_hover = valid ? 1 : -1;
		if ( valid )
			e.Action = DropAction.Link;
		Update();
	}

	public override void OnDragDrop( DragEvent e )
	{
		_hover = 0;
		var paths = TaDrop.Paths( e.Data, TaDrop.ModelExtensions );
		if ( paths.Count > 0 )
		{
			e.Action = DropAction.Link;
			_open( paths );
		}
		Update();
	}

	public override void OnDragLeave()
	{
		_hover = 0;
		Update();
	}

	protected override void OnPaint()
	{
		Paint.Antialiasing = true;
		Paint.SetPen( _hover == 1 ? TaStyle.Accent : _hover < 0 ? Theme.Red : Theme.ControlBackground.Lighten( .2f ), _hover == 0 ? 1 : 2 );
		Paint.SetBrush( _hover == 1 ? TaStyle.Accent.WithAlpha( .06f ) : _hover < 0 ? Theme.Red.WithAlpha( .05f ) : Paint.HasMouseOver ? Theme.ControlBackground.Lighten( .3f ) : Theme.ControlBackground );
		Paint.DrawRect( LocalRect.Shrink( 1 ), 6 );
	}

	sealed class BigIcon : Widget
	{
		public BigIcon( Widget parent ) : base( parent )
		{
			FixedHeight = 56;
		}

		protected override void OnPaint()
		{
			Paint.SetPen( Theme.TextLight );
			Paint.DrawIcon( new Rect( (Width - 48) * .5f, 4, 48, 48 ), "directions_run", 48 );
		}
	}
}