mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-04-19 13:59:22 -04:00
This commit imports the C# engine code and game files, excluding C++ source code. [Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
483 lines
9.9 KiB
C#
483 lines
9.9 KiB
C#
using Sandbox.Internal;
|
|
|
|
namespace Editor;
|
|
|
|
/// <summary>
|
|
/// The tab widget that sits at the top of the ControlSheet if you add [Features]
|
|
/// </summary>
|
|
public class FeatureTabWidget : Widget
|
|
{
|
|
List<FeatureTabOption> Tabs = new List<FeatureTabOption>();
|
|
|
|
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<FeatureEnabledAttribute>( 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<string>( $"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;
|
|
|
|
/// <summary>
|
|
/// If this feature has an option to enable and disable it, this is it.
|
|
/// </summary>
|
|
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 );
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Hide the page - we've been tabbed away
|
|
/// </summary>
|
|
internal void HidePage()
|
|
{
|
|
if ( Page is null ) return;
|
|
|
|
Page.Hidden = true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Convert the Properties into a ControlSheet
|
|
/// </summary>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Show the page, create it and add it to the layout if it doesn't already exist
|
|
/// </summary>
|
|
internal void ShowPage( Layout contentLayout )
|
|
{
|
|
if ( !Page.IsValid() )
|
|
{
|
|
CreatePage();
|
|
}
|
|
|
|
contentLayout.Add( Page );
|
|
Page.Hidden = false;
|
|
}
|
|
|
|
string GetTooltip()
|
|
{
|
|
var str = "<strong>";
|
|
str += $"<span style=\"color: #9CDCFE;\">{Name}</span>";
|
|
str += "</strong>";
|
|
|
|
if ( !string.IsNullOrWhiteSpace( Description ) )
|
|
{
|
|
str += $"<br/><br/><font>{Description}</font>";
|
|
}
|
|
|
|
return str;
|
|
}
|
|
}
|