Editor static catalog for the Advanced Mapping Tools panel. Defines the ToolCategoryAttribute and a ToolCatalog that lists BuilderEditor types from the editor type library, groups them into categories with icons, and stores favorites and recently used builders in editor cookies. Activate writes the chosen builder's type name to a cookie and restarts the primitive tool so it picks up the new mode.
namespace AdvancedMappingTools;
/// <summary>
/// Which studio of the tool panel a builder belongs to. Studios without tools do not exist.
/// </summary>
[AttributeUsage( AttributeTargets.Class )]
public sealed class ToolCategoryAttribute( string name ) : Attribute
{
public string Name { get; } = name;
}
/// <summary>
/// All builders with their categories, favorites and recent use. New builders need only
/// their <see cref="ToolCategoryAttribute"/> to show up.
/// </summary>
public static class ToolCatalog
{
const string EditorTypeCookie = "PrimitiveTool.EditorType";
const string FavoritesCookie = "AMT.Favorites";
const string RecentCookie = "AMT.Recent";
const int RecentCount = 5;
public static readonly (string Name, string Icon)[] Groups =
[
("Buildings", "apartment"),
("Walls & Floors", "view_week"),
("Doors & Windows", "sensor_door"),
("Roofs", "roofing"),
("Stairs & Ramps", "stairs"),
("Arches & Vaults", "architecture"),
("Tunnels & Bridges", "subway"),
("Roads & Paths", "add_road"),
("Cutters", "content_cut"),
("Editing", "hub"),
("Columns & Towers", "account_balance"),
("Street", "signpost"),
("Industrial", "plumbing"),
("Defense", "shield"),
("Interior", "chair"),
("Props", "inventory_2"),
("Scatter", "scatter_plot"),
("Terrain", "landscape"),
("Navigation", "route"),
("Vegetation", "park"),
("World", "public")
];
public static string[] Categories { get; } = [.. Groups.Select( x => x.Name )];
public static IEnumerable<TypeDescription> Tools =>
EditorTypeLibrary.GetTypes<BuilderEditor>().Where( x => !x.IsAbstract ).OrderBy( x => x.Title );
public static string CategoryOf( TypeDescription type ) =>
type.GetAttribute<ToolCategoryAttribute>()?.Name ?? "Other";
public static List<string> Favorites => EditorCookie.Get<List<string>>( FavoritesCookie, [] );
public static List<string> Recent => EditorCookie.Get<List<string>>( RecentCookie, [] );
public static bool IsFavorite( TypeDescription type ) => Favorites.Contains( type.FullName );
public static void ToggleFavorite( TypeDescription type )
{
var favorites = Favorites;
if ( !favorites.Remove( type.FullName ) )
favorites.Add( type.FullName );
EditorCookie.Set( FavoritesCookie, favorites );
}
/// <summary>
/// Open a builder in the primitive tool. The tool reads its mode from this cookie when
/// it starts, and choosing a mode directly is internal to the engine, so it is restarted.
/// </summary>
public static void Activate( TypeDescription type )
{
var recent = Recent;
recent.Remove( type.FullName );
recent.Insert( 0, type.FullName );
EditorCookie.Set( RecentCookie, recent.Take( RecentCount ).ToList() );
EditorCookie.SetString( EditorTypeCookie, type.FullName );
// The primitive tool picks its mode when it starts, and a tool change only happens
// on the next editor frame. So it has to leave first and come back a frame later.
EditorToolManager.SetTool( nameof( MeshTool ) );
EditorToolManager.SetSubTool( nameof( FaceTool ) );
_framesUntilReopen = 2;
}
static int _framesUntilReopen;
[EditorEvent.Frame]
public static void Frame()
{
if ( _framesUntilReopen == 0 || --_framesUntilReopen > 0 ) return;
EditorToolManager.SetSubTool( nameof( PrimitiveTool ) );
}
}