playtest-unity/playtest/Library/PackageCache/com.unity.visualscripting@1.../Editor/VisualScripting.Flow/Units/UnitWidget.cs

1123 lines
34 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
namespace Unity.VisualScripting
{
[Widget(typeof(IUnit))]
public class UnitWidget<TUnit> : NodeWidget<FlowCanvas, TUnit>, IUnitWidget where TUnit : class, IUnit
{
public UnitWidget(FlowCanvas canvas, TUnit unit) : base(canvas, unit)
{
unit.onPortsChanged += CacheDefinition;
unit.onPortsChanged += SubWidgetsChanged;
}
public override void Dispose()
{
base.Dispose();
unit.onPortsChanged -= CacheDefinition;
unit.onPortsChanged -= SubWidgetsChanged;
}
public override IEnumerable<IWidget> subWidgets => unit.ports.Select(port => canvas.Widget(port));
#region Model
protected TUnit unit => element;
IUnit IUnitWidget.unit => unit;
protected IUnitDebugData unitDebugData => GetDebugData<IUnitDebugData>();
private UnitDescription description;
private UnitAnalysis analysis => unit.Analysis<UnitAnalysis>(context);
protected readonly List<IUnitPortWidget> ports = new List<IUnitPortWidget>();
protected readonly List<IUnitPortWidget> inputs = new List<IUnitPortWidget>();
protected readonly List<IUnitPortWidget> outputs = new List<IUnitPortWidget>();
private readonly List<string> settingNames = new List<string>();
protected IEnumerable<Metadata> settings
{
get
{
foreach (var settingName in settingNames)
{
yield return metadata[settingName];
}
}
}
protected override void CacheItemFirstTime()
{
base.CacheItemFirstTime();
CacheDefinition();
}
protected virtual void CacheDefinition()
{
inputs.Clear();
outputs.Clear();
ports.Clear();
inputs.AddRange(unit.inputs.Select(port => canvas.Widget<IUnitPortWidget>(port)));
outputs.AddRange(unit.outputs.Select(port => canvas.Widget<IUnitPortWidget>(port)));
ports.AddRange(inputs);
ports.AddRange(outputs);
Reposition();
}
protected override void CacheDescription()
{
description = unit.Description<UnitDescription>();
titleContent.text = description.shortTitle;
titleContent.tooltip = description.summary;
surtitleContent.text = description.surtitle;
subtitleContent.text = description.subtitle;
Reposition();
}
protected override void CacheMetadata()
{
settingNames.Clear();
settingNames.AddRange(metadata.valueType
.GetMembers()
.Where(mi => mi.HasAttribute<UnitHeaderInspectableAttribute>())
.OrderBy(mi => mi.GetAttributes<Attribute>().OfType<IInspectableAttribute>().FirstOrDefault()?.order ?? int.MaxValue)
.ThenBy(mi => mi.MetadataToken)
.Select(mi => mi.Name));
lock (settingLabelsContents)
{
settingLabelsContents.Clear();
foreach (var setting in settings)
{
var settingLabel = setting.GetAttribute<UnitHeaderInspectableAttribute>().label;
GUIContent settingContent;
if (string.IsNullOrEmpty(settingLabel))
{
settingContent = null;
}
else
{
settingContent = new GUIContent(settingLabel);
}
settingLabelsContents.Add(setting, settingContent);
}
}
Reposition();
}
public virtual Inspector GetPortInspector(IUnitPort port, Metadata metadata)
{
return metadata.Inspector();
}
#endregion
#region Lifecycle
public override bool foregroundRequiresInput => showSettings || unit.valueInputs.Any(vip => vip.hasDefaultValue);
public override void HandleInput()
{
if (canvas.isCreatingConnection)
{
if (e.IsMouseDown(MouseButton.Left))
{
var source = canvas.connectionSource;
var destination = source.CompatiblePort(unit);
if (destination != null)
{
UndoUtility.RecordEditedObject("Connect Nodes");
source.ValidlyConnectTo(destination);
canvas.connectionSource = null;
canvas.Widget(source.unit).Reposition();
canvas.Widget(destination.unit).Reposition();
GUI.changed = true;
}
e.Use();
}
else if (e.IsMouseDown(MouseButton.Right))
{
canvas.CancelConnection();
e.Use();
}
}
base.HandleInput();
}
#endregion
#region Contents
protected readonly GUIContent titleContent = new GUIContent();
protected readonly GUIContent surtitleContent = new GUIContent();
protected readonly GUIContent subtitleContent = new GUIContent();
protected readonly Dictionary<Metadata, GUIContent> settingLabelsContents = new Dictionary<Metadata, GUIContent>();
#endregion
#region Positioning
protected override bool snapToGrid => BoltCore.Configuration.snapToGrid;
public override IEnumerable<IWidget> positionDependers => ports.Cast<IWidget>();
protected Rect _position;
public override Rect position
{
get { return _position; }
set { unit.position = value.position; }
}
public Rect titlePosition { get; private set; }
public Rect surtitlePosition { get; private set; }
public Rect subtitlePosition { get; private set; }
public Rect iconPosition { get; private set; }
public List<Rect> iconsPositions { get; private set; } = new List<Rect>();
public Dictionary<Metadata, Rect> settingsPositions { get; } = new Dictionary<Metadata, Rect>();
public Rect headerAddonPosition { get; private set; }
public Rect portsBackgroundPosition { get; private set; }
public override void CachePosition()
{
// Width
var inputsWidth = 0f;
var outputsWidth = 0f;
foreach (var input in inputs)
{
inputsWidth = Mathf.Max(inputsWidth, input.GetInnerWidth());
}
foreach (var output in outputs)
{
outputsWidth = Mathf.Max(outputsWidth, output.GetInnerWidth());
}
var portsWidth = 0f;
portsWidth += inputsWidth;
portsWidth += Styles.spaceBetweenInputsAndOutputs;
portsWidth += outputsWidth;
settingsPositions.Clear();
var settingsWidth = 0f;
if (showSettings)
{
foreach (var setting in settings)
{
var settingWidth = 0f;
var settingLabelContent = settingLabelsContents[setting];
if (settingLabelContent != null)
{
settingWidth += Styles.settingLabel.CalcSize(settingLabelContent).x;
}
settingWidth += setting.Inspector().GetAdaptiveWidth();
settingWidth = Mathf.Min(settingWidth, Styles.maxSettingsWidth);
settingsPositions.Add(setting, new Rect(0, 0, settingWidth, 0));
settingsWidth = Mathf.Max(settingsWidth, settingWidth);
}
}
var headerAddonWidth = 0f;
if (showHeaderAddon)
{
headerAddonWidth = GetHeaderAddonWidth();
}
var titleWidth = Styles.title.CalcSize(titleContent).x;
var headerTextWidth = titleWidth;
var surtitleWidth = 0f;
if (showSurtitle)
{
surtitleWidth = Styles.surtitle.CalcSize(surtitleContent).x;
headerTextWidth = Mathf.Max(headerTextWidth, surtitleWidth);
}
var subtitleWidth = 0f;
if (showSubtitle)
{
subtitleWidth = Styles.subtitle.CalcSize(subtitleContent).x;
headerTextWidth = Mathf.Max(headerTextWidth, subtitleWidth);
}
var iconsWidth = 0f;
if (showIcons)
{
var iconsColumns = Mathf.Ceil((float)description.icons.Length / Styles.iconsPerColumn);
iconsWidth = iconsColumns * Styles.iconsSize + ((iconsColumns - 1) * Styles.iconsSpacing);
}
var headerWidth = Mathf.Max(headerTextWidth + iconsWidth, Mathf.Max(settingsWidth, headerAddonWidth)) + Styles.iconSize + Styles.spaceAfterIcon;
var innerWidth = Mathf.Max(portsWidth, headerWidth);
var edgeWidth = InnerToEdgePosition(new Rect(0, 0, innerWidth, 0)).width;
// Height & Positioning
var edgeOrigin = unit.position;
var edgeX = edgeOrigin.x;
var edgeY = edgeOrigin.y;
var innerOrigin = EdgeToInnerPosition(new Rect(edgeOrigin, Vector2.zero)).position;
var innerX = innerOrigin.x;
var innerY = innerOrigin.y;
iconPosition = new Rect
(
innerX,
innerY,
Styles.iconSize,
Styles.iconSize
);
var headerTextX = iconPosition.xMax + Styles.spaceAfterIcon;
var y = innerY;
var headerHeight = 0f;
var surtitleHeight = 0f;
if (showSurtitle)
{
surtitleHeight = Styles.surtitle.CalcHeight(surtitleContent, headerTextWidth);
surtitlePosition = new Rect
(
headerTextX,
y,
headerTextWidth,
surtitleHeight
);
headerHeight += surtitleHeight;
y += surtitleHeight;
headerHeight += Styles.spaceAfterSurtitle;
y += Styles.spaceAfterSurtitle;
}
var titleHeight = 0f;
if (showTitle)
{
titleHeight = Styles.title.CalcHeight(titleContent, headerTextWidth);
titlePosition = new Rect
(
headerTextX,
y,
headerTextWidth,
titleHeight
);
headerHeight += titleHeight;
y += titleHeight;
}
var subtitleHeight = 0f;
if (showSubtitle)
{
headerHeight += Styles.spaceBeforeSubtitle;
y += Styles.spaceBeforeSubtitle;
subtitleHeight = Styles.subtitle.CalcHeight(subtitleContent, headerTextWidth);
subtitlePosition = new Rect
(
headerTextX,
y,
headerTextWidth,
subtitleHeight
);
headerHeight += subtitleHeight;
y += subtitleHeight;
}
iconsPositions.Clear();
if (showIcons)
{
var iconRow = 0;
var iconCol = 0;
for (int i = 0; i < description.icons.Length; i++)
{
var iconPosition = new Rect
(
innerX + innerWidth - ((iconCol + 1) * Styles.iconsSize) - ((iconCol) * Styles.iconsSpacing),
innerY + (iconRow * (Styles.iconsSize + Styles.iconsSpacing)),
Styles.iconsSize,
Styles.iconsSize
);
iconsPositions.Add(iconPosition);
iconRow++;
if (iconRow % Styles.iconsPerColumn == 0)
{
iconCol++;
iconRow = 0;
}
}
}
var settingsHeight = 0f;
if (showSettings)
{
headerHeight += Styles.spaceBeforeSettings;
foreach (var setting in settings)
{
var settingWidth = settingsPositions[setting].width;
using (LudiqGUIUtility.currentInspectorWidth.Override(settingWidth))
{
var settingHeight = LudiqGUI.GetInspectorHeight(null, setting, settingWidth, settingLabelsContents[setting] ?? GUIContent.none);
var settingPosition = new Rect
(
headerTextX,
y,
settingWidth,
settingHeight
);
settingsPositions[setting] = settingPosition;
settingsHeight += settingHeight;
y += settingHeight;
settingsHeight += Styles.spaceBetweenSettings;
y += Styles.spaceBetweenSettings;
}
}
settingsHeight -= Styles.spaceBetweenSettings;
y -= Styles.spaceBetweenSettings;
headerHeight += settingsHeight;
headerHeight += Styles.spaceAfterSettings;
y += Styles.spaceAfterSettings;
}
if (showHeaderAddon)
{
var headerAddonHeight = GetHeaderAddonHeight(headerAddonWidth);
headerAddonPosition = new Rect
(
headerTextX,
y,
headerAddonWidth,
headerAddonHeight
);
headerHeight += headerAddonHeight;
y += headerAddonHeight;
}
if (headerHeight < Styles.iconSize)
{
var difference = Styles.iconSize - headerHeight;
var centeringOffset = difference / 2;
if (showTitle)
{
var _titlePosition = titlePosition;
_titlePosition.y += centeringOffset;
titlePosition = _titlePosition;
}
if (showSubtitle)
{
var _subtitlePosition = subtitlePosition;
_subtitlePosition.y += centeringOffset;
subtitlePosition = _subtitlePosition;
}
if (showSettings)
{
foreach (var setting in settings)
{
var _settingPosition = settingsPositions[setting];
_settingPosition.y += centeringOffset;
settingsPositions[setting] = _settingPosition;
}
}
if (showHeaderAddon)
{
var _headerAddonPosition = headerAddonPosition;
_headerAddonPosition.y += centeringOffset;
headerAddonPosition = _headerAddonPosition;
}
headerHeight = Styles.iconSize;
}
y = innerY + headerHeight;
var innerHeight = 0f;
innerHeight += headerHeight;
if (showPorts)
{
innerHeight += Styles.spaceBeforePorts;
y += Styles.spaceBeforePorts;
var portsBackgroundY = y;
var portsBackgroundHeight = 0f;
portsBackgroundHeight += Styles.portsBackground.padding.top;
innerHeight += Styles.portsBackground.padding.top;
y += Styles.portsBackground.padding.top;
var portStartY = y;
var inputsHeight = 0f;
var outputsHeight = 0f;
foreach (var input in inputs)
{
input.y = y;
var inputHeight = input.GetHeight();
inputsHeight += inputHeight;
y += inputHeight;
inputsHeight += Styles.spaceBetweenPorts;
y += Styles.spaceBetweenPorts;
}
if (inputs.Count > 0)
{
inputsHeight -= Styles.spaceBetweenPorts;
y -= Styles.spaceBetweenPorts;
}
y = portStartY;
foreach (var output in outputs)
{
output.y = y;
var outputHeight = output.GetHeight();
outputsHeight += outputHeight;
y += outputHeight;
outputsHeight += Styles.spaceBetweenPorts;
y += Styles.spaceBetweenPorts;
}
if (outputs.Count > 0)
{
outputsHeight -= Styles.spaceBetweenPorts;
y -= Styles.spaceBetweenPorts;
}
var portsHeight = Math.Max(inputsHeight, outputsHeight);
portsBackgroundHeight += portsHeight;
innerHeight += portsHeight;
y = portStartY + portsHeight;
portsBackgroundHeight += Styles.portsBackground.padding.bottom;
innerHeight += Styles.portsBackground.padding.bottom;
y += Styles.portsBackground.padding.bottom;
portsBackgroundPosition = new Rect
(
edgeX,
portsBackgroundY,
edgeWidth,
portsBackgroundHeight
);
}
var edgeHeight = InnerToEdgePosition(new Rect(0, 0, 0, innerHeight)).height;
_position = new Rect
(
edgeX,
edgeY,
edgeWidth,
edgeHeight
);
}
protected virtual float GetHeaderAddonWidth()
{
return 0;
}
protected virtual float GetHeaderAddonHeight(float width)
{
return 0;
}
#endregion
#region Drawing
protected virtual NodeColorMix baseColor => NodeColor.Gray;
protected override NodeColorMix color
{
get
{
if (unitDebugData.runtimeException != null)
{
return NodeColor.Red;
}
var color = baseColor;
if (analysis.warnings.Count > 0)
{
var mostSevereWarning = Warning.MostSevereLevel(analysis.warnings);
switch (mostSevereWarning)
{
case WarningLevel.Error:
color = NodeColor.Red;
break;
case WarningLevel.Severe:
color = NodeColor.Orange;
break;
case WarningLevel.Caution:
color = NodeColor.Yellow;
break;
}
}
if (EditorApplication.isPaused)
{
if (EditorTimeBinding.frame == unitDebugData.lastInvokeFrame)
{
return NodeColor.Blue;
}
}
else
{
var mix = color;
mix.blue = Mathf.Lerp(1, 0, (EditorTimeBinding.time - unitDebugData.lastInvokeTime) / Styles.invokeFadeDuration);
return mix;
}
return color;
}
}
protected override NodeShape shape => NodeShape.Square;
protected virtual bool showTitle => !string.IsNullOrEmpty(description.shortTitle);
protected virtual bool showSurtitle => !string.IsNullOrEmpty(description.surtitle);
protected virtual bool showSubtitle => !string.IsNullOrEmpty(description.subtitle);
protected virtual bool showIcons => description.icons.Length > 0;
protected virtual bool showSettings => settingNames.Count > 0;
protected virtual bool showHeaderAddon => false;
protected virtual bool showPorts => ports.Count > 0;
protected override bool dim
{
get
{
var dim = BoltCore.Configuration.dimInactiveNodes && !analysis.isEntered;
if (isMouseOver || isSelected)
{
dim = false;
}
if (BoltCore.Configuration.dimIncompatibleNodes && canvas.isCreatingConnection)
{
dim = !unit.ports.Any(p => canvas.connectionSource == p || canvas.connectionSource.CanValidlyConnectTo(p));
}
return dim;
}
}
public override void DrawForeground()
{
BeginDim();
base.DrawForeground();
DrawIcon();
if (showSurtitle)
{
DrawSurtitle();
}
if (showTitle)
{
DrawTitle();
}
if (showSubtitle)
{
DrawSubtitle();
}
if (showIcons)
{
DrawIcons();
}
if (showSettings)
{
DrawSettings();
}
if (showHeaderAddon)
{
DrawHeaderAddon();
}
if (showPorts)
{
DrawPortsBackground();
}
EndDim();
}
protected void DrawIcon()
{
var icon = description.icon ?? BoltFlow.Icons.unit;
if (icon != null && icon[(int)iconPosition.width])
{
GUI.DrawTexture(iconPosition, icon[(int)iconPosition.width]);
}
}
protected void DrawTitle()
{
GUI.Label(titlePosition, titleContent, invertForeground ? Styles.titleInverted : Styles.title);
}
protected void DrawSurtitle()
{
GUI.Label(surtitlePosition, surtitleContent, invertForeground ? Styles.surtitleInverted : Styles.surtitle);
}
protected void DrawSubtitle()
{
GUI.Label(subtitlePosition, subtitleContent, invertForeground ? Styles.subtitleInverted : Styles.subtitle);
}
protected void DrawIcons()
{
for (int i = 0; i < description.icons.Length; i++)
{
var icon = description.icons[i];
var position = iconsPositions[i];
GUI.DrawTexture(position, icon?[(int)position.width]);
}
}
private void DrawSettings()
{
if (graph.zoom < FlowCanvas.inspectorZoomThreshold)
{
return;
}
EditorGUI.BeginDisabledGroup(!e.IsRepaint && isMouseThrough && !isMouseOver);
EditorGUI.BeginChangeCheck();
foreach (var setting in settings)
{
DrawSetting(setting);
}
if (EditorGUI.EndChangeCheck())
{
unit.Define();
Reposition();
}
EditorGUI.EndDisabledGroup();
}
protected void DrawSetting(Metadata setting)
{
var settingPosition = settingsPositions[setting];
using (LudiqGUIUtility.currentInspectorWidth.Override(settingPosition.width))
using (Inspector.expandTooltip.Override(false))
{
var label = settingLabelsContents[setting];
if (label == null)
{
LudiqGUI.Inspector(setting, settingPosition, GUIContent.none);
}
else
{
using (Inspector.defaultLabelStyle.Override(Styles.settingLabel))
using (LudiqGUIUtility.labelWidth.Override(Styles.settingLabel.CalcSize(label).x))
{
LudiqGUI.Inspector(setting, settingPosition, label);
}
}
}
}
protected virtual void DrawHeaderAddon() { }
protected void DrawPortsBackground()
{
if (canvas.showRelations)
{
foreach (var relation in unit.relations)
{
var start = ports.Single(pw => pw.port == relation.source).handlePosition.center;
var end = ports.Single(pw => pw.port == relation.destination).handlePosition.center;
var startTangent = start;
var endTangent = end;
if (relation.source is IUnitInputPort &&
relation.destination is IUnitInputPort)
{
//startTangent -= new Vector2(20, 0);
endTangent -= new Vector2(32, 0);
}
else
{
startTangent += new Vector2(innerPosition.width / 2, 0);
endTangent += new Vector2(-innerPosition.width / 2, 0);
}
Handles.DrawBezier
(
start,
end,
startTangent,
endTangent,
new Color(0.136f, 0.136f, 0.136f, 1.0f),
null,
3
);
}
}
else
{
if (e.IsRepaint)
{
Styles.portsBackground.Draw(portsBackgroundPosition, false, false, false, false);
}
}
}
#endregion
#region Selecting
public override bool canSelect => true;
#endregion
#region Dragging
public override bool canDrag => true;
public override void ExpandDragGroup(HashSet<IGraphElement> dragGroup)
{
if (BoltCore.Configuration.carryChildren)
{
foreach (var output in unit.outputs)
{
foreach (var connection in output.connections)
{
if (dragGroup.Contains(connection.destination.unit))
{
continue;
}
dragGroup.Add(connection.destination.unit);
canvas.Widget(connection.destination.unit).ExpandDragGroup(dragGroup);
}
}
}
}
#endregion
#region Deleting
public override bool canDelete => true;
#endregion
#region Clipboard
public override void ExpandCopyGroup(HashSet<IGraphElement> copyGroup)
{
copyGroup.UnionWith(unit.connections.Cast<IGraphElement>());
}
#endregion
#region Context
protected override IEnumerable<DropdownOption> contextOptions
{
get
{
yield return new DropdownOption((Action)ReplaceUnit, "Replace...");
foreach (var baseOption in base.contextOptions)
{
yield return baseOption;
}
}
}
private void ReplaceUnit()
{
UnitWidgetHelper.ReplaceUnit(unit, reference, context, selection, e);
}
#endregion
public static class Styles
{
static Styles()
{
// Disabling word wrap because Unity's CalcSize and CalcHeight
// are broken w.r.t. pixel-perfection and matrix
title = new GUIStyle(BoltCore.Styles.nodeLabel);
title.padding = new RectOffset(0, 5, 0, 2);
title.margin = new RectOffset(0, 0, 0, 0);
title.fontSize = 12;
title.alignment = TextAnchor.MiddleLeft;
title.wordWrap = false;
surtitle = new GUIStyle(BoltCore.Styles.nodeLabel);
surtitle.padding = new RectOffset(0, 5, 0, 0);
surtitle.margin = new RectOffset(0, 0, 0, 0);
surtitle.fontSize = 10;
surtitle.alignment = TextAnchor.MiddleLeft;
surtitle.wordWrap = false;
subtitle = new GUIStyle(surtitle);
subtitle.padding.bottom = 2;
titleInverted = new GUIStyle(title);
titleInverted.normal.textColor = ColorPalette.unityBackgroundDark;
surtitleInverted = new GUIStyle(surtitle);
surtitleInverted.normal.textColor = ColorPalette.unityBackgroundDark;
subtitleInverted = new GUIStyle(subtitle);
subtitleInverted.normal.textColor = ColorPalette.unityBackgroundDark;
if (EditorGUIUtility.isProSkin)
{
portsBackground = new GUIStyle("In BigTitle")
{
padding = new RectOffset(0, 0, 6, 5)
};
}
else
{
TextureResolution[] textureResolution = { 2 };
var createTextureOptions = CreateTextureOptions.Scalable;
EditorTexture normalTexture = BoltCore.Resources.LoadTexture($"NodePortsBackground.png", textureResolution, createTextureOptions);
portsBackground = new GUIStyle
{
normal = { background = normalTexture.Single() },
padding = new RectOffset(0, 0, 6, 5)
};
}
settingLabel = new GUIStyle(BoltCore.Styles.nodeLabel);
settingLabel.padding.left = 0;
settingLabel.padding.right = 5;
settingLabel.wordWrap = false;
settingLabel.clipping = TextClipping.Clip;
}
public static readonly GUIStyle title;
public static readonly GUIStyle surtitle;
public static readonly GUIStyle subtitle;
public static readonly GUIStyle titleInverted;
public static readonly GUIStyle surtitleInverted;
public static readonly GUIStyle subtitleInverted;
public static readonly GUIStyle settingLabel;
public static readonly float spaceAroundLineIcon = 5;
public static readonly float spaceBeforePorts = 5;
public static readonly float spaceBetweenInputsAndOutputs = 8;
public static readonly float spaceBeforeSettings = 2;
public static readonly float spaceBetweenSettings = 3;
public static readonly float spaceBetweenPorts = 3;
public static readonly float spaceAfterSettings = 0;
public static readonly float maxSettingsWidth = 150;
public static readonly GUIStyle portsBackground;
public static readonly float iconSize = IconSize.Medium;
public static readonly float iconsSize = IconSize.Small;
public static readonly float iconsSpacing = 3;
public static readonly int iconsPerColumn = 2;
public static readonly float spaceAfterIcon = 6;
public static readonly float spaceAfterSurtitle = 2;
public static readonly float spaceBeforeSubtitle = 0;
public static readonly float invokeFadeDuration = 0.5f;
}
}
internal class UnitWidgetHelper
{
internal static void ReplaceUnit(IUnit unit, GraphReference reference, IGraphContext context, GraphSelection selection, EventWrapper eventWrapper)
{
var oldUnit = unit;
var unitPosition = oldUnit.position;
var preservation = UnitPreservation.Preserve(oldUnit);
var options = new UnitOptionTree(new GUIContent("Node"));
options.filter = UnitOptionFilter.Any;
options.filter.NoConnection = false;
options.reference = reference;
var activatorPosition = new Rect(eventWrapper.mousePosition, new Vector2(200, 1));
LudiqGUI.FuzzyDropdown
(
activatorPosition,
options,
null,
delegate (object _option)
{
var option = (IUnitOption)_option;
context.BeginEdit();
UndoUtility.RecordEditedObject("Replace Node");
var graph = oldUnit.graph;
oldUnit.graph.units.Remove(oldUnit);
var newUnit = option.InstantiateUnit();
newUnit.guid = Guid.NewGuid();
newUnit.position = unitPosition;
graph.units.Add(newUnit);
preservation.RestoreTo(newUnit);
option.PreconfigureUnit(newUnit);
selection.Select(newUnit);
GUI.changed = true;
context.EndEdit();
}
);
}
}
}