A game about forced loneliness, made by TACStudios
1using System.Collections.Generic;
2using UnityEditor.Experimental.GraphView;
3using UnityEditor.ShaderGraph.Drawing.Interfaces;
4using UnityEngine;
5using UnityEngine.UIElements;
6
7namespace UnityEditor.ShaderGraph.Drawing.Views
8{
9 interface ISelectionProvider
10 {
11 List<ISelectable> GetSelection { get; }
12 }
13
14 class GraphSubWindow : GraphElement, ISGResizable
15 {
16 ISGViewModel m_ViewModel;
17
18 ISGViewModel ViewModel
19 {
20 get => m_ViewModel;
21 set => m_ViewModel = value;
22 }
23
24 Dragger m_Dragger;
25
26 // This needs to be something that each subclass defines for itself at creation time
27 // if they all use the same they'll be stacked on top of each other at SG window creation
28 protected WindowDockingLayout windowDockingLayout { get; private set; } = new WindowDockingLayout
29 {
30 dockingTop = true,
31 dockingLeft = false,
32 verticalOffset = 8,
33 horizontalOffset = 8,
34 };
35
36 // Used to cache the window docking layout between resizing operations as it interferes with window resizing operations
37 private IStyle cachedWindowDockingStyle;
38
39 protected VisualElement m_MainContainer;
40 protected VisualElement m_Root;
41 protected Label m_TitleLabel;
42 protected Label m_SubTitleLabel;
43 protected ScrollView m_ScrollView;
44 protected VisualElement m_ContentContainer;
45 protected VisualElement m_HeaderItem;
46 protected VisualElement m_ParentView;
47
48 // Added for test assembly access
49 internal ScrollView scrollView => m_ScrollView;
50
51 // These are used as default values for styling and layout purposes
52 // They can be overriden if a child class wants to roll its own style and layout behavior
53 public virtual string layoutKey => "UnityEditor.ShaderGraph.SubWindow";
54 public virtual string styleName => "GraphSubWindow";
55 public virtual string UxmlName => "GraphSubWindow";
56
57 // Each sub-window will override these if they need to
58 public virtual string elementName => "";
59 public virtual string windowTitle => "";
60
61 public VisualElement ParentView
62 {
63 get
64 {
65 if (!isWindowed && m_ParentView == null)
66 m_ParentView = GetFirstAncestorOfType<GraphView>();
67 return m_ParentView;
68 }
69
70 set
71 {
72 if (!isWindowed)
73 return;
74 m_ParentView = value;
75 }
76 }
77
78 public List<ISelectable> selection
79 {
80 get
81 {
82 if (ParentView is ISelectionProvider selectionProvider)
83 return selectionProvider.GetSelection;
84
85 AssertHelpers.Fail("GraphSubWindow was unable to find a selection provider. Please check if parent view of: " + name + " implements ISelectionProvider::GetSelection");
86 return new List<ISelectable>();
87 }
88 }
89
90 public override string title
91 {
92 get { return m_TitleLabel.text; }
93 set { m_TitleLabel.text = value; }
94 }
95
96 public string subTitle
97 {
98 get { return m_SubTitleLabel.text; }
99 set { m_SubTitleLabel.text = value; }
100 }
101
102 // Intended for future handling of docking to sides of the shader graph window
103 bool m_IsWindowed;
104 public bool isWindowed
105 {
106 get { return m_IsWindowed; }
107 set
108 {
109 if (m_IsWindowed == value) return;
110
111 if (value)
112 {
113 capabilities &= ~Capabilities.Movable;
114 AddToClassList("windowed");
115 this.RemoveManipulator(m_Dragger);
116 }
117 else
118 {
119 capabilities |= Capabilities.Movable;
120 RemoveFromClassList("windowed");
121 this.AddManipulator(m_Dragger);
122 }
123 m_IsWindowed = value;
124 }
125 }
126
127 public override VisualElement contentContainer => m_ContentContainer;
128
129 private bool m_IsResizable = false;
130
131 // Can be set by child classes as needed
132 protected bool isWindowResizable
133 {
134 get => m_IsResizable;
135 set
136 {
137 if (m_IsResizable != value)
138 {
139 m_IsResizable = value;
140 HandleResizingBehavior(m_IsResizable);
141 }
142 }
143 }
144
145 void HandleResizingBehavior(bool isResizable)
146 {
147 if (isResizable)
148 {
149 var resizeElement = this.Q<ResizableElement>();
150 resizeElement.BindOnResizeCallback(OnWindowResize);
151 hierarchy.Add(resizeElement);
152 }
153 else
154 {
155 var resizeElement = this.Q<ResizableElement>();
156 resizeElement.SetResizeRules(ResizableElement.Resizer.None);
157 hierarchy.Remove(resizeElement);
158 }
159 }
160
161 protected void SetResizingRules(ResizableElement.Resizer resizeDirections)
162 {
163 var resizeElement = this.Q<ResizableElement>();
164 resizeElement.SetResizeRules(resizeDirections);
165 }
166
167 private bool m_IsScrollable = false;
168
169 // Can be set by child classes as needed
170 protected bool isWindowScrollable
171 {
172 get => m_IsScrollable;
173 set
174 {
175 if (m_IsScrollable != value)
176 {
177 m_IsScrollable = value;
178 HandleScrollingBehavior(m_IsScrollable);
179 }
180 }
181 }
182
183 protected float scrollableWidth
184 {
185 get { return m_ScrollView.contentContainer.layout.width - m_ScrollView.contentViewport.layout.width; }
186 }
187
188 protected float scrollableHeight
189 {
190 get { return contentContainer.layout.height - m_ScrollView.contentViewport.layout.height; }
191 }
192
193 void HandleScrollingBehavior(bool scrollable)
194 {
195 if (scrollable)
196 {
197 // Remove the categories container from the content item and add it to the scrollview
198 m_ContentContainer.RemoveFromHierarchy();
199 m_ScrollView.Add(m_ContentContainer);
200 AddToClassList("scrollable");
201 }
202 else
203 {
204 // Remove the categories container from the scrollview and add it to the content item
205 m_ContentContainer.RemoveFromHierarchy();
206 m_Root.Add(m_ContentContainer);
207
208 RemoveFromClassList("scrollable");
209 }
210 }
211
212 protected GraphSubWindow(ISGViewModel viewModel)
213 {
214 ViewModel = viewModel;
215 m_ParentView = ViewModel.parentView;
216 ParentView.Add(this);
217
218 var styleSheet = Resources.Load<StyleSheet>($"Styles/{styleName}");
219 // Setup VisualElement from Stylesheet and UXML file
220 styleSheets.Add(styleSheet);
221 var uxml = Resources.Load<VisualTreeAsset>($"UXML/{UxmlName}");
222 m_MainContainer = uxml.Instantiate();
223 m_MainContainer.AddToClassList("mainContainer");
224
225 m_Root = m_MainContainer.Q("content");
226 m_HeaderItem = m_MainContainer.Q("header");
227 m_HeaderItem.AddToClassList("subWindowHeader");
228 m_ScrollView = m_MainContainer.Q<ScrollView>("scrollView");
229 m_TitleLabel = m_MainContainer.Q<Label>(name: "titleLabel");
230 m_SubTitleLabel = m_MainContainer.Q<Label>(name: "subTitleLabel");
231 m_ContentContainer = m_MainContainer.Q(name: "contentContainer");
232
233 hierarchy.Add(m_MainContainer);
234
235 capabilities |= Capabilities.Movable | Capabilities.Resizable;
236 style.overflow = Overflow.Hidden;
237 focusable = false;
238
239 name = elementName;
240 title = windowTitle;
241
242 ClearClassList();
243 AddToClassList(name);
244
245 BuildManipulators();
246
247 /* Event interception to prevent GraphView manipulators from being triggered */
248 //RegisterCallback<DragUpdatedEvent>(e =>
249 //{
250 // e.StopPropagation();
251 //});
252
253 // prevent Zoomer manipulator
254 RegisterCallback<WheelEvent>(e =>
255 {
256 e.StopPropagation();
257 });
258
259 //RegisterCallback<MouseDownEvent>(e =>
260 //{
261 // // prevent ContentDragger manipulator
262 // e.StopPropagation();
263 //});
264 }
265
266 public void ShowWindow()
267 {
268 this.style.visibility = Visibility.Visible;
269 this.m_ScrollView.style.display = DisplayStyle.Flex;
270 this.MarkDirtyRepaint();
271 }
272
273 public void HideWindow()
274 {
275 this.style.visibility = Visibility.Hidden;
276 this.m_ScrollView.style.display = DisplayStyle.None;
277 this.MarkDirtyRepaint();
278 }
279
280 void BuildManipulators()
281 {
282 m_Dragger = new Dragger { clampToParentEdges = true };
283 RegisterCallback<MouseUpEvent>(OnMoveEnd);
284 this.AddManipulator(m_Dragger);
285 }
286
287 #region Layout
288 public void ClampToParentLayout(Rect parentLayout)
289 {
290 windowDockingLayout.CalculateDockingCornerAndOffset(layout, parentLayout);
291 windowDockingLayout.ClampToParentWindow();
292
293 // If the parent shader graph window is being resized smaller than this window on either axis
294 if (parentLayout.width < this.layout.width || parentLayout.height < this.layout.height)
295 {
296 // Don't adjust the sub window in this case as it causes flickering errors and looks broken
297 }
298 else
299 {
300 windowDockingLayout.ApplyPosition(this);
301 }
302
303 SerializeLayout();
304 }
305
306 public void OnStartResize()
307 {
308 cachedWindowDockingStyle = this.style;
309 }
310
311 public void OnResized()
312 {
313 if (cachedWindowDockingStyle != null)
314 {
315 this.style.left = cachedWindowDockingStyle.left;
316 this.style.right = cachedWindowDockingStyle.right;
317 this.style.bottom = cachedWindowDockingStyle.bottom;
318 this.style.top = cachedWindowDockingStyle.top;
319 }
320 windowDockingLayout.size = layout.size;
321 SerializeLayout();
322 }
323
324 public void DeserializeLayout()
325 {
326 var serializedLayout = EditorUserSettings.GetConfigValue(layoutKey);
327 if (!string.IsNullOrEmpty(serializedLayout))
328 windowDockingLayout = JsonUtility.FromJson<WindowDockingLayout>(serializedLayout);
329 else
330 {
331 // The window size needs to come from the stylesheet or UXML as opposed to being defined in code
332 windowDockingLayout.size = layout.size;
333 }
334
335 windowDockingLayout.ApplySize(this);
336 windowDockingLayout.ApplyPosition(this);
337 }
338
339 protected void AddStyleSheetFromPath(string styleSheetPath)
340 {
341 StyleSheet sheetAsset = Resources.Load<StyleSheet>(styleSheetPath); ;
342
343 if (sheetAsset == null)
344 {
345 Debug.LogWarning(string.Format("Style sheet not found for path \"{0}\"", styleSheetPath));
346 return;
347 }
348 styleSheets.Add(sheetAsset);
349 }
350
351 void SerializeLayout()
352 {
353 windowDockingLayout.size = layout.size;
354 var serializedLayout = JsonUtility.ToJson(windowDockingLayout);
355 EditorUserSettings.SetConfigValue(layoutKey, serializedLayout);
356 }
357
358 void OnMoveEnd(MouseUpEvent upEvent)
359 {
360 windowDockingLayout.CalculateDockingCornerAndOffset(layout, ParentView.layout);
361 windowDockingLayout.ClampToParentWindow();
362
363 SerializeLayout();
364 }
365
366 public bool CanResizePastParentBounds()
367 {
368 return false;
369 }
370
371 void OnWindowResize(MouseUpEvent upEvent)
372 {
373 }
374
375 public virtual void Dispose()
376 {
377 m_MainContainer = null;
378 m_Root = null;
379 m_TitleLabel = null;
380 m_SubTitleLabel = null;
381 m_ScrollView = null;
382 m_ContentContainer = null;
383 m_HeaderItem = null;
384 m_ParentView = null;
385 cachedWindowDockingStyle = null;
386 styleSheets.Clear();
387 }
388 }
389 #endregion
390}