namespace Editor; public class EditorToolManager { /// /// 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. /// 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 ComponentTools { get; private set; } = new List(); 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() .Where( x => x.Type.IsNamed( currentMode ) ) .FirstOrDefault(); if ( bestType.Type is null ) { return; } CurrentTool = bestType.Type.Create(); 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 deleteList = new(); /// /// Check if the selection has changed. If it has, recreate component tools. /// private void UpdateSelection() { var gameObjects = CurrentSession.Selection.OfType().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(); ComponentTools.Add( et ); et.InitializeInternal( this ); return; } if ( t.BaseType != typeof( Component ) ) { CreateToolFor( t.BaseType ); } } /// /// Switches to the named tool type next editor frame. /// /// Type name of the tool. public static void SetTool( string name ) { LastModeName = CurrentModeName; CurrentModeName = name; } /// /// Switches to the named sub-tool type next editor frame. /// /// Type name of the sub-tool. 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; } }