Editor/Wall/ArchWallModUi.cs
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 => {
Adopt( modifier, kind );
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 );
}
// Taking a kind is more than writing the field: the band and the reach come with it. Both the sheet and the
// bool tool's detail page go through here, or picking Awning in one of them gives a canopy and in the other
// a three-inch shelf.
public static void Adopt( ArchWallModPart modifier, ArchWallMod kind ) {
Rebanded( modifier, kind );
modifier.Kind = kind;
modifier.Field = kind.Field();
}
// 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 ) {
// Through the same helper the resolve reads, or the sheet says three inches while the canopy builds
// thirty-four.
modifier.Depth = kind.Reach( modifier.Depth );
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"
};
}