diff --git a/Source/Editor/Surface/Archetypes/Tools.cs b/Source/Editor/Surface/Archetypes/Tools.cs index 9ed131f58..d924f69f5 100644 --- a/Source/Editor/Surface/Archetypes/Tools.cs +++ b/Source/Editor/Surface/Archetypes/Tools.cs @@ -1007,10 +1007,13 @@ namespace FlaxEditor.Surface.Archetypes } } - private class RerouteNode : SurfaceNode + internal class RerouteNode : SurfaceNode, IConnectionInstigator { internal static readonly Float2 DefaultSize = new Float2(FlaxEditor.Surface.Constants.BoxSize); private Rectangle _localBounds; + private InputBox _input; + private OutputBox _output; + private bool _isMouseDown, _isConnecting; /// public RerouteNode(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch) @@ -1023,7 +1026,7 @@ namespace FlaxEditor.Surface.Archetypes } /// - protected override bool ShowTooltip => _localBounds.Contains(ref _mousePosition) && !Surface.IsLeftMouseButtonDown && !Surface.IsRightMouseButtonDown && !Surface.IsPrimaryMenuOpened; + protected override bool ShowTooltip => !string.IsNullOrEmpty(TooltipText) && _localBounds.Contains(ref _mousePosition) && !Surface.IsLeftMouseButtonDown && !Surface.IsRightMouseButtonDown && !Surface.IsPrimaryMenuOpened; /// public override bool OnTestTooltipOverControl(ref Float2 location) @@ -1047,11 +1050,21 @@ namespace FlaxEditor.Surface.Archetypes { base.OnSurfaceLoaded(); - var inputBox = GetBox(0); - var outputBox = GetBox(1); - inputBox.Location = Float2.Zero; - outputBox.Location = Float2.Zero; + _input = (InputBox)GetBox(0); + _output = (OutputBox)GetBox(1); + _input.Location = Float2.Zero; + _output.Location = Float2.Zero; + _input.Visible = false; + _output.Visible = false; + + _input.CurrentTypeChanged += OnInputBoxTypeChanged; + + UpdateBoxes(); + } + + private void OnInputBoxTypeChanged(Box inputBox) + { UpdateBoxes(); } @@ -1065,15 +1078,23 @@ namespace FlaxEditor.Surface.Archetypes private void UpdateBoxes() { - var inputBox = GetBox(0); - var outputBox = GetBox(1); + if (Surface == null) + return; - inputBox.Visible = !inputBox.HasAnyConnection; - outputBox.Visible = !outputBox.HasAnyConnection; - if (Surface != null) + var type = _input.CurrentType; + if (_input.TooltipText != null) { - TooltipText = Surface.GetTypeName(inputBox.HasAnyConnection ? inputBox.CurrentType : outputBox.CurrentType); + TooltipText = _input.TooltipText; } + else + { + type = _input.HasAnyConnection ? _input.CurrentType : _output.CurrentType; + TooltipText = Surface.GetTypeName(type); + } + + var isImpulse = type.IsVoid; + _input.IsSingle = !isImpulse; + _output.IsSingle = isImpulse; } /// @@ -1090,42 +1111,153 @@ namespace FlaxEditor.Surface.Archetypes _footerRect = Rectangle.Empty; } + /// public override void Draw() { var style = Surface.Style; - var inputBox = GetBox(0); - var outputBox = GetBox(1); var connectionColor = style.Colors.Default; + var type = ScriptType.Null; float barHorizontalOffset = -2; float barHeight = 3; - if (inputBox.HasAnyConnection) + if (_input.HasAnyConnection) { - var hints = inputBox.Connections[0].ParentNode.Archetype.ConnectionsHints; - Surface.Style.GetConnectionColor(inputBox.Connections[0].CurrentType, hints, out connectionColor); - } - - if (!inputBox.HasAnyConnection) - { - Render2D.FillRectangle(new Rectangle(-barHorizontalOffset - barHeight * 2, (DefaultSize.Y - barHeight) / 2, barHeight * 2, barHeight), 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 type = inputBox.Connections[0].CurrentType; - var hints = inputBox.Connections[0].ParentNode.Archetype.ConnectionsHints; + type = _input.Connections[0].CurrentType; + var hints = _input.Connections[0].ParentNode.Archetype.ConnectionsHints; Surface.Style.GetConnectionColor(type, hints, out connectionColor); - var icon = type.IsVoid ? style.Icons.ArrowClose : style.Icons.BoxClose; - Render2D.DrawSprite(icon, _localBounds, connectionColor); } + if (!_input.HasAnyConnection) + Render2D.FillRectangle(new Rectangle(-barHorizontalOffset - barHeight * 2, (DefaultSize.Y - barHeight) / 2, barHeight * 2, barHeight), connectionColor); + if (!_output.HasAnyConnection) + Render2D.FillRectangle(new Rectangle(DefaultSize.X + barHorizontalOffset, (DefaultSize.Y - barHeight) / 2, barHeight * 2, barHeight), connectionColor); + + SpriteHandle icon; + if (_input.HasAnyConnection && _output.HasAnyConnection) + icon = type.IsVoid ? style.Icons.ArrowClose : style.Icons.BoxClose; + else + icon = type.IsVoid ? style.Icons.ArrowOpen : style.Icons.BoxOpen; + Render2D.DrawSprite(icon, _localBounds, connectionColor); + base.Draw(); } + + /// + public override bool OnMouseDown(Float2 location, MouseButton button) + { + if (base.OnMouseDown(location, button)) + return true; + + if (button == MouseButton.Left) + { + _isMouseDown = true; + _isConnecting = _localBounds.MakeExpanded(-10.0f).Contains(ref location); // Inner area for connecting, outer area for moving + if (_isConnecting) + { + Focus(); + return true; + } + } + return false; + } + + /// + public override void OnMouseLeave() + { + if (_isMouseDown) + { + _isMouseDown = false; + if (Surface.CanEdit && _isConnecting) + Surface.ConnectingStart(this); + } + base.OnMouseLeave(); + } + + /// + public override void OnMouseMove(Float2 location) + { + Surface.ConnectingOver(this); + base.OnMouseMove(location); + } + + /// + public override bool OnMouseUp(Float2 location, MouseButton button) + { + if (base.OnMouseUp(location, button)) + return true; + + if (button == MouseButton.Left) + { + _isMouseDown = false; + if (Surface.IsConnecting) + Surface.ConnectingEnd(this); + return true; + } + + return false; + } + + /// + public Float2 ConnectionOrigin + { + get + { + var center = _localBounds.Center; + return PointToParent(ref center); + } + } + + /// + public bool AreConnected(IConnectionInstigator other) + { + return _input.AreConnected(other) || _output.AreConnected(other); + } + + /// + public bool CanConnectWith(IConnectionInstigator other) + { + if (other is InputBox otherInput) + { + return _output.CanConnectWith(otherInput); + } + if (other is OutputBox otherOutput) + { + return _input.CanConnectWith(otherOutput); + } + if (other is RerouteNode otherReroute) + { + if (_output.CurrentType.IsVoid) + return otherReroute._input.CanConnectWith(_output); + return otherReroute._output.CanConnectWith(_input); + } + return false; + } + + /// + public void DrawConnectingLine(ref Float2 startPos, ref Float2 endPos, ref Color color) + { + OutputBox.DrawConnection(ref startPos, ref endPos, ref color, 2); + } + + /// + public void Connect(IConnectionInstigator other) + { + if (other is InputBox otherInput) + { + _output.Connect(otherInput); + } + if (other is OutputBox otherOutput) + { + _input.Connect(otherOutput); + } + if (other is RerouteNode otherReroute) + { + if (_output.CurrentType.IsVoid) + otherReroute._input.Connect(_output); + else + otherReroute._output.Connect(_input); + } + } } /// diff --git a/Source/Editor/Surface/Elements/Box.cs b/Source/Editor/Surface/Elements/Box.cs index 49340afb1..ddf8e59d0 100644 --- a/Source/Editor/Surface/Elements/Box.cs +++ b/Source/Editor/Surface/Elements/Box.cs @@ -17,7 +17,7 @@ namespace FlaxEditor.Surface.Elements [HideInEditor] public abstract class Box : SurfaceNodeElementControl, IConnectionInstigator { - private bool _isMouseDown; + private bool _isMouseDown, _isSingle; private DateTime _lastHighlightConnectionsTime = DateTime.MinValue; private string _originalTooltipText; @@ -180,6 +180,7 @@ namespace FlaxEditor.Surface.Elements : base(parentNode, archetype, location, new Float2(Constants.BoxSize), false) { _currentType = DefaultType; + _isSingle = Archetype.Single; Text = Archetype.Text; if (Surface != null) { @@ -317,18 +318,18 @@ namespace FlaxEditor.Surface.Elements /// /// Removes all existing connections of that box. /// - public void RemoveConnections() + /// Amount of connection to skip from removing. + public void RemoveConnections(int skipCount = 0) { - // Check if sth is connected - if (HasAnyConnection) + if (Connections.Count > skipCount) { // Remove all connections - var toUpdate = new List(1 + Connections.Count) + var toUpdate = new List(1 + skipCount) { this }; - for (int i = 0; i < Connections.Count; i++) + for (int i = skipCount; i < Connections.Count; i++) { var targetBox = Connections[i]; targetBox.Connections.Remove(this); @@ -419,7 +420,34 @@ namespace FlaxEditor.Surface.Elements /// /// True if box can use only single connection. /// - public bool IsSingle => Archetype.Single; + public bool IsSingle + { + get => _isSingle; + set + { + if (_isSingle != value) + { + _isSingle = value; + + // Limit connections COUNT + if (_isSingle && Connections.Count > 0) + { + if (Surface.Undo != null) + { + var action = new EditNodeConnections(ParentNode.Context, ParentNode); + RemoveConnections(1); + action.End(); + Surface.Undo.AddAction(action); + } + else + { + RemoveConnections(1); + } + Surface.MarkAsEdited(); + } + } + } + } /// /// True if box type depends on other boxes types of the node. @@ -710,6 +738,9 @@ namespace FlaxEditor.Surface.Elements /// public bool CanConnectWith(IConnectionInstigator other) { + if (other is Archetypes.Tools.RerouteNode reroute) + return reroute.CanConnectWith(this); + var start = this; var end = other as Box; @@ -788,6 +819,12 @@ namespace FlaxEditor.Surface.Elements /// public void Connect(IConnectionInstigator other) { + if (other is Archetypes.Tools.RerouteNode reroute) + { + reroute.Connect(this); + return; + } + var start = this; var end = (Box)other; var areConnected = start.AreConnected(end); diff --git a/Source/Editor/Surface/VisjectSurface.Input.cs b/Source/Editor/Surface/VisjectSurface.Input.cs index e359715e0..ddd4ae565 100644 --- a/Source/Editor/Surface/VisjectSurface.Input.cs +++ b/Source/Editor/Surface/VisjectSurface.Input.cs @@ -312,6 +312,7 @@ namespace FlaxEditor.Surface var mousePos = _rootControl.PointFromParent(ref _mousePos); if (IntersectsConnection(mousePos, out InputBox inputBox, out OutputBox outputBox) && GetControlUnderMouse() == null) { + // Insert reroute node if (Undo != null) { bool undoEnabled = Undo.Enabled;