diff --git a/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs b/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs index 7de90f743..e7568f439 100644 --- a/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs +++ b/Source/Editor/Surface/Archetypes/Animation.StateMachine.cs @@ -225,8 +225,7 @@ namespace FlaxEditor.Surface.Archetypes /// public override void OnDestroy() { - if (Surface != null) - Surface.RemoveContext(this); + Surface?.RemoveContext(this); _maxTransitionsPerUpdate = null; _reinitializeOnBecomingRelevant = null; @@ -717,9 +716,12 @@ namespace FlaxEditor.Surface.Archetypes LoadTransitions(); - // Register for surface mouse events to handle transition arrows interactions - Surface.CustomMouseUp += OnSurfaceMouseUp; - Surface.CustomMouseDoubleClick += OnSurfaceMouseDoubleClick; + if (Surface != null) + { + // Register for surface mouse events to handle transition arrows interactions + Surface.CustomMouseUp += OnSurfaceMouseUp; + Surface.CustomMouseDoubleClick += OnSurfaceMouseDoubleClick; + } } private void OnSurfaceMouseUp(ref Float2 mouse, MouseButton buttons, ref bool handled) @@ -1398,7 +1400,8 @@ namespace FlaxEditor.Surface.Archetypes if (context.FindNode(9, 21) == null) { var wasEnabled = true; - if (Surface.Undo != null) + var undo = Surface?.Undo; + if (undo != null) { wasEnabled = Surface.Undo.Enabled; Surface.Undo.Enabled = false; @@ -1406,7 +1409,7 @@ namespace FlaxEditor.Surface.Archetypes context.SpawnNode(9, 21, new Float2(100.0f)); - if (Surface.Undo != null) + if (undo != null) { Surface.Undo.Enabled = wasEnabled; } @@ -1492,7 +1495,7 @@ namespace FlaxEditor.Surface.Archetypes /// public override void OnDestroy() { - Surface.RemoveContext(this); + Surface?.RemoveContext(this); base.OnDestroy(); } @@ -1886,7 +1889,7 @@ namespace FlaxEditor.Surface.Archetypes if (context.FindNode(9, 22) == null) { var wasEnabled = true; - var undo = SourceState.Surface.Undo; + var undo = SourceState.Surface?.Undo; if (undo != null) { wasEnabled = undo.Enabled; diff --git a/Source/Editor/Surface/SurfaceComment.cs b/Source/Editor/Surface/SurfaceComment.cs index d072ee28e..94e7c21ba 100644 --- a/Source/Editor/Surface/SurfaceComment.cs +++ b/Source/Editor/Surface/SurfaceComment.cs @@ -87,7 +87,7 @@ namespace FlaxEditor.Surface Title = TitleValue; Color = ColorValue; var size = SizeValue; - if (Surface.GridSnappingEnabled) + if (Surface != null && Surface.GridSnappingEnabled) size = Surface.SnapToGrid(size, true); Size = size; diff --git a/Source/Editor/Surface/VisjectSurface.Context.cs b/Source/Editor/Surface/VisjectSurface.Context.cs index 1197b387b..3fe29adaa 100644 --- a/Source/Editor/Surface/VisjectSurface.Context.cs +++ b/Source/Editor/Surface/VisjectSurface.Context.cs @@ -2,8 +2,8 @@ using System; using System.Collections.Generic; +using System.Linq; using FlaxEditor.Surface.Undo; -using FlaxEngine; namespace FlaxEditor.Surface { @@ -57,6 +57,28 @@ namespace FlaxEditor.Surface return null; } + /// + /// Opens the surface context with the given owning nodes IDs path. + /// + /// The node ids path. + /// Found context or null if cannot. + public VisjectSurfaceContext OpenContext(Span nodePath) + { + OpenContext(RootContext.Context); + if (nodePath != null && nodePath.Length != 0) + { + for (int i = 0; i < nodePath.Length; i++) + { + var node = Context.FindNode(nodePath[i]); + if (node is ISurfaceContext context) + OpenContext(context); + else + return null; + } + } + return Context; + } + /// /// Creates the Visject surface context for the given surface data source context. /// @@ -101,7 +123,12 @@ namespace FlaxEditor.Surface if (_root == null) _root = surfaceContext; else if (ContextStack.Contains(surfaceContext)) - throw new ArgumentException("Context has been already added to the stack."); + { + // Go up until the given context + while (ContextStack.First() != surfaceContext) + CloseContext(); + return; + } // Change stack ContextStack.Push(surfaceContext); diff --git a/Source/Editor/Windows/Search/ContentSearchWindow.cs b/Source/Editor/Windows/Search/ContentSearchWindow.cs index 0e3d1efdc..dd506a849 100644 --- a/Source/Editor/Windows/Search/ContentSearchWindow.cs +++ b/Source/Editor/Windows/Search/ContentSearchWindow.cs @@ -13,7 +13,6 @@ using FlaxEditor.GUI.Docking; using FlaxEditor.GUI.Input; using FlaxEditor.GUI.Tree; using FlaxEditor.Options; -using FlaxEditor.Scripting; using FlaxEditor.Surface; using FlaxEditor.Windows; using FlaxEditor.Windows.Assets; @@ -496,6 +495,7 @@ namespace FlaxEngine.Windows.Search // Iterate over all assets var tempFolder = StringUtils.NormalizePath(Path.GetDirectoryName(Globals.TemporaryFolder)); + var nodePath = new List(); for (var i = 0; i < assets.Length && !_token.IsCancellationRequested; i++) { var id = assets[i]; @@ -512,20 +512,21 @@ namespace FlaxEngine.Windows.Search continue; // Search asset contents + nodePath.Clear(); if (asset is VisualScript visualScript) - SearchAsyncInnerVisject(asset, visualScript.LoadSurface()); + SearchAsyncInnerVisject(asset, visualScript.LoadSurface(), nodePath); else if (asset is Material material) - SearchAsyncInnerVisject(asset, material.LoadSurface(false)); + SearchAsyncInnerVisject(asset, material.LoadSurface(false), nodePath); else if (asset is MaterialFunction materialFunction) - SearchAsyncInnerVisject(asset, materialFunction.LoadSurface()); + SearchAsyncInnerVisject(asset, materialFunction.LoadSurface(), nodePath); else if (asset is AnimationGraph animationGraph) - SearchAsyncInnerVisject(asset, animationGraph.LoadSurface()); + SearchAsyncInnerVisject(asset, animationGraph.LoadSurface(), nodePath); else if (asset is AnimationGraphFunction animationGraphFunction) - SearchAsyncInnerVisject(asset, animationGraphFunction.LoadSurface()); + SearchAsyncInnerVisject(asset, animationGraphFunction.LoadSurface(), nodePath); else if (asset is ParticleEmitter particleEmitter) - SearchAsyncInnerVisject(asset, particleEmitter.LoadSurface(false)); + SearchAsyncInnerVisject(asset, particleEmitter.LoadSurface(false), nodePath); else if (asset is ParticleEmitterFunction particleEmitterFunction) - SearchAsyncInnerVisject(asset, particleEmitterFunction.LoadSurface()); + SearchAsyncInnerVisject(asset, particleEmitterFunction.LoadSurface(), nodePath); // Don't eat whole performance Thread.Sleep(15); @@ -551,7 +552,7 @@ namespace FlaxEngine.Windows.Search }; } - private void SearchAsyncInnerVisject(Asset asset, byte[] surfaceData) + private void SearchAsyncInnerVisject(Asset asset, byte[] surfaceData, List nodePath) { // Load Visject surface from data if (surfaceData == null || surfaceData.Length == 0) @@ -566,7 +567,6 @@ namespace FlaxEngine.Windows.Search if (_visjectSurfaceStyle == null) _visjectSurfaceStyle = SurfaceStyle.CreateDefault(Editor); SearchResultTreeNode assetTreeNode = null; - // TODO: support nested surfaces (eg. in Anim Graph) // Search parameters foreach (var parameter in _visjectSurfaceContext.Parameters) @@ -592,7 +592,8 @@ namespace FlaxEngine.Windows.Search // Search nodes var newTreeNodes = new List(); - foreach (var node in _visjectSurfaceContext.Nodes) + var nodes = _visjectSurfaceContext.Nodes.ToArray(); + foreach (var node in nodes) { newTreeNodes.Clear(); if (node.Values != null) @@ -602,12 +603,18 @@ namespace FlaxEngine.Windows.Search SearchVisjectMatch(value, (matchedValue, matchedText) => { var valueTreeNode = AddVisjectSearchResult(matchedValue, matchedText, node.Archetype.ConnectionsHints); - valueTreeNode.Tag = node.ID; + valueTreeNode.Tag = new VisjectNodeTag { NodeId = node.ID, NodePath = nodePath.ToArray() }; valueTreeNode.Navigate = OnNavigateVisjectNode; newTreeNodes.Add(valueTreeNode); }); } } + if (node is ISurfaceContext context) + { + nodePath.Add(node.ID); + SearchAsyncInnerVisject(asset, context.SurfaceData, nodePath); + nodePath.RemoveAt(nodePath.Count - 1); + } var nodeSearchText = node.ContentSearchText; if (newTreeNodes.Count != 0 || (nodeSearchText != null && IsSearchMatch(ref nodeSearchText))) @@ -617,7 +624,7 @@ namespace FlaxEngine.Windows.Search { Text = node.Title, TooltipText = node.TooltipText, - Tag = node.ID, + Tag = new VisjectNodeTag { NodeId = node.ID, NodePath = nodePath.ToArray() }, Navigate = OnNavigateVisjectNode, Parent = assetTreeNode, }; @@ -723,9 +730,15 @@ namespace FlaxEngine.Windows.Search Editor.ContentEditing.Open(contentItem); } + private struct VisjectNodeTag + { + public uint NodeId; + public uint[] NodePath; + } + private void OnNavigateVisjectNode(SearchResultTreeNode treeNode) { - var nodeId = (uint)treeNode.Tag; + var tag = (VisjectNodeTag)treeNode.Tag; var assetId = Guid.Empty; var assetTreeNode = treeNode.Parent; while (!(assetTreeNode.Tag is Guid)) @@ -734,7 +747,8 @@ namespace FlaxEngine.Windows.Search var contentItem = Editor.ContentDatabase.FindAsset(assetId); if (Editor.ContentEditing.Open(contentItem) is IVisjectSurfaceWindow window) { - var node = window.VisjectSurface.FindNode(nodeId); + var context = window.VisjectSurface.OpenContext(tag.NodePath) ?? window.VisjectSurface.Context; + var node = context.FindNode(tag.NodeId); if (node != null) { // Focus this node