diff --git a/Source/Editor/Surface/VisjectSurface.Draw.cs b/Source/Editor/Surface/VisjectSurface.Draw.cs index af5893907..d79b21e9c 100644 --- a/Source/Editor/Surface/VisjectSurface.Draw.cs +++ b/Source/Editor/Surface/VisjectSurface.Draw.cs @@ -213,6 +213,44 @@ namespace FlaxEditor.Surface } } + /// + /// Draw connection hints for lazy connect feature. + /// + protected virtual void DrawLazyConnect() + { + var style = FlaxEngine.GUI.Style.Current; + + if (_lazyConnectStartNode != null) + { + Float2 upperLeft = _rootControl.PointToParent(_lazyConnectStartNode.UpperLeft); + Rectangle startNodeOutline = new Rectangle(upperLeft + 1f, _lazyConnectStartNode.Size - 1f); + startNodeOutline.Size *= ViewScale; + Render2D.DrawRectangle(startNodeOutline.MakeExpanded(4f), style.BackgroundSelected, 4f); + } + + if (_lazyConnectEndNode != null) + { + Float2 upperLeft = _rootControl.PointToParent(_lazyConnectEndNode.UpperLeft); + Rectangle startNodeOutline = new Rectangle(upperLeft + 1f, _lazyConnectEndNode.Size - 1f); + startNodeOutline.Size *= ViewScale; + Render2D.DrawRectangle(startNodeOutline.MakeExpanded(4f), style.BackgroundSelected, 4f); + } + + Rectangle startRect = new Rectangle(_rightMouseDownPos - 6f, new Float2(12f)); + Rectangle endRect = new Rectangle(_mousePos - 6f, new Float2(12f)); + + // Start and end shadows/ outlines + Render2D.FillRectangle(startRect.MakeExpanded(2.5f), Color.Black); + Render2D.FillRectangle(endRect.MakeExpanded(2.5f), Color.Black); + + Render2D.DrawLine(_rightMouseDownPos, _mousePos, Color.Black, 7.5f); + Render2D.DrawLine(_rightMouseDownPos, _mousePos, style.ForegroundGrey, 5f); + + // Draw start and end boxes over the lines to hide ugly artifacts at the ends + Render2D.FillRectangle(startRect, style.ForegroundGrey); + Render2D.FillRectangle(endRect, style.ForegroundGrey); + } + /// /// Draws the contents of the surface (nodes, connections, comments, etc.). /// @@ -260,6 +298,9 @@ namespace FlaxEditor.Surface DrawContents(); + if (_isLazyConnecting) + DrawLazyConnect(); + //Render2D.DrawText(style.FontTitle, string.Format("Scale: {0}", _rootControl.Scale), rect, Enabled ? Color.Red : Color.Black); // Draw border diff --git a/Source/Editor/Surface/VisjectSurface.Input.cs b/Source/Editor/Surface/VisjectSurface.Input.cs index 388df0b9c..f7d90512e 100644 --- a/Source/Editor/Surface/VisjectSurface.Input.cs +++ b/Source/Editor/Surface/VisjectSurface.Input.cs @@ -29,6 +29,9 @@ namespace FlaxEditor.Surface private HashSet _movingNodes; private HashSet _temporarySelectedNodes; private readonly Stack _inputBrackets = new Stack(); + private bool _isLazyConnecting; + private SurfaceNode _lazyConnectStartNode; + private SurfaceNode _lazyConnectEndNode; private class InputBracket { @@ -250,8 +253,13 @@ namespace FlaxEditor.Surface // Cache mouse location _mousePos = location; + if (_isLazyConnecting && GetControlUnderMouse() is SurfaceNode nodeUnderMouse) + _lazyConnectEndNode = nodeUnderMouse; + else if (_isLazyConnecting && Nodes.Count > 0) + _lazyConnectEndNode = GetClosestNodeAtLocation(location); + // Moving around surface with mouse - if (_rightMouseDown) + if (_rightMouseDown && !_isLazyConnecting) { // Calculate delta var delta = location - _rightMouseDownPos; @@ -542,11 +550,17 @@ namespace FlaxEditor.Surface _middleMouseDownPos = location; } + if (root.GetKey(KeyboardKeys.Alt) && button == MouseButton.Right) + _isLazyConnecting = true; + // Check if any node is under the mouse SurfaceControl controlUnderMouse = GetControlUnderMouse(); var cLocation = _rootControl.PointFromParent(ref location); if (controlUnderMouse != null) { + if (controlUnderMouse is SurfaceNode node && _isLazyConnecting) + _lazyConnectStartNode = node; + // Check if mouse is over header and user is pressing mouse left button if (_leftMouseDown && controlUnderMouse.CanSelect(ref cLocation)) { @@ -581,6 +595,9 @@ namespace FlaxEditor.Surface } else { + if (_isLazyConnecting && Nodes.Count > 0) + _lazyConnectStartNode = GetClosestNodeAtLocation(location); + // Cache flags and state if (_leftMouseDown) { @@ -720,12 +737,36 @@ namespace FlaxEditor.Surface { // Check if any control is under the mouse _cmStartPos = location; - if (controlUnderMouse == null) + if (controlUnderMouse == null && !_isLazyConnecting) { showPrimaryMenu = true; } } _mouseMoveAmount = 0; + + if (_isLazyConnecting) + { + if (_lazyConnectStartNode != null && _lazyConnectEndNode != null && _lazyConnectStartNode != _lazyConnectEndNode) + { + // First check if there is a type matching input and output where input + OutputBox startNodeOutput = (OutputBox)_lazyConnectStartNode.GetBoxes().FirstOrDefault(b => b.IsOutput, null); + InputBox endNodeInput = null; + + if (startNodeOutput != null) + endNodeInput = (InputBox)_lazyConnectEndNode.GetBoxes().FirstOrDefault(b => !b.IsOutput && b.CurrentType == startNodeOutput.CurrentType && !b.HasAnyConnection && b.IsActive && b.CanConnectWith(startNodeOutput), null); + + // Perform less strict checks (less ideal conditions for connection but still good) if the first checks failed + if (endNodeInput == null) + endNodeInput = (InputBox)_lazyConnectEndNode.GetBoxes().FirstOrDefault(b => !b.IsOutput && !b.HasAnyConnection && b.CanConnectWith(startNodeOutput), null); + + if (startNodeOutput != null && endNodeInput != null) + TryConnect(startNodeOutput, endNodeInput); + } + + _isLazyConnecting = false; + _lazyConnectStartNode = null; + _lazyConnectEndNode = null; + } } if (_middleMouseDown && button == MouseButton.Middle) { @@ -927,6 +968,26 @@ namespace FlaxEditor.Surface return false; } + private SurfaceNode GetClosestNodeAtLocation(Float2 location) + { + SurfaceNode currentClosestNode = null; + float currentClosestDistanceSquared = float.MaxValue; + + foreach (var node in Nodes) + { + Float2 nodeSurfaceLocation = _rootControl.PointToParent(node.Center); + + float distanceSquared = Float2.DistanceSquared(location, nodeSurfaceLocation); + if (distanceSquared < currentClosestDistanceSquared) + { + currentClosestNode = node; + currentClosestDistanceSquared = distanceSquared; + } + } + + return currentClosestNode; + } + private void ResetInput() { InputText = "";