UI/Components/Menu/MenuWidgetBuilders.cs

A set of fluent builder classes for constructing UI menu widgets and composite menu components. Each builder holds a widget instance, exposes configuration methods (with chaining), and ends by adding the configured widget to a MenuScreenBuilder.

Native Interop
namespace Sunless.Libraries.UI;

using System;
using System.Collections.Generic;

public sealed class MenuLinkBuilder
{
	readonly MenuScreenBuilder _screen;
	readonly MenuLinkWidget _w;

	internal MenuLinkBuilder( MenuScreenBuilder screen, string label, string icon )
	{
		_screen = screen;
		_w = new MenuLinkWidget { Label = label, Icon = icon, Style = MenuLinkStyle.Row };
	}

	public MenuLinkBuilder WithStyle( MenuLinkStyle style )  { _w.Style = style; return this; }
	public MenuLinkBuilder WithTone( MenuTone tone )         { _w.Tone = tone; return this; }
	public MenuLinkBuilder WithHint( string hint )           { _w.Hint = hint; return this; }
	public MenuLinkBuilder WhenEnabled( Func<bool> enabled ) { _w.Enabled = enabled; return this; }
	public MenuLinkBuilder WithGoTo( string routeId )        { _w.RouteId = routeId; return this; }
	public MenuLinkBuilder WithAction( Action invoke )       { _w.Invoke = invoke; return this; }
	public MenuLinkBuilder Grows()                           { _w.Grow = true; return this; }

	public MenuScreenBuilder EndLink()
	{
		_screen.AddWidget( _w );
		return _screen;
	}
}

public sealed class MenuToggleBuilder
{
	readonly MenuScreenBuilder _screen;
	readonly MenuToggleWidget _w;

	internal MenuToggleBuilder( MenuScreenBuilder screen, string label )
	{
		_screen = screen;
		_w = new MenuToggleWidget { Label = label };
	}

	public MenuToggleBuilder WithHint( string hint )        { _w.Hint = hint; return this; }
	public MenuToggleBuilder WithGetter( Func<bool> get )   { _w.Get = get; return this; }
	public MenuToggleBuilder WithSetter( Action<bool> set ) { _w.Set = set; return this; }

	public MenuToggleBuilder Bind( Func<bool> get, Action<bool> set )
	{
		_w.Get = get;
		_w.Set = set;
		return this;
	}

	public MenuScreenBuilder EndToggle()
	{
		_screen.AddWidget( _w );
		return _screen;
	}
}

public sealed class MenuRangeBuilder
{
	readonly MenuScreenBuilder _screen;
	readonly MenuRangeWidget _w;

	internal MenuRangeBuilder( MenuScreenBuilder screen, string label )
	{
		_screen = screen;
		_w = new MenuRangeWidget { Label = label, Min = 0f, Max = 1f, Step = 0.01f, Format = "0.00" };
	}

	public MenuRangeBuilder WithHint( string hint )         { _w.Hint = hint; return this; }
	public MenuRangeBuilder WithRange( float min, float max ) { _w.Min = min; _w.Max = max; return this; }
	public MenuRangeBuilder WithStep( float step )            { _w.Step = step; return this; }
	public MenuRangeBuilder WithFormat( string format )       { _w.Format = format; return this; }

	public MenuRangeBuilder Bind( Func<float> get, Action<float> set )
	{
		_w.Get = get;
		_w.Set = set;
		return this;
	}

	public MenuScreenBuilder EndRange()
	{
		_screen.AddWidget( _w );
		return _screen;
	}
}

public sealed class MenuSegmentedBuilder
{
	readonly MenuScreenBuilder _screen;
	readonly MenuSegmentedWidget _w;

	internal MenuSegmentedBuilder( MenuScreenBuilder screen, string label, string[] choices )
	{
		_screen = screen;
		_w = new MenuSegmentedWidget { Label = label, Choices = choices };
	}

	public MenuSegmentedBuilder WithHint( string hint ) { _w.Hint = hint; return this; }

