OldBlueWater/BlueWater/Assets/Doozy/Editor/Nody/FlowPortView.cs
2023-08-02 15:08:03 +09:00

360 lines
13 KiB
C#

// Copyright (c) 2015 - 2023 Doozy Entertainment. All Rights Reserved.
// This code can only be used under the standard Unity Asset Store End User License Agreement
// A Copy of the EULA APPENDIX 1 is available at http://unity3d.com/company/legal/as_terms
using System;
using System.Collections.Generic;
using Doozy.Editor.EditorUI;
using Doozy.Editor.EditorUI.Utils;
using Doozy.Editor.Reactor.Internal;
using Doozy.Runtime.Nody;
using Doozy.Runtime.Reactor.Extensions;
using Doozy.Runtime.Reactor.Internal;
using Doozy.Runtime.Reactor.Reactions;
using Doozy.Runtime.UIElements.Extensions;
using UnityEditor;
using UnityEditor.Experimental.GraphView;
using UnityEngine;
using UnityEngine.UIElements;
using EditorStyles = Doozy.Editor.EditorUI.EditorStyles;
// ReSharper disable MemberCanBePrivate.Global
// ReSharper disable UnusedAutoPropertyAccessor.Global
namespace Doozy.Editor.Nody
{
public class FlowPortView : Port, IDisposable
{
public void Dispose()
{
capacityIconReaction?.Recycle();
RemoveFromHierarchy();
}
public bool canDeletePort => flowNode.CanDeletePort(flowPort.portId);
private static Type defaultPortType => typeof(bool);
public FlowGraphView graphView { get; }
public FlowNodeView nodeView { get; }
public FlowGraph flowGraph => graphView.flowGraph;
public FlowNode flowNode => flowPort.node;
public FlowPort flowPort { get; }
public VisualElement connectorBox { get; }
public VisualElement connectorBoxCap { get; }
public Image capacityIcon { get; }
public Texture2DReaction capacityIconReaction { get; }
public VisualElement portInfoContainer { get; }
public FlowPortView(FlowGraphView graphView, FlowNodeView nodeView, FlowPort flowPort) : this(graphView, nodeView, flowPort, Orientation.Horizontal, defaultPortType) {}
public FlowPortView(FlowGraphView graphView, FlowNodeView nodeView, FlowPort flowPort, Orientation portOrientation) : this(graphView, nodeView, flowPort, portOrientation, defaultPortType) {}
public FlowPortView(FlowGraphView graphView, FlowNodeView nodeView, FlowPort flowPort, Orientation portOrientation, Type type) : base(portOrientation, GetDirection(flowPort.direction), GetCapacity(flowPort.capacity), type)
{
this.graphView = graphView;
this.nodeView = nodeView;
this.flowPort = flowPort;
flowPort.node ??= graphView.flowGraph.GetNodeById(flowPort.nodeId);
this
.AddStyle(EditorStyles.Nody.FlowPort)
.SetStyleHeight(StyleKeyword.Auto)
.SetStylePadding(0, 4, 0, 4)
.SetStyleAlignSelf(Align.Center)
.SetStyleFlexDirection(FlexDirection.Row);
connectorBox = this.Q<VisualElement>("connector"); //get connector
connectorBoxCap = connectorBox.Q<VisualElement>("cap"); //get cap
this.Q<Label>().RemoveFromHierarchy(); //remove label
//create a container
portInfoContainer =
new VisualElement()
.SetName(nameof(portInfoContainer))
.SetStyleAlignItems(Align.Center)
.SetStyleFlexDirection(FlexDirection.Row);
switch (direction)
{
case Direction.Input: //add container to the right
this
.SetStyleAlignSelf(Align.FlexStart)
.SetStyleMarginLeft(DesignUtils.k_Spacing);
Add(portInfoContainer);
Add(DesignUtils.flexibleSpace);
break;
case Direction.Output: //add container to the left
this
.SetStyleAlignSelf(Align.FlexEnd)
.SetStyleMarginRight(DesignUtils.k_Spacing);
Insert(0, portInfoContainer);
Insert(0, DesignUtils.flexibleSpace);
break;
default:
throw new ArgumentOutOfRangeException();
}
portColor = portDirectionColor;
capacityIcon =
new Image()
.ResetLayout()
.SetStyleFlexShrink(0)
.SetStyleSize(capacityIconSize)
.SetStyleBackgroundImageTintColor(this.flowPort.isConnected ? connectorBox.GetStyleBackgroundColor() : portColor)
.SetTooltip
(
this.flowPort.capacity == PortCapacity.Single ? "Single connection port" : this.flowPort.capacity == PortCapacity.Multi ? "Multi connections port" : throw new ArgumentOutOfRangeException()
);
capacityIconReaction = capacityIcon
.GetTexture2DReaction(capacityIconTextures)
.SetEditorHeartbeat()
.SetDuration(0.6f);
RegisterCallback<PointerEnterEvent>(_ =>
{
capacityIconReaction?.Play();
capacityIcon.SetStyleBackgroundImageTintColor(connectorBox.GetStyleBackgroundColor());
});
RegisterCallback<PointerLeaveEvent>(_ =>
{
// capacityIconReaction?.Play();
UpdateCapacityIcon();
});
connectorBox.Add(capacityIcon);
var connectorListener = new DefaultEdgeConnectorListener();
m_EdgeConnector = new EdgeConnector<FlowEdgeView>(connectorListener);
this.AddManipulator(m_EdgeConnector);
this.AddManipulator(new ContextualMenuManipulator(BuildContextualMenu));
this.RegisterCallback<CustomStyleResolvedEvent>(_ => UpdateCapacityIcon());
}
/// <summary>
/// <para>Add menu items to the port contextual menu.</para>
/// </summary>
/// <param name="evt">The event holding the menu to populate.</param>
public void BuildContextualMenu(ContextualMenuPopulateEvent evt)
{
if (!(evt.target is FlowPortView))
return;
}
public FlowEdgeView ConnectTo(FlowPortView otherPortView) =>
ConnectTo<FlowEdgeView>(otherPortView)
.SetInputPortView(direction == Direction.Input ? this : otherPortView)
.SetOutputPortView(direction == Direction.Input ? otherPortView : this);
public override void Connect(Edge edge)
{
base.Connect(edge);
UpdateCapacityIcon();
((FlowPortView)edge.input).nodeView.RefreshData();
((FlowPortView)edge.output).nodeView.RefreshData();
}
public override void Disconnect(Edge edge)
{
base.Disconnect(edge);
UpdateCapacityIcon();
((FlowPortView)edge.input).nodeView.RefreshData();
((FlowPortView)edge.output).nodeView.RefreshData();
}
public override void DisconnectAll()
{
base.DisconnectAll();
UpdateCapacityIcon();
}
public override void OnStopEdgeDragging()
{
base.OnStopEdgeDragging();
UpdateCapacityIcon();
}
private void UpdateCapacityIcon()
{
schedule.Execute(() =>
{
capacityIcon
.SetStyleBackgroundImageTintColor
(
connected
? connectorBox.GetStyleBackgroundColor()
: portColor
);
});
}
private class DefaultEdgeConnectorListener : IEdgeConnectorListener
{
private GraphViewChange m_GraphViewChange;
private List<Edge> m_EdgesToCreate;
private List<GraphElement> m_EdgesToDelete;
public DefaultEdgeConnectorListener()
{
m_EdgesToCreate = new List<Edge>();
m_EdgesToDelete = new List<GraphElement>();
m_GraphViewChange.edgesToCreate = m_EdgesToCreate;
}
public void OnDropOutsidePort(Edge edge, Vector2 position)
{
}
public void OnDrop(GraphView graphView, Edge edge)
{
m_EdgesToCreate.Clear();
m_EdgesToCreate.Add(edge);
m_EdgesToDelete.Clear();
if (edge.input.capacity == Capacity.Single)
{
foreach (Edge connection in edge.input.connections)
{
if (connection != edge)
m_EdgesToDelete.Add(connection);
}
}
if (edge.output.capacity == Capacity.Single)
{
foreach (Edge connection in edge.output.connections)
{
if (connection != edge)
m_EdgesToDelete.Add(connection);
}
}
if (m_EdgesToDelete.Count > 0)
graphView.DeleteElements(m_EdgesToDelete);
List<Edge> edgesToCreate = m_EdgesToCreate;
if (graphView.graphViewChanged != null)
edgesToCreate = graphView.graphViewChanged(m_GraphViewChange).edgesToCreate;
foreach (Edge newEdge in edgesToCreate)
{
graphView.AddElement(newEdge);
edge.input.Connect(newEdge);
edge.output.Connect(newEdge);
}
}
}
private IEnumerable<Texture2D> capacityIconTextures
{
get
{
switch (flowPort.capacity)
{
case PortCapacity.Single:
return EditorSpriteSheets.Nody.Icons.One;
case PortCapacity.Multi:
return EditorSpriteSheets.Nody.Icons.Infinity;
default:
throw new ArgumentOutOfRangeException();
}
}
}
private int capacityIconSize
{
get
{
switch (flowPort.capacity)
{
case PortCapacity.Single:
return 8;
case PortCapacity.Multi:
return 10;
default:
throw new ArgumentOutOfRangeException();
}
}
}
private Color portDirectionColor
{
get
{
if (EditorApplication.isPlayingOrWillChangePlaymode)
return EditorColors.Nody.StateIdle;
switch (flowPort.direction)
{
case PortDirection.Input:
return EditorColors.Nody.Input;
case PortDirection.Output:
return EditorColors.Nody.Output;
default:
throw new ArgumentOutOfRangeException();
}
}
}
/// <summary> Convert PortDirection (Nody) to Direction (Unity) </summary>
public static Direction GetDirection(PortDirection portDirection)
{
switch (portDirection)
{
case PortDirection.Input:
return Direction.Input;
case PortDirection.Output:
return Direction.Output;
default:
throw new ArgumentOutOfRangeException(nameof(portDirection), portDirection, null);
}
}
/// <summary> Convert Direction (Unity) to PortDirection (Nody) </summary>
public static PortDirection GetPortDirection(Direction direction)
{
switch (direction)
{
case Direction.Input:
return PortDirection.Input;
case Direction.Output:
return PortDirection.Output;
default:
throw new ArgumentOutOfRangeException(nameof(direction), direction, null);
}
}
/// <summary> Convert PortCapacity (Nody) to Capacity (Unity) </summary>
public static Capacity GetCapacity(PortCapacity portCapacity)
{
switch (portCapacity)
{
case PortCapacity.Single:
return Capacity.Single;
case PortCapacity.Multi:
return Capacity.Multi;
default:
throw new ArgumentOutOfRangeException(nameof(portCapacity), portCapacity, null);
}
}
/// <summary> Convert Capacity (Unity) to PortCapacity (Nody) </summary>
public static PortCapacity GetCapacity(Capacity portCapacity)
{
switch (portCapacity)
{
case Capacity.Single:
return PortCapacity.Single;
case Capacity.Multi:
return PortCapacity.Multi;
default:
throw new ArgumentOutOfRangeException(nameof(portCapacity), portCapacity, null);
}
}
}
}