123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388 |
-
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using System.Runtime.CompilerServices;
- using System.Text.RegularExpressions;
- using System.Threading;
- using SingularityGroup.HotReload.DTO;
- using SingularityGroup.HotReload.Editor.Cli;
- using SingularityGroup.HotReload.Editor.Semver;
- using UnityEditor;
- using UnityEditor.Compilation;
- using UnityEngine;
- [assembly: InternalsVisibleTo("SingularityGroup.HotReload.EditorSamples")]
- namespace SingularityGroup.HotReload.Editor {
- class HotReloadWindow : EditorWindow {
- public static HotReloadWindow Current { get; private set; }
- List<HotReloadTabBase> tabs;
- List<HotReloadTabBase> Tabs => tabs ?? (tabs = new List<HotReloadTabBase> {
- RunTab,
- SettingsTab,
- AboutTab,
- });
- int selectedTab;
- internal static Vector2 scrollPos;
-
- static Timer timer;
- HotReloadRunTab runTab;
- internal HotReloadRunTab RunTab => runTab ?? (runTab = new HotReloadRunTab(this));
- HotReloadSettingsTab settingsTab;
- internal HotReloadSettingsTab SettingsTab => settingsTab ?? (settingsTab = new HotReloadSettingsTab(this));
- HotReloadAboutTab aboutTab;
- internal HotReloadAboutTab AboutTab => aboutTab ?? (aboutTab = new HotReloadAboutTab(this));
- static ShowOnStartupEnum _showOnStartupOption;
- /// <summary>
- /// This token is cancelled when the EditorWindow is disabled.
- /// </summary>
- /// <remarks>
- /// Use it for all tasks.
- /// When token is cancelled, scripts are about to be recompiled and this will cause tasks to fail for weird reasons.
- /// </remarks>
- public CancellationToken cancelToken;
- CancellationTokenSource cancelTokenSource;
- static readonly PackageUpdateChecker packageUpdateChecker = new PackageUpdateChecker();
- [MenuItem("Window/Hot Reload/Open &#H")]
- internal static void Open() {
- // opening the window on CI systems was keeping Unity open indefinitely
- if (EditorWindowHelper.IsHumanControllingUs()) {
- if (Current) {
- Current.Show();
- Current.Focus();
- } else {
- Current = GetWindow<HotReloadWindow>();
- }
- }
- }
-
- [MenuItem("Window/Hot Reload/Recompile")]
- internal static void Recompile() {
- HotReloadRunTab.Recompile();
- }
- void OnInterval(object o) {
- HotReloadRunTab.RepaintInstant();
- }
- void OnEnable() {
- if (timer == null) {
- timer = new Timer(OnInterval, null, 20 * 1000, 20 * 1000);
- }
- Current = this;
- if (cancelTokenSource != null) {
- cancelTokenSource.Cancel();
- }
- // Set min size initially so that full UI is visible
- if (!HotReloadPrefs.OpenedWindowAtLeastOnce) {
- this.minSize = new Vector2(Constants.RecompileButtonTextHideWidth + 1, Constants.EventsListHideHeight + 70);
- HotReloadPrefs.OpenedWindowAtLeastOnce = true;
- }
- cancelTokenSource = new CancellationTokenSource();
- cancelToken = cancelTokenSource.Token;
-
- this.titleContent = new GUIContent(" Hot Reload", GUIHelper.GetInvertibleIcon(InvertibleIcon.Logo));
- _showOnStartupOption = HotReloadPrefs.ShowOnStartup;
- packageUpdateChecker.StartCheckingForNewVersion();
- }
- void Update() {
- foreach (var tab in Tabs) {
- tab.Update();
- }
- }
- void OnDisable() {
- if (cancelTokenSource != null) {
- cancelTokenSource.Cancel();
- cancelTokenSource = null;
- }
- if (Current == this) {
- Current = null;
- }
- timer.Dispose();
- timer = null;
- }
- internal void SelectTab(Type tabType) {
- selectedTab = Tabs.FindIndex(x => x.GetType() == tabType);
- }
-
- public HotReloadRunTabState RunTabState { get; private set; }
- void OnGUI() {
- // TabState ensures rendering is consistent between Layout and Repaint calls
- // Without it errors like this happen:
- // ArgumentException: Getting control 2's position in a group with only 2 controls when doing repaint
- // See thread for more context: https://answers.unity.com/questions/17718/argumentexception-getting-control-2s-position-in-a.html
- if (Event.current.type == EventType.Layout) {
- RunTabState = HotReloadRunTabState.Current;
- }
- using(var scope = new EditorGUILayout.ScrollViewScope(scrollPos, false, false)) {
- scrollPos = scope.scrollPosition;
- // RenderDebug();
- RenderTabs();
- }
- GUILayout.FlexibleSpace(); // GUI below will be rendered on the bottom
- if (HotReloadWindowStyles.windowScreenHeight > 90)
- RenderBottomBar();
- }
- void RenderDebug() {
- if (GUILayout.Button("RESET WINDOW")) {
- OnDisable();
- RequestHelper.RequestLogin("test", "test", 1).Forget();
- HotReloadPrefs.LicenseEmail = null;
- HotReloadPrefs.ExposeServerToLocalNetwork = true;
- HotReloadPrefs.LicensePassword = null;
- HotReloadPrefs.LoggedBurstHint = false;
- HotReloadPrefs.DontShowPromptForDownload = false;
- HotReloadPrefs.RateAppShown = false;
- HotReloadPrefs.ActiveDays = string.Empty;
- HotReloadPrefs.LaunchOnEditorStart = false;
- HotReloadPrefs.ShowUnsupportedChanges = true;
- HotReloadPrefs.RedeemLicenseEmail = null;
- HotReloadPrefs.RedeemLicenseInvoice = null;
- OnEnable();
- File.Delete(EditorCodePatcher.serverDownloader.GetExecutablePath(HotReloadCli.controller));
- InstallUtility.DebugClearInstallState();
- InstallUtility.CheckForNewInstall();
- EditorPrefs.DeleteKey(Attribution.LastLoginKey);
- File.Delete(RedeemLicenseHelper.registerOutcomePath);
- CompileMethodDetourer.Reset();
- AssetDatabase.Refresh();
- }
- }
- internal static void RenderLogo(int width = 243) {
- var isDarkMode = HotReloadWindowStyles.IsDarkMode;
- var tex = Resources.Load<Texture>(isDarkMode ? "Logo_HotReload_DarkMode" : "Logo_HotReload_LightMode");
- //Can happen during player builds where Editor Resources are unavailable
- if(tex == null) {
- return;
- }
- var targetWidth = width;
- var targetHeight = 44;
- GUILayout.Space(4f);
- // background padding top and bottom
- float padding = 5f;
- // reserve layout space for the texture
- var backgroundRect = GUILayoutUtility.GetRect(targetWidth + padding, targetHeight + padding, HotReloadWindowStyles.LogoStyle);
- // draw the texture into that reserved space. First the bg then the logo.
- if (isDarkMode) {
- GUI.DrawTexture(backgroundRect, EditorTextures.DarkGray17, ScaleMode.StretchToFill);
- } else {
- GUI.DrawTexture(backgroundRect, EditorTextures.LightGray238, ScaleMode.StretchToFill);
- }
-
- var foregroundRect = backgroundRect;
- foregroundRect.yMin += padding;
- foregroundRect.yMax -= padding;
- // during player build (EditorWindow still visible), Resources.Load returns null
- if (tex) {
- GUI.DrawTexture(foregroundRect, tex, ScaleMode.ScaleToFit);
- }
- }
- int? collapsedTab;
- void RenderTabs() {
- using(new EditorGUILayout.VerticalScope(HotReloadWindowStyles.BoxStyle)) {
- if (HotReloadWindowStyles.windowScreenHeight > 210 && HotReloadWindowStyles.windowScreenWidth > 375) {
- selectedTab = GUILayout.Toolbar(
- selectedTab,
- Tabs.Select(t =>
- new GUIContent(t.Title.StartsWith(" ", StringComparison.Ordinal) ? t.Title : " " + t.Title,
- t.Icon, t.Tooltip)).ToArray(),
- GUILayout.Height(22f) // required, otherwise largest icon height determines toolbar height
- );
- if (collapsedTab != null) {
- selectedTab = collapsedTab.Value;
- collapsedTab = null;
- }
- } else {
- if (collapsedTab == null) {
- collapsedTab = selectedTab;
- }
- // When window is super small, we pretty much can only show run tab
- SelectTab(typeof(HotReloadRunTab));
- }
- if (HotReloadWindowStyles.windowScreenHeight > 250 && HotReloadWindowStyles.windowScreenWidth > 275) {
- RenderLogo();
- }
- Tabs[selectedTab].OnGUI();
- }
- }
- void RenderBottomBar() {
- SemVersion newVersion;
- var updateAvailable = packageUpdateChecker.TryGetNewVersion(out newVersion);
- if (HotReloadWindowStyles.windowScreenWidth > Constants.RateAppHideWidth
- && HotReloadWindowStyles.windowScreenHeight > Constants.RateAppHideHeight
- ) {
- RenderRateApp();
- }
- if (updateAvailable) {
- RenderUpdateButton(newVersion);
- }
-
- using(new EditorGUILayout.HorizontalScope("ProjectBrowserBottomBarBg", GUILayout.ExpandWidth(true), GUILayout.Height(25f))) {
- RenderBottomBarCore();
- }
- }
- static GUIStyle _renderAppBoxStyle;
- static GUIStyle renderAppBoxStyle => _renderAppBoxStyle ?? (_renderAppBoxStyle = new GUIStyle(GUI.skin.box) {
- padding = new RectOffset(10, 10, 0, 0)
- });
-
- static GUILayoutOption[] _nonExpandable;
- public static GUILayoutOption[] NonExpandableLayout => _nonExpandable ?? (_nonExpandable = new [] {GUILayout.ExpandWidth(false), GUILayout.ExpandHeight(true)});
-
- internal static void RenderRateApp() {
- if (!ShouldShowRateApp()) {
- return;
- }
- using (new EditorGUILayout.VerticalScope(renderAppBoxStyle)) {
- using (new EditorGUILayout.HorizontalScope()) {
- HotReloadGUIHelper.HelpBox("Are you enjoying using Hot Reload?", MessageType.Info, 11);
- if (GUILayout.Button("Hide", NonExpandableLayout)) {
- RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Debug, StatFeature.RateApp), new EditorExtraData { { "dismissed", true } }).Forget();
- HotReloadPrefs.RateAppShown = true;
- }
- }
- using (new EditorGUILayout.HorizontalScope()) {
- if (GUILayout.Button("Yes")) {
- var openedUrl = PackageConst.IsAssetStoreBuild && EditorUtility.DisplayDialog("Rate Hot Reload", "Thank you for using Hot Reload!\n\nPlease consider leaving a review on the Asset Store to support us.", "Open in browser", "Cancel");
- if (openedUrl) {
- Application.OpenURL(Constants.UnityStoreRateAppURL);
- }
- HotReloadPrefs.RateAppShown = true;
- var data = new EditorExtraData();
- if (PackageConst.IsAssetStoreBuild) {
- data.Add("opened_url", openedUrl);
- }
- data.Add("enjoy_app", true);
- RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Debug, StatFeature.RateApp), data).Forget();
- }
- if (GUILayout.Button("No")) {
- HotReloadPrefs.RateAppShown = true;
- var data = new EditorExtraData();
- data.Add("enjoy_app", false);
- RequestHelper.RequestEditorEventWithRetry(new Stat(StatSource.Client, StatLevel.Debug, StatFeature.RateApp), data).Forget();
- }
- }
- }
- }
- internal static bool ShouldShowRateApp() {
- if (HotReloadPrefs.RateAppShown) {
- return false;
- }
- var activeDays = EditorCodePatcher.GetActiveDaysForRateApp();
- if (activeDays.Count < Constants.DaysToRateApp) {
- return false;
- }
- return true;
- }
- void RenderUpdateButton(SemVersion newVersion) {
- if (GUILayout.Button($"Update To v{newVersion}", HotReloadWindowStyles.UpgradeButtonStyle)) {
- packageUpdateChecker.UpdatePackageAsync(newVersion).Forget(CancellationToken.None);
- }
- }
-
- internal static void RenderShowOnStartup() {
- var prevLabelWidth = EditorGUIUtility.labelWidth;
- try {
- EditorGUIUtility.labelWidth = 105f;
- using (new GUILayout.VerticalScope()) {
- using (new GUILayout.HorizontalScope()) {
- GUILayout.Label("Show On Startup");
- Rect buttonRect = GUILayoutUtility.GetLastRect();
- if (EditorGUILayout.DropdownButton(new GUIContent(Regex.Replace(_showOnStartupOption.ToString(), "([a-z])([A-Z])", "$1 $2")), FocusType.Passive, GUILayout.Width(110f))) {
- GenericMenu menu = new GenericMenu();
- foreach (ShowOnStartupEnum option in Enum.GetValues(typeof(ShowOnStartupEnum))) {
- menu.AddItem(new GUIContent(Regex.Replace(option.ToString(), "([a-z])([A-Z])", "$1 $2")), false, () => {
- if (_showOnStartupOption != option) {
- _showOnStartupOption = option;
- HotReloadPrefs.ShowOnStartup = _showOnStartupOption;
- }
- });
- }
- menu.DropDown(new Rect(buttonRect.x, buttonRect.y, 100, 0));
- }
- }
- }
- } finally {
- EditorGUIUtility.labelWidth = prevLabelWidth;
- }
- }
-
- internal static readonly OpenURLButton autoRefreshTroubleshootingBtn = new OpenURLButton("Troubleshooting", Constants.TroubleshootingURL);
- void RenderBottomBarCore() {
- bool troubleshootingShown = EditorCodePatcher.Started && HotReloadWindowStyles.windowScreenWidth >= 400;
- bool alertsShown = EditorCodePatcher.Started && HotReloadWindowStyles.windowScreenWidth > Constants.EventFiltersShownHideWidth;
- using (new EditorGUILayout.VerticalScope()) {
- using (new EditorGUILayout.HorizontalScope(HotReloadWindowStyles.FooterStyle)) {
- if (!troubleshootingShown) {
- GUILayout.FlexibleSpace();
- if (alertsShown) {
- GUILayout.Space(-20);
- }
- } else {
- GUILayout.Space(21);
- }
- GUILayout.Space(0);
- var lastRect = GUILayoutUtility.GetLastRect();
- // show events button when scrolls are hidden
- if (!HotReloadRunTab.CanRenderBars(RunTabState) && !RunTabState.starting) {
- using (new EditorGUILayout.VerticalScope()) {
- GUILayout.FlexibleSpace();
- var icon = HotReloadState.ShowingRedDot ? InvertibleIcon.EventsNew : InvertibleIcon.Events;
- if (GUILayout.Button(new GUIContent("", GUIHelper.GetInvertibleIcon(icon)))) {
- PopupWindow.Show(new Rect(lastRect.x, lastRect.y, 0, 0), HotReloadEventPopup.I);
- }
- GUILayout.FlexibleSpace();
- }
- GUILayout.Space(3f);
- }
- if (alertsShown) {
- using (new EditorGUILayout.VerticalScope()) {
- GUILayout.FlexibleSpace();
- HotReloadTimelineHelper.RenderAlertFilters();
- GUILayout.FlexibleSpace();
- }
- }
- GUILayout.FlexibleSpace();
- if (troubleshootingShown) {
- using (new EditorGUILayout.VerticalScope()) {
- GUILayout.FlexibleSpace();
- autoRefreshTroubleshootingBtn.OnGUI();
- GUILayout.FlexibleSpace();
- }
- GUILayout.Space(21);
- }
- }
- }
- }
- }
- }
|