using Sandbox.Internal;
namespace Editor;
///
/// The tab widget that sits at the top of the ControlSheet if you add [Features]
///
public class FeatureTabWidget : Widget
{
List Tabs = new List();
Layout TopRowLayout;
Layout TabLayout;
Layout ContentLayout;
string _cookie;
public string StateCookie
{
get => _cookie;
set
{
if ( _cookie == value ) return;
_cookie = value;
Restore();
}
}
public FeatureTabWidget( Widget parent ) : base( parent )
{
VerticalSizeMode = SizeMode.CanGrow;
HorizontalSizeMode = SizeMode.Flexible;
Layout = Layout.Column();
Layout.Margin = new Sandbox.UI.Margin( 0, 4, 0, 0 );
TopRowLayout = Layout.AddRow();
TopRowLayout.AddSpacingCell( 8 );
TabLayout = TopRowLayout.AddRow();
{
IconButton add = new IconButton( "add", OpenAddFeaturesMenu );
add.Background = Color.Transparent;
add.Foreground = Color.White.WithAlpha( 0.6f );
add.ForegroundActive = Theme.Primary;
add.IconSize = 15;
TopRowLayout.Add( add );
}
TopRowLayout.AddStretchCell();
ContentLayout = Layout.AddColumn();
ContentLayout.Margin = new Sandbox.UI.Margin( 0 );
}
public void OpenAddFeaturesMenu()
{
var menu = new ContextMenu( this );
int iAdded = 0;
foreach ( var tab in Tabs )
{
if ( tab.FeatureEnabled is null ) continue;
var state = tab.FeatureEnabled.As.Bool;
if ( state ) continue;
string icon = null;
string title = tab.FeatureEnabled.DisplayName;
if ( tab.FeatureEnabled.TryGetAttribute( out var attribute ) )
{
icon = attribute.Icon;
title = attribute.Title;
}
menu.AddOption( new Option( title, icon ) { Triggered = tab.SetFeatureEnabled } );
iAdded++;
}
if ( iAdded == 0 )
{
menu.AddOption( new Option( "No features available" ) { Enabled = false } );
}
menu.OpenAtCursor();
}
void AddTab( FeatureTabOption tab )
{
Tabs.Add( tab );
TabLayout.Add( tab );
if ( Tabs.Count == 1 )
{
Select( tab );
}
}
public void Select( FeatureTabOption option )
{
foreach ( var tab in Tabs.Where( x => x.IsSelected ) )
{
tab.IsSelected = false;
tab.HidePage();
}
option.IsSelected = true;
option.ShowPage( ContentLayout );
Save();
}
bool TrySelectIndex( int index )
{
if ( index < 0 ) return false;
if ( index >= Tabs.Count() ) return false;
var tab = Tabs[index];
if ( !tab.Visible ) return false;
Select( tab );
return true;
}
public void OnVisibilityChanged( FeatureTabOption option )
{
if ( !option.IsSelected ) return;
var idx = Tabs.IndexOf( option );
if ( idx < 0 ) idx = 0;
for ( int i = 0; i < Tabs.Count; i++ )
{
if ( TrySelectIndex( idx + i ) )
return;
if ( TrySelectIndex( idx - i ) )
return;
}
}
private void Save()
{
if ( string.IsNullOrEmpty( StateCookie ) ) return;
var selected = Tabs.FirstOrDefault( x => x.IsSelected );
if ( selected is null )
{
EditorCookie.Remove( $"featuretab.{StateCookie}" );
return;
}
EditorCookie.Set( $"featuretab.{StateCookie}", selected.UniqueId );
}
private void Restore()
{
if ( string.IsNullOrEmpty( StateCookie ) ) return;
var pageName = EditorCookie.Get( $"featuretab.{StateCookie}", null );
if ( string.IsNullOrWhiteSpace( pageName ) ) return;
var target = Tabs.Where( x => x.UniqueId == pageName ).FirstOrDefault();
if ( target is null ) return;
Select( target );
}
protected override void OnPaint()
{
Paint.ClearPen();
Paint.SetBrush( InspectorHeader.BackgroundColor );
Paint.DrawRect( TopRowLayout.OuterRect.Grow( 100, 50, 100, 0 ) );
}
internal void AddFeature( IControlSheet.Feature feature )
{
string title = feature.Name;
string icon = feature.Icon;
string desc = feature.Description;
Color tint = tint = Theme.GetTint( feature.Tint );
if ( string.IsNullOrEmpty( title ) )
{
title = "General";
}
var tab = new FeatureTabOption( this, title, icon, desc, tint, this );
tab.Feature = feature;
if ( tab.FeatureEnabled is not null && !tab.FeatureEnabled.As.Bool )
{
tab.Visible = false;
}
AddTab( tab );
}
}
public class FeatureTabOption : Widget
{
public string Icon { get; set; }
public string Description { get; set; }
public bool ShowText { get; set; } = true;
public int Index { get; set; }
public bool IsSelected { get; set; }
public Color Tint { get; set; }
public IControlSheet.Feature Feature { get; set; }
public string UniqueId => Name;
///
/// If this feature has an option to enable and disable it, this is it.
///
public SerializedProperty FeatureEnabled => Feature.EnabledProperty;
Widget Page;
FeatureTabWidget Owner;
public FeatureTabOption( FeatureTabWidget owner, string name, string icon, string desc, Color tint, Widget parent ) : base( parent )
{
Owner = owner;
Icon = icon;
Name = name;
Tint = tint;
Description = desc;
Cursor = CursorShape.Finger;
ToolTip = GetTooltip();
VerticalSizeMode = SizeMode.CanGrow;
}
protected override Vector2 SizeHint()
{
var height = 24;
if ( !ShowText && !IsSelected )
{
return height;
}
Paint.SetDefaultFont( 8 );
var textSize = Paint.MeasureText( Name );
textSize.x += 16;
textSize.y += 8;
if ( !string.IsNullOrWhiteSpace( Icon ) )
{
textSize.x += 22;
}
textSize.y = height;
return textSize;
}
protected override void OnMouseClick( MouseEvent e )
{
base.OnMouseClick( e );
Owner?.Select( this );
}
protected override void OnMousePress( MouseEvent e )
{
base.OnMousePress( e );
if ( e.LeftMouseButton )
{
Owner?.Select( this );
}
}
public bool CanFeatureBeRemoved => FeatureEnabled is not null && FeatureEnabled.As.Bool;
public void RemoveFeature()
{
if ( FeatureEnabled is not null )
{
FeatureEnabled.As.Bool = false;
}
UpdateVisibility();
}
protected override void OnContextMenu( ContextMenuEvent e )
{
Owner?.Select( this );
e.Accepted = true;
var menu = new ContextMenu( this );
{
var o = new Option( $"Remove {Name} Feature", "close" );
o.Enabled = CanFeatureBeRemoved;
o.Triggered = RemoveFeature;
menu.AddOption( o );
}
{
menu.AddSeparator();
menu.AddOption( $"Copy {Name} Properties", "content_copy", () =>
{
ClipboardTools.CopyProperties( Name, Feature.Properties );
} );
var pasteOption = menu.AddOption( $"Paste {Name} Properties", "content_paste", () =>
{
ClipboardTools.PasteProperties( Name, Feature.Properties );
} );
pasteOption.Enabled = ClipboardTools.CanPasteProperties( Name, Feature.Properties );
menu.AddOption( "Reset all to Default", "restart_alt", () =>
{
foreach ( var prop in Feature.Properties )
{
prop.SetValue( prop.GetDefault() );
}
} );
}
if ( FeatureEnabled is not null && CodeEditor.CanOpenFile( FeatureEnabled.SourceFile ) )
{
menu.AddSeparator();
var filename = System.IO.Path.GetFileName( FeatureEnabled.SourceFile );
menu.AddOption( $"Jump to code", "code", action: () => CodeEditor.OpenFile( FeatureEnabled.SourceFile, FeatureEnabled.SourceLine ) );
}
menu.OpenAtCursor();
}
public void UpdateVisibility()
{
if ( FeatureEnabled is null ) return;
bool enabled = FeatureEnabled.As.Bool;
if ( Visible == enabled ) return;
Visible = enabled;
Owner.OnVisibilityChanged( this );
}
public void SetFeatureEnabled()
{
if ( FeatureEnabled is null ) return;
if ( FeatureEnabled.As.Bool ) return;
FeatureEnabled.As.Bool = true;
UpdateVisibility();
Owner.Select( this );
}
protected override void OnPaint()
{
Paint.ClearPen();
Paint.ClearBrush();
Paint.Antialiasing = true;
Paint.TextAntialiasing = true;
var r = LocalRect;
Paint.ClearPen();
float alpha = 1;
if ( IsSelected )
{
var rr = LocalRect.Shrink( 0, 0, 0, 0 );
var underline = rr;
underline.Bottom = underline.Top + 2;
Paint.SetBrush( Theme.WidgetBackground.Lighten( 0.2f ) );
Paint.DrawRect( rr.Grow( 0, 0, 0, 0 ), 4 );
Paint.SetBrush( Theme.WidgetBackground );
Paint.DrawRect( rr.Grow( 0, -1, 0, 8 ), 3 );
}
else
{
alpha = 0.3f;
if ( Paint.HasMouseOver ) alpha += 0.4f;
}
if ( !ShowText && !IsSelected )
{
Paint.ClearBrush();
Paint.SetPen( Tint.WithAlpha( alpha ) );
Paint.DrawIcon( r, Icon, 12.0f );
return;
}
Paint.SetDefaultFont( 8 );
Paint.ClearBrush();
Paint.SetPen( Tint.WithAlpha( alpha ) );
if ( string.IsNullOrEmpty( Icon ) )
{
Paint.DrawText( r, Name, TextFlag.Center );
}
else
{
r.Left += 12.0f;
var nameRect = Paint.DrawText( r.Shrink( 0, 2, 0, 2 ), Name, TextFlag.Center );
r.Left -= nameRect.Width + 24.0f;
//r.Bottom += 6;
alpha = alpha + 0.2f;
alpha = alpha.Clamp( 0, 1 );
Paint.ClearBrush();
Paint.SetPen( Tint.WithAlpha( alpha ) );
Paint.DrawIcon( r, Icon, 13.0f, TextFlag.Center | TextFlag.DontClip );
}
}
///
/// Hide the page - we've been tabbed away
///
internal void HidePage()
{
if ( Page is null ) return;
Page.Hidden = true;
}
///
/// Convert the Properties into a ControlSheet
///
void CreatePage()
{
var page = new Widget( this );
page.Hidden = true;
page.VerticalSizeMode = SizeMode.CanGrow;
page.HorizontalSizeMode = SizeMode.Flexible;
var sheet = new ControlSheet();
sheet.Margin = 0;
IControlSheet.AddProperties( sheet, Feature.Properties.Where( x => x != FeatureEnabled ).ToList(), false );
page.Layout = sheet;
Page = page;
}
///
/// Show the page, create it and add it to the layout if it doesn't already exist
///
internal void ShowPage( Layout contentLayout )
{
if ( !Page.IsValid() )
{
CreatePage();
}
contentLayout.Add( Page );
Page.Hidden = false;
}
string GetTooltip()
{
var str = "";
str += $"{Name}";
str += "";
if ( !string.IsNullOrWhiteSpace( Description ) )
{
str += $"
{Description}";
}
return str;
}
}