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]
239 lines
5.8 KiB
C#
239 lines
5.8 KiB
C#
using Sandbox.Resources;
|
|
using System.Reflection;
|
|
|
|
namespace Editor;
|
|
|
|
/// <summary>
|
|
/// Allows resources to choose between different types of sources, such as embedded, disk, or a generator.
|
|
/// At its core this is just a button to switch types and a body cell holding the editor for whatever type is selected.
|
|
/// </summary>
|
|
public sealed class ResourceWrapperControlWidget : ControlWidget
|
|
{
|
|
public override bool SupportsMultiEdit => true;
|
|
public Resource Resource => SerializedProperty.GetValue<Resource>( null );
|
|
|
|
IconButton Button;
|
|
Layout Body;
|
|
string CurrentOption = null;
|
|
|
|
public ResourceWrapperControlWidget( SerializedProperty property ) : base( property )
|
|
{
|
|
HorizontalSizeMode = SizeMode.CanGrow | SizeMode.Expand;
|
|
MouseTracking = true;
|
|
AcceptDrops = true;
|
|
IsDraggable = true;
|
|
|
|
Layout = Layout.Row();
|
|
Layout.Spacing = 2;
|
|
|
|
Body = Layout.AddRow();
|
|
|
|
Layout.AddStretchCell();
|
|
|
|
if ( HasAvailableOptions() )
|
|
{
|
|
var buttonCell = Layout.AddColumn();
|
|
buttonCell.Alignment = TextFlag.RightTop;
|
|
Button = buttonCell.Add( new IconButton.WithCornerIcon( "category" )
|
|
{
|
|
OnClick = ShowMenu,
|
|
Background = Color.Transparent,
|
|
Foreground = Theme.Yellow,
|
|
IconSize = 16,
|
|
CornerIconSize = 16,
|
|
CornerIconOffset = 2
|
|
} );
|
|
Button.Enabled = SerializedProperty.IsEditable;
|
|
Button.FixedSize = Theme.RowHeight;
|
|
Button.ToolTip = "Source Type";
|
|
}
|
|
|
|
TryGetOption();
|
|
RebuildControl();
|
|
}
|
|
|
|
bool IsActive( string option ) => CurrentOption == option;
|
|
|
|
Option AddOption( Menu menu, string name, string icon = null, string option = null, Action action = null )
|
|
{
|
|
var o = menu.AddOption( name, icon, () =>
|
|
{
|
|
if ( action is null )
|
|
{
|
|
SwitchTo( option );
|
|
}
|
|
else
|
|
{
|
|
action();
|
|
}
|
|
} );
|
|
o.Checkable = true;
|
|
o.Checked = IsActive( option );
|
|
|
|
return o;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Special case, when we switch to embedded, we can take the current GameResource properties and apply them to the inline resource
|
|
/// </summary>
|
|
void SwitchToEmbedded()
|
|
{
|
|
//
|
|
// I think this is better than having a specific option to convert a file to embedded
|
|
//
|
|
var oldResource = SerializedProperty.GetValue<GameResource>();
|
|
|
|
CurrentOption = "embed";
|
|
|
|
if ( oldResource is null )
|
|
{
|
|
RebuildControl();
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Create a new embedded resource from the old one, this will copy all properties over
|
|
//
|
|
EmbeddedResourceControlWidget.CreateEmbeddedFromFile( SerializedProperty, oldResource );
|
|
|
|
RebuildControl();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Do we have any options available for this resource?
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private bool HasAvailableOptions()
|
|
{
|
|
//
|
|
// GameResources - CanEmbed?
|
|
//
|
|
var attribute = SerializedProperty.PropertyType.GetCustomAttribute<AssetTypeAttribute>();
|
|
if ( attribute is not null && !attribute.Flags.Contains( AssetTypeFlags.NoEmbedding ) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// Find the gernerators
|
|
//
|
|
var generators = EditorTypeLibrary.GetGenericTypes( typeof( ResourceGenerator<> ), [SerializedProperty.PropertyType] );
|
|
if ( generators.Any( x => !x.TargetType.IsAbstract ) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
//
|
|
// Anything else
|
|
//
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Show the list of options this resource can come from
|
|
/// </summary>
|
|
void ShowMenu()
|
|
{
|
|
var menu = new ContextMenu();
|
|
|
|
AddOption( menu, "From Disk", "folder" );
|
|
|
|
// Only allow embedded if the type is a subclass of GameResource - it doesn't make sense to embed native types
|
|
if ( SerializedProperty.PropertyType.IsSubclassOf( typeof( GameResource ) ) )
|
|
{
|
|
var attribute = SerializedProperty.PropertyType.GetCustomAttribute<AssetTypeAttribute>();
|
|
|
|
if ( attribute is not null && !attribute.Flags.Contains( AssetTypeFlags.NoEmbedding ) )
|
|
{
|
|
AddOption( menu, "Embedded", "document_scanner", "embed", SwitchToEmbedded );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Add The Generators
|
|
//
|
|
var generators = EditorTypeLibrary.GetGenericTypes( typeof( ResourceGenerator<> ), [SerializedProperty.PropertyType] );
|
|
|
|
foreach ( var generator in generators.OrderBy( x => x.Order ).ThenBy( x => x.Name ) )
|
|
{
|
|
if ( generator.TargetType.IsAbstract ) continue;
|
|
|
|
AddOption( menu, generator.Title, generator.Icon, generator.ClassName );
|
|
}
|
|
|
|
menu.OpenNextTo( Button, WidgetAnchor.BottomEnd with { AdjustSize = true, ConstrainToScreen = true } );
|
|
}
|
|
|
|
void TryGetOption()
|
|
{
|
|
// No need to check if not embedded
|
|
if ( Resource is null || !Resource.EmbeddedResource.HasValue )
|
|
return;
|
|
|
|
//
|
|
// If we're using the embed path, we don't want to use the generator at all
|
|
//
|
|
if ( Resource.EmbeddedResource.Value.ResourceCompiler == "embed" )
|
|
{
|
|
CurrentOption = "embed";
|
|
return;
|
|
}
|
|
|
|
CurrentOption = Resource.EmbeddedResource.Value.ResourceGenerator;
|
|
}
|
|
|
|
void SwitchTo( string name = null )
|
|
{
|
|
CurrentOption = name;
|
|
RebuildControl();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Rebuild the resource editing control
|
|
/// </summary>
|
|
public void RebuildControl()
|
|
{
|
|
Body.Clear( true );
|
|
|
|
var types = EditorTypeLibrary.GetGenericTypes( typeof( ResourceGenerator<> ), [SerializedProperty.PropertyType] );
|
|
var type = types.FirstOrDefault( x => x.ClassName == CurrentOption );
|
|
|
|
Widget editor = null;
|
|
|
|
// Which editor should we use?
|
|
{
|
|
if ( type is not null && type.Create<ResourceGenerator>() is var generator )
|
|
{
|
|
editor = ResourceGeneratorControlWidget.Create( generator, SerializedProperty );
|
|
|
|
if ( Button.IsValid() )
|
|
Button.Icon = DisplayInfo.For( generator ).Icon;
|
|
}
|
|
else if ( CurrentOption == "embed" )
|
|
{
|
|
editor = EmbeddedResourceControlWidget.CreateWidget( SerializedProperty );
|
|
|
|
if ( Button.IsValid() )
|
|
Button.Icon = "document_scanner";
|
|
}
|
|
else
|
|
{
|
|
editor = ControlWidget.Create( SerializedProperty );
|
|
|
|
if ( Button.IsValid() )
|
|
Button.Icon = "folder";
|
|
}
|
|
}
|
|
|
|
if ( editor is not null )
|
|
{
|
|
Body.Add( editor );
|
|
}
|
|
}
|
|
|
|
protected override void PaintUnder()
|
|
{
|
|
// nothing
|
|
}
|
|
}
|