Files
sbox-public/engine/Sandbox.Tools/Assets/IAssetEditor.cs
s&box team 71f266059a Open source release
This commit imports the C# engine code and game files, excluding C++ source code.

[Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
2025-11-24 09:05:18 +00:00

161 lines
3.9 KiB
C#

namespace Editor;
/// <summary>
/// A widget (usually window) implementing this will be able to edit assets via the asset browser.
/// The widget should be marked with the attribute of the asset's extension, like this <c>[CanEdit( "asset:vsndstck" )]</c>
/// </summary>
public interface IAssetEditor : IValid
{
/// <summary>
/// A list of open editors that support multiple assets at once.
/// </summary>
static Dictionary<string, IAssetEditor> OpenMultiAssetEditors = new();
/// <summary>
/// A list of open editors for individual assets.
/// </summary>
static Dictionary<string, IAssetEditor> OpenSingleEditors = new();
/// <summary>
/// Open given asset in a new asset editor window. Will reuse already open editors for same asset type if the editor supports it. (<see cref="CanOpenMultipleAssets"/>)
/// </summary>
/// <returns>Whether an asset editor was found for given asset.</returns>
public static bool OpenInEditor( Asset asset, out IAssetEditor editor )
{
System.ArgumentNullException.ThrowIfNull( asset );
//
// Stick this on the recent opened list
//
asset.RecordOpened();
var path = asset.AbsolutePath ?? asset.Name;
if ( OpenSingleEditors.TryGetValue( path, out var openEditor ) && openEditor.IsValid )
{
// ReSharper disable once SuspiciousTypeConversion.Global
if ( openEditor is BaseWindow window )
{
window.Focus();
}
editor = openEditor;
return true;
}
var extension = asset.AssetType.FileExtension;
if ( OpenMultiAssetEditors.TryGetValue( extension, out openEditor ) && openEditor.IsValid )
{
openEditor.AssetOpen( asset );
editor = openEditor;
return true;
}
//
// This is the new way, anything else should be considered bullshit
//
TryAgain:
if ( TryOpenUsingStaticMethod( asset ) )
{
editor = null;
return true;
}
var found = EditorTypeLibrary.GetTypesWithAttribute<EditorForAssetTypeAttribute>()
.Where( x => x.Attribute.Extension == extension )
.FirstOrDefault();
// not found lets create a generic editor
if ( found.Type == null && extension != "__fallback" && asset.AssetType.IsGameResource )
{
extension = "__fallback";
goto TryAgain;
}
// nothing found for real
if ( found.Type == null )
{
editor = null;
return false;
}
var created = found.Type.Create<Widget>();
if ( created is not IAssetEditor ae )
{
created.Destroy();
Log.Warning( $"Found editor {created} for {extension} - but it didn't implement IAssetEditor" );
editor = null;
return false;
}
ae.AssetOpen( asset );
if ( ae.CanOpenMultipleAssets )
{
OpenMultiAssetEditors[extension] = ae;
}
else
{
OpenSingleEditors[path] = ae;
}
editor = ae;
return true;
}
static bool TryOpenUsingStaticMethod( Asset asset )
{
var extension = asset.AssetType.FileExtension;
var found = EditorTypeLibrary.GetMethodsWithAttribute<EditorForAssetTypeAttribute>()
.Where( x => x.Attribute.Extension == extension )
.FirstOrDefault();
if ( found.Method is null )
return false;
if ( found.Method.Parameters.Count() == 1 )
{
var p = found.Method.Parameters[0];
// if it wants an asset, pass it in
if ( p.ParameterType == typeof( Asset ) )
{
found.Method.Invoke( null, new[] { asset } );
return true;
}
// if it wants a gameresource type, load it and convert it
if ( p.ParameterType.IsAssignableTo( typeof( GameResource ) ) )
{
var resource = asset.LoadResource( p.ParameterType );
found.Method.Invoke( null, new[] { resource } );
return true;
}
}
return false;
}
/// <summary>
/// If this editor is able to edit multiple assets at the same time then return true
/// and we'll try to create only one version of that editor and AssetOpen will be called multiple times.
/// </summary>
public bool CanOpenMultipleAssets { get; }
/// <summary>
/// Open the asset in this editor.
/// </summary>
public void AssetOpen( Asset asset );
void SelectMember( string memberName );
}