using System.Collections.Generic; using UnityEditor.Experimental.GraphView; using UnityEditor.ShaderGraph.Drawing.Interfaces; using UnityEngine; using UnityEngine.UIElements; namespace UnityEditor.ShaderGraph.Drawing.Views { interface ISelectionProvider { List GetSelection { get; } } class GraphSubWindow : GraphElement, ISGResizable { ISGViewModel m_ViewModel; ISGViewModel ViewModel { get => m_ViewModel; set => m_ViewModel = value; } Dragger m_Dragger; // This needs to be something that each subclass defines for itself at creation time // if they all use the same they'll be stacked on top of each other at SG window creation protected WindowDockingLayout windowDockingLayout { get; private set; } = new WindowDockingLayout { dockingTop = true, dockingLeft = false, verticalOffset = 8, horizontalOffset = 8, }; // Used to cache the window docking layout between resizing operations as it interferes with window resizing operations private IStyle cachedWindowDockingStyle; protected VisualElement m_MainContainer; protected VisualElement m_Root; protected Label m_TitleLabel; protected Label m_SubTitleLabel; protected ScrollView m_ScrollView; protected VisualElement m_ContentContainer; protected VisualElement m_HeaderItem; protected VisualElement m_ParentView; // Added for test assembly access internal ScrollView scrollView => m_ScrollView; // These are used as default values for styling and layout purposes // They can be overriden if a child class wants to roll its own style and layout behavior public virtual string layoutKey => "UnityEditor.ShaderGraph.SubWindow"; public virtual string styleName => "GraphSubWindow"; public virtual string UxmlName => "GraphSubWindow"; // Each sub-window will override these if they need to public virtual string elementName => ""; public virtual string windowTitle => ""; public VisualElement ParentView { get { if (!isWindowed && m_ParentView == null) m_ParentView = GetFirstAncestorOfType(); return m_ParentView; } set { if (!isWindowed) return; m_ParentView = value; } } public List selection { get { if (ParentView is ISelectionProvider selectionProvider) return selectionProvider.GetSelection; AssertHelpers.Fail("GraphSubWindow was unable to find a selection provider. Please check if parent view of: " + name + " implements ISelectionProvider::GetSelection"); return new List(); } } public override string title { get { return m_TitleLabel.text; } set { m_TitleLabel.text = value; } } public string subTitle { get { return m_SubTitleLabel.text; } set { m_SubTitleLabel.text = value; } } // Intended for future handling of docking to sides of the shader graph window bool m_IsWindowed; public bool isWindowed { get { return m_IsWindowed; } set { if (m_IsWindowed == value) return; if (value) { capabilities &= ~Capabilities.Movable; AddToClassList("windowed"); this.RemoveManipulator(m_Dragger); } else { capabilities |= Capabilities.Movable; RemoveFromClassList("windowed"); this.AddManipulator(m_Dragger); } m_IsWindowed = value; } } public override VisualElement contentContainer => m_ContentContainer; private bool m_IsResizable = false; // Can be set by child classes as needed protected bool isWindowResizable { get => m_IsResizable; set { if (m_IsResizable != value) { m_IsResizable = value; HandleResizingBehavior(m_IsResizable); } } } void HandleResizingBehavior(bool isResizable) { if (isResizable) { var resizeElement = this.Q(); resizeElement.BindOnResizeCallback(OnWindowResize); hierarchy.Add(resizeElement); } else { var resizeElement = this.Q(); resizeElement.SetResizeRules(ResizableElement.Resizer.None); hierarchy.Remove(resizeElement); } } protected void SetResizingRules(ResizableElement.Resizer resizeDirections) { var resizeElement = this.Q(); resizeElement.SetResizeRules(resizeDirections); } private bool m_IsScrollable = false; // Can be set by child classes as needed protected bool isWindowScrollable { get => m_IsScrollable; set { if (m_IsScrollable != value) { m_IsScrollable = value; HandleScrollingBehavior(m_IsScrollable); } } } protected float scrollableWidth { get { return m_ScrollView.contentContainer.layout.width - m_ScrollView.contentViewport.layout.width; } } protected float scrollableHeight { get { return contentContainer.layout.height - m_ScrollView.contentViewport.layout.height; } } void HandleScrollingBehavior(bool scrollable) { if (scrollable) { // Remove the categories container from the content item and add it to the scrollview m_ContentContainer.RemoveFromHierarchy(); m_ScrollView.Add(m_ContentContainer); AddToClassList("scrollable"); } else { // Remove the categories container from the scrollview and add it to the content item m_ContentContainer.RemoveFromHierarchy(); m_Root.Add(m_ContentContainer); RemoveFromClassList("scrollable"); } } protected GraphSubWindow(ISGViewModel viewModel) { ViewModel = viewModel; m_ParentView = ViewModel.parentView; ParentView.Add(this); var styleSheet = Resources.Load($"Styles/{styleName}"); // Setup VisualElement from Stylesheet and UXML file styleSheets.Add(styleSheet); var uxml = Resources.Load($"UXML/{UxmlName}"); m_MainContainer = uxml.Instantiate(); m_MainContainer.AddToClassList("mainContainer"); m_Root = m_MainContainer.Q("content"); m_HeaderItem = m_MainContainer.Q("header"); m_HeaderItem.AddToClassList("subWindowHeader"); m_ScrollView = m_MainContainer.Q("scrollView"); m_TitleLabel = m_MainContainer.Q