	public MenuSegmentedBuilder Bind( Func<int> get, Action<int> set )
	{
		_w.Get = get;
		_w.Set = set;
		return this;
	}

	public MenuScreenBuilder EndSegmented()
	{
		_screen.AddWidget( _w );
		return _screen;
	}
}

public sealed class MenuRadioBuilder
{
	readonly MenuScreenBuilder _screen;
	readonly MenuRadioWidget _w;
	readonly List<MenuRadioWidget.Option> _options = new();

	internal MenuRadioBuilder( MenuScreenBuilder screen, string label )
	{
		_screen = screen;
		_w = new MenuRadioWidget { Label = label };
	}

	public MenuRadioBuilder WithHint( string hint ) { _w.Hint = hint; return this; }

	public MenuRadioBuilder WithOption( string label, string icon = null, string hint = null )
	{
		_options.Add( new MenuRadioWidget.Option { Label = label, Icon = icon, Hint = hint } );
		return this;
	}

	public MenuRadioBuilder Bind( Func<int> get, Action<int> set )
	{
		_w.Get = get;
		_w.Set = set;
		return this;
	}

	public MenuScreenBuilder EndRadio()
	{
		_w.Options = _options;
		_screen.AddWidget( _w );
		return _screen;
	}
}

public sealed class MenuInfoBuilder
{
	readonly MenuScreenBuilder _screen;
	readonly MenuInfoWidget _w;

	internal MenuInfoBuilder( MenuScreenBuilder screen, string label )
	{
		_screen = screen;
		_w = new MenuInfoWidget { Label = label };
	}

	public MenuInfoBuilder WithIcon( string icon )         { _w.Icon = icon; return this; }
	public MenuInfoBuilder WithGetter( Func<string> get )  { _w.Get = get; return this; }
	public MenuInfoBuilder WithValue( string value )       { _w.Get = () => value; return this; }

	public MenuScreenBuilder EndInfo()
	{
		_screen.AddWidget( _w );
		return _screen;
	}
}

public sealed class MenuTextFieldBuilder
{
	readonly MenuScreenBuilder _screen;
	readonly MenuTextFieldWidget _w;

	internal MenuTextFieldBuilder( MenuScreenBuilder screen, string label )
	{
		_screen = screen;
		_w = new MenuTextFieldWidget { Label = label };
	}

	public MenuTextFieldBuilder WithHint( string hint ) { _w.Hint = hint; return this; }

	public MenuTextFieldBuilder Bind( Func<string> get, Action<string> set )
	{
		_w.Get = get;
		_w.Set = set;
		return this;
	}

	public MenuScreenBuilder EndTextField()
	{
		_screen.AddWidget( _w );
		return _screen;
	}
}

// === Composite widgets ===============================================

public sealed class MenuHeroCardBuilder
{
	readonly MenuScreenBuilder _screen;
	readonly MenuHeroCardWidget _w = new();

	internal MenuHeroCardBuilder( MenuScreenBuilder screen ) { _screen = screen; }

	public MenuHeroCardBuilder WithTitle( string title )             { _w.Title = title; return this; }
	public MenuHeroCardBuilder WithSubtitle( string subtitle )       { _w.Subtitle = subtitle; return this; }
	public MenuHeroCardBuilder WithDescription( string description ) { _w.Description = description; return this; }
	public MenuHeroCardBuilder WithImage( string url )               { _w.Image = url; return this; }
	public MenuHeroCardBuilder WithIcon( string icon )               { _w.Icon = icon; return this; }

	public MenuScreenBuilder EndHeroCard()
	{
		_screen.AddWidget( _w );
		return _screen;
	}
}

public sealed class MenuMapCardBuilder
{
	readonly MenuScreenBuilder _screen;
	readonly MenuMapCardWidget _w = new();

	internal MenuMapCardBuilder( MenuScreenBuilder screen ) { _screen = screen; }

