Editor UI helper for wall modifiers. It builds sidebar controls to choose modifier kind, which face/side it sits on, size parameters, and material role icons for wall parts.
using System;
using Editor;
using Sandbox;
namespace Sunless.Architecture;
// Read by the Wall detail subtool and by Select, off the same part - a draft and a placed modifier
// carry identical controls because they are the same record.
public static class ArchWallModUi
{
// What a block of masonry standing on or cut out of an elevation is worth being made of.
static readonly ArchSurface[] Fields =
{
ArchSurface.Pillar,
ArchSurface.WallBase,
ArchSurface.WallExterior,
ArchSurface.Foundation,
ArchSurface.Panel,
ArchSurface.Trim,
ArchSurface.Sign
};
public static void Kinds( ToolSidebarWidget panel, ArchWallMod chosen, Action<ArchWallMod> adopt )
{
using var grid = ArchIconGrid.In( panel.AddGroup( "Detail" ) );
foreach ( var value in Enum.GetValues<ArchWallMod>() )
{
var kind = value;
grid.Pick( kind.Label(), $"wallmod_{kind}".ToLowerInvariant(), kind.Glyph(), chosen == kind, () => adopt( kind ) );
}
}
// On surface is a placement mode - it asks the drag which face it landed on - so it is only ever
// offered on a draft. A part already standing on a wall has an answer.
public static void Sides( ToolSidebarWidget panel, ArchWallSide chosen, bool drafting, Action<ArchWallSide> adopt )
{
using var grid = ArchIconGrid.In( panel.AddGroup( "Face" ) );
foreach ( var value in Enum.GetValues<ArchWallSide>() )
{
var side = value;
if ( side == ArchWallSide.OnSurface && !drafting )
{
continue;
}
grid.Pick( side.Label(), $"wallside_{side}".ToLowerInvariant(), side.Glyph(), chosen == side, () => adopt( side ) );
}
}
public static void Build( ToolSidebarWidget panel, ArchWallModPart modifier, Action refresh, Action changed )
{
Kinds( panel, modifier.Kind, kind =>
{
Rebanded( modifier, kind );
modifier.Kind = kind;
modifier.Field = kind.Field();
changed?.Invoke();
refresh?.Invoke();
} );
Sides( panel, modifier.Side, false, side =>
{
modifier.Side = side;
changed?.Invoke();
refresh?.Invoke();
} );
Sizes( panel, modifier, changed );
Fill( panel, modifier, changed );
}
// A plate carries a band of its own where a pier climbs to the wall head, so a kind switch switches that
// too: a plate's own two feet left on a pier is a stub where a whole elevation was asked for.
static void Rebanded( ArchWallModPart modifier, ArchWallMod kind )
{
if ( kind.Plated() )
{
modifier.Height = kind.Rise( modifier.Height );
return;
}
if ( modifier.Kind.Plated() )
{
modifier.Base = 0f;
modifier.Height = 0f;
}
}
// Station first: it is the contract that keeps the modifier on the wall when the wall moves. A
// draft has none to show - the drag is what names it.
public static void Sizes( ToolSidebarWidget panel, ArchWallModPart modifier, Action changed, bool station = true )
{
var group = panel.AddGroup( "Block" );
if ( station )
{
group.Add( ArchPartUi.Number( "Along the wall", modifier.Along, 0f, value => modifier.Along = value, changed ) );
}
group.Add( ArchPartUi.Number( station ? "Width" : "Width when clicked", modifier.Width, 16f, value => modifier.Width = value, changed ) );
group.Add( ArchPartUi.Number( "Starts above the foot", modifier.Base, 0f, value => modifier.Base = value, changed ) );
// A draft's height belongs to its own Climb group, which says it in storeys.
if ( station )
{
group.Add( ArchPartUi.Number( "Height", modifier.Height, 0f, value => modifier.Height = value, changed ) );
}
group.Add( ArchPartUi.Number(
modifier.Carves ? "Bites in" : "Stands proud", modifier.Depth, 3f, value => modifier.Depth = value, changed ) );
}
public static void Fill( ToolSidebarWidget panel, ArchWallModPart modifier, Action changed )
{
using var grid = ArchIconGrid.In( panel.AddGroup( "Material role" ) );
foreach ( var value in Fields )
{
var field = value;
grid.Pick( $"Skinned as {field}", $"surface_{field}".ToLowerInvariant(), Glyph( field ),
modifier.Field == field, () => { modifier.Field = field; changed?.Invoke(); } );
}
}
static string Glyph( ArchSurface field ) => field switch
{
ArchSurface.Foundation => "foundation",
ArchSurface.WallBase => "grid_4x4",
ArchSurface.Panel => "crop_square",
ArchSurface.Pillar => "view_column",
ArchSurface.Trim => "horizontal_rule",
ArchSurface.Sign => "label",
_ => "villa"
};
}