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

220 lines
5.2 KiB
C#

namespace Editor;
public class EditorToolManager
{
/// <summary>
/// Holds the name of the current tool. We store this as a name
/// because it's easy to store and restore. This gives us a single
/// point to change, or read the current tool.
/// The tool won't change until the next frame update.
/// </summary>
public static string CurrentModeName { get; set; } = "ObjectEditorTool";
public static string LastModeName { get; set; } = CurrentModeName;
private static string CurrentSubModeName { get; set; }
public EditorTool CurrentTool { get; private set; }
public EditorTool CurrentSubTool => CurrentTool?.CurrentTool;
public List<EditorTool> ComponentTools { get; private set; } = new List<EditorTool>();
private SceneEditorSession _session;
public SceneEditorSession CurrentSession
{
get => _session ?? SceneEditorSession.Active;
set => _session = value;
}
public EditorToolManager()
{
}
internal void Frame( CameraComponent camera, SceneEditorSession session )
{
CurrentSession = session;
UpdateTool( CurrentModeName );
UpdateSubTool( CurrentSubModeName );
UpdateSelection();
if ( CurrentTool is not null )
{
CurrentTool.Frame( camera );
}
foreach ( var a in ComponentTools )
{
a.Frame( camera );
}
if ( CurrentTool is not null )
{
Gizmo.Settings.Selection = CurrentTool.AllowGameObjectSelection;
}
else if ( ComponentTools.Any() )
{
Gizmo.Settings.Selection = ComponentTools.All( x => x.AllowGameObjectSelection );
}
else
{
Gizmo.Settings.Selection = true;
}
}
private string currentMode;
private string currentSubMode;
internal void UpdateTool( string editMode )
{
if ( currentMode == editMode )
return;
currentMode = editMode;
// So that we update the sub tool immediately after updating the parent tool
currentSubMode = null;
CurrentTool?.Dispose();
CurrentTool = null;
var bestType = EditorTypeLibrary.GetTypesWithAttribute<EditorToolAttribute>()
.Where( x => x.Type.IsNamed( currentMode ) )
.FirstOrDefault();
if ( bestType.Type is null )
{
return;
}
CurrentTool = bestType.Type.Create<EditorTool>();
CurrentTool.InitializeInternal( this );
}
private void UpdateSubTool( string editMode )
{
if ( currentSubMode == editMode ) return;
if ( CurrentTool is not { } parentTool ) return;
currentSubMode = editMode;
parentTool.CurrentTool = parentTool.Tools.FirstOrDefault( x => editMode == x.GetType().Name )
?? parentTool.CurrentTool
?? parentTool.Tools.FirstOrDefault();
}
int previousHash;
int SelectionHash => CurrentSession?.Selection?.GetHashCode() ?? 0;
List<EditorTool> deleteList = new();
/// <summary>
/// Check if the selection has changed. If it has, recreate component tools.
/// </summary>
private void UpdateSelection()
{
var gameObjects = CurrentSession.Selection.OfType<GameObject>().ToArray();
// Add component count to hash if we have single object selected, to update when components are added/removed
var selectionHash = SelectionHash;
if ( gameObjects.Length == 1 )
{
selectionHash = 0;
var components = gameObjects[0].Components.GetAll();
foreach ( var component in components )
{
if ( !component.IsValid() )
continue;
selectionHash = HashCode.Combine( selectionHash, component.GetHashCode() );
}
}
if ( previousHash == selectionHash )
return;
previousHash = selectionHash;
CurrentTool?.OnSelectionChanged();
deleteList.Clear();
deleteList.AddRange( ComponentTools.Where( t => !t.ShouldKeepActive() ) );
if ( gameObjects.Length == 1 )
{
var allComponents = gameObjects[0].Components.GetAll().DistinctBy( x => x.GetType() ).ToArray();
foreach ( var a in allComponents )
{
CreateToolFor( a.GetType() );
}
}
foreach ( var deleteMe in deleteList )
{
ComponentTools.Remove( deleteMe );
deleteMe?.Dispose();
}
foreach ( var a in ComponentTools )
{
a.OnSelectionChanged();
}
}
private void CreateToolFor( Type t )
{
var targetType = typeof( EditorTool<> ).MakeGenericType( t );
var editors = EditorTypeLibrary.GetTypes( targetType ).ToArray();
foreach ( var editor in editors )
{
// Already exists
if ( ComponentTools.Any( x => x.GetType() == editor.TargetType ) )
{
deleteList.RemoveAll( x => x.GetType() == editor.TargetType ); // don't delete these
return;
}
var et = editor.Create<EditorTool>();
ComponentTools.Add( et );
et.InitializeInternal( this );
return;
}
if ( t.BaseType != typeof( Component ) )
{
CreateToolFor( t.BaseType );
}
}
/// <summary>
/// Switches to the named tool type next editor frame.
/// </summary>
/// <param name="name">Type name of the tool.</param>
public static void SetTool( string name )
{
LastModeName = CurrentModeName;
CurrentModeName = name;
}
/// <summary>
/// Switches to the named sub-tool type next editor frame.
/// </summary>
/// <param name="name">Type name of the sub-tool.</param>
public static void SetSubTool( string name )
{
CurrentSubModeName = name;
}
}
public class EditorToolAttribute : System.Attribute
{
internal string Shortcut { get; }
public bool Hidden { get; set; }
public EditorToolAttribute( string shortcut = "" )
{
Shortcut = shortcut;
}
}