	public MenuMapCardBuilder WithTitle( string title )             { _w.Title = title; return this; }
	public MenuMapCardBuilder WithDescription( string description ) { _w.Description = description; return this; }
	public MenuMapCardBuilder WithImage( string url )               { _w.Image = url; return this; }

	public MenuMapCardBuilder WithChip( string label, string icon = null, MenuTone tone = MenuTone.Confirm )
	{
		_w.ChipLabel = label;
		_w.ChipIcon = icon;
		_w.ChipTone = tone;
		return this;
	}

	public MenuMapCardBuilder WithAction( string label, string icon, Action invoke, string routeId = null )
	{
		_w.ActionLabel = label;
		_w.ActionIcon = icon;
		_w.ActionInvoke = invoke;
		_w.ActionRouteId = routeId;
		return this;
	}

	public MenuScreenBuilder EndMapCard()
	{
		_screen.AddWidget( _w );
		return _screen;
	}
}

public sealed class MenuMapGridBuilder
{
	readonly MenuScreenBuilder _screen;
	readonly MenuMapGridWidget _w = new();
	readonly List<MenuMapGridWidget.Card> _cards = new();

	internal MenuMapGridBuilder( MenuScreenBuilder screen ) { _screen = screen; }

	public MenuMapGridBuilder WithActive( Func<int> get )  { _w.Active = get; return this; }
	public MenuMapGridBuilder OnPick( Action<int> set )    { _w.Pick = set; return this; }

	public MenuMapCardCardBuilder WithCard( string title )
		=> new( this, title );

	internal void AddCard( MenuMapGridWidget.Card card ) => _cards.Add( card );

	bool _closed;

	public MenuScreenBuilder EndMapGrid()
	{
		if ( _closed ) return _screen;
		_closed = true;
		_w.Cards = _cards;
		_screen.AddWidget( _w );
		return _screen;
	}
}

public sealed class MenuMapCardCardBuilder
{
	readonly MenuMapGridBuilder _grid;
	readonly MenuMapGridWidget.Card _c;

	internal MenuMapCardCardBuilder( MenuMapGridBuilder grid, string title )
	{
		_grid = grid;
		_c = new MenuMapGridWidget.Card { Title = title };
	}

	public MenuMapCardCardBuilder WithDescription( string text ) { _c.Description = text; return this; }
	public MenuMapCardCardBuilder WithImage( string url )        { _c.Image = url; return this; }
	public MenuMapCardCardBuilder WithBadge( string label, MenuTone tone = MenuTone.Confirm )
	{
		_c.BadgeLabel = label;
		_c.BadgeTone = tone;
		return this;
	}

	public MenuMapCardCardBuilder ComingSoon( bool v = true ) { _c.ComingSoon = v; return this; }
	public MenuMapCardCardBuilder Locked( bool v = true )     { _c.Locked = v; return this; }

	public MenuMapGridBuilder EndCard()
	{
		_grid.AddCard( _c );
		return _grid;
	}
}

public sealed class MenuTileGridBuilder
{
	readonly MenuScreenBuilder _screen;
	readonly MenuTileGridWidget _w = new();
	readonly List<MenuTileGridWidget.Item> _items = new();

	internal MenuTileGridBuilder( MenuScreenBuilder screen ) { _screen = screen; }

	public MenuTileGridBuilder WithTile( string label, string color = null, string image = null )
	{
		_items.Add( new MenuTileGridWidget.Item { Label = label, Color = color, Image = image } );
		return this;
	}

	public MenuTileGridBuilder WithActive( Func<int> get ) { _w.Active = get; return this; }
	public MenuTileGridBuilder OnPick( Action<int> set )   { _w.Pick = set; return this; }

	public MenuScreenBuilder EndTileGrid()
	{
		_w.Items = _items;
		_screen.AddWidget( _w );
		return _screen;
	}
}

public sealed class MenuPlayerListBuilder
{
	readonly MenuScreenBuilder _screen;
	readonly MenuPlayerListWidget _w = new();
	readonly List<MenuPlayerListWidget.Player> _players = new();

