A game about forced loneliness, made by TACStudios
1using System; 2using System.Collections.Generic; 3using System.Linq; 4using UnityEditor; 5using UnityEngine; 6 7namespace Unity.VisualScripting 8{ 9 [Widget(typeof(IUnit))] 10 public class UnitWidget<TUnit> : NodeWidget<FlowCanvas, TUnit>, IUnitWidget where TUnit : class, IUnit 11 { 12 public UnitWidget(FlowCanvas canvas, TUnit unit) : base(canvas, unit) 13 { 14 unit.onPortsChanged += CacheDefinition; 15 unit.onPortsChanged += SubWidgetsChanged; 16 } 17 18 public override void Dispose() 19 { 20 base.Dispose(); 21 22 unit.onPortsChanged -= CacheDefinition; 23 unit.onPortsChanged -= SubWidgetsChanged; 24 } 25 26 public override IEnumerable<IWidget> subWidgets => unit.ports.Select(port => canvas.Widget(port)); 27 28 29 #region Model 30 31 protected TUnit unit => element; 32 33 IUnit IUnitWidget.unit => unit; 34 35 protected IUnitDebugData unitDebugData => GetDebugData<IUnitDebugData>(); 36 37 private UnitDescription description; 38 39 private UnitAnalysis analysis => unit.Analysis<UnitAnalysis>(context); 40 41 protected readonly List<IUnitPortWidget> ports = new List<IUnitPortWidget>(); 42 43 protected readonly List<IUnitPortWidget> inputs = new List<IUnitPortWidget>(); 44 45 protected readonly List<IUnitPortWidget> outputs = new List<IUnitPortWidget>(); 46 47 private readonly List<string> settingNames = new List<string>(); 48 49 protected IEnumerable<Metadata> settings 50 { 51 get 52 { 53 foreach (var settingName in settingNames) 54 { 55 yield return metadata[settingName]; 56 } 57 } 58 } 59 60 protected override void CacheItemFirstTime() 61 { 62 base.CacheItemFirstTime(); 63 CacheDefinition(); 64 } 65 66 protected virtual void CacheDefinition() 67 { 68 inputs.Clear(); 69 outputs.Clear(); 70 ports.Clear(); 71 inputs.AddRange(unit.inputs.Select(port => canvas.Widget<IUnitPortWidget>(port))); 72 outputs.AddRange(unit.outputs.Select(port => canvas.Widget<IUnitPortWidget>(port))); 73 ports.AddRange(inputs); 74 ports.AddRange(outputs); 75 76 Reposition(); 77 } 78 79 protected override void CacheDescription() 80 { 81 description = unit.Description<UnitDescription>(); 82 83 titleContent.text = description.shortTitle; 84 titleContent.tooltip = description.summary; 85 surtitleContent.text = description.surtitle; 86 subtitleContent.text = description.subtitle; 87 88 Reposition(); 89 } 90 91 protected override void CacheMetadata() 92 { 93 settingNames.Clear(); 94 95 settingNames.AddRange(metadata.valueType 96 .GetMembers() 97 .Where(mi => mi.HasAttribute<UnitHeaderInspectableAttribute>()) 98 .OrderBy(mi => mi.GetAttributes<Attribute>().OfType<IInspectableAttribute>().FirstOrDefault()?.order ?? int.MaxValue) 99 .ThenBy(mi => mi.MetadataToken) 100 .Select(mi => mi.Name)); 101 102 lock (settingLabelsContents) 103 { 104 settingLabelsContents.Clear(); 105 106 foreach (var setting in settings) 107 { 108 var settingLabel = setting.GetAttribute<UnitHeaderInspectableAttribute>().label; 109 110 GUIContent settingContent; 111 112 if (string.IsNullOrEmpty(settingLabel)) 113 { 114 settingContent = null; 115 } 116 else 117 { 118 settingContent = new GUIContent(settingLabel); 119 } 120 121 settingLabelsContents.Add(setting, settingContent); 122 } 123 } 124 125 Reposition(); 126 } 127 128 public virtual Inspector GetPortInspector(IUnitPort port, Metadata metadata) 129 { 130 return metadata.Inspector(); 131 } 132 133 #endregion 134 135 136 #region Lifecycle 137 138 public override bool foregroundRequiresInput => showSettings || unit.valueInputs.Any(vip => vip.hasDefaultValue); 139 140 public override void HandleInput() 141 { 142 if (canvas.isCreatingConnection) 143 { 144 if (e.IsMouseDown(MouseButton.Left)) 145 { 146 var source = canvas.connectionSource; 147 var destination = source.CompatiblePort(unit); 148 149 if (destination != null) 150 { 151 UndoUtility.RecordEditedObject("Connect Nodes"); 152 source.ValidlyConnectTo(destination); 153 canvas.connectionSource = null; 154 canvas.Widget(source.unit).Reposition(); 155 canvas.Widget(destination.unit).Reposition(); 156 GUI.changed = true; 157 } 158 159 e.Use(); 160 } 161 else if (e.IsMouseDown(MouseButton.Right)) 162 { 163 canvas.CancelConnection(); 164 e.Use(); 165 } 166 } 167 168 base.HandleInput(); 169 } 170 171 #endregion 172 173 174 #region Contents 175 176 protected readonly GUIContent titleContent = new GUIContent(); 177 178 protected readonly GUIContent surtitleContent = new GUIContent(); 179 180 protected readonly GUIContent subtitleContent = new GUIContent(); 181 182 protected readonly Dictionary<Metadata, GUIContent> settingLabelsContents = new Dictionary<Metadata, GUIContent>(); 183 184 #endregion 185 186 187 #region Positioning 188 189 protected override bool snapToGrid => BoltCore.Configuration.snapToGrid; 190 191 public override IEnumerable<IWidget> positionDependers => ports.Cast<IWidget>(); 192 193 protected Rect _position; 194 195 public override Rect position 196 { 197 get { return _position; } 198 set { unit.position = value.position; } 199 } 200 201 public Rect titlePosition { get; private set; } 202 203 public Rect surtitlePosition { get; private set; } 204 205 public Rect subtitlePosition { get; private set; } 206 207 public Rect iconPosition { get; private set; } 208 209 public List<Rect> iconsPositions { get; private set; } = new List<Rect>(); 210 211 public Dictionary<Metadata, Rect> settingsPositions { get; } = new Dictionary<Metadata, Rect>(); 212 213 public Rect headerAddonPosition { get; private set; } 214 215 public Rect portsBackgroundPosition { get; private set; } 216 217 public override void CachePosition() 218 { 219 // Width 220 221 var inputsWidth = 0f; 222 var outputsWidth = 0f; 223 224 foreach (var input in inputs) 225 { 226 inputsWidth = Mathf.Max(inputsWidth, input.GetInnerWidth()); 227 } 228 229 foreach (var output in outputs) 230 { 231 outputsWidth = Mathf.Max(outputsWidth, output.GetInnerWidth()); 232 } 233 234 var portsWidth = 0f; 235 236 portsWidth += inputsWidth; 237 portsWidth += Styles.spaceBetweenInputsAndOutputs; 238 portsWidth += outputsWidth; 239 240 settingsPositions.Clear(); 241 242 var settingsWidth = 0f; 243 244 if (showSettings) 245 { 246 foreach (var setting in settings) 247 { 248 var settingWidth = 0f; 249 250 var settingLabelContent = settingLabelsContents[setting]; 251 252 if (settingLabelContent != null) 253 { 254 settingWidth += Styles.settingLabel.CalcSize(settingLabelContent).x; 255 } 256 257 settingWidth += setting.Inspector().GetAdaptiveWidth(); 258 259 settingWidth = Mathf.Min(settingWidth, Styles.maxSettingsWidth); 260 261 settingsPositions.Add(setting, new Rect(0, 0, settingWidth, 0)); 262 263 settingsWidth = Mathf.Max(settingsWidth, settingWidth); 264 } 265 } 266 267 var headerAddonWidth = 0f; 268 269 if (showHeaderAddon) 270 { 271 headerAddonWidth = GetHeaderAddonWidth(); 272 } 273 274 var titleWidth = Styles.title.CalcSize(titleContent).x; 275 276 var headerTextWidth = titleWidth; 277 278 var surtitleWidth = 0f; 279 280 if (showSurtitle) 281 { 282 surtitleWidth = Styles.surtitle.CalcSize(surtitleContent).x; 283 headerTextWidth = Mathf.Max(headerTextWidth, surtitleWidth); 284 } 285 286 var subtitleWidth = 0f; 287 288 if (showSubtitle) 289 { 290 subtitleWidth = Styles.subtitle.CalcSize(subtitleContent).x; 291 headerTextWidth = Mathf.Max(headerTextWidth, subtitleWidth); 292 } 293 294 var iconsWidth = 0f; 295 296 if (showIcons) 297 { 298 var iconsColumns = Mathf.Ceil((float)description.icons.Length / Styles.iconsPerColumn); 299 iconsWidth = iconsColumns * Styles.iconsSize + ((iconsColumns - 1) * Styles.iconsSpacing); 300 } 301 302 var headerWidth = Mathf.Max(headerTextWidth + iconsWidth, Mathf.Max(settingsWidth, headerAddonWidth)) + Styles.iconSize + Styles.spaceAfterIcon; 303 304 var innerWidth = Mathf.Max(portsWidth, headerWidth); 305 306 var edgeWidth = InnerToEdgePosition(new Rect(0, 0, innerWidth, 0)).width; 307 308 // Height & Positioning 309 310 var edgeOrigin = unit.position; 311 var edgeX = edgeOrigin.x; 312 var edgeY = edgeOrigin.y; 313 var innerOrigin = EdgeToInnerPosition(new Rect(edgeOrigin, Vector2.zero)).position; 314 var innerX = innerOrigin.x; 315 var innerY = innerOrigin.y; 316 317 iconPosition = new Rect 318 ( 319 innerX, 320 innerY, 321 Styles.iconSize, 322 Styles.iconSize 323 ); 324 325 var headerTextX = iconPosition.xMax + Styles.spaceAfterIcon; 326 327 var y = innerY; 328 329 var headerHeight = 0f; 330 331 var surtitleHeight = 0f; 332 333 if (showSurtitle) 334 { 335 surtitleHeight = Styles.surtitle.CalcHeight(surtitleContent, headerTextWidth); 336 337 surtitlePosition = new Rect 338 ( 339 headerTextX, 340 y, 341 headerTextWidth, 342 surtitleHeight 343 ); 344 345 headerHeight += surtitleHeight; 346 y += surtitleHeight; 347 348 headerHeight += Styles.spaceAfterSurtitle; 349 y += Styles.spaceAfterSurtitle; 350 } 351 352 var titleHeight = 0f; 353 354 if (showTitle) 355 { 356 titleHeight = Styles.title.CalcHeight(titleContent, headerTextWidth); 357 358 titlePosition = new Rect 359 ( 360 headerTextX, 361 y, 362 headerTextWidth, 363 titleHeight 364 ); 365 366 headerHeight += titleHeight; 367 y += titleHeight; 368 } 369 370 var subtitleHeight = 0f; 371 372 if (showSubtitle) 373 { 374 headerHeight += Styles.spaceBeforeSubtitle; 375 y += Styles.spaceBeforeSubtitle; 376 377 subtitleHeight = Styles.subtitle.CalcHeight(subtitleContent, headerTextWidth); 378 379 subtitlePosition = new Rect 380 ( 381 headerTextX, 382 y, 383 headerTextWidth, 384 subtitleHeight 385 ); 386 387 headerHeight += subtitleHeight; 388 y += subtitleHeight; 389 } 390 391 iconsPositions.Clear(); 392 393 if (showIcons) 394 { 395 var iconRow = 0; 396 var iconCol = 0; 397 398 for (int i = 0; i < description.icons.Length; i++) 399 { 400 var iconPosition = new Rect 401 ( 402 innerX + innerWidth - ((iconCol + 1) * Styles.iconsSize) - ((iconCol) * Styles.iconsSpacing), 403 innerY + (iconRow * (Styles.iconsSize + Styles.iconsSpacing)), 404 Styles.iconsSize, 405 Styles.iconsSize 406 ); 407 408 iconsPositions.Add(iconPosition); 409 410 iconRow++; 411 412 if (iconRow % Styles.iconsPerColumn == 0) 413 { 414 iconCol++; 415 iconRow = 0; 416 } 417 } 418 } 419 420 var settingsHeight = 0f; 421 422 if (showSettings) 423 { 424 headerHeight += Styles.spaceBeforeSettings; 425 426 foreach (var setting in settings) 427 { 428 var settingWidth = settingsPositions[setting].width; 429 430 using (LudiqGUIUtility.currentInspectorWidth.Override(settingWidth)) 431 { 432 var settingHeight = LudiqGUI.GetInspectorHeight(null, setting, settingWidth, settingLabelsContents[setting] ?? GUIContent.none); 433 434 var settingPosition = new Rect 435 ( 436 headerTextX, 437 y, 438 settingWidth, 439 settingHeight 440 ); 441 442 settingsPositions[setting] = settingPosition; 443 444 settingsHeight += settingHeight; 445 y += settingHeight; 446 447 settingsHeight += Styles.spaceBetweenSettings; 448 y += Styles.spaceBetweenSettings; 449 } 450 } 451 452 settingsHeight -= Styles.spaceBetweenSettings; 453 y -= Styles.spaceBetweenSettings; 454 455 headerHeight += settingsHeight; 456 457 headerHeight += Styles.spaceAfterSettings; 458 y += Styles.spaceAfterSettings; 459 } 460 461 if (showHeaderAddon) 462 { 463 var headerAddonHeight = GetHeaderAddonHeight(headerAddonWidth); 464 465 headerAddonPosition = new Rect 466 ( 467 headerTextX, 468 y, 469 headerAddonWidth, 470 headerAddonHeight 471 ); 472 473 headerHeight += headerAddonHeight; 474 y += headerAddonHeight; 475 } 476 477 if (headerHeight < Styles.iconSize) 478 { 479 var difference = Styles.iconSize - headerHeight; 480 var centeringOffset = difference / 2; 481 482 if (showTitle) 483 { 484 var _titlePosition = titlePosition; 485 _titlePosition.y += centeringOffset; 486 titlePosition = _titlePosition; 487 } 488 489 if (showSubtitle) 490 { 491 var _subtitlePosition = subtitlePosition; 492 _subtitlePosition.y += centeringOffset; 493 subtitlePosition = _subtitlePosition; 494 } 495 496 if (showSettings) 497 { 498 foreach (var setting in settings) 499 { 500 var _settingPosition = settingsPositions[setting]; 501 _settingPosition.y += centeringOffset; 502 settingsPositions[setting] = _settingPosition; 503 } 504 } 505 506 if (showHeaderAddon) 507 { 508 var _headerAddonPosition = headerAddonPosition; 509 _headerAddonPosition.y += centeringOffset; 510 headerAddonPosition = _headerAddonPosition; 511 } 512 513 headerHeight = Styles.iconSize; 514 } 515 516 y = innerY + headerHeight; 517 518 var innerHeight = 0f; 519 520 innerHeight += headerHeight; 521 522 if (showPorts) 523 { 524 innerHeight += Styles.spaceBeforePorts; 525 y += Styles.spaceBeforePorts; 526 527 var portsBackgroundY = y; 528 var portsBackgroundHeight = 0f; 529 530 portsBackgroundHeight += Styles.portsBackground.padding.top; 531 innerHeight += Styles.portsBackground.padding.top; 532 y += Styles.portsBackground.padding.top; 533 534 var portStartY = y; 535 536 var inputsHeight = 0f; 537 var outputsHeight = 0f; 538 539 foreach (var input in inputs) 540 { 541 input.y = y; 542 543 var inputHeight = input.GetHeight(); 544 545 inputsHeight += inputHeight; 546 y += inputHeight; 547 548 inputsHeight += Styles.spaceBetweenPorts; 549 y += Styles.spaceBetweenPorts; 550 } 551 552 if (inputs.Count > 0) 553 { 554 inputsHeight -= Styles.spaceBetweenPorts; 555 y -= Styles.spaceBetweenPorts; 556 } 557 558 y = portStartY; 559 560 foreach (var output in outputs) 561 { 562 output.y = y; 563 564 var outputHeight = output.GetHeight(); 565 566 outputsHeight += outputHeight; 567 y += outputHeight; 568 569 outputsHeight += Styles.spaceBetweenPorts; 570 y += Styles.spaceBetweenPorts; 571 } 572 573 if (outputs.Count > 0) 574 { 575 outputsHeight -= Styles.spaceBetweenPorts; 576 y -= Styles.spaceBetweenPorts; 577 } 578 579 var portsHeight = Math.Max(inputsHeight, outputsHeight); 580 581 portsBackgroundHeight += portsHeight; 582 innerHeight += portsHeight; 583 y = portStartY + portsHeight; 584 585 portsBackgroundHeight += Styles.portsBackground.padding.bottom; 586 innerHeight += Styles.portsBackground.padding.bottom; 587 y += Styles.portsBackground.padding.bottom; 588 589 portsBackgroundPosition = new Rect 590 ( 591 edgeX, 592 portsBackgroundY, 593 edgeWidth, 594 portsBackgroundHeight 595 ); 596 } 597 598 var edgeHeight = InnerToEdgePosition(new Rect(0, 0, 0, innerHeight)).height; 599 600 _position = new Rect 601 ( 602 edgeX, 603 edgeY, 604 edgeWidth, 605 edgeHeight 606 ); 607 } 608 609 protected virtual float GetHeaderAddonWidth() 610 { 611 return 0; 612 } 613 614 protected virtual float GetHeaderAddonHeight(float width) 615 { 616 return 0; 617 } 618 619 #endregion 620 621 622 #region Drawing 623 624 protected virtual NodeColorMix baseColor => NodeColor.Gray; 625 626 protected override NodeColorMix color 627 { 628 get 629 { 630 if (unitDebugData.runtimeException != null) 631 { 632 return NodeColor.Red; 633 } 634 635 var color = baseColor; 636 637 if (analysis.warnings.Count > 0) 638 { 639 var mostSevereWarning = Warning.MostSevereLevel(analysis.warnings); 640 641 switch (mostSevereWarning) 642 { 643 case WarningLevel.Error: 644 color = NodeColor.Red; 645 646 break; 647 648 case WarningLevel.Severe: 649 color = NodeColor.Orange; 650 651 break; 652 653 case WarningLevel.Caution: 654 color = NodeColor.Yellow; 655 656 break; 657 } 658 } 659 660 if (EditorApplication.isPaused) 661 { 662 if (EditorTimeBinding.frame == unitDebugData.lastInvokeFrame) 663 { 664 return NodeColor.Blue; 665 } 666 } 667 else 668 { 669 var mix = color; 670 mix.blue = Mathf.Lerp(1, 0, (EditorTimeBinding.time - unitDebugData.lastInvokeTime) / Styles.invokeFadeDuration); 671 672 return mix; 673 } 674 675 return color; 676 } 677 } 678 679 protected override NodeShape shape => NodeShape.Square; 680 681 protected virtual bool showTitle => !string.IsNullOrEmpty(description.shortTitle); 682 683 protected virtual bool showSurtitle => !string.IsNullOrEmpty(description.surtitle); 684 685 protected virtual bool showSubtitle => !string.IsNullOrEmpty(description.subtitle); 686 687 protected virtual bool showIcons => description.icons.Length > 0; 688 689 protected virtual bool showSettings => settingNames.Count > 0; 690 691 protected virtual bool showHeaderAddon => false; 692 693 protected virtual bool showPorts => ports.Count > 0; 694 695 protected override bool dim 696 { 697 get 698 { 699 var dim = BoltCore.Configuration.dimInactiveNodes && !analysis.isEntered; 700 701 if (isMouseOver || isSelected) 702 { 703 dim = false; 704 } 705 706 if (BoltCore.Configuration.dimIncompatibleNodes && canvas.isCreatingConnection) 707 { 708 dim = !unit.ports.Any(p => canvas.connectionSource == p || canvas.connectionSource.CanValidlyConnectTo(p)); 709 } 710 711 return dim; 712 } 713 } 714 715 public override void DrawForeground() 716 { 717 BeginDim(); 718 719 base.DrawForeground(); 720 721 DrawIcon(); 722 723 if (showSurtitle) 724 { 725 DrawSurtitle(); 726 } 727 728 if (showTitle) 729 { 730 DrawTitle(); 731 } 732 733 if (showSubtitle) 734 { 735 DrawSubtitle(); 736 } 737 738 if (showIcons) 739 { 740 DrawIcons(); 741 } 742 743 if (showSettings) 744 { 745 DrawSettings(); 746 } 747 748 if (showHeaderAddon) 749 { 750 DrawHeaderAddon(); 751 } 752 753 if (showPorts) 754 { 755 DrawPortsBackground(); 756 } 757 758 EndDim(); 759 } 760 761 protected void DrawIcon() 762 { 763 var icon = description.icon ?? BoltFlow.Icons.unit; 764 765 if (icon != null && icon[(int)iconPosition.width]) 766 { 767 GUI.DrawTexture(iconPosition, icon[(int)iconPosition.width]); 768 } 769 } 770 771 protected void DrawTitle() 772 { 773 GUI.Label(titlePosition, titleContent, invertForeground ? Styles.titleInverted : Styles.title); 774 } 775 776 protected void DrawSurtitle() 777 { 778 GUI.Label(surtitlePosition, surtitleContent, invertForeground ? Styles.surtitleInverted : Styles.surtitle); 779 } 780 781 protected void DrawSubtitle() 782 { 783 GUI.Label(subtitlePosition, subtitleContent, invertForeground ? Styles.subtitleInverted : Styles.subtitle); 784 } 785 786 protected void DrawIcons() 787 { 788 for (int i = 0; i < description.icons.Length; i++) 789 { 790 var icon = description.icons[i]; 791 var position = iconsPositions[i]; 792 793 GUI.DrawTexture(position, icon?[(int)position.width]); 794 } 795 } 796 797 private void DrawSettings() 798 { 799 if (graph.zoom < FlowCanvas.inspectorZoomThreshold) 800 { 801 return; 802 } 803 804 EditorGUI.BeginDisabledGroup(!e.IsRepaint && isMouseThrough && !isMouseOver); 805 806 EditorGUI.BeginChangeCheck(); 807 808 foreach (var setting in settings) 809 { 810 DrawSetting(setting); 811 } 812 813 if (EditorGUI.EndChangeCheck()) 814 { 815 unit.Define(); 816 Reposition(); 817 } 818 819 EditorGUI.EndDisabledGroup(); 820 } 821 822 protected void DrawSetting(Metadata setting) 823 { 824 var settingPosition = settingsPositions[setting]; 825 826 using (LudiqGUIUtility.currentInspectorWidth.Override(settingPosition.width)) 827 using (Inspector.expandTooltip.Override(false)) 828 { 829 var label = settingLabelsContents[setting]; 830 831 if (label == null) 832 { 833 LudiqGUI.Inspector(setting, settingPosition, GUIContent.none); 834 } 835 else 836 { 837 using (Inspector.defaultLabelStyle.Override(Styles.settingLabel)) 838 using (LudiqGUIUtility.labelWidth.Override(Styles.settingLabel.CalcSize(label).x)) 839 { 840 LudiqGUI.Inspector(setting, settingPosition, label); 841 } 842 } 843 } 844 } 845 846 protected virtual void DrawHeaderAddon() { } 847 848 protected void DrawPortsBackground() 849 { 850 if (canvas.showRelations) 851 { 852 foreach (var relation in unit.relations) 853 { 854 var start = ports.Single(pw => pw.port == relation.source).handlePosition.center; 855 var end = ports.Single(pw => pw.port == relation.destination).handlePosition.center; 856 857 var startTangent = start; 858 var endTangent = end; 859 860 if (relation.source is IUnitInputPort && 861 relation.destination is IUnitInputPort) 862 { 863 //startTangent -= new Vector2(20, 0); 864 endTangent -= new Vector2(32, 0); 865 } 866 else 867 { 868 startTangent += new Vector2(innerPosition.width / 2, 0); 869 endTangent += new Vector2(-innerPosition.width / 2, 0); 870 } 871 872 Handles.DrawBezier 873 ( 874 start, 875 end, 876 startTangent, 877 endTangent, 878 new Color(0.136f, 0.136f, 0.136f, 1.0f), 879 null, 880 3 881 ); 882 } 883 } 884 else 885 { 886 if (e.IsRepaint) 887 { 888 Styles.portsBackground.Draw(portsBackgroundPosition, false, false, false, false); 889 } 890 } 891 } 892 893 #endregion 894 895 896 #region Selecting 897 898 public override bool canSelect => true; 899 900 #endregion 901 902 903 #region Dragging 904 905 public override bool canDrag => true; 906 907 public override void ExpandDragGroup(HashSet<IGraphElement> dragGroup) 908 { 909 if (BoltCore.Configuration.carryChildren) 910 { 911 foreach (var output in unit.outputs) 912 { 913 foreach (var connection in output.connections) 914 { 915 if (dragGroup.Contains(connection.destination.unit)) 916 { 917 continue; 918 } 919 920 dragGroup.Add(connection.destination.unit); 921 922 canvas.Widget(connection.destination.unit).ExpandDragGroup(dragGroup); 923 } 924 } 925 } 926 } 927 928 #endregion 929 930 931 #region Deleting 932 933 public override bool canDelete => true; 934 935 #endregion 936 937 938 #region Clipboard 939 940 public override void ExpandCopyGroup(HashSet<IGraphElement> copyGroup) 941 { 942 copyGroup.UnionWith(unit.connections.Cast<IGraphElement>()); 943 } 944 945 #endregion 946 947 948 #region Context 949 950 protected override IEnumerable<DropdownOption> contextOptions 951 { 952 get 953 { 954 yield return new DropdownOption((Action)ReplaceUnit, "Replace..."); 955 956 foreach (var baseOption in base.contextOptions) 957 { 958 yield return baseOption; 959 } 960 } 961 } 962 963 private void ReplaceUnit() 964 { 965 UnitWidgetHelper.ReplaceUnit(unit, reference, context, selection, e); 966 } 967 968 #endregion 969 970 971 public static class Styles 972 { 973 static Styles() 974 { 975 // Disabling word wrap because Unity's CalcSize and CalcHeight 976 // are broken w.r.t. pixel-perfection and matrix 977 978 title = new GUIStyle(BoltCore.Styles.nodeLabel); 979 title.padding = new RectOffset(0, 5, 0, 2); 980 title.margin = new RectOffset(0, 0, 0, 0); 981 title.fontSize = 12; 982 title.alignment = TextAnchor.MiddleLeft; 983 title.wordWrap = false; 984 985 surtitle = new GUIStyle(BoltCore.Styles.nodeLabel); 986 surtitle.padding = new RectOffset(0, 5, 0, 0); 987 surtitle.margin = new RectOffset(0, 0, 0, 0); 988 surtitle.fontSize = 10; 989 surtitle.alignment = TextAnchor.MiddleLeft; 990 surtitle.wordWrap = false; 991 992 subtitle = new GUIStyle(surtitle); 993 subtitle.padding.bottom = 2; 994 995 titleInverted = new GUIStyle(title); 996 titleInverted.normal.textColor = ColorPalette.unityBackgroundDark; 997 998 surtitleInverted = new GUIStyle(surtitle); 999 surtitleInverted.normal.textColor = ColorPalette.unityBackgroundDark; 1000 1001 subtitleInverted = new GUIStyle(subtitle); 1002 subtitleInverted.normal.textColor = ColorPalette.unityBackgroundDark; 1003 1004 if (EditorGUIUtility.isProSkin) 1005 { 1006 portsBackground = new GUIStyle("In BigTitle") 1007 { 1008 padding = new RectOffset(0, 0, 6, 5) 1009 }; 1010 } 1011 else 1012 { 1013 TextureResolution[] textureResolution = { 2 }; 1014 var createTextureOptions = CreateTextureOptions.Scalable; 1015 EditorTexture normalTexture = BoltCore.Resources.LoadTexture($"NodePortsBackground.png", textureResolution, createTextureOptions); 1016 1017 portsBackground = new GUIStyle 1018 { 1019 normal = { background = normalTexture.Single() }, 1020 padding = new RectOffset(0, 0, 6, 5) 1021 }; 1022 } 1023 1024 settingLabel = new GUIStyle(BoltCore.Styles.nodeLabel); 1025 settingLabel.padding.left = 0; 1026 settingLabel.padding.right = 5; 1027 settingLabel.wordWrap = false; 1028 settingLabel.clipping = TextClipping.Clip; 1029 } 1030 1031 public static readonly GUIStyle title; 1032 1033 public static readonly GUIStyle surtitle; 1034 1035 public static readonly GUIStyle subtitle; 1036 1037 public static readonly GUIStyle titleInverted; 1038 1039 public static readonly GUIStyle surtitleInverted; 1040 1041 public static readonly GUIStyle subtitleInverted; 1042 1043 public static readonly GUIStyle settingLabel; 1044 1045 public static readonly float spaceAroundLineIcon = 5; 1046 1047 public static readonly float spaceBeforePorts = 5; 1048 1049 public static readonly float spaceBetweenInputsAndOutputs = 8; 1050 1051 public static readonly float spaceBeforeSettings = 2; 1052 1053 public static readonly float spaceBetweenSettings = 3; 1054 1055 public static readonly float spaceBetweenPorts = 3; 1056 1057 public static readonly float spaceAfterSettings = 0; 1058 1059 public static readonly float maxSettingsWidth = 150; 1060 1061 public static readonly GUIStyle portsBackground; 1062 1063 public static readonly float iconSize = IconSize.Medium; 1064 1065 public static readonly float iconsSize = IconSize.Small; 1066 1067 public static readonly float iconsSpacing = 3; 1068 1069 public static readonly int iconsPerColumn = 2; 1070 1071 public static readonly float spaceAfterIcon = 6; 1072 1073 public static readonly float spaceAfterSurtitle = 2; 1074 1075 public static readonly float spaceBeforeSubtitle = 0; 1076 1077 public static readonly float invokeFadeDuration = 0.5f; 1078 } 1079 } 1080 1081 internal class UnitWidgetHelper 1082 { 1083 internal static void ReplaceUnit(IUnit unit, GraphReference reference, IGraphContext context, GraphSelection selection, EventWrapper eventWrapper) 1084 { 1085 var oldUnit = unit; 1086 var unitPosition = oldUnit.position; 1087 var preservation = UnitPreservation.Preserve(oldUnit); 1088 1089 var options = new UnitOptionTree(new GUIContent("Node")); 1090 options.filter = UnitOptionFilter.Any; 1091 options.filter.NoConnection = false; 1092 options.reference = reference; 1093 1094 var activatorPosition = new Rect(eventWrapper.mousePosition, new Vector2(200, 1)); 1095 1096 LudiqGUI.FuzzyDropdown 1097 ( 1098 activatorPosition, 1099 options, 1100 null, 1101 delegate (object _option) 1102 { 1103 var option = (IUnitOption)_option; 1104 1105 context.BeginEdit(); 1106 UndoUtility.RecordEditedObject("Replace Node"); 1107 var graph = oldUnit.graph; 1108 oldUnit.graph.units.Remove(oldUnit); 1109 var newUnit = option.InstantiateUnit(); 1110 newUnit.guid = Guid.NewGuid(); 1111 newUnit.position = unitPosition; 1112 graph.units.Add(newUnit); 1113 preservation.RestoreTo(newUnit); 1114 option.PreconfigureUnit(newUnit); 1115 selection.Select(newUnit); 1116 GUI.changed = true; 1117 context.EndEdit(); 1118 } 1119 ); 1120 } 1121 } 1122}