Files
sbox-public/game/addons/tools/Code/Scene/GameObjectInspector/GameObjectControlWidget.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

289 lines
6.9 KiB
C#

namespace Editor;
[CustomEditor( typeof( GameObject ) )]
public class GameObjectControlWidget : ControlWidget
{
public override bool SupportsMultiEdit => true;
bool IsInList => Parent?.Parent is ListControlWidget;
IconButton PickerButton;
public GameObjectControlWidget( SerializedProperty property ) : base( property )
{
if ( !property.IsEditable )
ReadOnly = true;
IsDraggable = true;
SetSizeMode( SizeMode.Default, SizeMode.Default );
Layout = Layout.Column();
Layout.Spacing = 2;
var inner = Layout.Add( Layout.Row() );
inner.Margin = 2;
inner.AddStretchCell( 1 );
PickerButton = inner.Add( new IconButton( "colorize", () =>
{
PropertyStartEdit();
EyeDropperTool.SetTargetProperty( property );
EyeDropperTool.OnBackToLastTool = () => PropertyFinishEdit();
}, this ) );
PickerButton.FixedWidth = Theme.RowHeight - 4;
PickerButton.FixedHeight = Theme.RowHeight - 4;
PickerButton.Visible = false;
Cursor = CursorShape.Finger;
AcceptDrops = true;
}
protected override void OnContextMenu( ContextMenuEvent e )
{
var m = new ContextMenu( this );
var go = SerializedProperty.GetValue<GameObject>();
if ( go is PrefabScene prefab )
{
var asset = AssetSystem.FindByPath( prefab.Source.ResourcePath );
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();
}
else if ( go is not null )
{
m.AddOption( "Find in Scene", "search", () => FindInScene( go ) );
m.AddSeparator();
}
//m.AddOption( "Copy", "file_copy" );
//m.AddOption( "Paste", "content_paste" );
//m.AddSeparator();
if ( !ReadOnly )
{
m.AddOption( "Clear", "backspace", action: Clear );
}
if ( m.OptionCount > 0 )
{
m.OpenAtCursor( false );
}
e.Accepted = true;
}
protected override void PaintControl()
{
var rect = LocalRect.Shrink( 6, 0 );
var go = SerializedProperty.GetValue<GameObject>();
Paint.SetDefaultFont();
if ( SerializedProperty.IsMultipleDifferentValues )
{
Paint.SetPen( Theme.MultipleValues );
Paint.DrawIcon( rect, "panorama_wide_angle_select", 14, TextFlag.LeftCenter );
rect.Left += 22;
Paint.DrawText( rect, "Multiple Values", TextFlag.LeftCenter );
}
else if ( !go.IsValid() )
{
Paint.SetPen( Theme.TextControl.WithAlpha( 0.3f ) );
Paint.DrawIcon( rect, "radio_button_unchecked", 14, TextFlag.LeftCenter );
rect.Left += 22;
Paint.DrawText( rect, "None (GameObject)", TextFlag.LeftCenter );
}
else if ( go is PrefabScene )
{
Paint.SetPen( Theme.Blue );
Paint.DrawIcon( rect, "panorama_wide_angle_select", 14, TextFlag.LeftCenter );
rect.Left += 22;
Paint.DrawText( rect, go.Name, TextFlag.LeftCenter );
}
else
{
Paint.SetPen( Theme.Green );
Paint.DrawIcon( rect, "panorama_wide_angle_select", 14, TextFlag.LeftCenter );
rect.Left += 22;
Paint.DrawText( rect, BuildName( go ), TextFlag.LeftCenter );
}
if ( EyeDropperTool.TargetProperty == SerializedProperty )
{
Paint.SetPen( ControlHighlightSecondary.WithAlpha( 0.8f ), 2, PenStyle.Dot );
Paint.SetBrush( ControlHighlightSecondary.WithAlpha( 0.2f ) );
Paint.DrawRect( LocalRect.Shrink( 2 ), Theme.ControlRadius );
}
PickerButton.Visible = IsControlHovered;
}
private string BuildName( GameObject go )
{
string str = "";
if ( go.Parent.IsValid() && go.Parent is not Scene )
{
str += $"{BuildName( go.Parent )} > ";
}
str += $"{go.Name}";
return str;
}
void UpdateFromAsset( Asset asset )
{
if ( asset.TryLoadResource( out PrefabFile prefabFile ) )
{
PropertyStartEdit();
SerializedProperty.SetValue( SceneUtility.GetPrefabScene( prefabFile ) );
PropertyFinishEdit();
}
}
void UpdateFromAssets( Asset[] assets )
{
if ( assets.Length == 0 ) return;
if ( assets[0].TryLoadResource( out PrefabFile prefabFile ) )
{
PropertyStartEdit();
SerializedProperty.SetValue( SceneUtility.GetPrefabScene( prefabFile ) );
PropertyFinishEdit();
}
if ( IsInList )
{
var list = Parent.Parent as ListControlWidget;
for ( int i = 1; i < assets.Length; i++ )
{
if ( assets[i].TryLoadResource( out PrefabFile newPrefabFile ) )
{
list.Collection.Add( SceneUtility.GetPrefabScene( newPrefabFile ) );
}
}
}
}
protected override void OnMouseClick( MouseEvent e )
{
if ( e.LeftMouseButton )
{
e.Accepted = true;
var go = SerializedProperty.GetValue<GameObject>();
if ( go.IsValid() && go is not PrefabScene )
{
// If the current value exists in the scene, select it
FindInScene( go );
}
else
{
if ( ReadOnly ) return;
var resource = (go as PrefabScene)?.Source ?? null;
var asset = resource != null ? AssetSystem.FindByPath( resource.ResourcePath ) : null;
// Open Prefab Asset Picker
var prefabAssetType = AssetType.Find( "prefab", false );
var picker = AssetPicker.Create( null, prefabAssetType, new AssetPicker.PickerOptions()
{
EnableMultiselect = IsInList
} );
picker.Window.Title = $"Select Prefab";
picker.OnAssetHighlighted = ( o ) => UpdateFromAsset( o.FirstOrDefault() );
picker.OnAssetPicked = ( o ) => UpdateFromAssets( o );
picker.Show();
picker.SetSelection( asset );
}
}
}
void Clear()
{
PropertyStartEdit();
SerializedProperty.SetValue<GameObject>( null );
SignalValuesChanged();
PropertyFinishEdit();
}
protected override void OnDragStart()
{
var drag = new Drag( this );
drag.Data.Object = SerializedProperty.GetValue<GameObject>();
drag.Execute();
}
public override void OnDragHover( DragEvent ev )
{
ev.Action = DropAction.Ignore;
if ( ev.Data.OfType<GameObject>().Any() )
{
ev.Action = DropAction.Link;
return;
}
if ( ev.Data.HasFileOrFolder )
{
var asset = AssetSystem.FindByPath( ev.Data.Files.First() );
if ( asset is null ) return;
if ( asset.AssetType.FileExtension != "prefab" ) return;
if ( asset.TryLoadResource( out PrefabFile prefabFile ) )
{
ev.Action = DropAction.Link;
return;
}
}
}
public override void OnDragDrop( DragEvent ev )
{
if ( ev.Data.OfType<GameObject>().FirstOrDefault() is { } go )
{
PropertyStartEdit();
SerializedProperty.SetValue( go );
PropertyFinishEdit();
return;
}
if ( ev.Data.HasFileOrFolder )
{
var asset = AssetSystem.FindByPath( ev.Data.Files.First() );
if ( asset is null ) return;
if ( asset.AssetType.FileExtension != "prefab" ) return;
if ( asset.TryLoadResource( out PrefabFile prefabFile ) )
{
PropertyStartEdit();
SerializedProperty.SetValue( SceneUtility.GetPrefabScene( prefabFile ) );
PropertyFinishEdit();
return;
}
}
}
static void FindInScene( GameObject go )
{
if ( !go.IsValid() )
return;
using ( SceneEditorSession.Active.UndoScope( $"Selected {go}" ).Push() )
{
SceneEditorSession.Active?.Selection.Set( go );
}
}
}