An editor UI panel for the SuperShot tool, named Home. It builds the capture/home view with cards and controls to quick-capture, open gallery/editor, show recent shots and output folder, and presents small custom widgets for summary pills, workspace cards and recent shot buttons.
using System;
using Sandbox;
namespace Editor.SuperShot;
public sealed class CapturePanel : Widget
{
readonly SuperShotWindow _window;
public CapturePanel( SuperShotWindow window ) : base( null )
{
_window = window;
Name = "Home";
WindowTitle = "Home";
SetWindowIcon( "home" );
Layout = Layout.Column();
Layout.Margin = 8;
Layout.Spacing = 8;
Build();
}
void Build()
{
var capture = _window.Settings.Capture;
SuperShotUI.AddBanner( Layout, "Home", "Capture, edit, filter, share.", "home" );
var scroll = new ScrollArea( this );
scroll.Canvas = new Widget( scroll );
scroll.Canvas.Layout = Layout.Column();
scroll.Canvas.Layout.Spacing = 10;
var body = scroll.Canvas.Layout;
var so = capture.GetSerialized();
so.OnPropertyChanged += _ =>
{
_window.Settings.Save();
_window.NotifyChanged();
};
AddHeroCapture( body );
AddWorkspaceCards( body );
var quickRow = body.AddRow();
quickRow.Spacing = 10;
AddQuickCaptureCard( quickRow );
AddPackageCard( quickRow );
var bottomRow = body.AddRow();
bottomRow.Spacing = 10;
AddRecentShotsCard( bottomRow );
AddOutputCard( bottomRow );
body.AddStretchCell();
Layout.Add( scroll, 1 );
}
static bool IsEssential( SerializedProperty p )
{
return p.Name is nameof( CaptureSettings.Source )
or nameof( CaptureSettings.Resolution )
or nameof( CaptureSettings.CustomWidth )
or nameof( CaptureSettings.CustomHeight );
}
void AddHeroCapture( Layout body )
{
var card = SuperShotUI.AddCard( body, "Current View", "photo_camera" );
card.Body.Add( new Label( "Shoot the editor camera using your current resolution and default filter." ) { WordWrap = true } );
var summary = card.Body.AddRow();
summary.Spacing = 8;
AddSummaryPill( summary, "Resolution", ResolutionLabel( _window.Settings.Capture ) );
AddSummaryPill( summary, "Source", SourceLabel( _window.Settings.Capture.Source ) );
AddSummaryPill( summary, "Default Filter", DefaultFilterLabel() );
AddSummaryPill( summary, "Game UI", _window.Settings.Capture.ShowGameUI ? "Shown" : "Hidden" );
AddSummaryPill( summary, "Format", _window.Settings.Output.Format.ToString().ToUpper() );
var actions = card.Body.AddRow();
actions.Spacing = 6;
actions.Add( new Button.Primary( "Capture Current View", "photo_camera" )
{
FixedHeight = Theme.RowHeight * 2,
Clicked = () => _window.Capture()
}, 1 );
actions.Add( new Button( "Capture + Open Editor", "edit" )
{
FixedHeight = Theme.RowHeight * 2,
Clicked = CaptureAndOpenEditor
} );
actions.Add( new Button( "Output Folder", "folder_open" )
{
FixedHeight = Theme.RowHeight * 2,
Clicked = () => SuperShotService.RevealInExplorer( _window.Settings.Output.ResolveFolder() )
} );
}
void AddWorkspaceCards( Layout body )
{
var row = body.AddRow();
row.Spacing = 10;
row.Add( new WorkspaceCard( "Editor", "tune", "Layer, text and compose captured shots.", () => _window.DockManager.RaiseDock( "Edit" ) ), 1 );
row.Add( new WorkspaceCard( "Gallery", "collections", "Browse, reopen and manage saved shots.", () => _window.DockManager.RaiseDock( "Gallery" ) ), 1 );
row.Add( new WorkspaceCard( "Settings", "settings", "Output folder, format and naming options.", () => _window.DockManager.RaiseDock( "Settings" ) ), 1 );
}
void AddQuickCaptureCard( Layout parent )
{
var quick = new Card( "Quick Capture", "bolt" );
parent.Add( quick, 1 );
AddPresetRow( quick.Body, clean: false,
("720p", ShotResolution.HD720),
("1080p", ShotResolution.HD1080),
("1440p", ShotResolution.QHD1440) );
AddPresetRow( quick.Body, clean: false,
("4K", ShotResolution.UHD4K),
("8K", ShotResolution.UHD8K),
("Square", ShotResolution.Square1080) );
}
void AddPackageCard( Layout parent )
{
var pkg = new Card( "Package Thumbnails", "inventory_2" );
parent.Add( pkg, 1 );
pkg.Body.Add( new Label( "Clean captures sized for s&box package artwork." ) { WordWrap = true } );
AddPresetRow( pkg.Body, clean: true,
("Square 512", ShotResolution.PackageSquare),
("Wide 910", ShotResolution.PackageWide),
("Tall 512", ShotResolution.PackageTall) );
pkg.Body.Add( new Button( "Capture All Three", "burst_mode" ) { Clicked = () => SuperShotService.CaptureAllPackageThumbnails() } );
}
void AddRecentShotsCard( Layout parent )
{
var recent = new Card( "Recent Shots", "history" );
parent.Add( recent, 1 );
if ( _window.Gallery.Count == 0 )
{
recent.Body.Add( new Label( "No shots yet. Capture something and your recent work will appear here." ) { WordWrap = true } );
return;
}
var row = recent.Body.AddRow();
row.Spacing = 6;
int shown = 0;
for ( int i = _window.Gallery.Count - 1; i >= 0 && shown < 5; i--, shown++ )
{
row.Add( new RecentShotButton( _window, _window.Gallery[i] ) );
}
row.AddStretchCell();
recent.Body.Add( new Button( "Open Gallery", "collections" ) { Clicked = () => _window.DockManager.RaiseDock( "Gallery" ) } );
}
void AddOutputCard( Layout parent )
{
var output = new Card( "Output", "folder" );
parent.Add( output, 1 );
output.Body.Add( new Label( $"Folder: {_window.Settings.Output.ResolveFolder()}" ) { WordWrap = true } );
output.Body.Add( new Label( $"Format: {_window.Settings.Output.Format.ToString().ToUpper()}" ) );
var actions = output.Body.AddRow();
actions.Spacing = 6;
actions.Add( new Button( "Open Folder", "folder_open" )
{
Clicked = () => SuperShotService.RevealInExplorer( _window.Settings.Output.ResolveFolder() )
} );
actions.Add( new Button( "Settings", "settings" )
{
Clicked = () => _window.DockManager.RaiseDock( "Settings" )
} );
}
void AddSummaryPill( Layout parent, string label, string value )
{
parent.Add( new SummaryPill( label, value ), 1 );
}
string DefaultFilterLabel()
{
_window.Settings.EnsureBuiltinFilterPresets();
var id = _window.Settings.Capture.DefaultFilterPresetId;
return string.IsNullOrEmpty( id )
? "None"
: _window.Settings.FindFilterPreset( id )?.Name ?? "None";
}
static string ResolutionLabel( CaptureSettings capture )
{
var (w, h) = SuperShotContext.ResolveSize( capture );
return $"{w}x{h}";
}
static string SourceLabel( CaptureSource source )
{
return source == CaptureSource.SceneMainCamera ? "Scene Camera" : "Editor Freecam";
}
void CaptureAndOpenEditor()
{
_window.CaptureNow();
if ( _window.HasCapture )
_window.DockManager.RaiseDock( "Edit" );
}
void AddPresetRow( Layout parent, bool clean, params (string label, ShotResolution res)[] presets )
{
var row = parent.AddRow();
row.Spacing = 4;
foreach ( var (label, res) in presets )
{
var resolution = res;
row.Add( new Button( label )
{
Clicked = clean
? () => SuperShotService.QuickCapture( resolution )
: () => _window.QuickCapture( resolution )
} );
}
}
sealed class SummaryPill : Widget
{
readonly string _label;
readonly string _value;
public SummaryPill( string label, string value ) : base( null )
{
_label = label;
_value = value;
MinimumSize = new Vector2( 120, 46 );
}
protected override void OnPaint()
{
Paint.ClearPen();
Paint.SetBrush( Theme.ControlBackground.Lighten( 0.08f ) );
Paint.DrawRect( LocalRect, 5f );
Paint.SetPen( SuperShotUI.Muted );
Paint.SetDefaultFont( 7, 500 );
Paint.DrawText( LocalRect.Shrink( 10, 6 ), _label, TextFlag.LeftTop );
Paint.SetPen( Theme.Text );
Paint.SetDefaultFont( 10, 600 );
Paint.DrawText( new Rect( 10, 21, Width - 20, 18 ), _value, TextFlag.LeftTop );
}
}
sealed class WorkspaceCard : Widget
{
readonly string _title;
readonly string _icon;
readonly string _description;
readonly Action _clicked;
public WorkspaceCard( string title, string icon, string description, Action clicked ) : base( null )
{
_title = title;
_icon = icon;
_description = description;
_clicked = clicked;
Cursor = CursorShape.Finger;
MinimumSize = new Vector2( 180, 100 );
}
protected override void OnMouseClick( MouseEvent e )
{
base.OnMouseClick( e );
if ( e.LeftMouseButton )
_clicked?.Invoke();
}
protected override void OnPaint()
{
Paint.ClearPen();
Paint.SetBrush( SuperShotUI.CardBackground );
Paint.DrawRect( LocalRect, 6f );
Paint.ClearBrush();
Paint.SetPen( SuperShotUI.CardBorder, 1f );
Paint.DrawRect( LocalRect.Shrink( 0.5f ), 6f );
Paint.SetPen( SuperShotUI.Accent );
Paint.DrawIcon( new Rect( 14, 14, 28, 28 ), _icon, 24, TextFlag.Center );
Paint.SetPen( Theme.Text );
Paint.SetDefaultFont( 11, 700 );
Paint.DrawText( new Rect( 52, 14, Width - 66, 22 ), _title, TextFlag.LeftTop );
Paint.SetPen( SuperShotUI.Muted );
Paint.SetDefaultFont( 8, 400 );
Paint.DrawText( new Rect( 14, 48, Width - 28, Height - 58 ), _description, TextFlag.LeftTop | TextFlag.WordWrap );
}
}
sealed class RecentShotButton : Widget
{
readonly SuperShotWindow _window;
readonly CapturedShot _shot;
public RecentShotButton( SuperShotWindow window, CapturedShot shot ) : base( null )
{
_window = window;
_shot = shot;
Cursor = CursorShape.Finger;
FixedSize = new Vector2( 120, 76 );
}
protected override void OnMouseClick( MouseEvent e )
{
base.OnMouseClick( e );
if ( e.LeftMouseButton )
_window.OpenInEditor( _shot );
}
protected override void OnPaint()
{
Paint.ClearPen();
Paint.SetBrush( Color.Black );
Paint.DrawRect( LocalRect, 4f );
if ( _shot.Thumbnail is not null )
{
var rect = FitRect( _shot.Thumbnail.Size, LocalRect.Shrink( 4 ).Size );
rect.Left += 4;
rect.Top += 4;
Paint.Draw( rect, _shot.Thumbnail );
}
Paint.ClearBrush();
Paint.SetPen( SuperShotUI.CardBorder, 1f );
Paint.DrawRect( LocalRect.Shrink( 0.5f ), 4f );
}
static Rect FitRect( Vector2 content, Vector2 area )
{
float scale = Math.Min( area.x / content.x, area.y / content.y );
var size = content * scale;
return new Rect( (area.x - size.x) / 2f, (area.y - size.y) / 2f, size.x, size.y );
}
}
}