A game about forced loneliness, made by TACStudios
1using System; 2using System.Collections.Generic; 3using System.Linq; 4using System.Reflection; 5using UnityEngine; 6using UnityEditor.Graphing; 7using UnityEditor.Graphing.Util; 8using UnityEditor.ShaderGraph.Drawing.Controls; 9using UnityEngine.Rendering; 10using UnityEditor.Experimental.GraphView; 11using UnityEditor.Rendering; 12using UnityEditor.ShaderGraph.Drawing.Inspector.PropertyDrawers; 13using UnityEditor.ShaderGraph.Internal; 14using UnityEngine.UIElements; 15using UnityEditor.UIElements; 16using Node = UnityEditor.Experimental.GraphView.Node; 17 18namespace UnityEditor.ShaderGraph.Drawing 19{ 20 sealed class MaterialNodeView : Node, IShaderNodeView, IInspectable 21 { 22 PreviewRenderData m_PreviewRenderData; 23 Image m_PreviewImage; 24 // Remove this after updated to the correct API call has landed in trunk. ------------ 25 VisualElement m_TitleContainer; 26 27 VisualElement m_PreviewContainer; 28 VisualElement m_PreviewFiller; 29 VisualElement m_ControlItems; 30 VisualElement m_ControlsDivider; 31 VisualElement m_DropdownItems; 32 VisualElement m_DropdownsDivider; 33 Action m_UnregisterAll; 34 35 IEdgeConnectorListener m_ConnectorListener; 36 37 MaterialGraphView m_GraphView; 38 39 public string inspectorTitle => $"{node.name} (Node)"; 40 public void Initialize(AbstractMaterialNode inNode, PreviewManager previewManager, IEdgeConnectorListener connectorListener, MaterialGraphView graphView) 41 { 42 styleSheets.Add(Resources.Load<StyleSheet>("Styles/MaterialNodeView")); 43 styleSheets.Add(Resources.Load<StyleSheet>($"Styles/ColorMode")); 44 AddToClassList("MaterialNode"); 45 46 if (inNode == null) 47 return; 48 49 var contents = this.Q("contents"); 50 51 m_GraphView = graphView; 52 mainContainer.style.overflow = StyleKeyword.None; // Override explicit style set in base class 53 m_ConnectorListener = connectorListener; 54 node = inNode; 55 viewDataKey = node.objectId; 56 UpdateTitle(); 57 58 // Add disabled overlay 59 Add(new VisualElement() { name = "disabledOverlay", pickingMode = PickingMode.Ignore }); 60 61 // Add controls container 62 var controlsContainer = new VisualElement { name = "controls" }; 63 { 64 m_ControlsDivider = new VisualElement { name = "divider" }; 65 m_ControlsDivider.AddToClassList("horizontal"); 66 controlsContainer.Add(m_ControlsDivider); 67 m_ControlItems = new VisualElement { name = "items" }; 68 controlsContainer.Add(m_ControlItems); 69 70 // Instantiate control views from node 71 foreach (var propertyInfo in node.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) 72 foreach (IControlAttribute attribute in propertyInfo.GetCustomAttributes(typeof(IControlAttribute), false)) 73 m_ControlItems.Add(attribute.InstantiateControl(node, propertyInfo)); 74 } 75 if (m_ControlItems.childCount > 0) 76 contents.Add(controlsContainer); 77 78 // Add dropdowns container 79 if (inNode is SubGraphNode) 80 { 81 var dropdownContainer = new VisualElement { name = "dropdowns" }; 82 { 83 m_DropdownsDivider = new VisualElement { name = "divider" }; 84 m_DropdownsDivider.AddToClassList("horizontal"); 85 dropdownContainer.Add(m_DropdownsDivider); 86 m_DropdownItems = new VisualElement { name = "items" }; 87 dropdownContainer.Add(m_DropdownItems); 88 UpdateDropdownEntries(); 89 } 90 contents.Add(dropdownContainer); 91 } 92 93 if (node.hasPreview) 94 { 95 // Add actual preview which floats on top of the node 96 m_PreviewContainer = new VisualElement 97 { 98 name = "previewContainer", 99 style = { overflow = Overflow.Hidden }, 100 pickingMode = PickingMode.Ignore 101 }; 102 m_PreviewImage = new Image 103 { 104 name = "preview", 105 pickingMode = PickingMode.Ignore, 106 image = Texture2D.whiteTexture, 107 }; 108 { 109 // Add preview collapse button on top of preview 110 var collapsePreviewButton = new VisualElement { name = "collapse" }; 111 collapsePreviewButton.Add(new VisualElement { name = "icon" }); 112 collapsePreviewButton.AddManipulator(new Clickable(() => 113 { 114 SetPreviewExpandedStateOnSelection(false); 115 })); 116 m_PreviewImage.Add(collapsePreviewButton); 117 } 118 m_PreviewContainer.Add(m_PreviewImage); 119 120 // Hook up preview image to preview manager 121 m_PreviewRenderData = previewManager.GetPreviewRenderData(inNode); 122 m_PreviewRenderData.onPreviewChanged += UpdatePreviewTexture; 123 UpdatePreviewTexture(); 124 125 // Add fake preview which pads out the node to provide space for the floating preview 126 m_PreviewFiller = new VisualElement { name = "previewFiller" }; 127 m_PreviewFiller.AddToClassList("expanded"); 128 { 129 var previewDivider = new VisualElement { name = "divider" }; 130 previewDivider.AddToClassList("horizontal"); 131 m_PreviewFiller.Add(previewDivider); 132 133 var expandPreviewButton = new VisualElement { name = "expand" }; 134 expandPreviewButton.Add(new VisualElement { name = "icon" }); 135 expandPreviewButton.AddManipulator(new Clickable(() => 136 { 137 SetPreviewExpandedStateOnSelection(true); 138 })); 139 m_PreviewFiller.Add(expandPreviewButton); 140 } 141 contents.Add(m_PreviewFiller); 142 143 UpdatePreviewExpandedState(node.previewExpanded); 144 } 145 146 base.expanded = node.drawState.expanded; 147 AddSlots(node.GetSlots<MaterialSlot>()); 148 149 switch (node) 150 { 151 case SubGraphNode: 152 RegisterCallback<MouseDownEvent>(OnSubGraphDoubleClick); 153 m_UnregisterAll += () => { UnregisterCallback<MouseDownEvent>(OnSubGraphDoubleClick); }; 154 break; 155 } 156 157 m_TitleContainer = this.Q("title"); 158 159 if (node is BlockNode blockData) 160 { 161 AddToClassList("blockData"); 162 m_TitleContainer.RemoveFromHierarchy(); 163 } 164 else 165 { 166 SetPosition(new Rect(node.drawState.position.x, node.drawState.position.y, 0, 0)); 167 } 168 169 // Update active state 170 SetActive(node.isActive); 171 172 // Register OnMouseHover callbacks for node highlighting 173 RegisterCallback<MouseEnterEvent>(OnMouseHover); 174 m_UnregisterAll += () => { UnregisterCallback<MouseEnterEvent>(OnMouseHover); }; 175 176 RegisterCallback<MouseLeaveEvent>(OnMouseHover); 177 m_UnregisterAll += () => { UnregisterCallback<MouseLeaveEvent>(OnMouseHover); }; 178 179 ShaderGraphPreferences.onAllowDeprecatedChanged += UpdateTitle; 180 } 181 182 public bool FindPort(SlotReference slotRef, out ShaderPort port) 183 { 184 port = inputContainer.Query<ShaderPort>().ToList() 185 .Concat(outputContainer.Query<ShaderPort>().ToList()) 186 .First(p => p.slot.slotReference.Equals(slotRef)); 187 188 return port != null; 189 } 190 191 public void AttachMessage(string errString, ShaderCompilerMessageSeverity severity) 192 { 193 ClearMessage(); 194 IconBadge badge; 195 if (severity == ShaderCompilerMessageSeverity.Error) 196 { 197 badge = IconBadge.CreateError(errString); 198 } 199 else 200 { 201 badge = IconBadge.CreateComment(errString); 202 } 203 204 Add(badge); 205 206 if (node is BlockNode) 207 { 208 FindPort(node.GetSlotReference(0), out var port); 209 badge.AttachTo(port.parent, SpriteAlignment.RightCenter); 210 } 211 else 212 { 213 badge.AttachTo(m_TitleContainer, SpriteAlignment.RightCenter); 214 } 215 } 216 217 public void SetActive(bool state) 218 { 219 // Setup 220 var disabledString = "disabled"; 221 var portDisabledString = "inactive"; 222 223 224 if (!state) 225 { 226 // Add elements to disabled class list 227 AddToClassList(disabledString); 228 229 var inputPorts = inputContainer.Query<ShaderPort>().ToList(); 230 foreach (var port in inputPorts) 231 { 232 port.AddToClassList(portDisabledString); 233 } 234 var outputPorts = outputContainer.Query<ShaderPort>().ToList(); 235 foreach (var port in outputPorts) 236 { 237 port.AddToClassList(portDisabledString); 238 } 239 } 240 else 241 { 242 // Remove elements from disabled class list 243 RemoveFromClassList(disabledString); 244 245 var inputPorts = inputContainer.Query<ShaderPort>().ToList(); 246 foreach (var port in inputPorts) 247 { 248 port.RemoveFromClassList(portDisabledString); 249 } 250 var outputPorts = outputContainer.Query<ShaderPort>().ToList(); 251 foreach (var port in outputPorts) 252 { 253 port.RemoveFromClassList(portDisabledString); 254 } 255 } 256 } 257 258 public void ClearMessage() 259 { 260 var badge = this.Q<IconBadge>(); 261 badge?.Detach(); 262 badge?.RemoveFromHierarchy(); 263 } 264 265 public void UpdateDropdownEntries() 266 { 267 if (node is SubGraphNode subGraphNode && subGraphNode.asset != null) 268 { 269 m_DropdownItems.Clear(); 270 var dropdowns = subGraphNode.asset.dropdowns; 271 foreach (var dropdown in dropdowns) 272 { 273 if (dropdown.isExposed) 274 { 275 var name = subGraphNode.GetDropdownEntryName(dropdown.referenceName); 276 if (!dropdown.ContainsEntry(name)) 277 { 278 name = dropdown.entryName; 279 subGraphNode.SetDropdownEntryName(dropdown.referenceName, name); 280 } 281 282 var field = new PopupField<string>(dropdown.entries.Select(x => x.displayName).ToList(), name); 283 284 // Create anonymous lambda 285 EventCallback<ChangeEvent<string>> eventCallback = (evt) => 286 { 287 subGraphNode.owner.owner.RegisterCompleteObjectUndo("Change Dropdown Value"); 288 subGraphNode.SetDropdownEntryName(dropdown.referenceName, field.value); 289 subGraphNode.Dirty(ModificationScope.Topological); 290 }; 291 292 field.RegisterValueChangedCallback(eventCallback); 293 294 // Setup so we can unregister this callback later 295 m_UnregisterAll += () => field.UnregisterValueChangedCallback(eventCallback); 296 297 m_DropdownItems.Add(new PropertyRow(new Label(dropdown.displayName)), (row) => 298 { 299 row.styleSheets.Add(Resources.Load<StyleSheet>("Styles/PropertyRow")); 300 row.Add(field); 301 }); 302 } 303 } 304 } 305 } 306 307 public VisualElement colorElement 308 { 309 get { return this; } 310 } 311 312 static readonly StyleColor noColor = new StyleColor(StyleKeyword.Null); 313 public void SetColor(Color color) 314 { 315 m_TitleContainer.style.borderBottomColor = color; 316 } 317 318 public void ResetColor() 319 { 320 m_TitleContainer.style.borderBottomColor = noColor; 321 } 322 323 public Color GetColor() 324 { 325 return m_TitleContainer.resolvedStyle.borderBottomColor; 326 } 327 328 void OnSubGraphDoubleClick(MouseDownEvent evt) 329 { 330 if (evt.clickCount == 2 && evt.button == 0) 331 { 332 SubGraphNode subgraphNode = node as SubGraphNode; 333 334 var path = AssetDatabase.GUIDToAssetPath(subgraphNode.subGraphGuid); 335 ShaderGraphImporterEditor.ShowGraphEditWindow(path); 336 } 337 } 338 339 public Node gvNode => this; 340 341 [Inspectable("Node", null)] 342 public AbstractMaterialNode node { get; private set; } 343 344 public override bool expanded 345 { 346 get => base.expanded; 347 set 348 { 349 if (base.expanded == value) 350 return; 351 352 base.expanded = value; 353 354 if (node.drawState.expanded != value) 355 { 356 var ds = node.drawState; 357 ds.expanded = value; 358 node.drawState = ds; 359 } 360 361 foreach (var inputPort in inputContainer.Query<ShaderPort>().ToList()) 362 { 363 inputPort.parent.style.visibility = inputPort.style.visibility; 364 } 365 366 RefreshExpandedState(); // Necessary b/c we can't override enough Node.cs functions to update only what's needed 367 } 368 } 369 370 public override void BuildContextualMenu(ContextualMenuPopulateEvent evt) 371 { 372 if (evt.target is Node) 373 { 374 var canViewShader = node.hasPreview || node is SubGraphOutputNode; 375 evt.menu.AppendAction("Copy Shader", CopyToClipboard, 376 _ => canViewShader ? DropdownMenuAction.Status.Normal : DropdownMenuAction.Status.Hidden, 377 GenerationMode.ForReals); 378 evt.menu.AppendAction("Show Generated Code", ShowGeneratedCode, 379 _ => canViewShader ? DropdownMenuAction.Status.Normal : DropdownMenuAction.Status.Hidden, 380 GenerationMode.ForReals); 381 382 if (Unsupported.IsDeveloperMode()) 383 { 384 evt.menu.AppendAction("Show Preview Code", ShowGeneratedCode, 385 _ => canViewShader ? DropdownMenuAction.Status.Normal : DropdownMenuAction.Status.Hidden, 386 GenerationMode.Preview); 387 } 388 } 389 390 base.BuildContextualMenu(evt); 391 } 392 393 void CopyToClipboard(DropdownMenuAction action) 394 { 395 GUIUtility.systemCopyBuffer = ConvertToShader((GenerationMode)action.userData); 396 } 397 398 public string SanitizeName(string name) 399 { 400 return new string(name.Where(c => !Char.IsWhiteSpace(c)).ToArray()); 401 } 402 403 public void ShowGeneratedCode(DropdownMenuAction action) 404 { 405 string name = GetFirstAncestorOfType<GraphEditorView>().assetName; 406 var mode = (GenerationMode)action.userData; 407 408 string path = String.Format("Temp/GeneratedFromGraph-{0}-{1}-{2}{3}.shader", SanitizeName(name), 409 SanitizeName(node.name), node.objectId, mode == GenerationMode.Preview ? "-Preview" : ""); 410 if (GraphUtil.WriteToFile(path, ConvertToShader(mode))) 411 GraphUtil.OpenFile(path); 412 } 413 414 string ConvertToShader(GenerationMode mode) 415 { 416 var generator = new Generator(node.owner, node, mode, node.name); 417 return generator.generatedShader; 418 } 419 420 void SetNodesAsDirty() 421 { 422 var editorView = GetFirstAncestorOfType<GraphEditorView>(); 423 var nodeList = m_GraphView.Query<MaterialNodeView>().ToList(); 424 editorView.colorManager.SetNodesDirty(nodeList); 425 } 426 427 void UpdateNodeViews() 428 { 429 var editorView = GetFirstAncestorOfType<GraphEditorView>(); 430 var nodeList = m_GraphView.Query<MaterialNodeView>().ToList(); 431 editorView.colorManager.UpdateNodeViews(nodeList); 432 } 433 434 public object GetObjectToInspect() 435 { 436 return node; 437 } 438 439 public void SupplyDataToPropertyDrawer(IPropertyDrawer propertyDrawer, Action inspectorUpdateDelegate) 440 { 441 if (propertyDrawer is IGetNodePropertyDrawerPropertyData nodePropertyDrawer) 442 { 443 nodePropertyDrawer.GetPropertyData(SetNodesAsDirty, UpdateNodeViews); 444 } 445 } 446 447 private void SetSelfSelected() 448 { 449 m_GraphView.ClearSelection(); 450 m_GraphView.AddToSelection(this); 451 } 452 453 protected override void ToggleCollapse() 454 { 455 node.owner.owner.RegisterCompleteObjectUndo(!expanded ? "Expand Nodes" : "Collapse Nodes"); 456 expanded = !expanded; 457 458 // If selected, expand/collapse the other applicable nodes that are also selected 459 if (selected) 460 { 461 m_GraphView.SetNodeExpandedForSelectedNodes(expanded, false); 462 } 463 } 464 465 void SetPreviewExpandedStateOnSelection(bool state) 466 { 467 // If selected, expand/collapse the other applicable nodes that are also selected 468 if (selected) 469 { 470 m_GraphView.SetPreviewExpandedForSelectedNodes(state); 471 } 472 else 473 { 474 node.owner.owner.RegisterCompleteObjectUndo(state ? "Expand Previews" : "Collapse Previews"); 475 node.previewExpanded = state; 476 } 477 } 478 479 public bool CanToggleNodeExpanded() 480 { 481 return !(node is BlockNode) && m_CollapseButton.enabledInHierarchy; 482 } 483 484 void UpdatePreviewExpandedState(bool expanded) 485 { 486 node.previewExpanded = expanded; 487 if (m_PreviewFiller == null) 488 return; 489 if (expanded) 490 { 491 if (m_PreviewContainer.parent != this) 492 { 493 Add(m_PreviewContainer); 494 m_PreviewContainer.PlaceBehind(this.Q("selection-border")); 495 } 496 m_PreviewFiller.AddToClassList("expanded"); 497 m_PreviewFiller.RemoveFromClassList("collapsed"); 498 } 499 else 500 { 501 if (m_PreviewContainer.parent == m_PreviewFiller) 502 { 503 m_PreviewContainer.RemoveFromHierarchy(); 504 } 505 m_PreviewFiller.RemoveFromClassList("expanded"); 506 m_PreviewFiller.AddToClassList("collapsed"); 507 } 508 UpdatePreviewTexture(); 509 } 510 511 void UpdateTitle() 512 { 513 if (node is SubGraphNode subGraphNode && subGraphNode.asset != null) 514 title = subGraphNode.asset.name; 515 else 516 { 517 if (node.sgVersion < node.latestVersion) 518 { 519 if (node is IHasCustomDeprecationMessage customDeprecationMessage) 520 { 521 title = customDeprecationMessage.GetCustomDeprecationLabel(); 522 } 523 else 524 { 525 title = node.name + $" (Legacy v{node.sgVersion})"; 526 } 527 } 528 else 529 { 530 title = node.name; 531 } 532 } 533 } 534 535 void UpdateShaderPortsForSlots(bool inputSlots, List<MaterialSlot> allSlots, ShaderPort[] slotShaderPorts) 536 { 537 VisualElement portContainer = inputSlots ? inputContainer : outputContainer; 538 var existingPorts = portContainer.Query<ShaderPort>().ToList(); 539 foreach (ShaderPort shaderPort in existingPorts) 540 { 541 var currentSlotId = shaderPort.slot.id; 542 int newSlotIndex = allSlots.FindIndex(s => s.id == currentSlotId); 543 if (newSlotIndex < 0) 544 { 545 // slot doesn't exist anymore, remove it 546 if (inputSlots) 547 portContainer.Remove(shaderPort.parent); // remove parent (includes the InputView) 548 else 549 portContainer.Remove(shaderPort); 550 } 551 else 552 { 553 var newSlot = allSlots[newSlotIndex]; 554 slotShaderPorts[newSlotIndex] = shaderPort; 555 556 // these should probably be in an UpdateShaderPort(shaderPort, newSlot) function 557 shaderPort.slot = newSlot; 558 shaderPort.portName = newSlot.displayName; 559 560 if (inputSlots) // input slots also have to update the InputView 561 UpdatePortInputView(shaderPort); 562 } 563 } 564 } 565 566 public void OnModified(ModificationScope scope) 567 { 568 UpdateTitle(); 569 SetActive(node.isActive); 570 if (node.hasPreview) 571 UpdatePreviewExpandedState(node.previewExpanded); 572 573 base.expanded = node.drawState.expanded; 574 575 switch (scope) 576 { 577 // Update slots to match node modification 578 case ModificationScope.Topological: 579 { 580 var slots = node.GetSlots<MaterialSlot>().ToList(); 581 // going to record the corresponding ShaderPort to each slot, so we can order them later 582 ShaderPort[] slotShaderPorts = new ShaderPort[slots.Count]; 583 584 // update existing input and output ports 585 UpdateShaderPortsForSlots(true, slots, slotShaderPorts); 586 UpdateShaderPortsForSlots(false, slots, slotShaderPorts); 587 588 // check if there are any new slots that must create new ports 589 for (int i = 0; i < slots.Count; i++) 590 { 591 if (slotShaderPorts[i] == null) 592 slotShaderPorts[i] = AddShaderPortForSlot(slots[i]); 593 } 594 595 // make sure they are in the right order 596 // by bringing each port to front in declaration order 597 // note that this sorts input and output containers at the same time 598 foreach (var shaderPort in slotShaderPorts) 599 { 600 if (shaderPort != null) 601 { 602 if (shaderPort.slot.isInputSlot) 603 shaderPort.parent.BringToFront(); 604 else 605 shaderPort.BringToFront(); 606 } 607 } 608 609 break; 610 } 611 } 612 613 RefreshExpandedState(); // Necessary b/c we can't override enough Node.cs functions to update only what's needed 614 615 foreach (var listener in m_ControlItems.Children().OfType<AbstractMaterialNodeModificationListener>()) 616 { 617 if (listener != null) 618 listener.OnNodeModified(scope); 619 } 620 } 621 622 ShaderPort AddShaderPortForSlot(MaterialSlot slot) 623 { 624 if (slot.hidden) 625 return null; 626 627 ShaderPort port = ShaderPort.Create(slot, m_ConnectorListener); 628 if (slot.isOutputSlot) 629 { 630 outputContainer.Add(port); 631 } 632 else 633 { 634 var portContainer = new VisualElement(); 635 portContainer.style.flexDirection = FlexDirection.Row; 636 var portInputView = new PortInputView(slot) { style = { position = Position.Absolute } }; 637 portContainer.Add(portInputView); 638 portContainer.Add(port); 639 inputContainer.Add(portContainer); 640 641 // Update active state 642 if (node.isActive) 643 { 644 portInputView.RemoveFromClassList("disabled"); 645 } 646 else 647 { 648 portInputView.AddToClassList("disabled"); 649 } 650 } 651 port.OnDisconnect = OnEdgeDisconnected; 652 653 return port; 654 } 655 656 void AddSlots(IEnumerable<MaterialSlot> slots) 657 { 658 foreach (var slot in slots) 659 AddShaderPortForSlot(slot); 660 // Make sure the visuals are properly updated to reflect port list 661 RefreshPorts(); 662 } 663 664 void OnEdgeDisconnected(Port obj) 665 { 666 RefreshExpandedState(); 667 } 668 669 static bool GetPortInputView(ShaderPort port, out PortInputView view) 670 { 671 view = port.parent.Q<PortInputView>(); 672 return view != null; 673 } 674 675 public void UpdatePortInputTypes() 676 { 677 var portList = inputContainer.Query<ShaderPort>().ToList(); 678 portList.AddRange(outputContainer.Query<ShaderPort>().ToList()); 679 foreach (var anchor in portList) 680 { 681 var slot = anchor.slot; 682 anchor.portName = slot.displayName; 683 anchor.visualClass = slot.concreteValueType.ToClassName(); 684 685 if (GetPortInputView(anchor, out var portInputView)) 686 { 687 portInputView.UpdateSlotType(); 688 UpdatePortInputVisibility(portInputView, anchor); 689 } 690 } 691 692 foreach (var control in m_ControlItems.Children()) 693 { 694 if (control is AbstractMaterialNodeModificationListener listener) 695 listener.OnNodeModified(ModificationScope.Graph); 696 } 697 } 698 699 void UpdatePortInputView(ShaderPort port) 700 { 701 if (GetPortInputView(port, out var portInputView)) 702 { 703 portInputView.UpdateSlot(port.slot); 704 UpdatePortInputVisibility(portInputView, port); 705 } 706 } 707 708 void UpdatePortInputVisibility(PortInputView portInputView, ShaderPort port) 709 { 710 SetElementVisible(portInputView, !port.slot.isConnected); 711 port.parent.style.visibility = port.style.visibility; 712 portInputView.MarkDirtyRepaint(); 713 } 714 715 void SetElementVisible(VisualElement element, bool isVisible) 716 { 717 const string k_HiddenClassList = "hidden"; 718 719 if (isVisible) 720 { 721 // Restore default value for visibility by setting it to StyleKeyword.Null. 722 // Setting it to Visibility.Visible would make it visible even if parent is hidden. 723 element.style.visibility = StyleKeyword.Null; 724 element.RemoveFromClassList(k_HiddenClassList); 725 } 726 else 727 { 728 element.style.visibility = Visibility.Hidden; 729 element.AddToClassList(k_HiddenClassList); 730 } 731 } 732 733 SGBlackboardRow GetAssociatedBlackboardRow() 734 { 735 var graphEditorView = GetFirstAncestorOfType<GraphEditorView>(); 736 if (graphEditorView == null) 737 return null; 738 739 var blackboardController = graphEditorView.blackboardController; 740 if (blackboardController == null) 741 return null; 742 743 if (node is KeywordNode keywordNode) 744 { 745 return blackboardController.GetBlackboardRow(keywordNode.keyword); 746 } 747 748 if (node is DropdownNode dropdownNode) 749 { 750 return blackboardController.GetBlackboardRow(dropdownNode.dropdown); 751 } 752 return null; 753 } 754 755 void OnMouseHover(EventBase evt) 756 { 757 // Keyword/Dropdown nodes should be highlighted when Blackboard entry is hovered 758 // TODO: Move to new NodeView type when keyword node has unique style 759 var blackboardRow = GetAssociatedBlackboardRow(); 760 if (blackboardRow != null) 761 { 762 if (evt.eventTypeId == MouseEnterEvent.TypeId()) 763 { 764 blackboardRow.AddToClassList("hovered"); 765 } 766 else 767 { 768 blackboardRow.RemoveFromClassList("hovered"); 769 } 770 } 771 } 772 773 void UpdatePreviewTexture() 774 { 775 if (m_PreviewRenderData.texture == null || !node.previewExpanded) 776 { 777 m_PreviewImage.visible = false; 778 m_PreviewImage.image = Texture2D.blackTexture; 779 } 780 else 781 { 782 m_PreviewImage.visible = true; 783 m_PreviewImage.AddToClassList("visible"); 784 m_PreviewImage.RemoveFromClassList("hidden"); 785 if (m_PreviewImage.image != m_PreviewRenderData.texture) 786 m_PreviewImage.image = m_PreviewRenderData.texture; 787 else 788 m_PreviewImage.MarkDirtyRepaint(); 789 790 if (m_PreviewRenderData.shaderData.isOutOfDate) 791 m_PreviewImage.tintColor = new Color(1.0f, 1.0f, 1.0f, 0.3f); 792 else 793 m_PreviewImage.tintColor = Color.white; 794 } 795 } 796 797 public void Dispose() 798 { 799 ClearMessage(); 800 801 foreach (var portInputView in inputContainer.Query<PortInputView>().ToList()) 802 portInputView.Dispose(); 803 804 foreach (var shaderPort in outputContainer.Query<ShaderPort>().ToList()) 805 shaderPort.Dispose(); 806 807 var propRow = GetAssociatedBlackboardRow(); 808 // If this node view is deleted, remove highlighting from associated blackboard row 809 if (propRow != null) 810 { 811 propRow.RemoveFromClassList("hovered"); 812 } 813 814 styleSheets.Clear(); 815 inputContainer?.Clear(); 816 outputContainer?.Clear(); 817 m_DropdownsDivider?.Clear(); 818 m_ControlsDivider?.Clear(); 819 m_PreviewContainer?.Clear(); 820 m_ControlItems?.Clear(); 821 822 m_ConnectorListener = null; 823 m_GraphView = null; 824 m_DropdownItems = null; 825 m_ControlItems = null; 826 m_ControlsDivider = null; 827 m_PreviewContainer = null; 828 m_PreviewFiller = null; 829 m_PreviewImage = null; 830 m_DropdownsDivider = null; 831 m_TitleContainer = null; 832 node = null; 833 userData = null; 834 835 // Unregister callback 836 m_UnregisterAll?.Invoke(); 837 m_UnregisterAll = null; 838 839 if (m_PreviewRenderData != null) 840 { 841 m_PreviewRenderData.onPreviewChanged -= UpdatePreviewTexture; 842 m_PreviewRenderData = null; 843 } 844 ShaderGraphPreferences.onAllowDeprecatedChanged -= UpdateTitle; 845 846 Clear(); 847 } 848 } 849}