Files
sbox-public/game/addons/tools/Code/Widgets/ControlWidgets/ResourceStringControlWidget.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

291 lines
8.2 KiB
C#

using Sandbox.Diagnostics;
using System.IO;
namespace Editor;
/// <summary>
/// Resources stored as strings
/// </summary>
[CustomEditor( typeof( string ), WithAllAttributes = new[] { typeof( AssetPathAttribute ) } )]
public class ResourceStringControlWidget : ControlWidget
{
AssetType AssetType;
public override bool IsControlButton => true;
public override bool SupportsMultiEdit => true;
public string WarningText { get; set; }
public ResourceStringControlWidget( SerializedProperty property ) : base( property )
{
property.TryGetAttribute<AssetPathAttribute>( out var attr );
Assert.NotNull( attr, "ResourceStringControlWidget property has no AssetPathAttribute" );
var resourceExtension = attr.AssetTypeExtension;
resourceExtension = resourceExtension.Replace( "resource:", "" );
resourceExtension = resourceExtension.Trim().ToLower();
AssetType = AssetType.All.FirstOrDefault( x => x.FileExtension == resourceExtension );
Cursor = CursorShape.Finger;
MouseTracking = true;
AcceptDrops = true;
IsDraggable = true;
OnValueChanged();
}
protected override void OnValueChanged()
{
base.OnValueChanged();
if ( AssetType == AssetType.MapFile )
{
var resource = SerializedProperty.GetValue<string>( null );
var asset = resource != null ? AssetSystem.FindByPath( resource ) : null;
if ( asset is not null )
{
bool isCompiled = File.Exists( Path.ChangeExtension( asset.AbsolutePath, ".vpk" ) );
if ( !isCompiled ) WarningText = ToolTip = "Map is not compiled";
}
}
}
protected override void PaintControl()
{
var resource = SerializedProperty.GetValue<string>( null );
var asset = resource != null ? AssetSystem.FindByPath( resource ) : null;
var rect = new Rect( 0, Size );
var iconRect = rect.Shrink( 2 );
iconRect.Width = iconRect.Height;
rect.Left = iconRect.Right + 10;
Paint.ClearPen();
Paint.SetBrush( Theme.SurfaceBackground.WithAlpha( 0.2f ) );
Paint.DrawRect( iconRect, 2 );
var pickerName = "Unknown Resource";
if ( AssetType is not null ) pickerName = AssetType.FriendlyName;
if ( !string.IsNullOrEmpty( WarningText ) )
{
Rect warningRect = iconRect;
warningRect.Left = rect.Left + 16;
Paint.SetPen( Theme.Yellow );
Paint.DrawIcon( warningRect, "warning", Math.Max( 16, warningRect.Height / 2 ) );
rect.Left += 16;
}
Pixmap icon = AssetType?.Icon64;
if ( SerializedProperty.IsMultipleDifferentValues )
{
var textRect = rect.Shrink( 0, 3 );
if ( icon != null ) Paint.Draw( iconRect, icon );
Paint.SetDefaultFont();
Paint.SetPen( Theme.MultipleValues );
Paint.DrawText( textRect, $"Multiple Values", TextFlag.LeftCenter );
}
else if ( asset is not null && !asset.IsDeleted )
{
Paint.Draw( iconRect, asset.GetAssetThumb( true ) );
var textRect = rect.Shrink( 0, 3 );
Paint.SetPen( Theme.Text.WithAlpha( 0.9f ) );
Paint.SetHeadingFont( 8, 450 );
var t = Paint.DrawText( textRect, $"{asset.Name}", TextFlag.LeftTop );
textRect.Left = t.Right + 6;
Paint.SetDefaultFont( 7 );
Theme.DrawFilename( textRect, asset.RelativePath, TextFlag.LeftCenter, Theme.Text.WithAlpha( 0.5f ) );
}
else if ( !string.IsNullOrWhiteSpace( resource ) )
{
var textRect = rect.Shrink( 0, 3 );
bool isPackage = !resource.Contains( ".vmap" ) && Package.TryParseIdent( resource, out _ );
if ( !isPackage )
{
Paint.SetBrush( Theme.Red.Darken( 0.8f ) );
Paint.DrawRect( iconRect, 2 );
Paint.SetPen( Theme.Red );
Paint.DrawIcon( iconRect, "error", Math.Max( 16, iconRect.Height / 2 ) );
}
else if ( icon != null ) Paint.Draw( iconRect, icon );
Paint.SetPen( Theme.Text.WithAlpha( 0.9f ) );
Paint.SetHeadingFont( 8, 450 );
var t = Paint.DrawText( textRect, isPackage ? (Package.TryGetCached( resource, out Package package ) ? $"{package.Title} ☁️" : $"Cloud {pickerName}") : $"Missing {pickerName}", TextFlag.LeftTop );
textRect.Left = t.Right + 6;
Paint.SetDefaultFont( 7 );
Theme.DrawFilename( textRect, resource, TextFlag.LeftCenter, Theme.Text.WithAlpha( 0.5f ) );
}
else
{
var textRect = rect.Shrink( 0, 3 );
if ( icon != null ) Paint.Draw( iconRect, icon );
Paint.SetDefaultFont( italic: true );
Paint.SetPen( Theme.Text.WithAlpha( 0.2f ) );
Paint.DrawText( textRect, $"{pickerName}", TextFlag.LeftCenter );
}
}
protected override void OnContextMenu( ContextMenuEvent e )
{
var m = new ContextMenu();
var resource = SerializedProperty.GetValue<string>( null );
var asset = (resource != null) ? AssetSystem.FindByPath( resource ) : null;
m.AddOption( "Open in Editor", "edit", () => asset?.OpenInEditor() ).Enabled = asset != null && !asset.IsProcedural;
m.AddOption( "Find in Asset Browser", "search", () => LocalAssetBrowser.OpenTo( asset, true ) ).Enabled = asset is not null;
m.AddSeparator();
m.AddOption( "Copy", "file_copy", action: Copy ).Enabled = AssetType == AssetType.MapFile ? !string.IsNullOrWhiteSpace( resource ) : asset != null;
m.AddOption( "Paste", "content_paste", action: Paste );
m.AddSeparator();
m.AddOption( "Clear", "backspace", action: Clear ).Enabled = resource != null;
m.OpenAtCursor( false );
e.Accepted = true;
}
protected override void OnMouseClick( MouseEvent e )
{
base.OnMouseClick( e );
if ( ReadOnly ) return;
var resource = SerializedProperty.GetValue<string>( null );
var asset = resource != null ? AssetSystem.FindByPath( resource ) : null;
var picker = AssetPicker.Create( null, AssetType );
PropertyStartEdit();
picker.SetSelection( resource );
picker.Title = $"Select {SerializedProperty.DisplayName}";
if ( AssetType == AssetType.MapFile )
{
picker.OnPackagePicked = ( o ) =>
{
SerializedProperty.SetValue( o.FullIdent );
PropertyFinishEdit();
};
}
else
{
picker.OnAssetHighlighted = ( o ) =>
{
UpdateFromAsset( o.FirstOrDefault() );
PropertyFinishEdit();
};
}
picker.OnAssetPicked = ( o ) => UpdateFromAsset( o.FirstOrDefault() );
picker.Show();
picker.SetSelection( asset );
}
private void UpdateFromAsset( Asset asset )
{
if ( asset is null ) return;
SerializedProperty.Parent?.NoteStartEdit( SerializedProperty );
SerializedProperty.SetValue( asset.RelativePath );
SerializedProperty.Parent?.NoteFinishEdit( SerializedProperty );
}
public override void OnDragHover( DragEvent ev )
{
if ( AssetType == AssetType.MapFile && ev.Data.Url?.Scheme == "https" )
{
ev.Action = DropAction.Link;
return;
}
if ( !ev.Data.HasFileOrFolder )
return;
var asset = AssetSystem.FindByPath( ev.Data.FileOrFolder );
if ( asset == null || AssetType != asset.AssetType )
return;
ev.Action = DropAction.Link;
}
public override void OnDragDrop( DragEvent ev )
{
if ( AssetType == AssetType.MapFile && ev.Data.Url?.Scheme == "https" )
{
if ( !Package.TryParseIdent( ev.Data.Url.ToString(), out var ident ) )
return;
PropertyStartEdit();
SerializedProperty.SetValue( $"{ident.org}.{ident.package}" );
PropertyFinishEdit();
ev.Action = DropAction.Link;
return;
}
if ( !ev.Data.HasFileOrFolder )
return;
var asset = AssetSystem.FindByPath( ev.Data.FileOrFolder );
if ( asset == null || AssetType != asset.AssetType )
return;
PropertyStartEdit();
UpdateFromAsset( asset );
PropertyFinishEdit();
ev.Action = DropAction.Link;
}
protected override void OnDragStart()
{
var resource = SerializedProperty.GetValue<string>( null );
var asset = resource != null ? AssetSystem.FindByPath( resource ) : null;
if ( asset == null )
return;
var drag = new Drag( this );
drag.Data.Url = new Uri( $"file://{asset.AbsolutePath}" );
drag.Execute();
}
void Copy()
{
var resource = SerializedProperty.GetValue<string>( null );
if ( resource == null ) return;
var asset = AssetSystem.FindByPath( resource );
if ( asset != null )
resource = asset.RelativePath;
EditorUtility.Clipboard.Copy( resource );
}
void Paste()
{
var path = EditorUtility.Clipboard.Paste();
var asset = AssetSystem.FindByPath( path );
UpdateFromAsset( asset );
}
void Clear()
{
SerializedProperty.Parent.NoteStartEdit( SerializedProperty );
SerializedProperty.SetValue( (Resource)null );
SerializedProperty.Parent.NoteFinishEdit( SerializedProperty );
}
}