VFXSystemBorder.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.UIElements;
  5. using UnityEditor.Experimental.GraphView;
  6. using System;
  7. using System.Linq;
  8. using UnityObject = UnityEngine.Object;
  9. namespace UnityEditor.VFX.UI
  10. {
  11. class VFXSystemBorderFactory : UxmlFactory<VFXSystemBorder>
  12. { }
  13. class VFXSystemBorder : GraphElement, IControlledElement<VFXSystemController>, IDisposable
  14. {
  15. class Content : ImmediateModeElement
  16. {
  17. VFXSystemBorder m_Border;
  18. public Content(VFXSystemBorder border)
  19. {
  20. m_Border = border;
  21. }
  22. protected override void ImmediateRepaint()
  23. {
  24. m_Border.RecreateResources();
  25. VFXView view = GetFirstAncestorOfType<VFXView>();
  26. if (view != null && m_Border.m_Mat != null)
  27. {
  28. float radius = m_Border.resolvedStyle.borderTopLeftRadius;
  29. float realBorder = m_Border.resolvedStyle.borderLeftWidth * view.scale;
  30. Vector4 size = new Vector4(layout.width * .5f, layout.height * 0.5f, 0, 0);
  31. m_Border.m_Mat.SetVector("_Size", size);
  32. m_Border.m_Mat.SetFloat("_Border", realBorder < 1.75f ? 1.75f / view.scale : m_Border.resolvedStyle.borderLeftWidth);
  33. m_Border.m_Mat.SetFloat("_Radius", radius);
  34. float opacity = m_Border.resolvedStyle.opacity;
  35. Color start = (QualitySettings.activeColorSpace == ColorSpace.Linear) ? m_Border.startColor.gamma : m_Border.startColor;
  36. start.a *= opacity;
  37. m_Border.m_Mat.SetColor("_ColorStart", start);
  38. Color end = (QualitySettings.activeColorSpace == ColorSpace.Linear) ? m_Border.endColor.gamma : m_Border.endColor;
  39. end.a *= opacity;
  40. m_Border.m_Mat.SetColor("_ColorEnd", end);
  41. Color middle = (QualitySettings.activeColorSpace == ColorSpace.Linear) ? m_Border.middleColor.gamma : m_Border.middleColor;
  42. middle.a *= opacity;
  43. m_Border.m_Mat.SetColor("_ColorMiddle", middle);
  44. m_Border.m_Mat.SetPass(0);
  45. Graphics.DrawMeshNow(s_Mesh, Matrix4x4.Translate(new Vector3(size.x, size.y, 0)));
  46. }
  47. }
  48. }
  49. Material m_Mat;
  50. static Mesh s_Mesh;
  51. public VFXSystemBorder()
  52. {
  53. RecreateResources();
  54. var tpl = VFXView.LoadUXML("VFXSystemBorder");
  55. tpl.CloneTree(this);
  56. this.AddStyleSheetPath("VFXSystemBorder");
  57. this.style.overflow = Overflow.Visible;
  58. m_Title = this.Query<Label>("title");
  59. m_TitleField = this.Query<TextField>("title-field");
  60. m_TitleField.style.display = DisplayStyle.None;
  61. m_Title.RegisterCallback<MouseDownEvent>(OnTitleMouseDown);
  62. m_TitleField.Q("unity-text-input").RegisterCallback<FocusOutEvent>(OnTitleBlur);
  63. m_TitleField.RegisterCallback<ChangeEvent<string>>(OnTitleChange);
  64. m_Title.RegisterCallback<GeometryChangedEvent>(OnTitleRelayout);
  65. Content content = new Content(this);
  66. content.style.position = UnityEngine.UIElements.Position.Absolute;
  67. content.style.top = content.style.left = content.style.right = content.style.bottom = 0f;
  68. content.pickingMode = PickingMode.Ignore;
  69. pickingMode = PickingMode.Ignore;
  70. Add(content);
  71. RegisterCallback<CustomStyleResolvedEvent>(OnCustomStyleResolved);
  72. this.AddManipulator(new ContextualMenuManipulator(BuildContextualMenu));
  73. visible = false;
  74. }
  75. public void BuildContextualMenu(ContextualMenuPopulateEvent evt)
  76. {
  77. }
  78. public void OnRename()
  79. {
  80. m_TitleField.RemoveFromClassList("empty");
  81. m_TitleField.value = m_Title.text;
  82. m_TitleField.style.display = DisplayStyle.Flex;
  83. UpdateTitleFieldRect();
  84. m_TitleField.Q(TextField.textInputUssName).Focus();
  85. m_TitleField.SelectAll();
  86. }
  87. Label m_Title;
  88. TextField m_TitleField;
  89. void OnTitleMouseDown(MouseDownEvent e)
  90. {
  91. if (e.clickCount == 2)
  92. {
  93. OnRename();
  94. e.StopPropagation();
  95. e.PreventDefault();
  96. }
  97. }
  98. bool IsDifferenceTooSmall(float x, float y)
  99. {
  100. return Mathf.Abs(x - y) < 1f;
  101. }
  102. void OnTitleRelayout(GeometryChangedEvent e)
  103. {
  104. if (IsDifferenceTooSmall(e.oldRect.x, e.newRect.x) &&
  105. IsDifferenceTooSmall(e.oldRect.y, e.newRect.y) &&
  106. IsDifferenceTooSmall(e.oldRect.width, e.newRect.width) &&
  107. IsDifferenceTooSmall(e.oldRect.height, e.newRect.height))
  108. {
  109. return;
  110. }
  111. UpdateTitleFieldRect();
  112. RecomputeBounds();
  113. }
  114. void UpdateTitleFieldRect()
  115. {
  116. Rect rect = m_Title.layout;
  117. m_Title.parent.ChangeCoordinatesTo(m_TitleField.parent, rect);
  118. m_TitleField.style.top = rect.yMin - 6;
  119. m_TitleField.style.left = rect.xMin - 5;
  120. m_TitleField.style.right = m_Title.resolvedStyle.marginRight + m_Title.resolvedStyle.borderRightWidth + 2;
  121. m_TitleField.style.height = rect.height - m_Title.resolvedStyle.marginTop - m_Title.resolvedStyle.marginBottom;
  122. }
  123. void OnTitleBlur(FocusOutEvent e)
  124. {
  125. title = m_TitleField.value;
  126. m_TitleField.style.display = DisplayStyle.None;
  127. controller.title = title;
  128. }
  129. void OnContextChanged(GeometryChangedEvent e)
  130. {
  131. RecomputeBounds();
  132. }
  133. void OnTitleChange(ChangeEvent<string> e)
  134. {
  135. title = m_TitleField.value;
  136. if (string.IsNullOrEmpty(e.previousValue) != string.IsNullOrEmpty(e.newValue))
  137. {
  138. RecomputeBounds();
  139. }
  140. }
  141. public override string title
  142. {
  143. get
  144. {
  145. return m_Title.text;
  146. }
  147. set
  148. {
  149. if (m_Title.text != value)
  150. {
  151. m_Title.text = value;
  152. RecomputeBounds();
  153. }
  154. }
  155. }
  156. public bool m_WaitingRecompute;
  157. public void RecomputeBounds()
  158. {
  159. if (m_WaitingRecompute)
  160. return;
  161. visible = true;
  162. //title width should be at least as wide as a context to be valid.
  163. float titleWidth = m_Title.layout.width;
  164. bool shouldDeferRecompute = float.IsNaN(titleWidth) || titleWidth < 50;
  165. bool titleEmpty = string.IsNullOrEmpty(m_Title.text) || shouldDeferRecompute;
  166. if (titleEmpty)
  167. {
  168. m_Title.AddToClassList("empty");
  169. }
  170. else
  171. {
  172. m_Title.RemoveFromClassList("empty");
  173. }
  174. Rect rect = Rect.zero;
  175. if (m_Contexts != null)
  176. {
  177. foreach (var context in m_Contexts)
  178. {
  179. if (context != null)
  180. {
  181. if (rect == Rect.zero)
  182. {
  183. rect = context.localBound;
  184. }
  185. else
  186. {
  187. rect = RectUtils.Encompass(rect, context.GetPosition());
  188. }
  189. }
  190. }
  191. }
  192. if (float.IsNaN(rect.xMin) || float.IsNaN(rect.yMin) || float.IsNaN(rect.width) || float.IsNaN(rect.height))
  193. {
  194. rect = Rect.zero;
  195. }
  196. rect = RectUtils.Inflate(rect, 20, titleEmpty ? 20 : m_Title.layout.height, 20, 20);
  197. if (shouldDeferRecompute)
  198. {
  199. SetPosition(rect);
  200. if (!m_WaitingRecompute)
  201. {
  202. m_WaitingRecompute = true;
  203. schedule.Execute(() => { m_WaitingRecompute = false; RecomputeBounds(); }).ExecuteLater(0); // title height might have changed if width have changed
  204. }
  205. }
  206. else
  207. {
  208. SetPosition(rect);
  209. }
  210. }
  211. VFXContextUI[] m_Contexts;
  212. private VFXContextUI[] contexts
  213. {
  214. get
  215. {
  216. return m_Contexts;
  217. }
  218. set
  219. {
  220. if (m_Contexts != null)
  221. {
  222. foreach (var context in m_Contexts)
  223. {
  224. context?.UnregisterCallback<GeometryChangedEvent>(OnContextChanged);
  225. }
  226. }
  227. m_Contexts = value;
  228. if (m_Contexts != null)
  229. {
  230. foreach (var context in m_Contexts)
  231. {
  232. context?.RegisterCallback<GeometryChangedEvent>(OnContextChanged);
  233. }
  234. }
  235. RecomputeBounds();
  236. }
  237. }
  238. void RecreateResources()
  239. {
  240. if (s_Mesh == null)
  241. {
  242. s_Mesh = new Mesh();
  243. int verticeCount = 20;
  244. var vertices = new Vector3[verticeCount];
  245. var uvsBorder = new Vector2[verticeCount];
  246. var uvsDistance = new Vector2[verticeCount];
  247. for (int ix = 0; ix < 4; ++ix)
  248. {
  249. for (int iy = 0; iy < 4; ++iy)
  250. {
  251. vertices[ix + iy * 4] = new Vector3(ix < 2 ? -1 : 1, iy < 2 ? -1 : 1, 0);
  252. uvsBorder[ix + iy * 4] = new Vector2(ix == 0 || ix == 3 ? 1 : 0, iy == 0 || iy == 3 ? 1 : 0);
  253. uvsDistance[ix + iy * 4] = new Vector2(iy < 2 ? ix / 2 : 2 - ix / 2, iy < 2 ? 0 : 1);
  254. }
  255. }
  256. for (int i = 16; i < 20; ++i)
  257. {
  258. vertices[i] = vertices[i - 16];
  259. uvsBorder[i] = uvsBorder[i - 16];
  260. uvsDistance[i] = new Vector2(2, 2);
  261. }
  262. vertices[16] = vertices[0];
  263. vertices[17] = vertices[1];
  264. vertices[18] = vertices[4];
  265. vertices[19] = vertices[5];
  266. uvsBorder[16] = uvsBorder[0];
  267. uvsBorder[17] = uvsBorder[1];
  268. uvsBorder[18] = uvsBorder[4];
  269. uvsBorder[19] = uvsBorder[5];
  270. uvsDistance[16] = new Vector2(2, 2);
  271. uvsDistance[17] = new Vector2(2, 2);
  272. uvsDistance[18] = new Vector2(2, 2);
  273. uvsDistance[19] = new Vector2(2, 2);
  274. var indices = new int[4 * 8];
  275. for (int ix = 0; ix < 3; ++ix)
  276. {
  277. for (int iy = 0; iy < 3; ++iy)
  278. {
  279. int quadIndex = (ix + iy * 3);
  280. if (quadIndex == 4)
  281. continue;
  282. else if (quadIndex > 4)
  283. --quadIndex;
  284. int vertIndex = quadIndex * 4;
  285. indices[vertIndex] = ix + iy * 4;
  286. indices[vertIndex + 1] = ix + (iy + 1) * 4;
  287. indices[vertIndex + 2] = ix + 1 + (iy + 1) * 4;
  288. indices[vertIndex + 3] = ix + 1 + iy * 4;
  289. if (quadIndex == 3)
  290. {
  291. indices[vertIndex] = 18;
  292. indices[vertIndex + 3] = 19;
  293. }
  294. }
  295. }
  296. s_Mesh.vertices = vertices;
  297. s_Mesh.uv = uvsBorder;
  298. s_Mesh.uv2 = uvsDistance;
  299. s_Mesh.SetIndices(indices, MeshTopology.Quads, 0);
  300. }
  301. if (m_Mat == null)
  302. m_Mat = new Material(Shader.Find("Hidden/VFX/GradientDashedBorder"));
  303. }
  304. void IDisposable.Dispose()
  305. {
  306. UnityObject.DestroyImmediate(m_Mat);
  307. }
  308. Color m_StartColor;
  309. public Color startColor
  310. {
  311. get
  312. {
  313. return m_StartColor;
  314. }
  315. set
  316. {
  317. m_StartColor = value;
  318. }
  319. }
  320. Color m_EndColor;
  321. public Color endColor
  322. {
  323. get
  324. {
  325. return m_EndColor;
  326. }
  327. set
  328. {
  329. m_EndColor = value;
  330. }
  331. }
  332. Color m_MiddleColor;
  333. public Color middleColor
  334. {
  335. get
  336. {
  337. return m_MiddleColor;
  338. }
  339. set
  340. {
  341. m_MiddleColor = value;
  342. }
  343. }
  344. static readonly CustomStyleProperty<Color> s_StartColorProperty = new CustomStyleProperty<Color>("--start-color");
  345. static readonly CustomStyleProperty<Color> s_EndColorProperty = new CustomStyleProperty<Color>("--end-color");
  346. static readonly CustomStyleProperty<Color> s_MiddleColorProperty = new CustomStyleProperty<Color>("--middle-color");
  347. private void OnCustomStyleResolved(CustomStyleResolvedEvent e)
  348. {
  349. var customStyle = e.customStyle;
  350. customStyle.TryGetValue(s_StartColorProperty, out m_StartColor);
  351. customStyle.TryGetValue(s_EndColorProperty, out m_EndColor);
  352. customStyle.TryGetValue(s_MiddleColorProperty, out m_MiddleColor);
  353. }
  354. VFXSystemController m_Controller;
  355. Controller IControlledElement.controller
  356. {
  357. get { return m_Controller; }
  358. }
  359. public VFXSystemController controller
  360. {
  361. get { return m_Controller; }
  362. set
  363. {
  364. if (m_Controller != null)
  365. {
  366. m_Controller.UnregisterHandler(this);
  367. }
  368. m_Controller = value;
  369. if (m_Controller != null)
  370. {
  371. m_Controller.RegisterHandler(this);
  372. }
  373. }
  374. }
  375. public void Update()
  376. {
  377. VFXView view = GetFirstAncestorOfType<VFXView>();
  378. if (view == null || m_Controller == null)
  379. return;
  380. contexts = controller.contexts.Select(t => view.GetGroupNodeElement(t) as VFXContextUI).ToArray();
  381. title = controller.contexts[0].model.GetGraph().systemNames.GetUniqueSystemName(controller.contexts[0].model.GetData());
  382. }
  383. public void OnControllerChanged(ref ControllerChangedEvent e)
  384. {
  385. Update();
  386. }
  387. }
  388. }