Editor UI panel for photo editing inside the SuperShot window. It builds controls for filters, color/detail/effect/transform/frame/watermark sections, handles applying presets, undo push timing, and save button visibility.
using System;
using System.Collections.Generic;
using Sandbox;
namespace Editor.SuperShot;
public sealed class EditPanel : Widget
{
readonly SuperShotWindow _window;
RealTimeSince _sinceUndoPush = 100f;
static readonly HashSet<string> ColorProps = new()
{
nameof( EditSettings.Brightness ), nameof( EditSettings.Contrast ), nameof( EditSettings.Saturation ),
nameof( EditSettings.Exposure ), nameof( EditSettings.Hue ), nameof( EditSettings.Filter )
};
static readonly HashSet<string> DetailProps = new() { nameof( EditSettings.Sharpen ), nameof( EditSettings.Blur ) };
static readonly HashSet<string> EffectProps = new() { nameof( EditSettings.Vignette ), nameof( EditSettings.Grain ) };
static readonly HashSet<string> TransformProps = new() { nameof( EditSettings.Rotate ), nameof( EditSettings.FlipH ), nameof( EditSettings.FlipV ) };
static readonly HashSet<string> FrameProps = new() { nameof( EditSettings.Border ), nameof( EditSettings.BorderColor ) };
static readonly HashSet<string> WatermarkProps = new()
{
nameof( EditSettings.Watermark ), nameof( EditSettings.WatermarkText ), nameof( EditSettings.WatermarkAnchor ),
nameof( EditSettings.WatermarkSize ), nameof( EditSettings.WatermarkColor )
};
Button _saveButton;
public EditPanel( SuperShotWindow window ) : base( null )
{
_window = window;
Name = "Edit";
WindowTitle = "Edit";
SetWindowIcon( "tune" );
Layout = Layout.Column();
Layout.Margin = 8;
Layout.Spacing = 8;
Build();
_window.Changed += OnWindowChanged;
}
void OnWindowChanged() => UpdateSaveVisibility();
void UpdateSaveVisibility()
{
if ( _saveButton.IsValid() )
_saveButton.Visible = _window.HasCapture;
}
public override void OnDestroyed()
{
base.OnDestroyed();
_window.Changed -= OnWindowChanged;
}
protected override void OnVisibilityChanged( bool visible )
{
base.OnVisibilityChanged( visible );
if ( !visible )
_window.LeaveEditReview();
}
void Rebuild()
{
Layout.Clear( true );
Build();
}
void Build()
{
SuperShotUI.AddBanner( Layout, "Edit", "Non-destructive photo adjustments.", "tune" );
var actions = Layout.AddRow();
actions.Spacing = 4;
actions.Add( new Button( "Reset", "restart_alt" ) { Clicked = _window.ResetEdit } );
actions.Add( new Button( "Undo", "undo" ) { Clicked = _window.Undo } );
actions.Add( new Button( "Redo", "redo" ) { Clicked = _window.Redo } );
actions.AddStretchCell();
_saveButton = new Button.Primary( "Save", "save" ) { Clicked = () => _window.SaveCurrent() };
actions.Add( _saveButton );
var scroll = new ScrollArea( this );
scroll.Canvas = new Widget( scroll );
scroll.Canvas.Layout = Layout.Column();
scroll.Canvas.Layout.Spacing = 8;
var body = scroll.Canvas.Layout;
var filterCard = SuperShotUI.AddCard( body, "Filters", "filter_vintage" );
var filters = filterCard.Body.AddRow();
filters.Spacing = 4;
AddFilter( filters, "Warm", ShotFilter.Warm );
AddFilter( filters, "Cool", ShotFilter.Cool );
AddFilter( filters, "Vintage", ShotFilter.Vintage );
AddFilter( filters, "Cinema", ShotFilter.Cinematic );
AddFilter( filters, "B&W", ShotFilter.BlackAndWhite );
filterCard.Body.Add( new Button( "Clear Filter", "clear" ) { Clicked = () => ApplyFilter( ShotFilter.None ) } );
var so = _window.Settings.Edit.GetSerialized();
so.OnPropertyChanged += _ =>
{
if ( _sinceUndoPush > 0.75f )
{
_window.PushEditUndo();
_sinceUndoPush = 0f;
}
_window.Settings.Save();
_window.NotifyChanged();
};
var color = SuperShotUI.AddCard( body, "Color", "palette" );
color.Body.Add( SuperShotUI.SheetWidget( so, p => ColorProps.Contains( p.Name ) ) );
SuperShotUI.AddSection( body, "Detail", "deblur", SuperShotUI.SheetWidget( so, p => DetailProps.Contains( p.Name ) ), "supershot.edit.detail" );
SuperShotUI.AddSection( body, "Effects", "auto_awesome", SuperShotUI.SheetWidget( so, p => EffectProps.Contains( p.Name ) ), "supershot.edit.effects" );
SuperShotUI.AddSection( body, "Transform", "crop_rotate", SuperShotUI.SheetWidget( so, p => TransformProps.Contains( p.Name ) ), "supershot.edit.transform" );
SuperShotUI.AddSection( body, "Frame", "crop_din", SuperShotUI.SheetWidget( so, p => FrameProps.Contains( p.Name ) ), "supershot.edit.frame" );
SuperShotUI.AddSection( body, "Watermark", "branding_watermark", SuperShotUI.SheetWidget( so, p => WatermarkProps.Contains( p.Name ) ), "supershot.edit.watermark" );
body.AddStretchCell();
Layout.Add( scroll, 1 );
UpdateSaveVisibility();
}
void AddFilter( Layout row, string label, ShotFilter filter )
{
row.Add( new Button( label ) { Clicked = () => ApplyFilter( filter ) } );
}
void ApplyFilter( ShotFilter filter )
{
_window.PushEditUndo();
var e = _window.Settings.Edit;
e.Brightness = 1f; e.Contrast = 1f; e.Saturation = 1f; e.Exposure = 0f; e.Hue = 0f;
e.Vignette = 0f; e.Grain = 0f;
e.Filter = filter;
switch ( filter )
{
case ShotFilter.Warm:
e.Exposure = 0.05f; e.Contrast = 1.05f; e.Saturation = 1.10f;
break;
case ShotFilter.Cool:
e.Contrast = 1.05f; e.Saturation = 1.05f;
break;
case ShotFilter.Vintage:
e.Contrast = 0.92f; e.Saturation = 0.85f; e.Vignette = 0.35f; e.Grain = 0.15f;
break;
case ShotFilter.Cinematic:
e.Contrast = 1.15f; e.Saturation = 1.05f; e.Vignette = 0.25f;
break;
case ShotFilter.BlackAndWhite:
e.Contrast = 1.10f; e.Saturation = 0f;
break;
default:
break;
}
_window.Settings.Save();
Rebuild();
_window.NotifyChanged();
}
}