From 0b54691f07771a737f6efa3aaf26a84f8a2dcf99 Mon Sep 17 00:00:00 2001 From: stefnotch Date: Wed, 17 Feb 2021 22:36:11 +0100 Subject: [PATCH 01/15] Basic Visject box arrow key navigation --- Source/Editor/Surface/SurfaceNode.cs | 19 ++-- Source/Editor/Surface/VisjectSurface.Input.cs | 95 +++++++++++++++++++ 2 files changed, 106 insertions(+), 8 deletions(-) diff --git a/Source/Editor/Surface/SurfaceNode.cs b/Source/Editor/Surface/SurfaceNode.cs index bdce94513..303324870 100644 --- a/Source/Editor/Surface/SurfaceNode.cs +++ b/Source/Editor/Surface/SurfaceNode.cs @@ -553,19 +553,22 @@ namespace FlaxEditor.Surface internal Box GetNextBox(Box box) { - int i = 0; - for (; i < Elements.Count; i++) + // Get the one after it + for (int i = box.IndexInParent + 1; i < Elements.Count; i++) { - if (Elements[i] == box) + if (Elements[i] is Box b) { - // We found the box - break; + return b; } } - // Get the one after it - i++; - for (; i < Elements.Count; i++) + return null; + } + + internal Box GetPreviousBox(Box box) + { + // Get the one before it + for (int i = box.IndexInParent - 1; i >= 0; i--) { if (Elements[i] is Box b) { diff --git a/Source/Editor/Surface/VisjectSurface.Input.cs b/Source/Editor/Surface/VisjectSurface.Input.cs index dec89e143..28cff42b8 100644 --- a/Source/Editor/Surface/VisjectSurface.Input.cs +++ b/Source/Editor/Surface/VisjectSurface.Input.cs @@ -527,6 +527,101 @@ namespace FlaxEditor.Surface } return true; } + + if (key == KeyboardKeys.ArrowUp) + { + Box selectedBox = GetSelectedBox(SelectedNodes); + Box toSelect = selectedBox?.ParentNode.GetPreviousBox(selectedBox); + + if (toSelect != null) + { + if (toSelect.Connections.Count > 1) + { + // Box has multiple connections + } + + if (toSelect.IsOutput != selectedBox.IsOutput) + { + // Jump up (nodes) + } + else + { + Select(toSelect.ParentNode); + toSelect.ParentNode.SelectBox(toSelect); + } + } + + return true; + } + if (key == KeyboardKeys.ArrowDown) + { + Box selectedBox = GetSelectedBox(SelectedNodes); + Box toSelect = selectedBox?.ParentNode.GetNextBox(selectedBox); + + if (toSelect != null) + { + if (toSelect.Connections.Count > 1) + { + // Box has multiple connections + } + + if (toSelect.IsOutput != selectedBox.IsOutput) + { + // Jump down (nodes) + } + else + { + Select(toSelect.ParentNode); + toSelect.ParentNode.SelectBox(toSelect); + } + } + + return true; + } + + if (key == KeyboardKeys.ArrowRight || key == KeyboardKeys.ArrowLeft) + { + Box selectedBox = GetSelectedBox(SelectedNodes); + if (selectedBox == null) return false; + + Box toSelect = null; + + if (key == KeyboardKeys.ArrowRight && selectedBox.IsOutput || key == KeyboardKeys.ArrowLeft && !selectedBox.IsOutput) + { + if (selectedBox.Connections.Count > 1) + { + // Box has multiple connections + } + else if (selectedBox.Connections.Count == 1) + { + toSelect = selectedBox.Connections[0]; + } + } + else + { + // Use the node with the closest Y-level + // Since there are cases like 3 nodes on one side and only 1 node on the other side + + var elements = selectedBox?.ParentNode.Elements; + float distance = float.PositiveInfinity; + for (int i = 0; i < elements.Count; i++) + { + if (elements[i] is Box box && box.IsOutput != selectedBox.IsOutput && Mathf.Abs(box.Y - selectedBox.Y) < distance) + { + toSelect = box; + distance = Mathf.Abs(box.Y - selectedBox.Y); + } + } + } + + if (toSelect != null) + { + Select(toSelect.ParentNode); + toSelect.ParentNode.SelectBox(toSelect); + + } + return true; + } } return false; From 32c5a8457601629e8a5aacd70b4fd2a27e4d4a8b Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 21 Jan 2021 14:28:02 +0100 Subject: [PATCH 02/15] Revert "Fix right-click context menu for Visject Nodes" This reverts commit a4763f630e9636da94c58201b86351562a69d827. It has been superseded by https://github.com/FlaxEngine/FlaxEngine/pull/107/commits/ef2dbb7818e8fc38b6a49a1f512be07a00e37c1a --- Source/Editor/Surface/SurfaceNode.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Surface/SurfaceNode.cs b/Source/Editor/Surface/SurfaceNode.cs index 303324870..2f79a4e8b 100644 --- a/Source/Editor/Surface/SurfaceNode.cs +++ b/Source/Editor/Surface/SurfaceNode.cs @@ -1010,7 +1010,7 @@ namespace FlaxEditor.Surface } // Secondary Context Menu - if (button == MouseButton.Right && false) + if (button == MouseButton.Right) { if (!IsSelected) Surface.Select(this); From 2498d1d4f22ff1aebb3cc187717500ed0bae5d03 Mon Sep 17 00:00:00 2001 From: stefnotch Date: Sun, 21 Feb 2021 19:44:47 +0100 Subject: [PATCH 03/15] Tweak items matching priority --- Source/Editor/Surface/ContextMenu/VisjectCMItem.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs index 015b9213d..9690d5fd3 100644 --- a/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs +++ b/Source/Editor/Surface/ContextMenu/VisjectCMItem.cs @@ -159,6 +159,7 @@ namespace FlaxEditor.Surface.ContextMenu var start = font.GetCharPosition(_archetype.Title, 0); var end = font.GetCharPosition(_archetype.Title, _archetype.Title.Length - 1); _highlights.Add(new Rectangle(start.X + 2, 0, end.X - start.X, Height)); + _isFullMatch = true; Visible = true; } else if (NodeArchetype.TryParseText != null && NodeArchetype.TryParseText(filterText, out var data)) From 81fa876940c72345890572ae5a9e3e2c20f1288d Mon Sep 17 00:00:00 2001 From: stefnotch Date: Tue, 23 Feb 2021 11:33:51 +0100 Subject: [PATCH 04/15] Drop unused boxes in Visject serialization --- Source/Editor/Surface/VisjectSurface.CopyPaste.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Editor/Surface/VisjectSurface.CopyPaste.cs b/Source/Editor/Surface/VisjectSurface.CopyPaste.cs index 0d991191a..2fbdb815c 100644 --- a/Source/Editor/Surface/VisjectSurface.CopyPaste.cs +++ b/Source/Editor/Surface/VisjectSurface.CopyPaste.cs @@ -103,7 +103,7 @@ namespace FlaxEditor.Surface { for (int j = 0; j < node.Elements.Count; j++) { - if (node.Elements[j] is Box box) + if (node.Elements[j] is Box box && box.Connections.Count > 0) { var dataModelBox = new DataModelBox { From 1dbbbeced14e992bc0234e4eb3e47db0af4607cd Mon Sep 17 00:00:00 2001 From: stefnotch Date: Wed, 24 Feb 2021 21:05:10 +0100 Subject: [PATCH 05/15] Specialized Visject Connection Broadphase Intersection --- Source/Editor/Surface/Elements/OutputBox.cs | 100 +++++++++++++++++++- 1 file changed, 99 insertions(+), 1 deletion(-) diff --git a/Source/Editor/Surface/Elements/OutputBox.cs b/Source/Editor/Surface/Elements/OutputBox.cs index 0c3ba8da4..251d599b8 100644 --- a/Source/Editor/Surface/Elements/OutputBox.cs +++ b/Source/Editor/Surface/Elements/OutputBox.cs @@ -2,6 +2,7 @@ using FlaxEngine; using FlaxEngine.GUI; +using System; namespace FlaxEditor.Surface.Elements { @@ -43,10 +44,86 @@ namespace FlaxEditor.Surface.Elements */ } + private static bool IsRight(ref Vector2 a, ref Vector2 b, ref Vector2 point) + { + return ((b.X - a.X) * (point.Y - a.Y) - (b.Y - a.Y) * (point.X - a.X)) <= 0; + } + + /// + /// Checks if a point intersects a bezier curve + /// + /// The start location. + /// The end location. + /// The point + /// Distance at which its an intersection + public static bool IntersectsConnection(ref Vector2 start, ref Vector2 end, ref Vector2 point, float distance) + { + if ((point.X - start.X) * (end.X - point.X) < 0) return false; + + var dst = (end - start) * new Vector2(0.25f, 0.05f); // Purposefully tweaked to 0.25 + Vector2 control1 = new Vector2(start.X + dst.X, start.Y + dst.Y); + Vector2 control2 = new Vector2(end.X - dst.X, end.Y + dst.Y); + + // I'm ignoring the start.Y + dst.Y + + Vector2 direction = end - start; + float offset = Mathf.Sign(direction.Y) * distance; + + Vector2 pointAbove1 = new Vector2(control1.X, start.Y - offset); + Vector2 pointAbove2 = new Vector2(end.X, end.Y - offset); + Vector2 pointBelow1 = new Vector2(start.X, start.Y + offset); + Vector2 pointBelow2 = new Vector2(control2.X, end.Y + offset); + + /* Render2D.DrawLine(pointAbove1, pointAbove2, Color.Red); + Render2D.DrawRectangle(new Rectangle(pointAbove2 - new Vector2(5), new Vector2(10)), Color.Red); + Render2D.DrawLine(pointBelow1, pointBelow2, Color.Green); + Render2D.DrawRectangle(new Rectangle(pointBelow2 - new Vector2(5), new Vector2(10)), Color.Green); + */ + // TODO: are they parallel ^? + + return IsRight(ref pointAbove1, ref pointAbove2, ref point) == IsRight(ref pointBelow2, ref pointBelow1, ref point); + + + // Taken from the Render2D.DrawBezier code + float squaredDistance = distance; + + Vector2 d1 = control1 - start; + Vector2 d2 = control2 - control1; + Vector2 d3 = end - control2; + float len = d1.Length + d2.Length + d3.Length; + int segmentCount = Math.Min(Math.Max(Mathf.CeilToInt(len * 0.05f), 1), 100); + float segmentCountInv = 1.0f / segmentCount; + + Bezier(ref start, ref control1, ref control2, ref end, 0, out Vector2 p); + for (int i = 1; i <= segmentCount; i++) + { + Vector2 oldp = p; + float t = i * segmentCountInv; + Bezier(ref start, ref control1, ref control2, ref end, t, out p); + + CollisionsHelper.ClosestPointPointLine(ref point, ref oldp, ref p, out Vector2 result); + if (Vector2.DistanceSquared(point, result) <= squaredDistance) + { + return true; + } + } + return false; + } + + private static void Bezier(ref Vector2 p0, ref Vector2 p1, ref Vector2 p2, ref Vector2 p3, float alpha, out Vector2 result) + { + Vector2.Lerp(ref p0, ref p1, alpha, out var p01); + Vector2.Lerp(ref p1, ref p2, alpha, out var p12); + Vector2.Lerp(ref p2, ref p3, alpha, out var p23); + Vector2.Lerp(ref p01, ref p12, alpha, out var p012); + Vector2.Lerp(ref p12, ref p23, alpha, out var p123); + Vector2.Lerp(ref p012, ref p123, alpha, out result); + } + /// /// Draw all connections coming from this box. /// - public void DrawConnections() + public void DrawConnections(ref Vector2 mousePosition) { // Draw all the connections var center = Size * 0.5f; @@ -60,10 +137,31 @@ namespace FlaxEditor.Surface.Elements Vector2 endPos = targetBox.Parent.PointToParent(ref tmp); var highlight = 1 + Mathf.Max(startHighlight, targetBox.ConnectionsHighlightIntensity); var color = _currentTypeColor * highlight; + + // if (PointInRectangle(ref startPos, ref endPos, ref mousePosition)) + { + for (int ix = 0; ix < 10000; ix++) + { + OutputBox.IntersectsConnection(ref startPos, ref endPos, ref mousePosition, 30f); + } + if (OutputBox.IntersectsConnection(ref startPos, ref endPos, ref mousePosition, 30f)) + { + highlight += 2; + } + } + + DrawConnection(ref startPos, ref endPos, ref color, highlight); } } + private bool PointInRectangle(ref Vector2 a, ref Vector2 b, ref Vector2 point) + { + // This has the convenient property that it doesn't matter if a and b are switched + Vector2 signs = (point - a) * (b - point); + return signs.X >= 0 && signs.Y >= 0; + } + /// /// Draw a selected connections coming from this box. /// From bcc036f8080917beac47f5725bce4fd3428200e1 Mon Sep 17 00:00:00 2001 From: stefnotch Date: Thu, 25 Feb 2021 09:55:17 +0100 Subject: [PATCH 06/15] Hover over connections --- Source/Editor/Surface/Elements/OutputBox.cs | 56 ++++----------------- Source/Editor/Surface/SurfaceNode.cs | 2 +- 2 files changed, 12 insertions(+), 46 deletions(-) diff --git a/Source/Editor/Surface/Elements/OutputBox.cs b/Source/Editor/Surface/Elements/OutputBox.cs index 251d599b8..852132baf 100644 --- a/Source/Editor/Surface/Elements/OutputBox.cs +++ b/Source/Editor/Surface/Elements/OutputBox.cs @@ -44,11 +44,6 @@ namespace FlaxEditor.Surface.Elements */ } - private static bool IsRight(ref Vector2 a, ref Vector2 b, ref Vector2 point) - { - return ((b.X - a.X) * (point.Y - a.Y) - (b.Y - a.Y) * (point.X - a.X)) <= 0; - } - /// /// Checks if a point intersects a bezier curve /// @@ -58,35 +53,21 @@ namespace FlaxEditor.Surface.Elements /// Distance at which its an intersection public static bool IntersectsConnection(ref Vector2 start, ref Vector2 end, ref Vector2 point, float distance) { + // Maybe I should make this a non-static method and automatically read the Surface.ViewScale? + + // Pretty much a point in rectangle check if ((point.X - start.X) * (end.X - point.X) < 0) return false; - var dst = (end - start) * new Vector2(0.25f, 0.05f); // Purposefully tweaked to 0.25 - Vector2 control1 = new Vector2(start.X + dst.X, start.Y + dst.Y); - Vector2 control2 = new Vector2(end.X - dst.X, end.Y + dst.Y); - - // I'm ignoring the start.Y + dst.Y - - Vector2 direction = end - start; - float offset = Mathf.Sign(direction.Y) * distance; - - Vector2 pointAbove1 = new Vector2(control1.X, start.Y - offset); - Vector2 pointAbove2 = new Vector2(end.X, end.Y - offset); - Vector2 pointBelow1 = new Vector2(start.X, start.Y + offset); - Vector2 pointBelow2 = new Vector2(control2.X, end.Y + offset); - - /* Render2D.DrawLine(pointAbove1, pointAbove2, Color.Red); - Render2D.DrawRectangle(new Rectangle(pointAbove2 - new Vector2(5), new Vector2(10)), Color.Red); - Render2D.DrawLine(pointBelow1, pointBelow2, Color.Green); - Render2D.DrawRectangle(new Rectangle(pointBelow2 - new Vector2(5), new Vector2(10)), Color.Green); - */ - // TODO: are they parallel ^? - - return IsRight(ref pointAbove1, ref pointAbove2, ref point) == IsRight(ref pointBelow2, ref pointBelow1, ref point); - + float offset = Mathf.Sign(end.Y - start.Y) * distance; + if ((point.Y - (start.Y - offset)) * ((end.Y + offset) - point.Y) < 0) return false; // Taken from the Render2D.DrawBezier code float squaredDistance = distance; + var dst = (end - start) * new Vector2(0.5f, 0.05f); + Vector2 control1 = new Vector2(start.X + dst.X, start.Y + dst.Y); + Vector2 control2 = new Vector2(end.X - dst.X, end.Y + dst.Y); + Vector2 d1 = control1 - start; Vector2 d2 = control2 - control1; Vector2 d3 = end - control2; @@ -138,30 +119,15 @@ namespace FlaxEditor.Surface.Elements var highlight = 1 + Mathf.Max(startHighlight, targetBox.ConnectionsHighlightIntensity); var color = _currentTypeColor * highlight; - // if (PointInRectangle(ref startPos, ref endPos, ref mousePosition)) + if (IntersectsConnection(ref startPos, ref endPos, ref mousePosition, 100f / Surface.ViewScale)) { - for (int ix = 0; ix < 10000; ix++) - { - OutputBox.IntersectsConnection(ref startPos, ref endPos, ref mousePosition, 30f); - } - if (OutputBox.IntersectsConnection(ref startPos, ref endPos, ref mousePosition, 30f)) - { - highlight += 2; - } + highlight += 2; } - DrawConnection(ref startPos, ref endPos, ref color, highlight); } } - private bool PointInRectangle(ref Vector2 a, ref Vector2 b, ref Vector2 point) - { - // This has the convenient property that it doesn't matter if a and b are switched - Vector2 signs = (point - a) * (b - point); - return signs.X >= 0 && signs.Y >= 0; - } - /// /// Draw a selected connections coming from this box. /// diff --git a/Source/Editor/Surface/SurfaceNode.cs b/Source/Editor/Surface/SurfaceNode.cs index 2f79a4e8b..d1ea32da7 100644 --- a/Source/Editor/Surface/SurfaceNode.cs +++ b/Source/Editor/Surface/SurfaceNode.cs @@ -758,7 +758,7 @@ namespace FlaxEditor.Surface { if (Elements[j] is OutputBox ob && ob.HasAnyConnection) { - ob.DrawConnections(); + ob.DrawConnections(ref mousePosition); } } From 9265f23681fe749048f30677a208fd3b8a16ec1a Mon Sep 17 00:00:00 2001 From: stefnotch Date: Thu, 25 Feb 2021 20:22:54 +0100 Subject: [PATCH 07/15] Basic reroute nodes --- Source/Editor/Surface/Archetypes/Tools.cs | 126 ++++++++++++++++++ Source/Editor/Surface/Elements/OutputBox.cs | 40 ++++-- Source/Editor/Surface/SurfaceNode.cs | 3 +- Source/Editor/Surface/VisjectSurface.Input.cs | 44 +++++- 4 files changed, 195 insertions(+), 18 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Tools.cs b/Source/Editor/Surface/Archetypes/Tools.cs index 2b63bdfb2..6330b27dd 100644 --- a/Source/Editor/Surface/Archetypes/Tools.cs +++ b/Source/Editor/Surface/Archetypes/Tools.cs @@ -983,6 +983,115 @@ namespace FlaxEditor.Surface.Archetypes } } + private class RerouteNode : SurfaceNode + { + public static readonly Vector2 DefaultSize = new Vector2(16); + + private bool _deleteNode; + + /// + protected override bool ShowTooltip => false; + + /// + public RerouteNode(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch) + : base(id, context, nodeArch, groupArch) + { + Size = DefaultSize; + Title = string.Empty; + BackgroundColor = Color.Transparent; + } + + /// + public override void OnSurfaceLoaded() + { + base.OnSurfaceLoaded(); + + var inputBox = GetBox(0); + var outputBox = GetBox(1); + inputBox.Location = Vector2.Zero; + outputBox.Location = Vector2.Zero; + + inputBox.Visible = false; + outputBox.Visible = false; + } + + /// + public override void ConnectionTick(Box box) + { + base.ConnectionTick(box); + + var inputBox = GetBox(0); + var outputBox = GetBox(1); + + _deleteNode = !inputBox.HasAnyConnection; + outputBox.Visible = !outputBox.HasAnyConnection; + } + + /*public override void OnDeleted() + { + var inputBox = GetBox(0); + var outputBox = GetBox(1); + + var connectionA = inputBox.HasAnyConnection ? inputBox.Connections[0] : null; // This doesn't work + var connectionB = outputBox.HasAnyConnection ? outputBox.Connections[0] : null; + + base.OnDeleted(); + + if (connectionA != null && connectionB != null) + { + connectionA.CreateConnection(connectionB); // TODO: handle undo + } + }*/ + + public override void Update(float deltaTime) + { + base.Update(deltaTime); + + if (_deleteNode) + { + _deleteNode = false; + Surface.Delete(this); // TODO: handle undo + } + } + + + /// + public override bool CanSelect(ref Vector2 location) + { + return new Rectangle(Location, DefaultSize).Contains(ref location); + } + + /// + protected override void UpdateRectangles() + { + _headerRect = Rectangle.Empty; + _closeButtonRect = Rectangle.Empty; + _footerRect = Rectangle.Empty; + } + + public override void Draw() + { + var style = Surface.Style; + var inputBox = GetBox(0); + var connectionColor = style.Colors.Default; + + if (inputBox.HasAnyConnection) + { + var hints = inputBox.Connections[0].ParentNode.Archetype.ConnectionsHints; + Surface.Style.GetConnectionColor(inputBox.Connections[0].CurrentType, hints, out connectionColor); + } + + //float barHeight = 4; + //Render2D.FillRectangle(new Rectangle(DefaultSize.X / 2, (DefaultSize.Y - barHeight) / 2, barHeight * 4, barHeight), connectionColor); + + SpriteHandle icon = style.Icons.BoxClose; + Render2D.DrawSprite(icon, new Rectangle(Vector2.Zero, DefaultSize), connectionColor); + + base.Draw(); + + } + } + /// /// The nodes for that group. /// @@ -1438,6 +1547,23 @@ namespace FlaxEditor.Surface.Archetypes NodeElementArchetype.Factory.Input(0, string.Empty, true, typeof(object), 1), } }, + new NodeArchetype + { + TypeID = 29, + Title = "Reroute", + Create = (id, context, arch, groupArch) => new RerouteNode(id, context, arch, groupArch), + Description = "Reroute a connection.", + Flags = NodeFlags.NoCloseButton | NodeFlags.NoSpawnViaGUI | NodeFlags.AllGraphs, + Size = RerouteNode.DefaultSize, + ConnectionsHints = ConnectionsHint.All, + IndependentBoxes = new int[] { 0 }, + DependentBoxes = new int[] { 1 }, + Elements = new[] + { + NodeElementArchetype.Factory.Input(0, string.Empty, true, null, 0), + NodeElementArchetype.Factory.Output(0, string.Empty, null, 1), + } + }, }; } } diff --git a/Source/Editor/Surface/Elements/OutputBox.cs b/Source/Editor/Surface/Elements/OutputBox.cs index 852132baf..683c7eebd 100644 --- a/Source/Editor/Surface/Elements/OutputBox.cs +++ b/Source/Editor/Surface/Elements/OutputBox.cs @@ -13,6 +13,11 @@ namespace FlaxEditor.Surface.Elements [HideInEditor] public class OutputBox : Box { + /// + /// Distance for the mouse to be considered above the connection + /// + public float MouseOverConnectionDistance => 100f / Surface.ViewScale; + /// public OutputBox(SurfaceNode parentNode, NodeElementArchetype archetype) : base(parentNode, archetype, archetype.Position + new Vector2(parentNode.Archetype.Size.X, 0)) @@ -44,6 +49,18 @@ namespace FlaxEditor.Surface.Elements */ } + /// + /// Checks if a point intersects a connection + /// + /// The other box. + /// The mouse position + public bool IntersectsConnection(Box targetBox, ref Vector2 mousePosition) + { + var startPos = Parent.PointToParent(Center); + Vector2 endPos = targetBox.Parent.PointToParent(targetBox.Center); + return IntersectsConnection(ref startPos, ref endPos, ref mousePosition, MouseOverConnectionDistance); + } + /// /// Checks if a point intersects a bezier curve /// @@ -53,8 +70,6 @@ namespace FlaxEditor.Surface.Elements /// Distance at which its an intersection public static bool IntersectsConnection(ref Vector2 start, ref Vector2 end, ref Vector2 point, float distance) { - // Maybe I should make this a non-static method and automatically read the Surface.ViewScale? - // Pretty much a point in rectangle check if ((point.X - start.X) * (end.X - point.X) < 0) return false; @@ -82,6 +97,7 @@ namespace FlaxEditor.Surface.Elements float t = i * segmentCountInv; Bezier(ref start, ref control1, ref control2, ref end, t, out p); + // Maybe it would be reasonable to return the point? CollisionsHelper.ClosestPointPointLine(ref point, ref oldp, ref p, out Vector2 result); if (Vector2.DistanceSquared(point, result) <= squaredDistance) { @@ -106,22 +122,21 @@ namespace FlaxEditor.Surface.Elements /// public void DrawConnections(ref Vector2 mousePosition) { + float mouseOverDistance = MouseOverConnectionDistance; // Draw all the connections - var center = Size * 0.5f; - var tmp = PointToParent(ref center); - var startPos = Parent.PointToParent(ref tmp); + var startPos = Parent.PointToParent(Center); var startHighlight = ConnectionsHighlightIntensity; for (int i = 0; i < Connections.Count; i++) { Box targetBox = Connections[i]; - tmp = targetBox.PointToParent(ref center); - Vector2 endPos = targetBox.Parent.PointToParent(ref tmp); + Vector2 endPos = targetBox.Parent.PointToParent(targetBox.Center); var highlight = 1 + Mathf.Max(startHighlight, targetBox.ConnectionsHighlightIntensity); var color = _currentTypeColor * highlight; - if (IntersectsConnection(ref startPos, ref endPos, ref mousePosition, 100f / Surface.ViewScale)) + // TODO: Figure out how to only draw the topmost connection + if (IntersectsConnection(ref startPos, ref endPos, ref mousePosition, mouseOverDistance)) { - highlight += 2; + highlight += 1; } DrawConnection(ref startPos, ref endPos, ref color, highlight); @@ -134,11 +149,8 @@ namespace FlaxEditor.Surface.Elements public void DrawSelectedConnection(Box targetBox) { // Draw all the connections - var center = Size * 0.5f; - var tmp = PointToParent(ref center); - var startPos = Parent.PointToParent(ref tmp); - tmp = targetBox.PointToParent(ref center); - Vector2 endPos = targetBox.Parent.PointToParent(ref tmp); + var startPos = Parent.PointToParent(Center); + Vector2 endPos = targetBox.Parent.PointToParent(targetBox.Center); DrawConnection(ref startPos, ref endPos, ref _currentTypeColor, 2); } diff --git a/Source/Editor/Surface/SurfaceNode.cs b/Source/Editor/Surface/SurfaceNode.cs index d1ea32da7..3c544dbdd 100644 --- a/Source/Editor/Surface/SurfaceNode.cs +++ b/Source/Editor/Surface/SurfaceNode.cs @@ -125,6 +125,7 @@ namespace FlaxEditor.Surface AutoFocus = false; TooltipText = nodeArch.Description; CullChildren = false; + BackgroundColor = Style.Current.BackgroundNormal; if (Archetype.DefaultValues != null) { @@ -947,7 +948,7 @@ namespace FlaxEditor.Surface // Background var backgroundRect = new Rectangle(Vector2.Zero, Size); - Render2D.FillRectangle(backgroundRect, style.BackgroundNormal); + Render2D.FillRectangle(backgroundRect, BackgroundColor); // Breakpoint hit if (Breakpoint.Hit) diff --git a/Source/Editor/Surface/VisjectSurface.Input.cs b/Source/Editor/Surface/VisjectSurface.Input.cs index 28cff42b8..b28ac340f 100644 --- a/Source/Editor/Surface/VisjectSurface.Input.cs +++ b/Source/Editor/Surface/VisjectSurface.Input.cs @@ -274,12 +274,24 @@ namespace FlaxEditor.Surface bool handled = base.OnMouseDoubleClick(location, button); if (!handled) CustomMouseDoubleClick?.Invoke(ref location, button, ref handled); - if (handled) + + if (!handled) { - return true; + var mousePos = _rootControl.PointFromParent(ref _mousePos); + if (IntersectsConnection(mousePos, out OutputBox outputBox, out Box connectedBox)) + { + var rerouteNode = Context.SpawnNode(7, 29, mousePos); + + // TODO: Undo action + outputBox.BreakConnection(connectedBox); + outputBox.CreateConnection(rerouteNode.GetBoxes().First(b => !b.IsOutput)); + rerouteNode.GetBoxes().First(b => b.IsOutput).CreateConnection(connectedBox); + + handled = true; + } } - return false; + return handled; } /// @@ -815,5 +827,31 @@ namespace FlaxEditor.Surface yLocation ); } + + private bool IntersectsConnection(Vector2 mousePosition, out OutputBox outputBox, out Box connectedBox) + { + for (int i = 0; i < Nodes.Count; i++) + { + for (int j = 0; j < Nodes[i].Elements.Count; j++) + { + if (Nodes[i].Elements[j] is OutputBox ob) + { + for (int k = 0; k < ob.Connections.Count; k++) + { + if (ob.IntersectsConnection(ob.Connections[k], ref mousePosition)) + { + outputBox = ob; + connectedBox = ob.Connections[k]; + return true; + } + } + } + } + } + + outputBox = null; + connectedBox = null; + return false; + } } } From fd802e54fbe8cdc4e3f6d9900bfd8fcbb1d06e6c Mon Sep 17 00:00:00 2001 From: stefnotch Date: Thu, 25 Feb 2021 22:01:45 +0100 Subject: [PATCH 08/15] Implement reroute node code --- Source/Engine/Visject/ShaderGraph.cpp | 5 +++++ Source/Engine/Visject/VisjectGraph.cpp | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/Source/Engine/Visject/ShaderGraph.cpp b/Source/Engine/Visject/ShaderGraph.cpp index c8a8782e9..0e1fe12c4 100644 --- a/Source/Engine/Visject/ShaderGraph.cpp +++ b/Source/Engine/Visject/ShaderGraph.cpp @@ -764,6 +764,11 @@ void ShaderGenerator::ProcessGroupTools(Box* box, Node* node, Value& value) #undef PLATFORM_CASE break; } + case 29: + { + value = tryGetValue(node->GetBox(0), Value::Zero); + break; + } default: break; } diff --git a/Source/Engine/Visject/VisjectGraph.cpp b/Source/Engine/Visject/VisjectGraph.cpp index 939013ebd..c8a7a7b19 100644 --- a/Source/Engine/Visject/VisjectGraph.cpp +++ b/Source/Engine/Visject/VisjectGraph.cpp @@ -732,6 +732,12 @@ void VisjectExecutor::ProcessGroupTools(Box* box, Node* node, Value& value) value = Scripting::FindObject((Guid)node->Values[0]); break; } + + case 29: + { + value = tryGetValue(node->GetBox(0), Value::Zero); + break; + } default: break; } From 9ed0ace01e1feb5399936985da317d7acd9879f1 Mon Sep 17 00:00:00 2001 From: stefnotch Date: Fri, 26 Feb 2021 09:14:16 +0100 Subject: [PATCH 09/15] Tweak DrawFPS documentation comment --- Source/Engine/Engine/Time.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Source/Engine/Engine/Time.h b/Source/Engine/Engine/Time.h index 0d44afa22..eb176ae7c 100644 --- a/Source/Engine/Engine/Time.h +++ b/Source/Engine/Engine/Time.h @@ -121,8 +121,11 @@ public: API_FIELD() static float PhysicsFPS; /// - /// The target amount of the frames rendered per second (actual game FPS). + /// The target amount of the frames rendered per second (target game FPS). /// + /// + /// To get the actual game FPS use + /// API_FIELD() static float DrawFPS; /// From 69162ad8d985f14e786171cad90457e6a5a1297b Mon Sep 17 00:00:00 2001 From: stefnotch Date: Fri, 26 Feb 2021 10:43:31 +0100 Subject: [PATCH 10/15] Fix visject connections redo Previously, it would create double connections --- Source/Editor/Surface/Undo/BoxHandle.cs | 24 ++++++++++++++++++- .../Editor/Surface/Undo/ConnectBoxesAction.cs | 9 ++++--- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/Source/Editor/Surface/Undo/BoxHandle.cs b/Source/Editor/Surface/Undo/BoxHandle.cs index f50adcfeb..4b77b95f7 100644 --- a/Source/Editor/Surface/Undo/BoxHandle.cs +++ b/Source/Editor/Surface/Undo/BoxHandle.cs @@ -10,7 +10,7 @@ namespace FlaxEditor.Surface.Undo /// The helper structure for Surface node box handle. /// [HideInEditor] - public struct BoxHandle + public struct BoxHandle : IEquatable { private readonly uint _nodeId; private readonly int _boxId; @@ -51,5 +51,27 @@ namespace FlaxEditor.Surface.Undo throw new Exception("Missing box."); return box; } + + /// + public override bool Equals(object obj) + { + return obj is BoxHandle handle && Equals(handle); + } + + /// + public bool Equals(BoxHandle other) + { + return _nodeId == other._nodeId && + _boxId == other._boxId; + } + + /// + public override int GetHashCode() + { + unchecked + { + return (_nodeId.GetHashCode() * 397) ^ _boxId.GetHashCode(); + } + } } } diff --git a/Source/Editor/Surface/Undo/ConnectBoxesAction.cs b/Source/Editor/Surface/Undo/ConnectBoxesAction.cs index 6627364ed..a4e59a8ac 100644 --- a/Source/Editor/Surface/Undo/ConnectBoxesAction.cs +++ b/Source/Editor/Surface/Undo/ConnectBoxesAction.cs @@ -104,9 +104,12 @@ namespace FlaxEditor.Surface.Undo } for (int i = 0; i < output.Length; i++) { - var box = output[i].Get(context); - oB.Connections.Add(box); - box.Connections.Add(oB); + if (!output[i].Equals(_input)) + { + var box = output[i].Get(context); + oB.Connections.Add(box); + box.Connections.Add(oB); + } } toUpdate.AddRange(iB.Connections); From 70b0aa25b620c4a0cd6b8edd61c00f3d522f7a70 Mon Sep 17 00:00:00 2001 From: stefnotch Date: Fri, 26 Feb 2021 11:14:22 +0100 Subject: [PATCH 11/15] Undo-able reroute node adding --- Source/Editor/Surface/VisjectSurface.Input.cs | 41 +++++++++++++++---- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/Source/Editor/Surface/VisjectSurface.Input.cs b/Source/Editor/Surface/VisjectSurface.Input.cs index b28ac340f..dc3406e45 100644 --- a/Source/Editor/Surface/VisjectSurface.Input.cs +++ b/Source/Editor/Surface/VisjectSurface.Input.cs @@ -278,14 +278,37 @@ namespace FlaxEditor.Surface if (!handled) { var mousePos = _rootControl.PointFromParent(ref _mousePos); - if (IntersectsConnection(mousePos, out OutputBox outputBox, out Box connectedBox)) + if (IntersectsConnection(mousePos, out InputBox inputBox, out OutputBox outputBox)) { - var rerouteNode = Context.SpawnNode(7, 29, mousePos); + if (Undo != null) + { + bool undoEnabled = Undo.Enabled; + Undo.Enabled = false; + var rerouteNode = Context.SpawnNode(7, 29, mousePos); + Undo.Enabled = undoEnabled; - // TODO: Undo action - outputBox.BreakConnection(connectedBox); - outputBox.CreateConnection(rerouteNode.GetBoxes().First(b => !b.IsOutput)); - rerouteNode.GetBoxes().First(b => b.IsOutput).CreateConnection(connectedBox); + var spawnNodeAction = new AddRemoveNodeAction(rerouteNode, true); + + var disconnectBoxesAction = new ConnectBoxesAction(inputBox, outputBox, false); + inputBox.BreakConnection(outputBox); + disconnectBoxesAction.End(); + + var addConnectionsAction = new EditNodeConnections(Context, rerouteNode); + outputBox.CreateConnection(rerouteNode.GetBoxes().First(b => !b.IsOutput)); + rerouteNode.GetBoxes().First(b => b.IsOutput).CreateConnection(inputBox); + addConnectionsAction.End(); + + + Undo.AddAction(new MultiUndoAction(spawnNodeAction, disconnectBoxesAction, addConnectionsAction)); + } + else + { + var rerouteNode = Context.SpawnNode(7, 29, mousePos); + inputBox.BreakConnection(outputBox); + outputBox.CreateConnection(rerouteNode.GetBoxes().First(b => !b.IsOutput)); + rerouteNode.GetBoxes().First(b => b.IsOutput).CreateConnection(inputBox); + } + MarkAsEdited(); handled = true; } @@ -828,7 +851,7 @@ namespace FlaxEditor.Surface ); } - private bool IntersectsConnection(Vector2 mousePosition, out OutputBox outputBox, out Box connectedBox) + private bool IntersectsConnection(Vector2 mousePosition, out InputBox inputBox, out OutputBox outputBox) { for (int i = 0; i < Nodes.Count; i++) { @@ -841,7 +864,7 @@ namespace FlaxEditor.Surface if (ob.IntersectsConnection(ob.Connections[k], ref mousePosition)) { outputBox = ob; - connectedBox = ob.Connections[k]; + inputBox = ob.Connections[k] as InputBox; return true; } } @@ -850,7 +873,7 @@ namespace FlaxEditor.Surface } outputBox = null; - connectedBox = null; + inputBox = null; return false; } } From 0334329c507ceea0a33d2caf98ac61266c3d5439 Mon Sep 17 00:00:00 2001 From: stefnotch Date: Fri, 26 Feb 2021 12:55:32 +0100 Subject: [PATCH 12/15] Handle lost connections in reroute nodes An alternative solution to that problem --- Source/Editor/Surface/Archetypes/Tools.cs | 49 +++++++++++------------ 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Tools.cs b/Source/Editor/Surface/Archetypes/Tools.cs index 6330b27dd..cd5a0a0b9 100644 --- a/Source/Editor/Surface/Archetypes/Tools.cs +++ b/Source/Editor/Surface/Archetypes/Tools.cs @@ -1023,38 +1023,23 @@ namespace FlaxEditor.Surface.Archetypes var inputBox = GetBox(0); var outputBox = GetBox(1); - _deleteNode = !inputBox.HasAnyConnection; + inputBox.Visible = !inputBox.HasAnyConnection; outputBox.Visible = !outputBox.HasAnyConnection; + + _deleteNode = !inputBox.HasAnyConnection && !outputBox.HasAnyConnection; } - /*public override void OnDeleted() - { - var inputBox = GetBox(0); - var outputBox = GetBox(1); - - var connectionA = inputBox.HasAnyConnection ? inputBox.Connections[0] : null; // This doesn't work - var connectionB = outputBox.HasAnyConnection ? outputBox.Connections[0] : null; - - base.OnDeleted(); - - if (connectionA != null && connectionB != null) - { - connectionA.CreateConnection(connectionB); // TODO: handle undo - } - }*/ - + /// public override void Update(float deltaTime) { base.Update(deltaTime); if (_deleteNode) { - _deleteNode = false; - Surface.Delete(this); // TODO: handle undo + Surface.Delete(this); } } - /// public override bool CanSelect(ref Vector2 location) { @@ -1073,7 +1058,10 @@ namespace FlaxEditor.Surface.Archetypes { var style = Surface.Style; var inputBox = GetBox(0); + var outputBox = GetBox(1); var connectionColor = style.Colors.Default; + float barHorizontalOffset = -2; + float barHeight = 3; if (inputBox.HasAnyConnection) { @@ -1081,14 +1069,25 @@ namespace FlaxEditor.Surface.Archetypes Surface.Style.GetConnectionColor(inputBox.Connections[0].CurrentType, hints, out connectionColor); } - //float barHeight = 4; - //Render2D.FillRectangle(new Rectangle(DefaultSize.X / 2, (DefaultSize.Y - barHeight) / 2, barHeight * 4, barHeight), connectionColor); + if (!inputBox.HasAnyConnection) + { + Render2D.FillRectangle(new Rectangle(-barHorizontalOffset - barHeight * 4, (DefaultSize.Y - barHeight) / 2, barHeight * 2, barHeight), connectionColor); + } - SpriteHandle icon = style.Icons.BoxClose; - Render2D.DrawSprite(icon, new Rectangle(Vector2.Zero, DefaultSize), connectionColor); + if (!outputBox.HasAnyConnection) + { + Render2D.FillRectangle(new Rectangle(DefaultSize.X + barHorizontalOffset, (DefaultSize.Y - barHeight) / 2, barHeight * 2, barHeight), connectionColor); + } + + if (inputBox.HasAnyConnection && outputBox.HasAnyConnection) + { + var hints = inputBox.Connections[0].ParentNode.Archetype.ConnectionsHints; + Surface.Style.GetConnectionColor(inputBox.Connections[0].CurrentType, hints, out connectionColor); + SpriteHandle icon = style.Icons.BoxClose; + Render2D.DrawSprite(icon, new Rectangle(Vector2.Zero, DefaultSize), connectionColor); + } base.Draw(); - } } From ebfa3822efe9b571db850b900591d3d49b42175d Mon Sep 17 00:00:00 2001 From: stefnotch Date: Fri, 26 Feb 2021 13:19:53 +0100 Subject: [PATCH 13/15] Fixes for the reroute node --- Source/Editor/Surface/Archetypes/Tools.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Tools.cs b/Source/Editor/Surface/Archetypes/Tools.cs index cd5a0a0b9..dbec033bd 100644 --- a/Source/Editor/Surface/Archetypes/Tools.cs +++ b/Source/Editor/Surface/Archetypes/Tools.cs @@ -1011,8 +1011,7 @@ namespace FlaxEditor.Surface.Archetypes inputBox.Location = Vector2.Zero; outputBox.Location = Vector2.Zero; - inputBox.Visible = false; - outputBox.Visible = false; + UpdateBoxes(); } /// @@ -1020,6 +1019,11 @@ namespace FlaxEditor.Surface.Archetypes { base.ConnectionTick(box); + UpdateBoxes(); + } + + private void UpdateBoxes() + { var inputBox = GetBox(0); var outputBox = GetBox(1); @@ -1036,7 +1040,8 @@ namespace FlaxEditor.Surface.Archetypes if (_deleteNode) { - Surface.Delete(this); + _deleteNode = false; + // Surface.Delete(this); // Doesn't work with undo-ing } } @@ -1071,7 +1076,7 @@ namespace FlaxEditor.Surface.Archetypes if (!inputBox.HasAnyConnection) { - Render2D.FillRectangle(new Rectangle(-barHorizontalOffset - barHeight * 4, (DefaultSize.Y - barHeight) / 2, barHeight * 2, barHeight), connectionColor); + Render2D.FillRectangle(new Rectangle(-barHorizontalOffset - barHeight * 2, (DefaultSize.Y - barHeight) / 2, barHeight * 2, barHeight), connectionColor); } if (!outputBox.HasAnyConnection) From 09f8ad948a4f23ee4b25cca526ce9aebcb58a391 Mon Sep 17 00:00:00 2001 From: stefnotch Date: Fri, 26 Feb 2021 19:08:25 +0100 Subject: [PATCH 14/15] Prevent multiple connections on reroute node --- Source/Editor/Surface/Archetypes/Tools.cs | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/Source/Editor/Surface/Archetypes/Tools.cs b/Source/Editor/Surface/Archetypes/Tools.cs index dbec033bd..e5513b598 100644 --- a/Source/Editor/Surface/Archetypes/Tools.cs +++ b/Source/Editor/Surface/Archetypes/Tools.cs @@ -987,8 +987,6 @@ namespace FlaxEditor.Surface.Archetypes { public static readonly Vector2 DefaultSize = new Vector2(16); - private bool _deleteNode; - /// protected override bool ShowTooltip => false; @@ -1029,20 +1027,6 @@ namespace FlaxEditor.Surface.Archetypes inputBox.Visible = !inputBox.HasAnyConnection; outputBox.Visible = !outputBox.HasAnyConnection; - - _deleteNode = !inputBox.HasAnyConnection && !outputBox.HasAnyConnection; - } - - /// - public override void Update(float deltaTime) - { - base.Update(deltaTime); - - if (_deleteNode) - { - _deleteNode = false; - // Surface.Delete(this); // Doesn't work with undo-ing - } } /// @@ -1565,7 +1549,7 @@ namespace FlaxEditor.Surface.Archetypes Elements = new[] { NodeElementArchetype.Factory.Input(0, string.Empty, true, null, 0), - NodeElementArchetype.Factory.Output(0, string.Empty, null, 1), + NodeElementArchetype.Factory.Output(0, string.Empty, null, 1, true), } }, }; From f3c608f2f3a155134a3bd44a6f8bf769395e2d29 Mon Sep 17 00:00:00 2001 From: stefnotch Date: Fri, 26 Feb 2021 19:09:59 +0100 Subject: [PATCH 15/15] Improve arrow key navigation --- Source/Editor/Surface/Elements/OutputBox.cs | 4 +- Source/Editor/Surface/SurfaceNode.cs | 52 +++++++++--- Source/Editor/Surface/VisjectSurface.Draw.cs | 1 + Source/Editor/Surface/VisjectSurface.Input.cs | 82 +++++++------------ Source/Editor/Surface/VisjectSurface.cs | 3 + 5 files changed, 78 insertions(+), 64 deletions(-) diff --git a/Source/Editor/Surface/Elements/OutputBox.cs b/Source/Editor/Surface/Elements/OutputBox.cs index 683c7eebd..16094d621 100644 --- a/Source/Editor/Surface/Elements/OutputBox.cs +++ b/Source/Editor/Surface/Elements/OutputBox.cs @@ -136,7 +136,7 @@ namespace FlaxEditor.Surface.Elements // TODO: Figure out how to only draw the topmost connection if (IntersectsConnection(ref startPos, ref endPos, ref mousePosition, mouseOverDistance)) { - highlight += 1; + highlight += 0.5f; } DrawConnection(ref startPos, ref endPos, ref color, highlight); @@ -151,7 +151,7 @@ namespace FlaxEditor.Surface.Elements // Draw all the connections var startPos = Parent.PointToParent(Center); Vector2 endPos = targetBox.Parent.PointToParent(targetBox.Center); - DrawConnection(ref startPos, ref endPos, ref _currentTypeColor, 2); + DrawConnection(ref startPos, ref endPos, ref _currentTypeColor, 2.5f); } /// diff --git a/Source/Editor/Surface/SurfaceNode.cs b/Source/Editor/Surface/SurfaceNode.cs index 3c544dbdd..9c6f55004 100644 --- a/Source/Editor/Surface/SurfaceNode.cs +++ b/Source/Editor/Surface/SurfaceNode.cs @@ -763,25 +763,29 @@ namespace FlaxEditor.Surface } } + } + + /// + /// Draws all selected connections between surface objects related to this node. + /// + /// The index of the currently selected connection. + public void DrawSelectedConnections(int selectedConnectionIndex) + { if (_isSelected) { - bool hasBoxesSelection = HasBoxesSelection; - for (int j = 0; j < Elements.Count; j++) + if (HasBoxesSelection) { - if (Elements[j] is Box box && box.HasAnyConnection && (!hasBoxesSelection || box.IsSelected)) + for (int j = 0; j < Elements.Count; j++) { - if (box is OutputBox ob) + if (Elements[j] is Box box && box.IsSelected && selectedConnectionIndex < box.Connections.Count) { - for (int i = 0; i < ob.Connections.Count; i++) + if (box is OutputBox ob) { - ob.DrawSelectedConnection(ob.Connections[i]); + ob.DrawSelectedConnection(ob.Connections[selectedConnectionIndex]); } - } - else - { - for (int i = 0; i < box.Connections.Count; i++) + else { - if (box.Connections[i] is OutputBox outputBox) + if (box.Connections[selectedConnectionIndex] is OutputBox outputBox) { outputBox.DrawSelectedConnection(box); } @@ -789,6 +793,32 @@ namespace FlaxEditor.Surface } } } + else + { + for (int j = 0; j < Elements.Count; j++) + { + if (Elements[j] is Box box) + { + if (box is OutputBox ob) + { + for (int i = 0; i < ob.Connections.Count; i++) + { + ob.DrawSelectedConnection(ob.Connections[i]); + } + } + else + { + for (int i = 0; i < box.Connections.Count; i++) + { + if (box.Connections[i] is OutputBox outputBox) + { + outputBox.DrawSelectedConnection(box); + } + } + } + } + } + } } } diff --git a/Source/Editor/Surface/VisjectSurface.Draw.cs b/Source/Editor/Surface/VisjectSurface.Draw.cs index 488324a0a..016e0083f 100644 --- a/Source/Editor/Surface/VisjectSurface.Draw.cs +++ b/Source/Editor/Surface/VisjectSurface.Draw.cs @@ -103,6 +103,7 @@ namespace FlaxEditor.Surface for (int i = 0; i < Nodes.Count; i++) { Nodes[i].DrawConnections(ref mousePosition); + Nodes[i].DrawSelectedConnections(_selectedConnectionIndex); } } diff --git a/Source/Editor/Surface/VisjectSurface.Input.cs b/Source/Editor/Surface/VisjectSurface.Input.cs index dc3406e45..996f9d2e0 100644 --- a/Source/Editor/Surface/VisjectSurface.Input.cs +++ b/Source/Editor/Surface/VisjectSurface.Input.cs @@ -562,89 +562,69 @@ namespace FlaxEditor.Surface } return true; } - - if (key == KeyboardKeys.ArrowUp) + if (key == KeyboardKeys.ArrowUp || key == KeyboardKeys.ArrowDown) { Box selectedBox = GetSelectedBox(SelectedNodes); - Box toSelect = selectedBox?.ParentNode.GetPreviousBox(selectedBox); + if (selectedBox == null) return true; - if (toSelect != null) + Box toSelect = (key == KeyboardKeys.ArrowUp) ? + selectedBox?.ParentNode.GetPreviousBox(selectedBox) : + selectedBox?.ParentNode.GetNextBox(selectedBox); + + if (toSelect != null && toSelect.IsOutput == selectedBox.IsOutput) { - if (toSelect.Connections.Count > 1) - { - // Box has multiple connections - } - - if (toSelect.IsOutput != selectedBox.IsOutput) - { - // Jump up (nodes) - } - else - { - Select(toSelect.ParentNode); - toSelect.ParentNode.SelectBox(toSelect); - } + Select(toSelect.ParentNode); + toSelect.ParentNode.SelectBox(toSelect); } - - return true; } - if (key == KeyboardKeys.ArrowDown) + + if (key == KeyboardKeys.Tab) { Box selectedBox = GetSelectedBox(SelectedNodes); - Box toSelect = selectedBox?.ParentNode.GetNextBox(selectedBox); + if (selectedBox == null) return true; - if (toSelect != null) + int connectionCount = selectedBox.Connections.Count; + if (connectionCount == 0) return true; + + if (Root.GetKey(KeyboardKeys.Shift)) { - if (toSelect.Connections.Count > 1) - { - // Box has multiple connections - } - - if (toSelect.IsOutput != selectedBox.IsOutput) - { - // Jump down (nodes) - } - else - { - Select(toSelect.ParentNode); - toSelect.ParentNode.SelectBox(toSelect); - } + _selectedConnectionIndex = ((_selectedConnectionIndex - 1) % connectionCount + connectionCount) % connectionCount; + } + else + { + _selectedConnectionIndex = (_selectedConnectionIndex + 1) % connectionCount; } - - return true; } + if (key == KeyboardKeys.ArrowRight || key == KeyboardKeys.ArrowLeft) { Box selectedBox = GetSelectedBox(SelectedNodes); - if (selectedBox == null) return false; + if (selectedBox == null) return true; Box toSelect = null; - if (key == KeyboardKeys.ArrowRight && selectedBox.IsOutput || key == KeyboardKeys.ArrowLeft && !selectedBox.IsOutput) + if ((key == KeyboardKeys.ArrowRight && selectedBox.IsOutput) || (key == KeyboardKeys.ArrowLeft && !selectedBox.IsOutput)) { - if (selectedBox.Connections.Count > 1) + if (_selectedConnectionIndex < 0 || _selectedConnectionIndex >= selectedBox.Connections.Count) { - // Box has multiple connections - } - else if (selectedBox.Connections.Count == 1) - { - toSelect = selectedBox.Connections[0]; + _selectedConnectionIndex = 0; } + toSelect = selectedBox.Connections[_selectedConnectionIndex]; } else { // Use the node with the closest Y-level // Since there are cases like 3 nodes on one side and only 1 node on the other side - var elements = selectedBox?.ParentNode.Elements; - float distance = float.PositiveInfinity; + var elements = selectedBox.ParentNode.Elements; + float minDistance = float.PositiveInfinity; for (int i = 0; i < elements.Count; i++) { - if (elements[i] is Box box && box.IsOutput != selectedBox.IsOutput && Mathf.Abs(box.Y - selectedBox.Y) < distance) + if (elements[i] is Box box && box.IsOutput != selectedBox.IsOutput && Mathf.Abs(box.Y - selectedBox.Y) < minDistance) { toSelect = box; - distance = Mathf.Abs(box.Y - selectedBox.Y); + minDistance = Mathf.Abs(box.Y - selectedBox.Y); } } } diff --git a/Source/Editor/Surface/VisjectSurface.cs b/Source/Editor/Surface/VisjectSurface.cs index d98e2c421..c4b14ee5c 100644 --- a/Source/Editor/Surface/VisjectSurface.cs +++ b/Source/Editor/Surface/VisjectSurface.cs @@ -39,6 +39,7 @@ namespace FlaxEditor.Surface private GroupArchetype _customNodesGroup; private List _customNodes; private Action _onSave; + private int _selectedConnectionIndex; internal int _isUpdatingBoxTypes; @@ -362,6 +363,8 @@ namespace FlaxEditor.Surface Context.ControlSpawned += OnSurfaceControlSpawned; Context.ControlDeleted += OnSurfaceControlDeleted; + SelectionChanged += () => { _selectedConnectionIndex = 0; }; + // Init drag handlers DragHandlers.Add(_dragAssets = new DragAssets(ValidateDragItem)); DragHandlers.Add(_dragParameters = new DragNames(SurfaceParameter.DragPrefix, ValidateDragParameter));