	internal MenuPlayerListBuilder( MenuScreenBuilder screen ) { _screen = screen; }

	public MenuPlayerListBuilder WithPlayer( string name, bool ready = false, bool host = false )
	{
		_players.Add( new MenuPlayerListWidget.Player { Name = name, Ready = ready, Host = host } );
		return this;
	}

	public MenuScreenBuilder EndPlayerList()
	{
		_w.Players = _players;
		_screen.AddWidget( _w );
		return _screen;
	}
}

public sealed class MenuKeyBindingsBuilder
{
	readonly MenuScreenBuilder _screen;
	readonly MenuKeyBindingsWidget _w = new();
	readonly List<MenuKeyBindingsWidget.Binding> _bindings = new();

	internal MenuKeyBindingsBuilder( MenuScreenBuilder screen ) { _screen = screen; }

	public MenuKeyBindingsBuilder WithBinding( string action, params string[] keys )
	{
		_bindings.Add( new MenuKeyBindingsWidget.Binding { Action = action, Keys = keys } );
		return this;
	}

	public MenuScreenBuilder EndKeyBindings()
	{
		_w.Bindings = _bindings;
		_screen.AddWidget( _w );
		return _screen;
	}
}

public sealed class MenuPromptCardBuilder
{
	readonly MenuScreenBuilder _screen;
	readonly MenuPromptCardWidget _w = new() { Verb = "Press" };

	internal MenuPromptCardBuilder( MenuScreenBuilder screen ) { _screen = screen; }

	public MenuPromptCardBuilder WithVerb( string verb )      { _w.Verb = verb; return this; }
	public MenuPromptCardBuilder WithPrompt( string prompt )  { _w.Prompt = prompt; return this; }
	public MenuPromptCardBuilder WithGlyph( string ligature ) { _w.Glyph = ligature; return this; }
	public MenuPromptCardBuilder WithCost( int cost )         { _w.Cost = cost; return this; }
	public MenuPromptCardBuilder WhenDisabled( Func<bool> d ) { _w.Disabled = d; return this; }
	public MenuPromptCardBuilder WithHoldProgress( Func<float> p ) { _w.Progress = p; return this; }

	public MenuScreenBuilder EndPromptCard()
	{
		_screen.AddWidget( _w );
		return _screen;
	}
}

public sealed class MenuActionRowBuilder
{
	readonly MenuScreenBuilder _screen;
	readonly MenuActionRowWidget _w = new();
	readonly List<MenuLinkWidget> _links = new();

	internal MenuActionRowBuilder( MenuScreenBuilder screen ) { _screen = screen; }

	public MenuActionRowLinkBuilder WithLink( string label, string icon = null )
		=> new( this, label, icon );

	internal void AddLink( MenuLinkWidget link ) => _links.Add( link );

	public MenuScreenBuilder EndActionRow()
	{
		_w.Links = _links;
		_screen.AddWidget( _w );
		return _screen;
	}
}

public sealed class MenuActionRowLinkBuilder
{
	readonly MenuActionRowBuilder _row;
	readonly MenuLinkWidget _w;

	internal MenuActionRowLinkBuilder( MenuActionRowBuilder row, string label, string icon )
	{
		_row = row;
		_w = new MenuLinkWidget { Label = label, Icon = icon, Style = MenuLinkStyle.Button };
	}

	public MenuActionRowLinkBuilder WithTone( MenuTone tone )         { _w.Tone = tone; return this; }
	public MenuActionRowLinkBuilder WithGoTo( string routeId )        { _w.RouteId = routeId; return this; }
	public MenuActionRowLinkBuilder WithAction( Action invoke )       { _w.Invoke = invoke; return this; }
	public MenuActionRowLinkBuilder WhenEnabled( Func<bool> enabled ) { _w.Enabled = enabled; return this; }
	public MenuActionRowLinkBuilder Grows()                           { _w.Grow = true; return this; }

	public MenuActionRowBuilder EndLink()
	{
		_row.AddLink( _w );
		return _row;
	}
}