HotReloadOverlay.cs 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. #if UNITY_2021_2_OR_NEWER
  2. using System;
  3. using System.Collections.Generic;
  4. using UnityEditor.Overlays;
  5. using UnityEngine.UIElements;
  6. using UnityEditor;
  7. using UnityEngine;
  8. using UnityEditor.Toolbars;
  9. namespace SingularityGroup.HotReload.Editor {
  10. [Overlay(typeof(SceneView), "Hot Reload", true)]
  11. [Icon("Assets/HotReload/Editor/Resources/Icon_DarkMode.png")]
  12. internal class HotReloadOverlay : ToolbarOverlay {
  13. HotReloadOverlay() : base(HotReloadToolbarIndicationButton.id, HotReloadToolbarEventsButton.id, HotReloadToolbarRecompileButton.id) {
  14. EditorApplication.update += Update;
  15. }
  16. EditorIndicationState.IndicationStatus lastIndicationStatus;
  17. [EditorToolbarElement(id, typeof(SceneView))]
  18. class HotReloadToolbarIndicationButton : EditorToolbarButton, IAccessContainerWindow {
  19. internal const string id = "HotReloadOverlay/LogoButton";
  20. public EditorWindow containerWindow { get; set; }
  21. EditorIndicationState.IndicationStatus lastIndicationStatus;
  22. internal HotReloadToolbarIndicationButton() {
  23. icon = GetIndicationIcon();
  24. tooltip = EditorIndicationState.IndicationStatusText;
  25. clicked += OnClick;
  26. EditorApplication.update += Update;
  27. }
  28. void OnClick() {
  29. EditorWindow.GetWindow<HotReloadWindow>().Show();
  30. EditorWindow.GetWindow<HotReloadWindow>().SelectTab(typeof(HotReloadRunTab));
  31. }
  32. void Update() {
  33. if (lastIndicationStatus != EditorIndicationState.CurrentIndicationStatus) {
  34. icon = GetIndicationIcon();
  35. tooltip = EditorIndicationState.IndicationStatusText;
  36. lastIndicationStatus = EditorIndicationState.CurrentIndicationStatus;
  37. }
  38. }
  39. ~HotReloadToolbarIndicationButton() {
  40. clicked -= OnClick;
  41. EditorApplication.update -= Update;
  42. }
  43. }
  44. [EditorToolbarElement(id, typeof(SceneView))]
  45. class HotReloadToolbarEventsButton : EditorToolbarButton, IAccessContainerWindow {
  46. internal const string id = "HotReloadOverlay/EventsButton";
  47. public EditorWindow containerWindow { get; set; }
  48. bool lastShowingRedDot;
  49. internal HotReloadToolbarEventsButton() {
  50. icon = HotReloadState.ShowingRedDot ? GUIHelper.GetInvertibleIcon(InvertibleIcon.EventsNew) : GUIHelper.GetInvertibleIcon(InvertibleIcon.Events);
  51. tooltip = "Events";
  52. clicked += OnClick;
  53. EditorApplication.update += Update;
  54. }
  55. void OnClick() {
  56. HotReloadEventPopup.Open(PopupSource.Overlay, Event.current.mousePosition);
  57. }
  58. void Update() {
  59. if (lastShowingRedDot != HotReloadState.ShowingRedDot) {
  60. icon = HotReloadState.ShowingRedDot ? GUIHelper.GetInvertibleIcon(InvertibleIcon.EventsNew) : GUIHelper.GetInvertibleIcon(InvertibleIcon.Events);
  61. lastShowingRedDot = HotReloadState.ShowingRedDot;
  62. }
  63. }
  64. ~HotReloadToolbarEventsButton() {
  65. clicked -= OnClick;
  66. EditorApplication.update -= Update;
  67. }
  68. }
  69. [EditorToolbarElement(id, typeof(SceneView))]
  70. class HotReloadToolbarRecompileButton : EditorToolbarButton, IAccessContainerWindow {
  71. internal const string id = "HotReloadOverlay/RecompileButton";
  72. public EditorWindow containerWindow { get; set; }
  73. private Texture2D refreshIcon => GUIHelper.GetInvertibleIcon(InvertibleIcon.Recompile);
  74. internal HotReloadToolbarRecompileButton() {
  75. icon = refreshIcon;
  76. tooltip = "Recompile";
  77. clicked += HotReloadRunTab.RecompileWithChecks;
  78. }
  79. }
  80. private static Texture2D latestIcon;
  81. private static Dictionary<string, Texture2D> iconTextures = new Dictionary<string, Texture2D>();
  82. private static Spinner spinner = new Spinner(100);
  83. private static Texture2D GetIndicationIcon() {
  84. if (EditorIndicationState.IndicationIconPath == null || EditorIndicationState.SpinnerActive) {
  85. latestIcon = spinner.GetIcon();
  86. } else {
  87. latestIcon = GUIHelper.GetLocalIcon(EditorIndicationState.IndicationIconPath);
  88. }
  89. return latestIcon;
  90. }
  91. private static Image indicationIcon;
  92. private static Label indicationText;
  93. bool initialized;
  94. /// <summary>
  95. /// Create Hot Reload overlay panel.
  96. /// </summary>
  97. public override VisualElement CreatePanelContent() {
  98. var root = new VisualElement() { name = "Hot Reload Indication" };
  99. root.style.flexDirection = FlexDirection.Row;
  100. indicationIcon = new Image() { image = GUIHelper.GetLocalIcon(EditorIndicationState.greyIconPath) };
  101. indicationIcon.style.height = 30;
  102. indicationIcon.style.width = 30;
  103. indicationIcon.style.marginLeft = 2;
  104. indicationIcon.style.marginTop = 1;
  105. indicationIcon.style.marginRight = 5;
  106. indicationText = new Label(){text = EditorIndicationState.IndicationStatusText};
  107. indicationText.style.paddingTop = 9;
  108. indicationText.style.marginLeft = new StyleLength(StyleKeyword.Auto);
  109. indicationText.style.marginRight = new StyleLength(StyleKeyword.Auto);
  110. root.Add(indicationIcon);
  111. root.Add(indicationText);
  112. root.style.width = 190;
  113. root.style.height = 32;
  114. initialized = true;
  115. return root;
  116. }
  117. static bool _repaint;
  118. static bool _instantRepaint;
  119. static DateTime _lastRepaint;
  120. private void Update() {
  121. if (!initialized) {
  122. return;
  123. }
  124. if (lastIndicationStatus != EditorIndicationState.CurrentIndicationStatus) {
  125. indicationIcon.image = GetIndicationIcon();
  126. indicationText.text = EditorIndicationState.IndicationStatusText;
  127. lastIndicationStatus = EditorIndicationState.CurrentIndicationStatus;
  128. }
  129. try {
  130. if (HotReloadEventPopup.I.open
  131. && EditorWindow.mouseOverWindow
  132. && EditorWindow.mouseOverWindow?.GetType() == typeof(UnityEditor.PopupWindow)
  133. ) {
  134. _repaint = true;
  135. }
  136. } catch (NullReferenceException) {
  137. // Unity randomly throws nullrefs when EditorWindow.mouseOverWindow gets accessed
  138. }
  139. if (_repaint && DateTime.UtcNow - _lastRepaint > TimeSpan.FromMilliseconds(33)) {
  140. _repaint = false;
  141. _instantRepaint = true;
  142. }
  143. if (_instantRepaint) {
  144. HotReloadEventPopup.I.Repaint();
  145. }
  146. }
  147. ~HotReloadOverlay() {
  148. EditorApplication.update -= Update;
  149. }
  150. }
  151. }
  152. #endif