Component context menu tidy (#3582)

* Only show property-prefab options on prefab instances, simplify names a bit

* Tweak these to match

* Transform

* Order

* Guessing this meant to pick the shortest source location path

* dotnet format
This commit is contained in:
Sol Williams
2025-12-22 07:15:06 +00:00
committed by GitHub
parent 08c4ae6d73
commit c496bedb0d
5 changed files with 107 additions and 112 deletions

View File

@@ -448,7 +448,7 @@ public sealed class TypeDescription : ISourceLineProvider
SourceLine = 0;
SourceFile = null;
if ( OwnAttributes.OfType<SourceLocationAttribute>().MinBy( x => x.Path ) is { } sourceLocation )
if ( OwnAttributes.OfType<SourceLocationAttribute>().MinBy( x => x.Path.Length ) is { } sourceLocation )
{
SourceLine = sourceLocation.Line;
SourceFile = sourceLocation.Path;

View File

@@ -248,7 +248,8 @@ class ControlSheetRow : Widget
property.Parent.NoteFinishEdit( property );
} );
if ( IsEditingComponent || IsEditingGameObject )
bool isPrefab = property.GetContainingGameObject()?.IsPrefabInstance ?? false;
if ( isPrefab && (IsEditingComponent || IsEditingGameObject) )
{
menu.AddSeparator();
@@ -258,28 +259,20 @@ class ControlSheetRow : Widget
editedObject ??= EditedGameObjects.FirstOrDefault();
var prefabName = EditorUtility.Prefabs.GetOuterMostPrefabName( editedObject ) ?? "";
var revertChangesActionName = $"Revert Property instance change";
menu.AddOption( revertChangesActionName, "history", () =>
var revertActionName = "Revert Change";
menu.AddOption( revertActionName, "history", () =>
{
using var scene = SceneEditorSession.Scope();
using ( SceneEditorSession.Active.UndoScope( revertChangesActionName ).WithComponentChanges( EditedComponents ).WithGameObjectChanges( EditedGameObjects, GameObjectUndoFlags.Properties ).Push() )
using ( SceneEditorSession.Active.UndoScope( revertActionName ).WithComponentChanges( EditedComponents ).WithGameObjectChanges( EditedGameObjects, GameObjectUndoFlags.Properties ).Push() )
{
EditorUtility.Prefabs.RevertPropertyChange( property );
}
} ).Enabled = isPropertyModified;
var applyChangesActionName = $"Apply Property instance change to prefab \"{prefabName}\"";
menu.AddOption( applyChangesActionName, "update", () =>
menu.AddOption( "Apply to Prefab", "save", () =>
{
using var scene = SceneEditorSession.Scope();
using ( SceneEditorSession.Active.UndoScope( applyChangesActionName ).WithComponentChanges( EditedComponents ).WithGameObjectChanges( EditedGameObjects, GameObjectUndoFlags.Properties ).Push() )
{
EditorUtility.Prefabs.ApplyPropertyChange( property );
}
EditorUtility.Prefabs.ApplyPropertyChange( property );
} ).Enabled = isPropertyModified;
}

View File

@@ -196,6 +196,7 @@ public class ComponentListWidget : Widget
component.Reset();
}
} );
menu.AddSeparator();
var componentIndex = componentList.GetAll().ToList().IndexOf( component );
@@ -230,66 +231,6 @@ public class ComponentListWidget : Widget
menu.AddSeparator();
menu.AddOption( "Remove Component", "remove", action: () =>
{
ActivateSession();
using var scene = SceneEditorSession.Scope();
using ( SceneEditorSession.Active.UndoScope( $"Remove {component.GetType().Name} Component" ).WithComponentDestructions( component ).Push() )
{
component.Destroy();
}
} );
if ( component.GameObject.IsPrefabInstance )
{
var isComponentModified = EditorUtility.Prefabs.IsComponentInstanceModified( component );
var prefabName = EditorUtility.Prefabs.GetOuterMostPrefabName( component );
var revertChangesActionName = $"Revert Component instance changes";
menu.AddOption( revertChangesActionName, "history", action: () =>
{
ActivateSession();
using var scene = SceneEditorSession.Scope();
using ( SceneEditorSession.Active.UndoScope( revertChangesActionName ).WithComponentChanges( component ).Push() )
{
EditorUtility.Prefabs.RevertComponentInstanceChanges( component );
}
} ).Enabled = isComponentModified;
var applyChangesActionName = $"Apply Component instance changes to prefab \"{prefabName}\"";
menu.AddOption( applyChangesActionName, "update", action: () =>
{
ActivateSession();
using var scene = SceneEditorSession.Scope();
EditorUtility.Prefabs.ApplyComponentInstanceChangesToPrefab( component );
} ).Enabled = isComponentModified;
}
var replace = menu.AddMenu( "Replace Component", "find_replace" );
replace.AddWidget( new MenuComponentTypeSelectorWidget( replace )
{
OnSelect = ( t ) =>
{
ActivateSession();
using var scene = SceneEditorSession.Scope();
using ( SceneEditorSession.Active.UndoScope( $"Replace {component.GetType().Name} Component" ).WithComponentDestructions( component ).WithComponentCreations().Push() )
{
var go = component.GameObject;
var jso = component.Serialize().AsObject();
component.Destroy();
var newComponent = go.Components.Create( t );
newComponent.DeserializeImmediately( jso );
}
}
} );
menu.AddOption( $"Cut {title}", "content_cut", action: () =>
{
ActivateSession();
@@ -305,20 +246,84 @@ public class ComponentListWidget : Widget
menu.AddOption( $"Copy {title}", "copy_all", action: () => component.CopyToClipboard() );
if ( editable && SceneEditor.HasComponentInClipboard() )
if ( editable )
{
menu.AddOption( "Paste Values", "content_paste", action: () => component.PasteValues() );
menu.AddOption( "Paste As New", "content_paste_go", action: () => component.GameObject.PasteComponent() );
bool clipboardComponent = SceneEditor.HasComponentInClipboard();
menu.AddOption( "Paste Values", "content_paste", action: () => component.PasteValues() ).Enabled = clipboardComponent;
menu.AddOption( "Paste As New", "content_paste_go", action: () => component.GameObject.PasteComponent() ).Enabled = clipboardComponent;
}
menu.AddSeparator();
if ( component.GameObject.IsPrefabInstance )
{
var isComponentModified = EditorUtility.Prefabs.IsComponentInstanceModified( component );
var prefabName = EditorUtility.Prefabs.GetOuterMostPrefabName( component );
var revertChangesActionName = "Revert Changes";
menu.AddOption( revertChangesActionName, "history", action: () =>
{
ActivateSession();
using var scene = SceneEditorSession.Scope();
using ( SceneEditorSession.Active.UndoScope( revertChangesActionName ).WithComponentChanges( component ).Push() )
{
EditorUtility.Prefabs.RevertComponentInstanceChanges( component );
}
} ).Enabled = isComponentModified;
menu.AddOption( "Apply to Prefab", "save", action: () =>
{
ActivateSession();
using var scene = SceneEditorSession.Scope();
EditorUtility.Prefabs.ApplyComponentInstanceChangesToPrefab( component );
} ).Enabled = isComponentModified;
menu.AddSeparator();
}
menu.AddOption( "Remove Component", "remove", action: () =>
{
ActivateSession();
using var scene = SceneEditorSession.Scope();
using ( SceneEditorSession.Active.UndoScope( $"Remove {component.GetType().Name} Component" ).WithComponentDestructions( component ).Push() )
{
component.Destroy();
}
} );
var replace = menu.AddMenu( "Replace Component", "find_replace" );
replace.AddWidget( new MenuComponentTypeSelectorWidget( replace )
{
OnSelect = ( t ) =>
{
ActivateSession();
using var scene = SceneEditorSession.Scope();
using ( SceneEditorSession.Active.UndoScope( $"Replace {component.GetType().Name} Component" ).WithComponentDestructions( component ).WithComponentCreations().Push() )
{
var go = component.GameObject;
var jso = component.Serialize().AsObject();
component.Destroy();
var newComponent = go.Components.Create( t );
newComponent.DeserializeImmediately( jso );
}
}
} );
menu.AddSeparator();
var t = EditorTypeLibrary.GetType( component.GetType() );
if ( t.SourceFile is not null )
{
bool isPackage = component.GetType().Assembly.IsPackage();
var filename = System.IO.Path.GetFileName( t.SourceFile );
menu.AddOption( $"Open {filename}", "open_in_new", action: () => CodeEditor.OpenFile( t ) ).Enabled = isPackage;
menu.AddOption( $"Open {filename}", "code", action: () => CodeEditor.OpenFile( t ) ).Enabled = isPackage;
}
}

View File

@@ -94,7 +94,7 @@ partial class TransformComponentWidget : ComponentEditorWidget
menu.Clear();
menu.AddOption( UseLocal ? "Display Worldspace" : "Display Localspace", null, () =>
menu.AddOption( UseLocal ? "Display Worldspace" : "Display Localspace", "public", () =>
{
UseLocal = !UseLocal;
Rebuild();
@@ -102,19 +102,19 @@ partial class TransformComponentWidget : ComponentEditorWidget
menu.AddSeparator();
menu.AddOption( "Reset", action: () =>
menu.AddOption( "Reset", "restart_alt", () =>
{
SerializedObject.GetProperty( UseLocal ? "Local" : "World" ).SetValue( Transform.Zero );
} );
menu.AddSeparator();
menu.AddOption( "Copy Local Transform", action: () =>
menu.AddOption( "Copy Local Transform", "content_copy", () =>
{
var tx = SerializedObject.GetProperty( "Local" ).GetValue<Transform>();
EditorUtility.Clipboard.Copy( Json.Serialize( tx ) );
} );
menu.AddOption( "Copy World Transform", action: () =>
menu.AddOption( "Copy World Transform", "content_copy", () =>
{
var tx = SerializedObject.GetProperty( "World" ).GetValue<Transform>();
EditorUtility.Clipboard.Copy( Json.Serialize( tx ) );
@@ -126,12 +126,12 @@ partial class TransformComponentWidget : ComponentEditorWidget
var tx = Json.Deserialize<Transform>( clipText );
if ( tx != default )
{
menu.AddOption( "Paste as Local Transform", action: () =>
menu.AddOption( "Paste as Local Transform", "content_paste", () =>
{
SerializedObject.GetProperty( "Local" ).SetValue( tx );
} );
menu.AddOption( "Paste as World Transform", action: () =>
menu.AddOption( "Paste as World Transform", "content_paste", () =>
{
SerializedObject.GetProperty( "World" ).SetValue( tx );
} );

View File

@@ -600,8 +600,28 @@ partial class GameObjectNode : TreeNode<GameObject>
var isModified = selectedGos.Any( x => (x.IsPrefabInstanceRoot && EditorUtility.Prefabs.IsInstanceModified( x ))
|| (x.IsPrefabInstance && EditorUtility.Prefabs.IsGameObjectInstanceModified( gameObject )) );
var isAdded = selectedGos.All( x => x.IsPrefabInstance && EditorUtility.Prefabs.IsGameObjectAddedToInstance( x ) );
var revertChangesActionName = "Revert Changes";
prefabMenu.AddOption( revertChangesActionName, "history", () =>
{
using var scene = SceneEditorSession.Scope();
using ( SceneEditorSession.Active.UndoScope( revertChangesActionName ).WithGameObjectChanges( selectedGos, GameObjectUndoFlags.Properties ).Push() )
{
foreach ( var go in selectedGos )
{
if ( go.IsPrefabInstanceRoot )
{
EditorUtility.Prefabs.RevertInstanceToPrefab( go );
}
else if ( go.IsPrefabInstance )
{
EditorUtility.Prefabs.RevertGameObjectInstanceChanges( go );
}
}
}
} ).Enabled = isModified;
var isAdded = selectedGos.All( x => x.IsPrefabInstance && EditorUtility.Prefabs.IsGameObjectAddedToInstance( x ) );
if ( isAdded )
{
var applyAddActionName = multipleSources ? "Add Objects to Prefabs" : "Add Object to Prefab";
@@ -610,7 +630,7 @@ partial class GameObjectNode : TreeNode<GameObject>
var parentPrefabName = EditorUtility.Prefabs.GetOuterMostPrefabName( gameObject.Parent );
applyAddActionName += $" '{parentPrefabName ?? "Invalid"}'";
}
prefabMenu.AddOption( applyAddActionName, "update", () =>
prefabMenu.AddOption( applyAddActionName, "save", () =>
{
using var scene = SceneEditorSession.Scope();
@@ -629,8 +649,8 @@ partial class GameObjectNode : TreeNode<GameObject>
}
else
{
var applyChangesActionName = multipleSources ? "Apply Instance Changes to Prefabs" : "Apply Instance Changes To Prefab";
prefabMenu.AddOption( applyChangesActionName, "update", () =>
var applyChangesActionName = multipleSources ? "Apply to Prefabs" : "Apply to Prefab";
prefabMenu.AddOption( applyChangesActionName, "save", () =>
{
using var scene = SceneEditorSession.Scope();
@@ -651,29 +671,6 @@ partial class GameObjectNode : TreeNode<GameObject>
}
} ).Enabled = isModified;
}
var revertChangesActionName = "Revert Instance Changes";
prefabMenu.AddOption( revertChangesActionName, "history", () =>
{
using var scene = SceneEditorSession.Scope();
using ( SceneEditorSession.Active.UndoScope( revertChangesActionName ).WithGameObjectChanges( selectedGos, GameObjectUndoFlags.Properties ).Push() )
{
foreach ( var go in selectedGos )
{
if ( go.IsPrefabInstanceRoot )
{
EditorUtility.Prefabs.RevertInstanceToPrefab( go );
}
else if ( go.IsPrefabInstance )
{
EditorUtility.Prefabs.RevertGameObjectInstanceChanges( go );
}
}
}
} ).Enabled = isModified;
}
if ( !isPrefabRoot )