Refactor Visject reroute node to support reconnecting and have more usability

This commit is contained in:
Wojtek Figat
2022-07-19 23:06:24 +02:00
parent 543c7efc0f
commit d77a7921b0
3 changed files with 212 additions and 42 deletions

View File

@@ -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;
/// <inheritdoc />
public RerouteNode(uint id, VisjectSurfaceContext context, NodeArchetype nodeArch, GroupArchetype groupArch)
@@ -1023,7 +1026,7 @@ namespace FlaxEditor.Surface.Archetypes
}
/// <inheritdoc />
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;
/// <inheritdoc />
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;
}
/// <inheritdoc />
@@ -1090,42 +1111,153 @@ namespace FlaxEditor.Surface.Archetypes
_footerRect = Rectangle.Empty;
}
/// <inheritdoc />
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();
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
public override void OnMouseLeave()
{
if (_isMouseDown)
{
_isMouseDown = false;
if (Surface.CanEdit && _isConnecting)
Surface.ConnectingStart(this);
}
base.OnMouseLeave();
}
/// <inheritdoc />
public override void OnMouseMove(Float2 location)
{
Surface.ConnectingOver(this);
base.OnMouseMove(location);
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
public Float2 ConnectionOrigin
{
get
{
var center = _localBounds.Center;
return PointToParent(ref center);
}
}
/// <inheritdoc />
public bool AreConnected(IConnectionInstigator other)
{
return _input.AreConnected(other) || _output.AreConnected(other);
}
/// <inheritdoc />
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;
}
/// <inheritdoc />
public void DrawConnectingLine(ref Float2 startPos, ref Float2 endPos, ref Color color)
{
OutputBox.DrawConnection(ref startPos, ref endPos, ref color, 2);
}
/// <inheritdoc />
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);
}
}
}
/// <summary>

View File

@@ -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
/// <summary>
/// Removes all existing connections of that box.
/// </summary>
public void RemoveConnections()
/// <param name="skipCount">Amount of connection to skip from removing.</param>
public void RemoveConnections(int skipCount = 0)
{
// Check if sth is connected
if (HasAnyConnection)
if (Connections.Count > skipCount)
{
// Remove all connections
var toUpdate = new List<Box>(1 + Connections.Count)
var toUpdate = new List<Box>(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
/// <summary>
/// True if box can use only single connection.
/// </summary>
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();
}
}
}
}
/// <summary>
/// True if box type depends on other boxes types of the node.
@@ -710,6 +738,9 @@ namespace FlaxEditor.Surface.Elements
/// <inheritdoc />
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
/// <inheritdoc />
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);

View File

@@ -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;