Editor/UI/ArchExportDialog.cs
using System.Globalization;
namespace Sunless.Architecture;
// The one question an export cannot answer for itself. A Source unit is an inch, so a run written straight out
// lands twenty-four METRES wide in anything scaled to metres - and the author is the only one who knows which
// package it is going into. The answer is remembered, so it is asked once and confirmed thereafter.
public sealed class ArchExportDialog : Dialog {
readonly Action<ArchExportSettings> export;
readonly string subject;
readonly Widget body;
ArchExportSettings settings = ArchExportSettings.Remembered();
public ArchExportDialog( Widget parent, string subject, Action<ArchExportSettings> export ) : base( parent ) {
this.export = export;
this.subject = subject;
Window.Title = $"Export {subject}";
Window.SetWindowIcon( "download" );
Window.SetModal( true, true );
Window.Size = new Vector2( 380, 340 );
Layout = Layout.Column();
Layout.Margin = 12;
Layout.Spacing = 8;
// The picks rebuild the body, and a control cannot clear the layout it is being clicked in - so the part
// that changes lives one widget down and the footer never moves.
body = new Widget( this ) { Layout = Layout.Column() };
body.Layout.Spacing = 6;
Layout.Add( body, 1 );
var footer = Layout.AddRow();
footer.Spacing = 4;
footer.AddStretchCell();
footer.Add( new Button( "Cancel", "close" ) { Clicked = Close } );
footer.Add( new Button.Primary( "Export", "download" ) { Clicked = Keep } );
Build();
}
void Build() {
body.Layout.Clear( true );
body.Layout.Add( ArchPartUi.Wrapped( $"Writing {subject} to Assets/{ArchExport.Directory}. Everything the "
+ "preview stages is written, one object per generated piece." ) );
body.Layout.Add( Heading( "Units" ) );
using ( var grid = ArchIconGrid.In( body.Layout ) ) {
Unit( grid, ArchExportUnits.SourceUnits, "Source units — inches, as the plan holds it, needing no import scale", "grid_4x4" );
Unit( grid, ArchExportUnits.Metres, "Metres — a 600mm module comes in at 0.6", "straighten" );
Unit( grid, ArchExportUnits.Centimetres, "Centimetres — a 600mm module comes in at 60", "square_foot" );
}
body.Layout.Add( Heading( "Up axis" ) );
using ( var grid = ArchIconGrid.In( body.Layout ) ) {
Up( grid, true, "Y up — what most OBJ importers expect", "swap_vert" );
Up( grid, false, "Z up — Source's own, for an importer told to keep it", "vertical_align_top" );
}
body.Layout.Add( ArchPartUi.Wrapped( Reads() ) );
body.Layout.AddStretchCell();
}
// Stated as a real-world length rather than as a factor, because the mistake this dialog exists to stop is a
// scale one and a number with a unit on it is the only form of it anybody checks.
string Reads() {
var metre = ArchExportSettings.UnitsPerMetre * settings.Scale;
return $"One metre writes out as {metre.ToString( "0.###", CultureInfo.InvariantCulture )} "
+ $"{ArchExportSettings.Named( settings.Units )}.";
}
void Unit( ArchIconGrid grid, ArchExportUnits units, string tooltip, string glyph ) {
grid.Pick( tooltip, $"arch_export_{units}".ToLowerInvariant(), glyph, settings.Units == units, () => {
settings = settings with { Units = units };
Build();
} );
}
void Up( ArchIconGrid grid, bool y, string tooltip, string glyph ) {
grid.Pick( tooltip, $"arch_export_up_{(y ? "y" : "z")}", glyph, settings.YUp == y, () => {
settings = settings with { YUp = y };
Build();
} );
}
static Widget Heading( string text ) {
var label = new Label( text );
label.SetStyles( "font-size: 13px; font-weight: 700; margin-top: 8px;" );
return label;
}
void Keep() {
settings.Remember();
export?.Invoke( settings );
Close();
}
}