From d8a015171e83c6c6f5df8378ca190b98a4145f06 Mon Sep 17 00:00:00 2001 From: Unity Technologies Date: Mon, 26 Aug 2019 15:03:03 +0000 Subject: [PATCH 01/32] Unity 2019.3.0b1 C# reference source code --- .../AssetDatabase/AssetPreview.bindings.cs | 8 + Editor/Mono/BuildTargetDiscovery.bindings.cs | 2 +- Editor/Mono/EditorAssemblies.cs | 7 +- Editor/Mono/EditorGUIUtility.bindings.cs | 2 +- Editor/Mono/EditorMode/ModeService.cs | 20 +- Editor/Mono/EditorResources.cs | 4 +- Editor/Mono/GUI/AboutWindow.cs | 2 +- Editor/Mono/GUI/EditorApplicationLayout.cs | 28 +-- .../GUI/TreeView/AssetsTreeViewDataSource.cs | 72 ++++++- .../Mono/GUI/TreeView/ITreeViewDataSource.cs | 1 + .../Mono/GUI/TreeView/TreeViewController.cs | 37 ++-- .../Mono/GUI/TreeView/TreeViewDataSource.cs | 14 ++ Editor/Mono/GUI/WindowLayout.cs | 26 +-- Editor/Mono/GameView/GameView.cs | 10 +- Editor/Mono/Handles.cs | 10 +- .../AssemblyDefinitionImporterInspector.cs | 50 +---- Editor/Mono/Inspector/CameraEditor.cs | 20 +- Editor/Mono/Inspector/LightEditor.cs | 4 +- .../PlayerSettingsEditor.cs | 36 ++-- .../PlayerSettingsSplashScreenEditor.cs | 10 +- Editor/Mono/Inspector/RectTransformEditor.cs | 4 +- Editor/Mono/Inspector/ShaderInspector.cs | 26 ++- Editor/Mono/InternalEditorUtility.bindings.cs | 7 +- Editor/Mono/InternalEditorUtility.cs | 178 ++++++++++++++---- Editor/Mono/ObjectListArea.cs | 52 ++--- Editor/Mono/ObjectListAssetStoreGroup.cs | 3 +- Editor/Mono/ObjectListLocalGroup.cs | 151 ++++++++++----- Editor/Mono/PerformanceTools/FrameDebugger.cs | 6 +- .../PlayModeView.cs} | 92 ++++----- Editor/Mono/PlayerSettings.bindings.cs | 3 +- .../Prefabs/PrefabOverrides/PrefabOverride.cs | 3 +- .../PrefabOverrides/PrefabOverridesUtility.cs | 8 + .../PreferencesSettingsProviders.cs | 4 +- Editor/Mono/ProjectBrowser.cs | 2 +- .../ProjectWindow/CachedFilteredHierachy.cs | 37 +++- .../Mono/SceneModeWindows/LightingWindow.cs | 2 +- .../Mono/SceneModeWindows/NavigationWindow.cs | 2 +- Editor/Mono/SceneView/SceneView.cs | 12 +- .../ScriptCompilation/CompilationPipeline.cs | 10 + .../ScriptCompilation/EditorCompilation.cs | 32 ++-- .../SemVersionRangesFactory.cs | 4 +- .../VisualStudioIntegration/UnityVSSupport.cs | 2 +- LICENSE.md | 1 + .../ScriptBindings/AssetDatabase.bindings.cs | 2 - .../AssetDatabaseExperimental.bindings.cs | 69 ++++++- .../ModelImporterPostProcessor.cs | 2 +- .../ParticleSystemEditor/ParticleEffectUI.cs | 2 +- .../ParticleSystemWindow.cs | 2 +- .../ProfilerWindow/ProfilerWindow.cs | 10 +- .../Public/ProfilerAPI.bindings.cs | 8 +- .../ProfilerEditor/Public/ProfilerSettings.cs | 5 +- Modules/TextCore/Managed/TextGenerator.cs | 4 +- Modules/UMPE/Editor/DataService.cs | 2 +- .../Mono/Oculus/VRCustomOptionsOculus.cs | 30 ++- .../ScriptBindings/VREditor.bindings.cs | 28 +++ Projects/CSharp/UnityEditor.csproj | 6 +- README.md | 2 +- .../SpriteDataAccess.bindings.cs | 15 +- .../Common/ScriptBindings/Sprites.bindings.cs | 1 + .../ScriptBindings/ProfilerMarker.bindings.cs | 21 ++- .../ScriptBindings/ProfilerMarkerUtils.cs | 36 +++- .../Editor/InternalUtilityBindings.gen.cs | 5 + 62 files changed, 861 insertions(+), 393 deletions(-) rename Editor/Mono/{Preview/PreviewEditorWindow.cs => PlayModeView/PlayModeView.cs} (75%) create mode 100644 LICENSE.md diff --git a/Editor/Mono/AssetDatabase/AssetPreview.bindings.cs b/Editor/Mono/AssetDatabase/AssetPreview.bindings.cs index 44d04e28d4..4cca1c5fc8 100644 --- a/Editor/Mono/AssetDatabase/AssetPreview.bindings.cs +++ b/Editor/Mono/AssetDatabase/AssetPreview.bindings.cs @@ -33,6 +33,14 @@ internal static Texture2D GetAssetPreview(int instanceID) [FreeFunction("AssetPreviewBindings::HasAssetPreview")] internal static extern bool HasAssetPreview(int instanceID, int clientID); + internal static Texture2D GetAssetPreviewFromGUID(string guid) + { + return GetAssetPreviewFromGUID(guid, kSharedClientID); + } + + [FreeFunction("AssetPreviewBindings::GetAssetPreviewFromGUID")] + internal static extern Texture2D GetAssetPreviewFromGUID(string guid, int clientID); + public static bool IsLoadingAssetPreview(int instanceID) { return IsLoadingAssetPreview(instanceID, kSharedClientID); diff --git a/Editor/Mono/BuildTargetDiscovery.bindings.cs b/Editor/Mono/BuildTargetDiscovery.bindings.cs index 3a927494ad..eab6ca1a6b 100644 --- a/Editor/Mono/BuildTargetDiscovery.bindings.cs +++ b/Editor/Mono/BuildTargetDiscovery.bindings.cs @@ -42,7 +42,7 @@ public enum TargetAttributes StrippingNotSupported = (1 << 16), DisableNativeHDRLightmaps = (1 << 17), UsesNativeHDR = (1 << 18), - ProtectedGraphicsMem = (1 << 19), + // ProtectedGraphicsMem = (1 << 19), This was removed. IsMTRenderingDisabledByDefault = (1 << 20) } diff --git a/Editor/Mono/EditorAssemblies.cs b/Editor/Mono/EditorAssemblies.cs index 22b31ce5d9..c6848d1635 100644 --- a/Editor/Mono/EditorAssemblies.cs +++ b/Editor/Mono/EditorAssemblies.cs @@ -7,7 +7,6 @@ using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; -using Unity.Profiling; using UnityEngine; using UnityEngine.Scripting; @@ -33,6 +32,12 @@ public class InitializeOnLoadMethodAttribute : Attribute { } + [RequiredByNativeCode] + [AttributeUsage(AttributeTargets.Method)] + public class InitializeOnEnterPlayModeAttribute : Attribute + { + } + /// /// Holds information about the current set of editor assemblies. /// diff --git a/Editor/Mono/EditorGUIUtility.bindings.cs b/Editor/Mono/EditorGUIUtility.bindings.cs index feccfbbfd6..fc3bb6a891 100644 --- a/Editor/Mono/EditorGUIUtility.bindings.cs +++ b/Editor/Mono/EditorGUIUtility.bindings.cs @@ -150,7 +150,7 @@ public static void RenderGameViewCameras(RenderTexture target, int targetDisplay internal static extern Texture2D GetIconForObject(Object obj); // Render all ingame cameras bound to a specific Display. - internal static extern void RenderPreviewCamerasInternal(RenderTexture target, int targetDisplay, Vector2 mousePosition, bool gizmos, bool renderIMGUI); + internal static extern void RenderPlayModeViewCamerasInternal(RenderTexture target, int targetDisplay, Vector2 mousePosition, bool gizmos, bool renderIMGUI); internal static extern void SetupWindowSpaceAndVSyncInternal(Rect screenRect); private static extern Texture2D FindTextureByName(string name); diff --git a/Editor/Mono/EditorMode/ModeService.cs b/Editor/Mono/EditorMode/ModeService.cs index b875b82a9f..721ff77368 100644 --- a/Editor/Mono/EditorMode/ModeService.cs +++ b/Editor/Mono/EditorMode/ModeService.cs @@ -293,21 +293,15 @@ private static void LoadModes(bool checkStartupMode = false) private static void ScanModes() { var modesData = new Dictionary { [k_DefaultModeId] = new Dictionary { [k_LabelSectionName] = "Default" } }; - var modeDescriptors = AssetDatabase.EnumerateAllAssets(new SearchFilter + var modeFilePaths = AssetDatabase.GetAllAssetPaths() + .Where(IsEditorModeDescriptor) + .OrderBy(path => - new FileInfo(path).Length); + foreach (var modeFilePath in modeFilePaths) { - searchArea = SearchFilter.SearchArea.InPackagesOnly, - classNames = new[] { nameof(ModeDescriptor) }, - skipHidden = true, - showAllHits = true - }); - while (modeDescriptors.MoveNext()) - { - var md = modeDescriptors.Current.pptrValue as ModeDescriptor; - if (md == null) - continue; try { - var json = SJSON.Load(md.path); + var json = SJSON.Load(modeFilePath); + foreach (var rawModeId in json.Keys) { var modeId = ((string)rawModeId).ToLower(); @@ -326,7 +320,7 @@ private static void ScanModes() } catch (Exception ex) { - Debug.LogError($"[ModeService] Error while parsing mode file {md.path}.\n{ex}"); + Debug.LogError($"[ModeService] Error while parsing mode file {modeFilePath}.\n{ex}"); } } diff --git a/Editor/Mono/EditorResources.cs b/Editor/Mono/EditorResources.cs index aa403b2f2b..59e449a0ef 100644 --- a/Editor/Mono/EditorResources.cs +++ b/Editor/Mono/EditorResources.cs @@ -237,7 +237,7 @@ internal static void BuildCatalog() s_RefreshGlobalStyleCatalog = false; var paths = GetDefaultStyleCatalogPaths(); - foreach (var editorUssPath in AssetDatabase.FindAssets("t:StyleSheet").Select(AssetDatabase.GUIDToAssetPath).Where(IsEditorStyleSheet)) + foreach (var editorUssPath in AssetDatabase.GetAllAssetPaths().Where(IsEditorStyleSheet)) paths.Add(editorUssPath); Console.WriteLine($"Building style catalogs ({paths.Count})\r\n\t{String.Join("\r\n\t", paths.ToArray())}"); @@ -303,7 +303,7 @@ private static void ResetDeprecatedBackgroundImage(GUIStyleState state) internal static void RefreshStyles() { Unsupported.ClearSkinCache(); - InternalEditorUtility.RequestScriptReload(); + EditorUtility.RequestScriptReload(); InternalEditorUtility.RepaintAllViews(); Debug.Log($"Style refreshed {DateTime.Now}"); } diff --git a/Editor/Mono/GUI/AboutWindow.cs b/Editor/Mono/GUI/AboutWindow.cs index 945f7962c9..df817275d8 100644 --- a/Editor/Mono/GUI/AboutWindow.cs +++ b/Editor/Mono/GUI/AboutWindow.cs @@ -218,7 +218,7 @@ private void ListenForSecretCodes() bool enabled = !EditorPrefs.GetBool("DeveloperMode", false); EditorPrefs.SetBool("DeveloperMode", enabled); ShowNotification(new GUIContent(string.Format(L10n.Tr("Developer Mode {0}"), (enabled ? L10n.Tr("On") : L10n.Tr("Off"))))); - InternalEditorUtility.RequestScriptReload(); + EditorUtility.RequestScriptReload(); // Repaint all views to show/hide debug repaint indicator InternalEditorUtility.RepaintAllViews(); diff --git a/Editor/Mono/GUI/EditorApplicationLayout.cs b/Editor/Mono/GUI/EditorApplicationLayout.cs index af33df0b54..79d79e85c0 100644 --- a/Editor/Mono/GUI/EditorApplicationLayout.cs +++ b/Editor/Mono/GUI/EditorApplicationLayout.cs @@ -23,13 +23,13 @@ namespace UnityEditor { internal class EditorApplicationLayout { - static private PreviewEditorWindow m_PreviewWindow = null; + static private PlayModeView m_PlayModeView = null; static private bool m_MaximizePending = false; static internal bool IsInitializingPlaymodeLayout() { - return m_PreviewWindow != null; + return m_PlayModeView != null; } static internal void SetPlaymodeLayout() @@ -52,34 +52,34 @@ static internal void SetPausemodeLayout() static internal void InitPlaymodeLayout() { - m_PreviewWindow = WindowLayout.ShowAppropriateViewOnEnterExitPlaymode(true) as PreviewEditorWindow; - if (m_PreviewWindow == null) + m_PlayModeView = WindowLayout.ShowAppropriateViewOnEnterExitPlaymode(true) as PlayModeView; + if (m_PlayModeView == null) return; - if (m_PreviewWindow.maximizeOnPlay) + if (m_PlayModeView.maximizeOnPlay) { - DockArea da = m_PreviewWindow.m_Parent as DockArea; + DockArea da = m_PlayModeView.m_Parent as DockArea; if (da != null) m_MaximizePending = WindowLayout.MaximizePrepare(da.actualView); } - // Mark this preview window as the start preview so the backend - // can set size and mouseoffset properly for this preview - m_PreviewWindow.m_Parent.SetAsStartView(); - m_PreviewWindow.m_Parent.SetAsLastPlayModeView(); + // Mark this PlayModeView window as the start view so the backend + // can set size and mouseoffset properly for this view + m_PlayModeView.m_Parent.SetAsStartView(); + m_PlayModeView.m_Parent.SetAsLastPlayModeView(); Toolbar.RepaintToolbar(); } static internal void FinalizePlaymodeLayout() { - if (m_PreviewWindow != null) + if (m_PlayModeView != null) { if (m_MaximizePending) - WindowLayout.MaximizePresent(m_PreviewWindow); + WindowLayout.MaximizePresent(m_PlayModeView); - m_PreviewWindow.m_Parent.ClearStartView(); + m_PlayModeView.m_Parent.ClearStartView(); } Clear(); @@ -88,7 +88,7 @@ static internal void FinalizePlaymodeLayout() static private void Clear() { m_MaximizePending = false; - m_PreviewWindow = null; + m_PlayModeView = null; } } } // namespace diff --git a/Editor/Mono/GUI/TreeView/AssetsTreeViewDataSource.cs b/Editor/Mono/GUI/TreeView/AssetsTreeViewDataSource.cs index b262019563..fd034f8987 100644 --- a/Editor/Mono/GUI/TreeView/AssetsTreeViewDataSource.cs +++ b/Editor/Mono/GUI/TreeView/AssetsTreeViewDataSource.cs @@ -11,6 +11,7 @@ using UnityEngine; using UnityEditorInternal; using UnityEditor.Experimental; +using AssetReference = UnityEditorInternal.InternalEditorUtility.AssetReference; namespace UnityEditor { @@ -61,6 +62,50 @@ public AssetsTreeViewDataSource(TreeViewController treeView, int rootInstanceID) m_rootInstanceID = rootInstanceID; } + public override List GetNewSelection(TreeViewItem clickedItem, TreeViewSelectState selectState) + { + var assetTreeClickedItem = clickedItem as IAssetTreeViewItem; + var clickedEntry = new AssetReference() { instanceID = clickedItem.id }; + if (clickedItem.id == 0 && assetTreeClickedItem != null) + clickedEntry.guid = assetTreeClickedItem.Guid; + + // Get ids from items + var visibleRows = GetRows(); + var allIDs = new List(visibleRows.Count); + var allGuids = new List(visibleRows.Count); + + for (int i = 0; i < visibleRows.Count; ++i) + { + int instanceID = visibleRows[i].id; + string guid = null; + if (instanceID == 0) + { + var assetTreeViewItem = visibleRows[i] as IAssetTreeViewItem; + if (assetTreeViewItem != null) + guid = assetTreeViewItem.Guid; + } + + allGuids.Add(guid); + allIDs.Add(instanceID); + } + + List selectedIDs = selectState.selectedIDs; + int lastClickedID = selectState.lastClickedID; + bool allowMultiselection = CanBeMultiSelected(clickedItem); + + var result = InternalEditorUtility.GetNewSelection(ref clickedEntry, allIDs, allGuids, selectedIDs, lastClickedID, selectState.keepMultiSelection, selectState.useShiftAsActionKey, allowMultiselection); + + clickedItem.id = clickedEntry.instanceID; + + for (int i = 0; i < allIDs.Count; ++i) + { + if (allGuids[i] != null && allIDs[i] != 0) + visibleRows[i].id = allIDs[i]; + } + + return result; + } + static string CreateDisplayName(int instanceID) { return Path.GetFileNameWithoutExtension(AssetDatabase.GetAssetPath(instanceID)); @@ -131,6 +176,7 @@ public override void FetchData() var rootInstanceID = root.instanceID; var displayName = root.displayName ?? CreateDisplayName(rootInstanceID); var rootPath = root.path ?? AssetDatabase.GetAssetPath(rootInstanceID); + var rootGuid = AssetDatabase.AssetPathToGUID(rootPath); var property = new HierarchyProperty(rootPath); if (!root.skipValidation && !property.Find(rootInstanceID, null)) @@ -165,7 +211,7 @@ public override void FetchData() // Create root item TreeView item if (subDepth > 0) - rootItem = new FolderTreeItem(rootInstanceID, rootDepth, parentItem, displayName); + rootItem = new FolderTreeItem(rootGuid, rootInstanceID, rootDepth, parentItem, displayName); else rootItem = new RootTreeItem(rootInstanceID, rootDepth, parentItem, displayName); rootItem.icon = folderIcon; @@ -195,9 +241,9 @@ public override void FetchData() depth = property.depth - minDepth; TreeViewItem item; if (property.isFolder) - item = new FolderTreeItem(property.instanceID, depth + subDepth, null, property.name); + item = new FolderTreeItem(property.guid, property.instanceID, depth + subDepth, null, property.name); else - item = new NonFolderTreeItem(property.instanceID, depth + subDepth, null, property.name); + item = new NonFolderTreeItem(property.guid, property.GetInstanceIDIfImported(), depth + subDepth, null, property.name); if (property.isFolder && !property.hasChildren) item.icon = emptyFolderIcon; @@ -473,6 +519,11 @@ public int Compare(TreeViewItem x, TreeViewItem y) } // Classes used for type checking + internal interface IAssetTreeViewItem + { + string Guid { get; } + } + internal class RootTreeItem : TreeViewItem { public RootTreeItem(int id, int depth, TreeViewItem parent, string displayName) @@ -480,18 +531,25 @@ public RootTreeItem(int id, int depth, TreeViewItem parent, string displayName) { } } - class FolderTreeItem : TreeViewItem + + class FolderTreeItem : TreeViewItem, IAssetTreeViewItem { - public FolderTreeItem(int id, int depth, TreeViewItem parent, string displayName) + public string Guid { get; } + + public FolderTreeItem(string guid, int id, int depth, TreeViewItem parent, string displayName) : base(id, depth, parent, displayName) { + Guid = guid; } } - class NonFolderTreeItem : TreeViewItem + + class NonFolderTreeItem : TreeViewItem, IAssetTreeViewItem { - public NonFolderTreeItem(int id, int depth, TreeViewItem parent, string displayName) + public string Guid { get; } + public NonFolderTreeItem(string guid, int id, int depth, TreeViewItem parent, string displayName) : base(id, depth, parent, displayName) { + Guid = guid; } } } diff --git a/Editor/Mono/GUI/TreeView/ITreeViewDataSource.cs b/Editor/Mono/GUI/TreeView/ITreeViewDataSource.cs index c64838a2dc..d8486cfc8b 100644 --- a/Editor/Mono/GUI/TreeView/ITreeViewDataSource.cs +++ b/Editor/Mono/GUI/TreeView/ITreeViewDataSource.cs @@ -64,6 +64,7 @@ internal interface ITreeViewDataSource // Selection bool CanBeMultiSelected(TreeViewItem item); bool CanBeParent(TreeViewItem item); + List GetNewSelection(TreeViewItem clickedItem, TreeViewSelectState selectState); // Renaming bool IsRenamingItemAllowed(TreeViewItem item); diff --git a/Editor/Mono/GUI/TreeView/TreeViewController.cs b/Editor/Mono/GUI/TreeView/TreeViewController.cs index 8132a7119b..c9b86eb771 100644 --- a/Editor/Mono/GUI/TreeView/TreeViewController.cs +++ b/Editor/Mono/GUI/TreeView/TreeViewController.cs @@ -9,7 +9,6 @@ using UnityEngine; using UnityEditorInternal; - namespace UnityEditor.IMGUI.Controls { /* @@ -59,6 +58,14 @@ internal virtual void OnAwake() } } + internal struct TreeViewSelectState + { + public List selectedIDs; + public int lastClickedID; + public bool keepMultiSelection; + public bool useShiftAsActionKey; + } + internal class TreeViewController { public System.Action selectionChangedCallback { get; set; } // ids @@ -110,6 +117,7 @@ internal class TreeViewController bool m_HadFocusLastEvent; // Cached from last event for keyboard focus changed event int m_KeyboardControlID; + const double kSlowSelectTimeout = 0.2; const float kSpaceForScrollBar = 16f; public TreeViewItem hoveredItem { get; set; } @@ -333,13 +341,16 @@ public void HandleUnusedMouseEventsForItem(Rect rect, TreeViewItem item, int row } else { - var dragSelection = GetNewSelection(item, true, false); - bool canStartDrag = dragging != null && dragSelection.Count != 0 && dragging.CanStartDrag(item, dragSelection, Event.current.mousePosition); + double selectStartTime = Time.realtimeSinceStartup; + var dragSelection = GetNewSelection(item, true, false); + bool dragAbortedBySlowSelect = (Time.realtimeSinceStartup - selectStartTime) > kSlowSelectTimeout; + + bool canStartDrag = !dragAbortedBySlowSelect && dragging != null && dragSelection.Count != 0 && dragging.CanStartDrag(item, dragSelection, Event.current.mousePosition); if (canStartDrag) { // Prepare drag and drop delay (we start the drag after a couple of pixels mouse drag: See the case MouseDrag below) m_DragSelection = dragSelection; - DragAndDropDelay delay = (DragAndDropDelay)GUIUtility.GetStateObject(typeof(DragAndDropDelay), itemControlID); + DragAndDropDelay delay = (DragAndDropDelay)GUIUtility.GetStateObject(typeof(DragAndDropDelay), GetItemControlID(item)); delay.mouseDownPosition = Event.current.mousePosition; } else @@ -357,7 +368,7 @@ public void HandleUnusedMouseEventsForItem(Rect rect, TreeViewItem item, int row itemSingleClickedCallback(item.id); } - GUIUtility.hotControl = itemControlID; + GUIUtility.hotControl = GetItemControlID(item); } evt.Use(); } @@ -1135,17 +1146,15 @@ List GetNewSelection(TreeViewItem clickedItem, bool keepMultiSelection, boo { if (getNewSelectionOverride != null) return getNewSelectionOverride(clickedItem, keepMultiSelection, useShiftAsActionKey); - // Get ids from items - var visibleRows = data.GetRows(); - List allIDs = new List(visibleRows.Count); - for (int i = 0; i < visibleRows.Count; ++i) - allIDs.Add(visibleRows[i].id); - List selectedIDs = state.selectedIDs; - int lastClickedID = state.lastClickedID; - bool allowMultiselection = data.CanBeMultiSelected(clickedItem); + var selectState = new TreeViewSelectState() { + selectedIDs = state.selectedIDs, + lastClickedID = state.lastClickedID, + keepMultiSelection = keepMultiSelection, + useShiftAsActionKey = useShiftAsActionKey + }; - return InternalEditorUtility.GetNewSelection(clickedItem.id, allIDs, selectedIDs, lastClickedID, keepMultiSelection, useShiftAsActionKey, allowMultiselection); + return data.GetNewSelection(clickedItem, selectState); } void SelectionByKey(TreeViewItem itemSelected) diff --git a/Editor/Mono/GUI/TreeView/TreeViewDataSource.cs b/Editor/Mono/GUI/TreeView/TreeViewDataSource.cs index e7932ba3b2..4c0c9d6b46 100644 --- a/Editor/Mono/GUI/TreeView/TreeViewDataSource.cs +++ b/Editor/Mono/GUI/TreeView/TreeViewDataSource.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using UnityEngine; +using UnityEditorInternal; namespace UnityEditor.IMGUI.Controls { @@ -349,6 +350,19 @@ virtual public bool CanBeParent(TreeViewItem item) return true; } + virtual public List GetNewSelection(TreeViewItem clickedItem, TreeViewSelectState selectState) + { + // Get ids from items + var visibleRows = GetRows(); + List allIDs = new List(visibleRows.Count); + for (int i = 0; i < visibleRows.Count; ++i) + allIDs.Add(visibleRows[i].id); + + bool allowMultiselection = CanBeMultiSelected(clickedItem); + + return InternalEditorUtility.GetNewSelection(clickedItem.id, allIDs, selectState.selectedIDs, selectState.lastClickedID, selectState.keepMultiSelection, selectState.useShiftAsActionKey, allowMultiselection); + } + virtual public void OnExpandedStateChanged() { if (m_TreeView.expandedStateChanged != null) diff --git a/Editor/Mono/GUI/WindowLayout.cs b/Editor/Mono/GUI/WindowLayout.cs index a299c80203..0ab12b4f77 100644 --- a/Editor/Mono/GUI/WindowLayout.cs +++ b/Editor/Mono/GUI/WindowLayout.cs @@ -250,14 +250,14 @@ internal static EditorWindow TryGetLastFocusedWindowInSameDock() if (windowTypeName != "") type = Type.GetType(windowTypeName); - // Also get the Preview Window - var previewWindow = PreviewEditorWindow.GetMainPreviewWindow(); - if (type != null && previewWindow && previewWindow.m_Parent != null && previewWindow.m_Parent is DockArea) + // Also get the PlayModeView Window + var playModeView = PlayModeView.GetMainPlayModeView(); + if (type != null && playModeView && playModeView != null && playModeView.m_Parent is DockArea) { // Get all windows of that type object[] potentials = Resources.FindObjectsOfTypeAll(type); - DockArea dock = previewWindow.m_Parent as DockArea; + DockArea dock = playModeView.m_Parent as DockArea; // Find the one that is actually docked together with the GameView for (int i = 0; i < potentials.Length; i++) @@ -295,14 +295,14 @@ internal static EditorWindow TryFocusAppropriateWindow(bool enteringPlaymode) { if (enteringPlaymode) { - var previewWindow = PreviewEditorWindow.GetMainPreviewWindow(); - if (previewWindow) + var playModeView = PlayModeView.GetMainPlayModeView(); + if (playModeView) { - SaveCurrentFocusedWindowInSameDock(previewWindow); - previewWindow.Focus(); + SaveCurrentFocusedWindowInSameDock(playModeView); + playModeView.Focus(); } - return previewWindow; + return playModeView; } else { @@ -820,10 +820,10 @@ public static bool LoadWindowLayout(string path, bool newProjectLayoutWasCreated containerWindow.Show(containerWindow.showMode, loadPosition: false, displayImmediately: true, setFocus: true); } - // Unmaximize maximized Preview window if maximize on play is enabled - PreviewEditorWindow preview = GetMaximizedWindow() as PreviewEditorWindow; - if (preview != null && preview.maximizeOnPlay) - Unmaximize(preview); + // Unmaximize maximized PlayModeView window if maximize on play is enabled + PlayModeView playModeView = GetMaximizedWindow() as PlayModeView; + if (playModeView != null && playModeView.maximizeOnPlay) + Unmaximize(playModeView); } catch (Exception ex) { diff --git a/Editor/Mono/GameView/GameView.cs b/Editor/Mono/GameView/GameView.cs index 8e081d8a98..644c1e531d 100644 --- a/Editor/Mono/GameView/GameView.cs +++ b/Editor/Mono/GameView/GameView.cs @@ -30,7 +30,7 @@ Floating GameView in separate window namespace UnityEditor { [EditorWindowTitle(title = "Game", useTypeNameAsIconName = true)] - internal class GameView : PreviewEditorWindow, IHasCustomMenu, IGameViewSizeMenuUser + internal class GameView : PlayModeView, IHasCustomMenu, IGameViewSizeMenuUser { const int kScaleSliderMinWidth = 30; const int kScaleSliderMaxWidth = 150; @@ -110,7 +110,7 @@ public GameView() { autoRepaintOnSceneChange = true; InitializeZoomArea(); - previewName = "GameView"; + playModeViewName = "GameView"; clearColor = kClearBlack; showGizmos = m_Gizmos; targetDisplay = 0; @@ -292,7 +292,7 @@ public void OnDisable() [UsedImplicitly] // This is here because NGUI uses it via reflection (noted in https://confluence.hq.unity3d.com/display/DEV/Game+View+Bucket) internal static Vector2 GetSizeOfMainGameView() { - return GetMainPreviewTargetSize(); + return GetMainPlayModeViewTargetSize(); } private void UpdateZoomAreaAndParent() @@ -666,7 +666,7 @@ public void RenderToHMDOnly() showGizmos = false; renderIMGUI = false; - m_RenderTexture = RenderPreview(mousePos, clearTexture: false); + m_RenderTexture = RenderView(mousePos, clearTexture: false); } private void OnPlayModeStateChanged(PlayModeStateChange state) @@ -773,7 +773,7 @@ private void OnGUI() renderIMGUI = true; if (!EditorApplication.isPlaying || (EditorApplication.isPlaying && Time.frameCount % OnDemandRendering.GetRenderFrameInterval() == 0)) - m_RenderTexture = RenderPreview(gameMousePosition, clearTexture); + m_RenderTexture = RenderView(gameMousePosition, clearTexture); if (m_TargetClamped) Debug.LogWarningFormat("GameView reduced to a reasonable size for this system ({0}x{1})", targetSize.x, targetSize.y); EditorGUIUtility.SetupWindowSpaceAndVSyncInternal(GUIClip.Unclip(viewInWindow)); diff --git a/Editor/Mono/Handles.cs b/Editor/Mono/Handles.cs index 2afcfafcf2..490c976a3a 100644 --- a/Editor/Mono/Handles.cs +++ b/Editor/Mono/Handles.cs @@ -332,11 +332,11 @@ public static void DrawWireCube(Vector3 center, Vector3 size) public static bool ShouldRenderGizmos() { - var preview = PreviewEditorWindow.GetRenderingPreview(); + var playModeView = PlayModeView.GetRenderingView(); SceneView sv = SceneView.currentDrawingSceneView; - if (preview != null) - return preview.IsShowingGizmos(); + if (playModeView != null) + return playModeView.IsShowingGizmos(); if (sv != null) return sv.drawGizmos; @@ -1311,10 +1311,10 @@ internal static Rect GetCameraRect(Rect position) return cameraRect; } - // Get the size of the main preview window + // Get the size of the main playModeView window public static Vector2 GetMainGameViewSize() { - return PreviewEditorWindow.GetMainPreviewTargetSize(); + return PlayModeView.GetMainPlayModeViewTargetSize(); } // Clears the camera. diff --git a/Editor/Mono/Inspector/AssemblyDefinitionImporterInspector.cs b/Editor/Mono/Inspector/AssemblyDefinitionImporterInspector.cs index f963fd1cf1..8d0ca0f266 100644 --- a/Editor/Mono/Inspector/AssemblyDefinitionImporterInspector.cs +++ b/Editor/Mono/Inspector/AssemblyDefinitionImporterInspector.cs @@ -51,14 +51,6 @@ internal class DefineConstraint public string name; } - [Serializable] - internal class VersionDefine - { - public string name; - public string expression; - public string define; - } - [Serializable] internal class AssemblyDefinitionReference { @@ -92,10 +84,7 @@ internal class PrecompiledReference class AssemblyDefinitionState : ScriptableObject { - public string path - { - get { return AssetDatabase.GetAssetPath(asset); } - } + public string path => AssetDatabase.GetAssetPath(asset); public string assemblyName; public AssemblyDefinitionAsset asset; @@ -113,6 +102,7 @@ public string path } SemVersionRangesFactory m_SemVersionRanges; + ReorderableList m_ReferencesList; ReorderableList m_PrecompiledReferencesList; ReorderableList m_VersionDefineList; @@ -130,7 +120,7 @@ public string path string[] m_Defines; Exception initializeException; - public override bool showImportedObject { get { return false; } } + public override bool showImportedObject => false; public override void OnEnable() { @@ -160,7 +150,7 @@ public override void OnInspectorGUI() extraDataSerializedObject.Update(); - var platforms = Compilation.CompilationPipeline.GetAssemblyDefinitionPlatforms(); + var platforms = CompilationPipeline.GetAssemblyDefinitionPlatforms(); using (new EditorGUI.DisabledScope(false)) { if (targets.Length > 1) @@ -311,7 +301,7 @@ protected override void Apply() static void InversePlatformCompatibility(AssemblyDefinitionState state) { - var platforms = Compilation.CompilationPipeline.GetAssemblyDefinitionPlatforms(); + var platforms = CompilationPipeline.GetAssemblyDefinitionPlatforms(); for (int i = 0; i < platforms.Length; ++i) state.platformCompatibility[i] = !state.platformCompatibility[i]; } @@ -332,7 +322,7 @@ protected override void InitializeExtraDataInstance(Object extraTarget, int targ { try { - LoadAssemblyDefintionState((AssemblyDefinitionState)extraTarget, ((AssetImporter)targets[targetIndex]).assetPath); + LoadAssemblyDefinitionState((AssemblyDefinitionState)extraTarget, ((AssetImporter)targets[targetIndex]).assetPath); initializeException = null; } catch (Exception e) @@ -545,7 +535,7 @@ static void AddPrecompiledReferenceListElement(ReorderableList list) newProp.FindPropertyRelative("fileName").stringValue = string.Empty; } - static void LoadAssemblyDefintionState(AssemblyDefinitionState state, string path) + static void LoadAssemblyDefinitionState(AssemblyDefinitionState state, string path) { var asset = AssetDatabase.LoadAssetAtPath(path); if (asset == null) @@ -584,20 +574,7 @@ static void LoadAssemblyDefintionState(AssemblyDefinitionState state, string pat { foreach (var versionDefine in data.versionDefines) { - if (!SymbolNameRestrictions.IsValid(versionDefine.define)) - { - var exception = new AssemblyDefinitionException($"Invalid version define {versionDefine.define}", path); - Debug.LogException(exception, asset); - } - else - { - state.versionDefines.Add(new VersionDefine - { - name = versionDefine.name, - expression = versionDefine.expression, - define = versionDefine.define, - }); - } + state.versionDefines.Add(versionDefine); } } @@ -645,7 +622,7 @@ static void LoadAssemblyDefintionState(AssemblyDefinitionState state, string pat } catch (AssemblyDefinitionException e) { - UnityEngine.Debug.LogException(e, asset); + Debug.LogException(e, asset); state.references.Add(new AssemblyDefinitionReference()); } } @@ -742,20 +719,13 @@ static void SaveAssemblyDefinitionState(AssemblyDefinitionState state) .Select(r => r.name) .ToArray(); - data.versionDefines = state.versionDefines.Select(x => new UnityEditor.Scripting.ScriptCompilation.VersionDefine - { - name = x.name, - expression = x.expression, - define = x.define, - }).ToArray(); - + data.versionDefines = state.versionDefines.ToArray(); data.autoReferenced = state.autoReferenced; data.overrideReferences = state.overrideReferences; data.precompiledReferences = state.precompiledReferences .Select(r => r.name).ToArray(); - data.allowUnsafeCode = state.allowUnsafeCode; data.noEngineReferences = state.noEngineReferences; diff --git a/Editor/Mono/Inspector/CameraEditor.cs b/Editor/Mono/Inspector/CameraEditor.cs index 0d4e4d119e..6d4745cf47 100644 --- a/Editor/Mono/Inspector/CameraEditor.cs +++ b/Editor/Mono/Inspector/CameraEditor.cs @@ -626,7 +626,7 @@ private void CommandBufferGUI() { cam.RemoveCommandBuffer(ce, cb); SceneView.RepaintAll(); - PreviewEditorWindow.RepaintAll(); + PlayModeView.RepaintAll(); GUIUtility.ExitGUI(); } } @@ -640,7 +640,7 @@ private void CommandBufferGUI() { cam.RemoveAllCommandBuffers(); SceneView.RepaintAll(); - PreviewEditorWindow.RepaintAll(); + PlayModeView.RepaintAll(); } } EditorGUI.indentLevel--; @@ -720,7 +720,7 @@ public virtual void OnOverlayGUI(Object target, SceneView sceneView) if (targetStage != sceneViewStage) return; - Vector2 previewSize = c.targetTexture ? new Vector2(c.targetTexture.width, c.targetTexture.height) : PreviewEditorWindow.GetMainPreviewTargetSize(); + Vector2 previewSize = c.targetTexture ? new Vector2(c.targetTexture.width, c.targetTexture.height) : PlayModeView.GetMainPlayModeViewTargetSize(); if (previewSize.x < 0f) { @@ -823,7 +823,7 @@ private RenderTexture GetPreviewTextureWithSize(int width, int height) [RequiredByNativeCode] internal static float GetGameViewAspectRatio() { - Vector2 gameViewSize = PreviewEditorWindow.GetMainPreviewTargetSize(); + Vector2 gameViewSize = PlayModeView.GetMainPlayModeViewTargetSize(); if (gameViewSize.x < 0f) { // Fallback to Scene View of not a valid game view size @@ -835,9 +835,9 @@ internal static float GetGameViewAspectRatio() } [RequiredByNativeCode] - internal static Vector2 GetMainPreviewSize() + internal static void GetMainPlayModeViewSize(out Vector2 size) { - return PreviewEditorWindow.GetMainPreviewTargetSize(); + size = PlayModeView.GetMainPlayModeViewTargetSize(); } // Called from C++ when we need to render a Camera's gizmo @@ -846,7 +846,7 @@ internal static void RenderGizmo(Camera camera) CameraEditorUtils.DrawFrustumGizmo(camera); } - private static Vector2 s_PreviousMainGameViewTargetSize; + private static Vector2 s_PreviousMainPlayModeViewTargetSize; public virtual void OnSceneGUI() { if (!target) @@ -856,12 +856,12 @@ public virtual void OnSceneGUI() if (!CameraEditorUtils.IsViewportRectValidToRender(c.rect)) return; - Vector2 currentMainGameViewTargetSize = PreviewEditorWindow.GetMainPreviewTargetSize(); - if (s_PreviousMainGameViewTargetSize != currentMainGameViewTargetSize) + Vector2 currentMainPlayModeViewTargetSize = PlayModeView.GetMainPlayModeViewTargetSize(); + if (s_PreviousMainPlayModeViewTargetSize != currentMainPlayModeViewTargetSize) { // a gameView size change can affect horizontal FOV, refresh the inspector when that happens. Repaint(); - s_PreviousMainGameViewTargetSize = currentMainGameViewTargetSize; + s_PreviousMainPlayModeViewTargetSize = currentMainPlayModeViewTargetSize; } SceneViewOverlay.Window(EditorGUIUtility.TrTextContent("Camera Preview"), OnOverlayGUI, (int)SceneViewOverlay.Ordering.Camera, target, SceneViewOverlay.WindowDisplayOption.OneWindowPerTarget); diff --git a/Editor/Mono/Inspector/LightEditor.cs b/Editor/Mono/Inspector/LightEditor.cs index 8ce72e2081..efaf02a84a 100644 --- a/Editor/Mono/Inspector/LightEditor.cs +++ b/Editor/Mono/Inspector/LightEditor.cs @@ -682,7 +682,7 @@ private void CommandBufferGUI() { light.RemoveCommandBuffer(le, cb); SceneView.RepaintAll(); - PreviewEditorWindow.RepaintAll(); + PlayModeView.RepaintAll(); GUIUtility.ExitGUI(); } } @@ -696,7 +696,7 @@ private void CommandBufferGUI() { light.RemoveAllCommandBuffers(); SceneView.RepaintAll(); - PreviewEditorWindow.RepaintAll(); + PlayModeView.RepaintAll(); } } EditorGUI.indentLevel--; diff --git a/Editor/Mono/Inspector/PlayerSettingsEditor/PlayerSettingsEditor.cs b/Editor/Mono/Inspector/PlayerSettingsEditor/PlayerSettingsEditor.cs index 20a2f9f04b..e9faef7ed0 100644 --- a/Editor/Mono/Inspector/PlayerSettingsEditor/PlayerSettingsEditor.cs +++ b/Editor/Mono/Inspector/PlayerSettingsEditor/PlayerSettingsEditor.cs @@ -88,7 +88,6 @@ class SettingsContent public static readonly GUIContent iPhoneScriptCallOptimization = EditorGUIUtility.TrTextContent("Script Call Optimization*"); public static readonly GUIContent enableInternalProfiler = EditorGUIUtility.TrTextContent("Enable Internal Profiler* (Deprecated)", "Internal profiler counters should be accessed by scripts using UnityEngine.Profiling::Profiler API."); public static readonly GUIContent stripUnusedMeshComponents = EditorGUIUtility.TrTextContent("Optimize Mesh Data*", "Remove unused mesh components"); - public static readonly GUIContent protectGraphicsMemory = EditorGUIUtility.TrTextContent("Protect Graphics Memory", "Protect GPU memory from being read (on supported devices). Will prevent user from taking screenshots"); public static readonly GUIContent enableFrameTimingStats = EditorGUIUtility.TrTextContent("Enable Frame Timing Stats", "Enable gathering of CPU/GPU frame timing statistics."); public static readonly GUIContent useOSAutoRotation = EditorGUIUtility.TrTextContent("Use Animated Autorotation", "If set OS native animated autorotation method will be used. Otherwise orientation will be changed immediately."); public static readonly GUIContent UIPrerenderedIcon = EditorGUIUtility.TrTextContent("Prerendered Icon"); @@ -138,7 +137,8 @@ class SettingsContent public static readonly GUIContent mTRendering = EditorGUIUtility.TrTextContent("Multithreaded Rendering*"); public static readonly GUIContent staticBatching = EditorGUIUtility.TrTextContent("Static Batching"); public static readonly GUIContent dynamicBatching = EditorGUIUtility.TrTextContent("Dynamic Batching"); - public static readonly GUIContent graphicsJobs = EditorGUIUtility.TrTextContent("Graphics Jobs"); + public static readonly GUIContent graphicsJobsNonExperimental = EditorGUIUtility.TrTextContent("Graphics Jobs"); + public static readonly GUIContent graphicsJobsExperimental = EditorGUIUtility.TrTextContent("Graphics Jobs (Experimental)"); public static readonly GUIContent graphicsJobsMode = EditorGUIUtility.TrTextContent("Graphics Jobs Mode*"); public static readonly GUIContent applicationBuildNumber = EditorGUIUtility.TrTextContent("Build"); public static readonly GUIContent appleDeveloperTeamID = EditorGUIUtility.TrTextContent("iOS Developer Team ID", "Developers can retrieve their Team ID by visiting the Apple Developer site under Account > Membership."); @@ -816,11 +816,6 @@ private static bool TargetSupportsOptionalBuiltinSplashScreen(BuildTargetGroup t return targetGroup == BuildTargetGroup.Standalone; } - private static bool TargetSupportsProtectedGraphicsMem(BuildTargetGroup targetGroup) - { - return BuildTargetDiscovery.PlatformGroupHasFlag(targetGroup, TargetAttributes.ProtectedGraphicsMem); - } - public void ResolutionSectionGUI(BuildTargetGroup targetGroup, ISettingEditorExtension settingsExtension, int sectionIndex = 0) { if (BeginSettingsBox(sectionIndex, SettingsContent.resolutionPresentationTitle)) @@ -1643,6 +1638,7 @@ private void OtherSectionRenderingGUI(BuildPlatform platform, BuildTargetGroup t bool graphicsJobsOptionEnabled = true; bool graphicsJobs = PlayerSettings.GetGraphicsJobsForPlatform(platform.defaultTarget); bool newGraphicsJobs = graphicsJobs; + bool graphicsJobsModeOptionEnabled = graphicsJobs; if (targetGroup == BuildTargetGroup.XboxOne) { @@ -1657,6 +1653,7 @@ private void OtherSectionRenderingGUI(BuildPlatform platform, BuildTargetGroup t if (graphicsJobs == false) { PlayerSettings.SetGraphicsJobsForPlatform(platform.defaultTarget, true); + graphicsJobs = true; newGraphicsJobs = true; } } @@ -1664,17 +1661,31 @@ private void OtherSectionRenderingGUI(BuildPlatform platform, BuildTargetGroup t { PlayerSettings.graphicsJobMode = GraphicsJobMode.Legacy; } + graphicsJobsModeOptionEnabled = false; } EditorGUI.BeginChangeCheck(); + GUIContent graphicsJobsGUI = SettingsContent.graphicsJobsNonExperimental; + switch (platform.defaultTarget) + { + case BuildTarget.StandaloneOSX: + case BuildTarget.iOS: + case BuildTarget.tvOS: + case BuildTarget.Android: + graphicsJobsGUI = SettingsContent.graphicsJobsExperimental; + break; + default: + break; + } + using (new EditorGUI.DisabledScope(!graphicsJobsOptionEnabled)) { if (GUI.enabled) { - newGraphicsJobs = EditorGUILayout.Toggle(SettingsContent.graphicsJobs, graphicsJobs); + newGraphicsJobs = EditorGUILayout.Toggle(graphicsJobsGUI, graphicsJobs); } else { - EditorGUILayout.Toggle(SettingsContent.graphicsJobs, false); + EditorGUILayout.Toggle(graphicsJobsGUI, graphicsJobs); } } if (EditorGUI.EndChangeCheck() && (newGraphicsJobs != graphicsJobs)) @@ -1685,7 +1696,7 @@ private void OtherSectionRenderingGUI(BuildPlatform platform, BuildTargetGroup t if (gfxJobModesSupported) { - using (new EditorGUI.DisabledScope(!graphicsJobs)) + using (new EditorGUI.DisabledScope(!graphicsJobsModeOptionEnabled)) { GraphicsJobMode currGfxJobMode = PlayerSettings.graphicsJobMode; GraphicsJobMode newGfxJobMode = BuildEnumPopup(SettingsContent.graphicsJobsMode, currGfxJobMode, m_GfxJobModeValues, m_GfxJobModeNames); @@ -1770,11 +1781,6 @@ private void OtherSectionRenderingGUI(BuildPlatform platform, BuildTargetGroup t } } - if (TargetSupportsProtectedGraphicsMem(targetGroup)) - { - PlayerSettings.protectGraphicsMemory = EditorGUILayout.Toggle(SettingsContent.protectGraphicsMemory, PlayerSettings.protectGraphicsMemory); - } - if (targetGroup == BuildTargetGroup.Standalone || (settingsExtension != null && settingsExtension.SupportsFrameTimingStatistics())) { PlayerSettings.enableFrameTimingStats = EditorGUILayout.Toggle(SettingsContent.enableFrameTimingStats, PlayerSettings.enableFrameTimingStats); diff --git a/Editor/Mono/Inspector/PlayerSettingsEditor/PlayerSettingsSplashScreenEditor.cs b/Editor/Mono/Inspector/PlayerSettingsEditor/PlayerSettingsSplashScreenEditor.cs index e5457d5b0b..8eec603de7 100644 --- a/Editor/Mono/Inspector/PlayerSettingsEditor/PlayerSettingsSplashScreenEditor.cs +++ b/Editor/Mono/Inspector/PlayerSettingsEditor/PlayerSettingsSplashScreenEditor.cs @@ -331,11 +331,11 @@ private void BuiltinCustomSplashScreenGUI() if (SplashScreen.isFinished) { SplashScreen.Begin(); - PreviewEditorWindow.RepaintAll(); - var preview = PreviewEditorWindow.GetMainPreviewWindow(); - if (preview) + PlayModeView.RepaintAll(); + var playModeView = PlayModeView.GetMainPlayModeView(); + if (playModeView) { - preview.Focus(); + playModeView.Focus(); } EditorApplication.update += PollSplashState; } @@ -423,7 +423,7 @@ private void BuiltinCustomSplashScreenGUI() void PollSplashState() { // Force the GameViews to repaint whilst showing the splash(1166664) - PreviewEditorWindow.RepaintAll(); + PlayModeView.RepaintAll(); // When the splash screen is playing we need to keep track so that we can update the preview button when it has finished. if (SplashScreen.isFinished) diff --git a/Editor/Mono/Inspector/RectTransformEditor.cs b/Editor/Mono/Inspector/RectTransformEditor.cs index 33e538bdfe..039975e4ab 100644 --- a/Editor/Mono/Inspector/RectTransformEditor.cs +++ b/Editor/Mono/Inspector/RectTransformEditor.cs @@ -795,7 +795,7 @@ void UpdateTemporaryRect() { s_ParentDragTime = Time.realtimeSinceStartup; Canvas.ForceUpdateCanvases(); - PreviewEditorWindow.RepaintAll(); + PlayModeView.RepaintAll(); return; } @@ -816,7 +816,7 @@ void UpdateTemporaryRect() } Canvas.ForceUpdateCanvases(); SceneView.RepaintAll(); - PreviewEditorWindow.RepaintAll(); + PlayModeView.RepaintAll(); } void AllAnchorsSceneGUI(RectTransform gui, RectTransform guiParent, Transform parentSpace, Transform transform) diff --git a/Editor/Mono/Inspector/ShaderInspector.cs b/Editor/Mono/Inspector/ShaderInspector.cs index 082bd99238..0687e7fb70 100644 --- a/Editor/Mono/Inspector/ShaderInspector.cs +++ b/Editor/Mono/Inspector/ShaderInspector.cs @@ -59,6 +59,19 @@ internal class Styles static readonly int kErrorViewHash = "ShaderErrorView".GetHashCode(); Vector2 m_ScrollPosition = Vector2.zero; + private Material m_SrpCompatibilityCheckMaterial = null; + + public Material srpCompatibilityCheckMaterial + { + get + { + if (m_SrpCompatibilityCheckMaterial == null) + { + m_SrpCompatibilityCheckMaterial = new Material(target as Shader); + } + return m_SrpCompatibilityCheckMaterial; + } + } public virtual void OnEnable() { @@ -66,6 +79,14 @@ public virtual void OnEnable() ShaderUtil.FetchCachedMessages(s); } + public virtual void OnDisable() + { + if (m_SrpCompatibilityCheckMaterial != null) + { + GameObject.DestroyImmediate(m_SrpCompatibilityCheckMaterial); + } + } + private static string GetPropertyType(Shader s, int index) { ShaderUtil.ShaderPropertyType type = ShaderUtil.GetPropertyType(s, index); @@ -116,8 +137,8 @@ public override void OnInspectorGUI() // If any SRP is active, then display the SRP Batcher compatibility status if (RenderPipelineManager.currentPipeline != null) { - var mat = new Material(s); - mat.SetPass(0); // NOTE: Force the shader compilation to ensure GetSRPBatcherCompatibilityCode will be up to date + // NOTE: Force the shader compilation to ensure GetSRPBatcherCompatibilityCode will be up to date + srpCompatibilityCheckMaterial.SetPass(0); int subShader = ShaderUtil.GetShaderActiveSubshaderIndex(s); int SRPErrCode = ShaderUtil.GetSRPBatcherCompatibilityCode(s, subShader); string result = (0 == SRPErrCode) ? "compatible" : "not compatible"; @@ -127,6 +148,7 @@ public override void OnInspectorGUI() EditorGUILayout.HelpBox(ShaderUtil.GetSRPBatcherCompatibilityIssueReason(s, subShader, SRPErrCode), MessageType.Info); } } + ShowShaderProperties(s); } } diff --git a/Editor/Mono/InternalEditorUtility.bindings.cs b/Editor/Mono/InternalEditorUtility.bindings.cs index e99cce90f8..7304442463 100644 --- a/Editor/Mono/InternalEditorUtility.bindings.cs +++ b/Editor/Mono/InternalEditorUtility.bindings.cs @@ -198,8 +198,11 @@ internal static void FixNormalmapTexture(MaterialProperty prop) [FreeFunction("InternalEditorUtilityBindings::CreateScriptableObjectUnchecked")] extern internal static int CreateScriptableObjectUnchecked(MonoScript script); - [StaticAccessor("GetApplication()", StaticAccessorType.Dot)] - extern public static void RequestScriptReload(); + [Obsolete("RequestScriptReload has been deprecated. Use UnityEditor.EditorUtility.RequestScriptReload instead (UnityUpgradable) -> [UnityEditor] UnityEditor.EditorUtility.RequestScriptReload(*)")] + public static void RequestScriptReload() + { + EditorUtility.RequestScriptReload(); + } // Repaint all views on next tick. Used when the user changes skins in the prefs. [StaticAccessor("GetApplication()", StaticAccessorType.Dot)] diff --git a/Editor/Mono/InternalEditorUtility.cs b/Editor/Mono/InternalEditorUtility.cs index 0a1e272ff0..2e490d7845 100644 --- a/Editor/Mono/InternalEditorUtility.cs +++ b/Editor/Mono/InternalEditorUtility.cs @@ -150,52 +150,163 @@ public static void ShowGameView() WindowLayout.ShowAppropriateViewOnEnterExitPlaymode(true); } - // Multi selection handling. Returns new list of selected instanceIDs + internal struct AssetReference + { + // The guid is always valid. Assets not yet present will not have an instanceID yet. + // Could be because it is on-demand imported. + public string guid; + public int instanceID; // instanceID of an object in an asset if the asset is available in imported form. Else 0. + + public sealed class GuidThenInstanceIDEqualityComparer : IEqualityComparer + { + public bool Equals(AssetReference x, AssetReference y) + { + if (!string.IsNullOrEmpty(x.guid) || !string.IsNullOrEmpty(y.guid)) + string.Equals(x.guid, y.guid); + + // Both guids are nullOrEmpty now + return x.instanceID == y.instanceID; + } + + public int GetHashCode(AssetReference assetReference) + { + return (assetReference.instanceID * 397) + ^ (assetReference.guid != null ? assetReference.guid.GetHashCode() : 0); + } + } + } + public static List GetNewSelection(int clickedInstanceID, List allInstanceIDs, List selectedInstanceIDs, int lastClickedInstanceID, bool keepMultiSelection, bool useShiftAsActionKey, bool allowMultiSelection) { - List newSelection = new List(); + return GetNewSelection(clickedInstanceID, allInstanceIDs, selectedInstanceIDs, lastClickedInstanceID, keepMultiSelection, useShiftAsActionKey, allowMultiSelection, Event.current.shift, EditorGUI.actionKey); + } + + internal static List GetNewSelection(int clickedInstanceID, List allInstanceIDs, List selectedInstanceIDs, int lastClickedInstanceID, bool keepMultiSelection, bool useShiftAsActionKey, bool allowMultiSelection, bool shiftKeyIsDown, bool actionKeyIsDown) + { + List allGuids = null; + var clicked = new AssetReference() { guid = null, instanceID = clickedInstanceID }; - bool useShift = Event.current.shift || (EditorGUI.actionKey && useShiftAsActionKey); - bool useActionKey = EditorGUI.actionKey && !useShiftAsActionKey; + return GetNewSelection(ref clicked, allInstanceIDs, allGuids, selectedInstanceIDs, lastClickedInstanceID, keepMultiSelection, useShiftAsActionKey, allowMultiSelection, shiftKeyIsDown, actionKeyIsDown); + } + + internal static void EnsureInstanceId(ref AssetReference entry) + { + if (entry.instanceID != 0 || string.IsNullOrEmpty(entry.guid)) + return; + + var guids = new string[] { entry.guid }; + UnityEditor.Experimental.AssetDatabaseExperimental.GetArtifactHashes(guids); + string path = AssetDatabase.GUIDToAssetPath(entry.guid); + entry.instanceID = AssetDatabase.GetMainAssetInstanceID(path); + } + + internal static List EnsureInstanceIds(List entryInstanceIds, List entryInstanceGuids, int from, int to) + { + List guids = new List(); + + for (int i = from; i <= to; ++i) + { + if (entryInstanceIds[i] == 0) + guids.Add(entryInstanceGuids[i]); + } + + // Force import if needed so that we can get an instance ID for the entry + + if (guids.Count == 0) + { + return entryInstanceIds.GetRange(from, to - from + 1); + } + else + { + UnityEditor.Experimental.AssetDatabaseExperimental.GetArtifactHashes(guids.ToArray()); + var newSelection = new List(to - from + 1); + + for (int i = from; i <= to; ++i) + { + int instanceID = entryInstanceIds[i]; + if (instanceID == 0) + { + string path = AssetDatabase.GUIDToAssetPath(entryInstanceGuids[i]); + instanceID = AssetDatabase.GetMainAssetInstanceID(path); + entryInstanceIds[i] = instanceID; + } + newSelection.Add(instanceID); + } + return newSelection; + } + } + + internal static List GetNewSelection(ref AssetReference clickedEntry, List allEntryInstanceIDs, List allEntryGuids, List selectedInstanceIDs, int lastClickedInstanceID, bool keepMultiSelection, bool useShiftAsActionKey, bool allowMultiSelection) + { + return GetNewSelection(ref clickedEntry, allEntryInstanceIDs, allEntryGuids, selectedInstanceIDs, lastClickedInstanceID, keepMultiSelection, useShiftAsActionKey, allowMultiSelection, Event.current.shift, EditorGUI.actionKey); + } + + // Multi selection handling. Returns new list of selected instanceIDs + internal static List GetNewSelection(ref AssetReference clickedEntry, List allEntryInstanceIDs, List allEntryGuids, List selectedInstanceIDs, int lastClickedInstanceID, bool keepMultiSelection, bool useShiftAsActionKey, bool allowMultiSelection, bool shiftKeyIsDown, bool actionKeyIsDown) + { + bool useShift = shiftKeyIsDown || (actionKeyIsDown && useShiftAsActionKey); + bool useActionKey = actionKeyIsDown && !useShiftAsActionKey; if (!allowMultiSelection) useShift = useActionKey = false; // Toggle selected node from selection if (useActionKey) { - newSelection.AddRange(selectedInstanceIDs); - if (newSelection.Contains(clickedInstanceID)) - newSelection.Remove(clickedInstanceID); + var newSelection = new List(selectedInstanceIDs); + if (newSelection.Contains(clickedEntry.instanceID)) + newSelection.Remove(clickedEntry.instanceID); else - newSelection.Add(clickedInstanceID); + { + EnsureInstanceId(ref clickedEntry); + newSelection.Add(clickedEntry.instanceID); + } + return newSelection; } // Select everything between the first selected object and the selected else if (useShift) { - if (clickedInstanceID == lastClickedInstanceID) + if (clickedEntry.instanceID == lastClickedInstanceID) { - newSelection.AddRange(selectedInstanceIDs); - return newSelection; + return new List(selectedInstanceIDs); } int firstIndex; int lastIndex; - if (!GetFirstAndLastSelected(allInstanceIDs, selectedInstanceIDs, out firstIndex, out lastIndex)) + if (!GetFirstAndLastSelected(allEntryInstanceIDs, selectedInstanceIDs, out firstIndex, out lastIndex)) { // We had no selection - newSelection.Add(clickedInstanceID); + EnsureInstanceId(ref clickedEntry); + var newSelection = new List(1); + newSelection.Add(clickedEntry.instanceID); return newSelection; } int newIndex = -1; int prevIndex = -1; - for (int i = 0; i < allInstanceIDs.Count; ++i) + + // Only valid in case the selection concerns assets + + EnsureInstanceId(ref clickedEntry); + int clickedInstanceID = clickedEntry.instanceID; + + if (lastClickedInstanceID != 0) { - if (allInstanceIDs[i] == clickedInstanceID) - newIndex = i; - if (lastClickedInstanceID != 0) - if (allInstanceIDs[i] == lastClickedInstanceID) + for (int i = 0; i < allEntryInstanceIDs.Count; ++i) + { + if (allEntryInstanceIDs[i] == clickedInstanceID) + newIndex = i; + + if (allEntryInstanceIDs[i] == lastClickedInstanceID) prevIndex = i; + } + } + else + { + for (int i = 0; i < allEntryInstanceIDs.Count; ++i) + { + if (allEntryInstanceIDs[i] == clickedInstanceID) + newIndex = i; + } } System.Diagnostics.Debug.Assert(newIndex != -1); // new item should be part of visible folder set @@ -229,10 +340,11 @@ public static List GetNewSelection(int clickedInstanceID, List allInst } // Outcomment to debug - //Debug.Log (clickedInstanceID + ", firstIndex " + firstIndex + ", lastIndex " + lastIndex + ", newIndex " + newIndex + " " + ", lastClickedIndex " + prevIndex + ", from " + from + ", to " + to); - - for (int i = from; i <= to; ++i) - newSelection.Add(allInstanceIDs[i]); + //Debug.Log (clickedEntry + ", firstIndex " + firstIndex + ", lastIndex " + lastIndex + ", newIndex " + newIndex + " " + ", lastClickedIndex " + prevIndex + ", from " + from + ", to " + to); + if (allEntryGuids == null) + return allEntryInstanceIDs.GetRange(from, to - from + 1); + else + return EnsureInstanceIds(allEntryInstanceIDs, allEntryGuids, from, to); } // Just set the selection to the clicked object else @@ -241,26 +353,26 @@ public static List GetNewSelection(int clickedInstanceID, List allInst { // Don't change selection on mouse down when clicking on selected item. // This is for dragging in case with multiple items selected or right click (mouse down should not unselect the rest). - if (selectedInstanceIDs.Contains(clickedInstanceID)) + if (selectedInstanceIDs.Contains(clickedEntry.instanceID)) { - newSelection.AddRange(selectedInstanceIDs); - return newSelection; + return new List(selectedInstanceIDs); } } - newSelection.Add(clickedInstanceID); + EnsureInstanceId(ref clickedEntry); + var newSelection = new List(1); + newSelection.Add(clickedEntry.instanceID); + return newSelection; } - - return newSelection; } - static bool GetFirstAndLastSelected(List allInstanceIDs, List selectedInstanceIDs, out int firstIndex, out int lastIndex) + static bool GetFirstAndLastSelected(List allEntries, List selectedInstanceIDs, out int firstIndex, out int lastIndex) { firstIndex = -1; lastIndex = -1; - for (int i = 0; i < allInstanceIDs.Count; ++i) + for (int i = 0; i < allEntries.Count; ++i) { - if (selectedInstanceIDs.Contains(allInstanceIDs[i])) + if (selectedInstanceIDs.Contains(allEntries[i])) { if (firstIndex == -1) firstIndex = i; @@ -424,10 +536,10 @@ internal static string[] GetCompilationDefines(EditorScriptCompilationOptions op public static void SetShowGizmos(bool value) { - var view = PreviewEditorWindow.GetMainPreviewWindow(); + var view = PlayModeView.GetMainPlayModeView(); if (view == null) - view = PreviewEditorWindow.GetRenderingPreview(); + view = PlayModeView.GetRenderingView(); if (view == null) return; diff --git a/Editor/Mono/ObjectListArea.cs b/Editor/Mono/ObjectListArea.cs index ea3cf67bc5..0f6fae4ae5 100644 --- a/Editor/Mono/ObjectListArea.cs +++ b/Editor/Mono/ObjectListArea.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Linq; using UnityEditorInternal; +using AssetReference = UnityEditorInternal.InternalEditorUtility.AssetReference; namespace UnityEditor { @@ -111,7 +112,7 @@ static GUIStyle GetStyle(string styleName) public bool foldersFirst { get; set; } int m_KeyboardControlID; - Dictionary m_InstanceIDToCroppedNameMap = new Dictionary(); + Dictionary m_AssetReferenceToCroppedNameMap = new Dictionary(new AssetReference.GuidThenInstanceIDEqualityComparer()); int m_WidthUsedForCroppingName; bool m_AllowRenameOnMouseUp = true; @@ -529,9 +530,12 @@ public void OnGUI(Rect position, int keyboardControlID) // For key navigation: Auto set selection to first element if selection is not shown currently when tabbing if (evt.keyCode == KeyCode.Tab && evt.type == EventType.KeyDown && !hasKeyboardFocus && !IsShowingAny(GetSelection())) { - int firstInstanceID; - if (m_LocalAssets.InstanceIdAtIndex(0, out firstInstanceID)) - Selection.activeInstanceID = firstInstanceID; + AssetReference firstAssetReference; + if (m_LocalAssets.AssetReferenceAtIndex(0, out firstAssetReference)) + { + m_LocalAssets.GetNewSelection(ref firstAssetReference, false, false); + Selection.activeInstanceID = firstAssetReference.instanceID; + } } @@ -760,8 +764,12 @@ public bool IsLastClickedItemVisible() public void SelectAll() { - List list = m_LocalAssets.GetInstanceIDs(); - SetSelection(list.ToArray(), false); + List instanceIDs; + List guids; + m_LocalAssets.GetAssetReferences(out instanceIDs, out guids); + if (instanceIDs.Count != 0) + InternalEditorUtility.EnsureInstanceIds(instanceIDs, guids, 0, instanceIDs.Count - 1); + SetSelection(instanceIDs.ToArray(), false); } void SetSelection(int[] selectedInstanceIDs, bool doubleClicked) @@ -1044,19 +1052,11 @@ public void SelectFirst() SetSelectedAssetByIdx(startIndex); } - public int GetInstanceIDByIndex(int index) - { - int instanceID; - if (m_LocalAssets.InstanceIdAtIndex(index, out instanceID)) - return instanceID; - return 0; - } - void SetSelectedAssetByIdx(int selectedIdx) { // instanceID can be 0 if 'None' item is at index - int instanceID; - if (m_LocalAssets.InstanceIdAtIndex(selectedIdx, out instanceID)) + AssetReference assetReference; + if (m_LocalAssets.AssetReferenceAtIndex(selectedIdx, out assetReference)) { Rect r = m_LocalAssets.m_Grid.CalcRect(selectedIdx, 0f); ScrollToPosition(AdjustRectForFraming(r)); @@ -1064,12 +1064,12 @@ void SetSelectedAssetByIdx(int selectedIdx) int[] newSelection; if (IsLocalAssetsCurrentlySelected()) - newSelection = m_LocalAssets.GetNewSelection(instanceID, false, true).ToArray(); // Handle multi selection + newSelection = m_LocalAssets.GetNewSelection(ref assetReference, false, true).ToArray(); // Handle multi selection else - newSelection = new[] {instanceID}; // If current selection is asset store asset do not allow multiselection + newSelection = m_LocalAssets.GetNewSelection(ref assetReference, false, false).ToArray(); // If current selection is asset store asset do not allow multiselection SetSelection(newSelection, false); - m_State.m_LastClickedInstanceID = instanceID; + m_State.m_LastClickedInstanceID = assetReference.instanceID; return; } @@ -1530,20 +1530,20 @@ public void OnInspectorUpdate() void ClearCroppedLabelCache() { - m_InstanceIDToCroppedNameMap.Clear(); + m_AssetReferenceToCroppedNameMap.Clear(); } - protected string GetCroppedLabelText(int instanceID, string fullText, float cropWidth) + protected string GetCroppedLabelText(AssetReference assetReference, string fullText, float cropWidth) { // Clear when width changes if (m_WidthUsedForCroppingName != (int)cropWidth) ClearCroppedLabelCache(); string croppedText; - if (!m_InstanceIDToCroppedNameMap.TryGetValue(instanceID, out croppedText)) + if (!m_AssetReferenceToCroppedNameMap.TryGetValue(assetReference, out croppedText)) { // Ensure to clean up once in a while - if (m_InstanceIDToCroppedNameMap.Count > GetMaxNumVisibleItems() * 2 + 30) + if (m_AssetReferenceToCroppedNameMap.Count > GetMaxNumVisibleItems() * 2 + 30) ClearCroppedLabelCache(); // Check if we need to crop @@ -1559,7 +1559,7 @@ protected string GetCroppedLabelText(int instanceID, string fullText, float crop else croppedText = fullText; - m_InstanceIDToCroppedNameMap[instanceID] = croppedText; + m_AssetReferenceToCroppedNameMap[assetReference] = croppedText; m_WidthUsedForCroppingName = (int)cropWidth; } return croppedText; @@ -1636,8 +1636,8 @@ public void BeginPing(int instanceID) m_pingIndex = index; float vcPadding = s_VCEnabled ? k_ListModeVersionControlOverlayPadding : 0f; - - GUIContent cont = new GUIContent(m_LocalAssets.ListMode ? name : GetCroppedLabelText(instanceID, name, m_WidthUsedForCroppingName)); + var assetReference = new AssetReference() { instanceID = instanceID }; + GUIContent cont = new GUIContent(m_LocalAssets.ListMode ? name : GetCroppedLabelText(assetReference, name, m_WidthUsedForCroppingName)); string label = cont.text; if (m_LocalAssets.ListMode) diff --git a/Editor/Mono/ObjectListAssetStoreGroup.cs b/Editor/Mono/ObjectListAssetStoreGroup.cs index 5fd769ca52..a1a6cee1d0 100644 --- a/Editor/Mono/ObjectListAssetStoreGroup.cs +++ b/Editor/Mono/ObjectListAssetStoreGroup.cs @@ -250,7 +250,8 @@ void DrawLabel(Rect position, AssetStoreAsset assetStoreResource, bool selected) { // We crop the text in Preview mode int pseudoInstanceID = assetStoreResource.id + 10000000; // we add a large offset to the asset store id to ensure there is no overlap with instanceIDs - string labeltext = m_Owner.GetCroppedLabelText(pseudoInstanceID, assetStoreResource.displayName, position.width); + var assetReference = new InternalEditorUtility.AssetReference() { instanceID = pseudoInstanceID }; + string labeltext = m_Owner.GetCroppedLabelText(assetReference, assetStoreResource.displayName, position.width); position.height -= s_Styles.resultsGridLabel.fixedHeight; // The -1 is to ensure the label has same width as the image and that it aligns with the bottom of the image s_Styles.resultsGridLabel.Draw(new Rect(position.x, position.yMax + 1, position.width - 1, diff --git a/Editor/Mono/ObjectListLocalGroup.cs b/Editor/Mono/ObjectListLocalGroup.cs index 0fbb018377..f03fde526b 100644 --- a/Editor/Mono/ObjectListLocalGroup.cs +++ b/Editor/Mono/ObjectListLocalGroup.cs @@ -11,6 +11,7 @@ using System.Linq; using Math = System.Math; using IndexOutOfRangeException = System.IndexOutOfRangeException; +using AssetReference = UnityEditorInternal.InternalEditorUtility.AssetReference; namespace UnityEditor { @@ -290,7 +291,7 @@ protected override void HandleUnusedDragEvents(float yOffset) } } - void HandleMouseWithDragging(int instanceID, int controlID, Rect rect) + void HandleMouseWithDragging(ref AssetReference assetReference, int controlID, Rect rect) { // Handle mouse down on entire line Event evt = Event.current; @@ -303,17 +304,30 @@ void HandleMouseWithDragging(int instanceID, int controlID, Rect rect) if (evt.clickCount == 2) { // Double clicked - m_Owner.SetSelection(new[] {instanceID}, true); + var newSelection = GetNewSelection(ref assetReference, false, false); + m_Owner.SetSelection(newSelection.ToArray(), true); m_DragSelection.Clear(); } else { // Begin drag - m_DragSelection = GetNewSelection(instanceID, true, false); + var newSelection = GetNewSelection(ref assetReference, false, false); + var oldItemControlID = controlID; + controlID = GetControlIDFromInstanceID(assetReference.instanceID); + if (controlID == oldItemControlID) + { + newSelection = GetNewSelection(ref assetReference, true, false); + m_DragSelection = newSelection; + DragAndDropDelay delay = (DragAndDropDelay)GUIUtility.GetStateObject(typeof(DragAndDropDelay), controlID); + delay.mouseDownPosition = Event.current.mousePosition; + m_Owner.ScrollToPosition(ObjectListArea.AdjustRectForFraming(rect)); + } + else + { + m_Owner.SetSelection(newSelection.ToArray(), false); + m_DragSelection.Clear(); + } GUIUtility.hotControl = controlID; - DragAndDropDelay delay = (DragAndDropDelay)GUIUtility.GetStateObject(typeof(DragAndDropDelay), controlID); - delay.mouseDownPosition = Event.current.mousePosition; - m_Owner.ScrollToPosition(ObjectListArea.AdjustRectForFraming(rect)); } evt.Use(); @@ -321,7 +335,7 @@ void HandleMouseWithDragging(int instanceID, int controlID, Rect rect) else if (Event.current.button == 1 && rect.Contains(Event.current.mousePosition)) { // Right mouse down selection (do NOT use event since we need ContextClick event, which is not fired if right click is used) - m_Owner.SetSelection(GetNewSelection(instanceID, true, false).ToArray(), false); + m_Owner.SetSelection(GetNewSelection(ref assetReference, true, false).ToArray(), false); } break; case EventType.MouseDrag: @@ -330,7 +344,7 @@ void HandleMouseWithDragging(int instanceID, int controlID, Rect rect) DragAndDropDelay delay = (DragAndDropDelay)GUIUtility.GetStateObject(typeof(DragAndDropDelay), controlID); if (delay.CanStartDrag()) { - StartDrag(instanceID, m_DragSelection); + StartDrag(assetReference.instanceID, m_DragSelection); GUIUtility.hotControl = 0; } @@ -343,7 +357,7 @@ void HandleMouseWithDragging(int instanceID, int controlID, Rect rect) bool perform = evt.type == EventType.DragPerform; if (rect.Contains(evt.mousePosition)) { - DragAndDropVisualMode mode = DoDrag(instanceID, perform); + DragAndDropVisualMode mode = DoDrag(assetReference.instanceID, perform); if (mode != DragAndDropVisualMode.None) { if (perform) @@ -382,13 +396,13 @@ void HandleMouseWithDragging(int instanceID, int controlID, Rect rect) } List selected = m_Owner.m_State.m_SelectedInstanceIDs; - if (clickedOnText && m_Owner.allowRenaming && m_Owner.m_AllowRenameOnMouseUp && selected.Count == 1 && selected[0] == instanceID && !EditorGUIUtility.HasHolddownKeyModifiers(evt)) + if (clickedOnText && m_Owner.allowRenaming && m_Owner.m_AllowRenameOnMouseUp && selected.Count == 1 && selected[0] == assetReference.instanceID && !EditorGUIUtility.HasHolddownKeyModifiers(evt)) { m_Owner.BeginRename(0.5f); } else { - List newSelection = GetNewSelection(instanceID, false, false); + List newSelection = GetNewSelection(ref assetReference, false, false); m_Owner.SetSelection(newSelection.ToArray(), false); } @@ -413,7 +427,7 @@ void HandleMouseWithDragging(int instanceID, int controlID, Rect rect) } } - void HandleMouseWithoutDragging(int instanceID, int controlID, Rect position) + void HandleMouseWithoutDragging(ref AssetReference assetReference, int controlID, Rect position) { Event evt = Event.current; @@ -430,7 +444,7 @@ void HandleMouseWithoutDragging(int instanceID, int controlID, Rect position) } evt.Use(); - List newSelection = GetNewSelection(instanceID, false, false); + List newSelection = GetNewSelection(ref assetReference, false, false); m_Owner.SetSelection(newSelection.ToArray(), evt.clickCount == 2); } break; @@ -439,7 +453,8 @@ void HandleMouseWithoutDragging(int instanceID, int controlID, Rect position) if (position.Contains(evt.mousePosition)) { // Select it - m_Owner.SetSelection(new[] {instanceID}, false); + List newSelection = GetNewSelection(ref assetReference, false, false); + m_Owner.SetSelection(newSelection.ToArray(), false); Rect overlayPos = position; overlayPos.x += 2; @@ -595,27 +610,28 @@ void DrawItem(Rect position, FilteredHierarchy.FilterResult filterItem, BuiltinR Event evt = Event.current; Rect itemRect = position; - int instanceID = 0; + var assetReference = new AssetReference() { instanceID = 0 }; bool showFoldout = false; if (filterItem != null) { - instanceID = filterItem.instanceID; + assetReference.instanceID = filterItem.instanceID; + assetReference.guid = filterItem.guid; showFoldout = filterItem.hasChildren && !filterItem.isFolder && isFolderBrowsing; // we do not want to be able to expand folders } else if (builtinResource != null) { - instanceID = builtinResource.m_InstanceID; + assetReference.instanceID = builtinResource.m_InstanceID; } - int controlID = GetControlIDFromInstanceID(instanceID); + int controlID = GetControlIDFromInstanceID(assetReference.instanceID); bool selected; if (m_Owner.allowDragging) - selected = m_DragSelection.Count > 0 ? m_DragSelection.Contains(instanceID) : m_Owner.IsSelected(instanceID); + selected = m_DragSelection.Count > 0 ? m_DragSelection.Contains(assetReference.instanceID) : m_Owner.IsSelected(assetReference.instanceID); else - selected = m_Owner.IsSelected(instanceID); + selected = m_Owner.IsSelected(assetReference.instanceID); - if (selected && instanceID == m_Owner.m_State.m_LastClickedInstanceID) + if (selected && assetReference.instanceID == m_Owner.m_State.m_LastClickedInstanceID) m_LastClickedDrawTime = EditorApplication.timeSinceStartup; Rect foldoutRect = new Rect(position.x + s_Styles.groupFoldout.margin.left, position.y, s_Styles.groupFoldout.padding.left, position.height); // ListMode foldout @@ -649,10 +665,10 @@ void DrawItem(Rect position, FilteredHierarchy.FilterResult filterItem, BuiltinR case KeyCode.LeftArrow: if (ListMode || m_Owner.IsPreviewIconExpansionModifierPressed()) { - if (IsExpanded(instanceID)) + if (IsExpanded(assetReference.instanceID)) toggleState = true; else - SelectAndFrameParentOf(instanceID); + SelectAndFrameParentOf(assetReference.instanceID); evt.Use(); } break; @@ -661,7 +677,7 @@ void DrawItem(Rect position, FilteredHierarchy.FilterResult filterItem, BuiltinR case KeyCode.RightArrow: if (ListMode || m_Owner.IsPreviewIconExpansionModifierPressed()) { - if (!IsExpanded(instanceID)) + if (!IsExpanded(assetReference.instanceID)) toggleState = true; evt.Use(); } @@ -675,15 +691,15 @@ void DrawItem(Rect position, FilteredHierarchy.FilterResult filterItem, BuiltinR if (toggleState) { - bool expanded = !IsExpanded(instanceID); + bool expanded = !IsExpanded(assetReference.instanceID); if (expanded) - m_ItemFader.Start(m_FilteredHierarchy.GetSubAssetInstanceIDs(instanceID)); - ChangeExpandedState(instanceID, expanded); + m_ItemFader.Start(m_FilteredHierarchy.GetSubAssetInstanceIDs(assetReference.instanceID)); + ChangeExpandedState(assetReference.instanceID, expanded); evt.Use(); GUIUtility.ExitGUI(); } - bool isRenaming = IsRenaming(instanceID); + bool isRenaming = IsRenaming(assetReference.instanceID); Rect labelRect = position; if (!ListMode) @@ -723,14 +739,21 @@ void DrawItem(Rect position, FilteredHierarchy.FilterResult filterItem, BuiltinR m_Content.image = null; Texture2D icon; - if (m_Owner.GetCreateAssetUtility().instanceID == instanceID && m_Owner.GetCreateAssetUtility().icon != null) + if (assetReference.instanceID != 0 && m_Owner.GetCreateAssetUtility().instanceID == assetReference.instanceID && m_Owner.GetCreateAssetUtility().icon != null) { // If we are creating a new asset we might have an icon to use icon = m_Owner.GetCreateAssetUtility().icon; } else { - icon = filterItem != null ? filterItem.icon : AssetPreview.GetAssetPreview(instanceID, m_Owner.GetAssetPreviewManagerID()); + icon = filterItem != null ? filterItem.icon : null; + if (icon == null) + { + if (assetReference.instanceID != 0) + icon = AssetPreview.GetAssetPreview(assetReference.instanceID, m_Owner.GetAssetPreviewManagerID()); + else if (!string.IsNullOrEmpty(assetReference.guid)) + icon = AssetPreview.GetAssetPreviewFromGUID(assetReference.guid, m_Owner.GetAssetPreviewManagerID()); + } } if (selected) @@ -744,13 +767,13 @@ void DrawItem(Rect position, FilteredHierarchy.FilterResult filterItem, BuiltinR // Foldout if (showFoldout) - s_Styles.groupFoldout.Draw(foldoutRect, !ListMode, !ListMode, IsExpanded(instanceID), false); + s_Styles.groupFoldout.Draw(foldoutRect, !ListMode, !ListMode, IsExpanded(assetReference.instanceID), false); } else // Icon grid { // Get icon bool drawDropShadow = false; - if (m_Owner.GetCreateAssetUtility().instanceID == instanceID && m_Owner.GetCreateAssetUtility().icon != null) + if (m_Owner.GetCreateAssetUtility().instanceID == assetReference.instanceID && m_Owner.GetCreateAssetUtility().icon != null) { // If we are creating a new asset we might have an icon to use m_Content.image = m_Owner.GetCreateAssetUtility().icon; @@ -758,7 +781,13 @@ void DrawItem(Rect position, FilteredHierarchy.FilterResult filterItem, BuiltinR else { // Check for asset preview - m_Content.image = AssetPreview.GetAssetPreview(instanceID, m_Owner.GetAssetPreviewManagerID()); + Texture image = null; + if (assetReference.instanceID != 0) + image = AssetPreview.GetAssetPreview(assetReference.instanceID, m_Owner.GetAssetPreviewManagerID()); + else if (!string.IsNullOrEmpty(assetReference.guid)) + image = AssetPreview.GetAssetPreviewFromGUID(assetReference.guid, m_Owner.GetAssetPreviewManagerID()); + + m_Content.image = image; if (m_Content.image != null) drawDropShadow = true; @@ -836,7 +865,7 @@ void DrawItem(Rect position, FilteredHierarchy.FilterResult filterItem, BuiltinR if (isDropTarget) s_Styles.resultsLabel.Draw(new Rect(labelRect.x - 10, labelRect.y, labelRect.width + 20, labelRect.height), GUIContent.none, true, true, false, false); - labeltext = m_Owner.GetCroppedLabelText(instanceID, labeltext, position.width); + labeltext = m_Owner.GetCroppedLabelText(assetReference, labeltext, position.width); var labelNewRect = s_Styles.resultsGridLabel.CalcSizeWithConstraints(GUIContent.Temp(labeltext), position.size); labelRect.x = position.x + (position.width - labelNewRect.x) / 2.0f; labelRect.width = labelNewRect.x; @@ -855,7 +884,7 @@ void DrawItem(Rect position, FilteredHierarchy.FilterResult filterItem, BuiltinR { style = s_Styles.subAssetExpandButtonMedium; } - style.Draw(foldoutRect, !ListMode, !ListMode, IsExpanded(instanceID), false); + style.Draw(foldoutRect, !ListMode, !ListMode, IsExpanded(assetReference.instanceID), false); } if (filterItem != null && filterItem.isMainRepresentation) @@ -893,9 +922,12 @@ void DrawItem(Rect position, FilteredHierarchy.FilterResult filterItem, BuiltinR // Mouse handling (must be after rename overlay to ensure overlay get mouseevents) if (m_Owner.allowDragging) - HandleMouseWithDragging(instanceID, controlID, position); + HandleMouseWithDragging(ref assetReference, controlID, position); else - HandleMouseWithoutDragging(instanceID, controlID, position); + HandleMouseWithoutDragging(ref assetReference, controlID, position); + + if (filterItem != null && filterItem.instanceID == 0) + filterItem.instanceID = assetReference.instanceID; } private static Rect ActualImageDrawPosition(Rect position, float imageWidth, float imageHeight) @@ -939,37 +971,51 @@ private void BeginPing(int instanceID) { } - public List GetInstanceIDs() + public void GetAssetReferences(out List instanceIDs, out List guids) { - List result = new List(); + instanceIDs = new List(); + guids = new List(); // 1. None item if (m_NoneList.Length > 0) - result.Add(m_NoneList[0].m_InstanceID); // 0 + { + instanceIDs.Add(m_NoneList[0].m_InstanceID); // 0 + guids.Add(null); + } // 2. Project Assets foreach (FilteredHierarchy.FilterResult r in m_FilteredHierarchy.results) - result.Add(r.instanceID); + { + instanceIDs.Add(r.instanceID); + guids.Add(r.guid); + } + if (m_Owner.m_State.m_NewAssetIndexInList >= 0) - result.Add(m_Owner.GetCreateAssetUtility().instanceID); + { + instanceIDs.Add(m_Owner.GetCreateAssetUtility().instanceID); + guids.Add(null); + } // 3. Builtin for (int i = 0; i < m_ActiveBuiltinList.Length; ++i) - result.Add(m_ActiveBuiltinList[i].m_InstanceID); - - return result; + { + instanceIDs.Add(m_ActiveBuiltinList[i].m_InstanceID); + guids.Add(null); + } } // Returns list of selected instanceIDs - public List GetNewSelection(int clickedInstanceID, bool beginOfDrag, bool useShiftAsActionKey) + public List GetNewSelection(ref AssetReference clickedAssetReference, bool beginOfDrag, bool useShiftAsActionKey) { // Flatten grid - List allInstanceIDs = GetInstanceIDs(); + List instanceIDs; + List guids; + GetAssetReferences(out instanceIDs, out guids); List selectedInstanceIDs = m_Owner.m_State.m_SelectedInstanceIDs; int lastClickedInstanceID = m_Owner.m_State.m_LastClickedInstanceID; bool allowMultiselection = m_Owner.allowMultiSelect; - return InternalEditorUtility.GetNewSelection(clickedInstanceID, allInstanceIDs, selectedInstanceIDs, lastClickedInstanceID, beginOfDrag, useShiftAsActionKey, allowMultiselection); + return InternalEditorUtility.GetNewSelection(ref clickedAssetReference, instanceIDs, guids, selectedInstanceIDs, lastClickedInstanceID, beginOfDrag, useShiftAsActionKey, allowMultiselection); } public override void UpdateFilter(HierarchyType hierarchyType, SearchFilter searchFilter, bool foldersFirst) @@ -1228,9 +1274,9 @@ public FilteredHierarchy.FilterResult LookupByInstanceID(int instanceID) } // Returns true if index was valid. Note that instance can be 0 if 'None' item was found at index - public bool InstanceIdAtIndex(int index, out int instanceID) + public bool AssetReferenceAtIndex(int index, out AssetReference assetReference) { - instanceID = 0; + assetReference = new AssetReference() { instanceID = 0 }; if (index >= m_Grid.rows * m_Grid.columns) return false; @@ -1248,7 +1294,8 @@ public bool InstanceIdAtIndex(int index, out int instanceID) // 2. Project assets foreach (FilteredHierarchy.FilterResult r in m_FilteredHierarchy.results) { - instanceID = r.instanceID; + assetReference.instanceID = r.instanceID; + assetReference.guid = r.guid; if (idx == index) return true; idx++; @@ -1257,7 +1304,7 @@ public bool InstanceIdAtIndex(int index, out int instanceID) // 3. Builtin resources foreach (BuiltinResource b in m_ActiveBuiltinList) { - instanceID = b.m_InstanceID; + assetReference.instanceID = b.m_InstanceID; if (idx == index) return true; idx++; diff --git a/Editor/Mono/PerformanceTools/FrameDebugger.cs b/Editor/Mono/PerformanceTools/FrameDebugger.cs index e03ab8932a..5f8a680497 100644 --- a/Editor/Mono/PerformanceTools/FrameDebugger.cs +++ b/Editor/Mono/PerformanceTools/FrameDebugger.cs @@ -485,10 +485,10 @@ private void ClickEnableFrameDebugger() // Make sure game view is visible when enabling frame debugger locally if (FrameDebuggerUtility.IsLocalEnabled()) { - var previewWindow = PreviewEditorWindow.GetMainPreviewWindow(); - if (previewWindow) + var playModeView = PlayModeView.GetMainPlayModeView(); + if (playModeView) { - previewWindow.ShowTab(); + playModeView.ShowTab(); } } diff --git a/Editor/Mono/Preview/PreviewEditorWindow.cs b/Editor/Mono/PlayModeView/PlayModeView.cs similarity index 75% rename from Editor/Mono/Preview/PreviewEditorWindow.cs rename to Editor/Mono/PlayModeView/PlayModeView.cs index f25d814aff..e92e41939b 100644 --- a/Editor/Mono/Preview/PreviewEditorWindow.cs +++ b/Editor/Mono/PlayModeView/PlayModeView.cs @@ -13,14 +13,22 @@ namespace UnityEditor { + internal static class PreviewEditorWindow + { + internal static void RepaintAll() + { + PlayModeView.RepaintAll(); + } + } + [Serializable] - internal abstract class PreviewEditorWindow : EditorWindow + internal abstract class PlayModeView : EditorWindow { - static List s_PreviewWindows = new List(); - static PreviewEditorWindow s_LastFocused; - static PreviewEditorWindow s_RenderingPreview; + static List s_PlayModeViews = new List(); + static PlayModeView s_LastFocused; + static PlayModeView s_RenderingView; - [SerializeField] string m_PreviewName; + [SerializeField] string m_PlayModeViewName; [SerializeField] bool m_ShowGizmos; [SerializeField] int m_TargetDisplay; [SerializeField] Color m_ClearColor; @@ -32,10 +40,10 @@ internal abstract class PreviewEditorWindow : EditorWindow private Dictionary m_AvailableWindowTypes; - protected string previewName + protected string playModeViewName { - get { return m_PreviewName; } - set { m_PreviewName = value; } + get { return m_PlayModeViewName; } + set { m_PlayModeViewName = value; } } protected bool showGizmos @@ -92,13 +100,13 @@ public bool maximizeOnPlay RenderTexture m_TargetTexture; ColorSpace m_CurrentColorSpace = ColorSpace.Uninitialized; - class RenderingPreview : IDisposable + class RenderingView : IDisposable { bool disposed = false; - public RenderingPreview(PreviewEditorWindow previewWindow) + public RenderingView(PlayModeView playModeView) { - PreviewEditorWindow.s_RenderingPreview = previewWindow; + PlayModeView.s_RenderingView = playModeView; } public void Dispose() @@ -115,22 +123,22 @@ protected virtual void Dispose(bool disposing) if (disposing) { - PreviewEditorWindow.s_RenderingPreview = null; + PlayModeView.s_RenderingView = null; } disposed = true; } } - protected PreviewEditorWindow() + protected PlayModeView() { RegisterWindow(); SetPlayModeView(); } - protected RenderTexture RenderPreview(Vector2 mousePosition, bool clearTexture) + protected RenderTexture RenderView(Vector2 mousePosition, bool clearTexture) { - using (var renderingPreview = new RenderingPreview(this)) + using (var renderingView = new RenderingView(this)) { SetPlayModeViewSize(targetSize); var currentTargetDisplay = 0; @@ -141,7 +149,7 @@ protected RenderTexture RenderPreview(Vector2 mousePosition, bool clearTexture) currentTargetDisplay = targetDisplay; } - ConfigureTargetTexture((int)targetSize.x, (int)targetSize.y, clearTexture, previewName); + ConfigureTargetTexture((int)targetSize.x, (int)targetSize.y, clearTexture, playModeViewName); if (Event.current == null || Event.current.type != EventType.Repaint) return m_TargetTexture; @@ -150,7 +158,7 @@ protected RenderTexture RenderPreview(Vector2 mousePosition, bool clearTexture) GUIUtility.s_EditorScreenPointOffset = Vector2.zero; SavedGUIState oldState = SavedGUIState.Create(); - EditorGUIUtility.RenderPreviewCamerasInternal(m_TargetTexture, currentTargetDisplay, mousePosition, showGizmos, renderIMGUI); + EditorGUIUtility.RenderPlayModeViewCamerasInternal(m_TargetTexture, currentTargetDisplay, mousePosition, showGizmos, renderIMGUI); oldState.ApplyAndForget(); GUIUtility.s_EditorScreenPointOffset = oldOffset; @@ -167,17 +175,17 @@ protected string GetWindowTitle(Type type) protected Dictionary GetAvailableWindowTypes() { - return m_AvailableWindowTypes ?? (m_AvailableWindowTypes = TypeCache.GetTypesDerivedFrom(typeof(PreviewEditorWindow)).OrderBy(GetWindowTitle).ToDictionary(t => t, GetWindowTitle)); + return m_AvailableWindowTypes ?? (m_AvailableWindowTypes = TypeCache.GetTypesDerivedFrom(typeof(PlayModeView)).OrderBy(GetWindowTitle).ToDictionary(t => t, GetWindowTitle)); } protected void SwapMainWindow(Type type) { - if (type.BaseType != typeof(PreviewEditorWindow)) - throw new ArgumentException("Type should derive from " + typeof(PreviewEditorWindow).Name); + if (type.BaseType != typeof(PlayModeView)) + throw new ArgumentException("Type should derive from " + typeof(PlayModeView).Name); if (type.Name != GetType().Name) { - var window = CreateInstance(type) as PreviewEditorWindow; + var window = CreateInstance(type) as PlayModeView; window.autoRepaintOnSceneChange = true; var da = m_Parent as DockArea; if (da) @@ -234,18 +242,18 @@ private void ConfigureTargetTexture(int width, int height, bool clearTexture, st } } - internal static PreviewEditorWindow GetRenderingPreview() + internal static PlayModeView GetRenderingView() { - return s_RenderingPreview; + return s_RenderingView; } - internal static PreviewEditorWindow GetMainPreviewWindow() + internal static PlayModeView GetMainPlayModeView() { - if (s_LastFocused == null && s_PreviewWindows != null) + if (s_LastFocused == null && s_PlayModeViews != null) { RemoveDisabledWindows(); - if (s_PreviewWindows.Count > 0) - s_LastFocused = s_PreviewWindows[0]; + if (s_PlayModeViews.Count > 0) + s_LastFocused = s_PlayModeViews[0]; } return s_LastFocused; @@ -253,21 +261,21 @@ internal static PreviewEditorWindow GetMainPreviewWindow() private static void RemoveDisabledWindows() { - if (s_PreviewWindows == null) + if (s_PlayModeViews == null) return; - s_PreviewWindows.RemoveAll(window => window == null); + s_PlayModeViews.RemoveAll(window => window == null); } - internal static Vector2 GetMainPreviewTargetSize() + internal static Vector2 GetMainPlayModeViewTargetSize() { - var prevWindow = GetMainPreviewWindow(); + var prevWindow = GetMainPlayModeView(); if (prevWindow) - return prevWindow.GetPreviewSize(); + return prevWindow.GetPlayModeViewSize(); return new Vector2(640f, 480f); } - internal Vector2 GetPreviewSize() + internal Vector2 GetPlayModeViewSize() { return targetSize; } @@ -275,8 +283,8 @@ internal Vector2 GetPreviewSize() private void RegisterWindow() { RemoveDisabledWindows(); - if (!s_PreviewWindows.Contains(this)) - s_PreviewWindows.Add(this); + if (!s_PlayModeViews.Contains(this)) + s_PlayModeViews.Add(this); } public bool IsShowingGizmos() @@ -310,24 +318,24 @@ protected void SetFocus(bool focused) } } - internal static bool IsPreviewWindowOpen() + internal static bool IsPlayModeViewOpen() { - return GetMainPreviewWindow() != null; + return GetMainPlayModeView() != null; } internal static void RepaintAll() { - if (s_PreviewWindows == null) + if (s_PlayModeViews == null) return; - foreach (PreviewEditorWindow previewWindow in s_PreviewWindows) - previewWindow.Repaint(); + foreach (PlayModeView playModeView in s_PlayModeViews) + playModeView.Repaint(); } [RequiredByNativeCode] - private static void GetMainPreviewTargetSizeNoBox(out Vector2 result) + private static void GetMainPlayModeViewargetSizeNoBox(out Vector2 result) { - result = GetMainPreviewTargetSize(); + result = GetMainPlayModeViewTargetSize(); } } } diff --git a/Editor/Mono/PlayerSettings.bindings.cs b/Editor/Mono/PlayerSettings.bindings.cs index b4c303b380..39c6319e8e 100644 --- a/Editor/Mono/PlayerSettings.bindings.cs +++ b/Editor/Mono/PlayerSettings.bindings.cs @@ -508,7 +508,8 @@ public static bool singlePassStereoRendering public static extern StereoRenderingPath stereoRenderingPath { get; set; } - public static extern bool protectGraphicsMemory { get; set; } + [Obsolete("protectGraphicsMemory is deprecated. This field has no effect.", false)] + public static bool protectGraphicsMemory { get { return false; } set {} } public static extern bool enableFrameTimingStats { get; set; } diff --git a/Editor/Mono/Prefabs/PrefabOverrides/PrefabOverride.cs b/Editor/Mono/Prefabs/PrefabOverrides/PrefabOverride.cs index 97fe906114..c2cfcd1b41 100644 --- a/Editor/Mono/Prefabs/PrefabOverrides/PrefabOverride.cs +++ b/Editor/Mono/Prefabs/PrefabOverrides/PrefabOverride.cs @@ -106,11 +106,12 @@ public override void Apply(string prefabAssetPath) public override void Revert() { + var coupledComponent = instanceComponent.GetCoupledComponent(); + PrefabUtility.RevertAddedComponent( instanceComponent, InteractionMode.UserAction); - var coupledComponent = instanceComponent.GetCoupledComponent(); if (coupledComponent != null) { PrefabUtility.RevertAddedComponent( diff --git a/Editor/Mono/Prefabs/PrefabOverrides/PrefabOverridesUtility.cs b/Editor/Mono/Prefabs/PrefabOverrides/PrefabOverridesUtility.cs index f6ccf7c5cb..1adf18cc8c 100644 --- a/Editor/Mono/Prefabs/PrefabOverrides/PrefabOverridesUtility.cs +++ b/Editor/Mono/Prefabs/PrefabOverrides/PrefabOverridesUtility.cs @@ -105,6 +105,10 @@ static void CheckForAddedComponents(Transform transform, object userData) if (component == null) continue; + // Don't list DontSave objects as they won't get applied or reverted. + if ((component.hideFlags & HideFlags.DontSaveInEditor) != 0) + continue; + bool isAddedObject = PrefabUtility.GetCorrespondingObjectFromSource(component) == null; if (isAddedObject) { @@ -206,6 +210,10 @@ public static List GetAddedGameObjects(GameObject prefabInstanc static bool CheckForAddedGameObjectAndIfSoAddItAndReturnFalse(Transform transform, object userData) { + // Don't list DontSave objects or their children as they won't get applied or reverted. + if ((transform.gameObject.hideFlags & HideFlags.DontSaveInEditor) != 0) + return false; + var addedGameObjectUserData = (AddedGameObjectUserData)userData; if (IsAddedGameObject(addedGameObjectUserData.contextGameObject, transform.gameObject)) { diff --git a/Editor/Mono/PreferencesWindow/PreferencesSettingsProviders.cs b/Editor/Mono/PreferencesWindow/PreferencesSettingsProviders.cs index 5318819e18..2c91f3308b 100644 --- a/Editor/Mono/PreferencesWindow/PreferencesSettingsProviders.cs +++ b/Editor/Mono/PreferencesWindow/PreferencesSettingsProviders.cs @@ -551,7 +551,7 @@ private void ShowGeneral(string searchContext) // Refresh skin to get new font Unsupported.ClearSkinCache(); EditorResources.BuildCatalog(); - InternalEditorUtility.RequestScriptReload(); + EditorUtility.RequestScriptReload(); InternalEditorUtility.RepaintAllViews(); } } @@ -803,7 +803,7 @@ private void ShowLanguage(string searchContext) { SystemLanguage lang = (SystemLanguage)Enum.Parse(typeof(SystemLanguage), m_SelectedLanguage); EditorGUIUtility.NotifyLanguageChanged(lang); - InternalEditorUtility.RequestScriptReload(); + EditorUtility.RequestScriptReload(); } ApplyChangesToPrefs(); diff --git a/Editor/Mono/ProjectBrowser.cs b/Editor/Mono/ProjectBrowser.cs index c3439d35f9..c9dee2b801 100644 --- a/Editor/Mono/ProjectBrowser.cs +++ b/Editor/Mono/ProjectBrowser.cs @@ -2165,7 +2165,7 @@ void ToggleExpansionAnimationPreference() { bool oldValue = EditorPrefs.GetBool(TreeViewController.kExpansionAnimationPrefKey, false); EditorPrefs.SetBool(TreeViewController.kExpansionAnimationPrefKey, !oldValue); - InternalEditorUtility.RequestScriptReload(); + EditorUtility.RequestScriptReload(); } public virtual void AddItemsToMenu(GenericMenu menu) diff --git a/Editor/Mono/ProjectWindow/CachedFilteredHierachy.cs b/Editor/Mono/ProjectWindow/CachedFilteredHierachy.cs index 6519d69063..99ae81b1ec 100644 --- a/Editor/Mono/ProjectWindow/CachedFilteredHierachy.cs +++ b/Editor/Mono/ProjectWindow/CachedFilteredHierachy.cs @@ -33,7 +33,8 @@ public Texture2D icon // Note: Do not set m_Icon as GetCachedIcon uses its own cache that is cleared on reaching a max limit. // This is because when having e.g very large projects (1000s of textures with unique icons) we do not want all icons loaded // at the same time so don't keep a reference in m_Icon here - string path = AssetDatabase.GetAssetPath(instanceID); + string path = instanceID == 0 ? null : AssetDatabase.GetAssetPath(instanceID); + if (path != null) // Finding icon based on only file extension fails in several ways, and a different approach have to be found. // Using InternalEditorUtility.FindIconForFile first in revision f25945218bb6 / 29b23dbe4b5c introduced several regressions. @@ -44,6 +45,10 @@ public Texture2D icon // Support for specific file types based on file extensiom have to be supported inside AssetDatabase.GetCachedIcon // itself to work correctly and universally. for e.g. uxml files from within GetCachedIcon without relying on FindIconForFile. return AssetDatabase.GetCachedIcon(path) as Texture2D; + + path = string.IsNullOrEmpty(m_Guid) ? null : AssetDatabase.GUIDToAssetPath(m_Guid); + if (path != null) + return UnityEditorInternal.InternalEditorUtility.FindIconForFile(path); } else if (type == HierarchyType.GameObjects) { @@ -65,15 +70,21 @@ public Texture2D icon } private Texture2D m_Icon; + internal string m_Guid; + public string guid { get { if (type == HierarchyType.Assets) { - string path = AssetDatabase.GetAssetPath(instanceID); - if (path != null) - return AssetDatabase.AssetPathToGUID(path); + if (instanceID != 0 && string.IsNullOrEmpty(m_Guid)) + { + string path = AssetDatabase.GetAssetPath(instanceID); + if (path != null) + m_Guid = AssetDatabase.AssetPathToGUID(path); + } + return m_Guid; } return null; } @@ -162,7 +173,7 @@ void CopyPropertyData(ref FilterResult result, HierarchyProperty property) if (result == null) result = new FilterResult(); - result.instanceID = property.instanceID; + result.instanceID = property.GetInstanceIDIfImported(); result.name = property.name; result.hasChildren = property.hasChildren; result.colorCode = property.colorCode; @@ -180,6 +191,9 @@ void CopyPropertyData(ref FilterResult result, HierarchyProperty property) result.icon = EditorGUIUtility.FindTexture(EditorResources.emptyFolderIconName); else result.icon = null; + + if (m_HierarchyType == HierarchyType.Assets) + result.m_Guid = property.guid; } void SearchAllAssets(SearchFilter.SearchArea area) @@ -456,7 +470,13 @@ public void Reset() public int instanceID { - get { return m_Hierarchy.results[m_Position].instanceID; } + get + { + var id = m_Hierarchy.results[m_Position].instanceID; + if (id == 0) + m_Hierarchy.results[m_Position].instanceID = AssetDatabase.GetMainAssetInstanceID(guid); + return m_Hierarchy.results[m_Position].instanceID; + } } public Object pptrValue @@ -591,5 +611,10 @@ public int CountRemaining(int[] expanded) { return m_Hierarchy.results.Length - m_Position - 1; } + + public int GetInstanceIDIfImported() + { + return m_Hierarchy.results[m_Position].instanceID; + } } } diff --git a/Editor/Mono/SceneModeWindows/LightingWindow.cs b/Editor/Mono/SceneModeWindows/LightingWindow.cs index 5d2475efa6..32da822636 100644 --- a/Editor/Mono/SceneModeWindows/LightingWindow.cs +++ b/Editor/Mono/SceneModeWindows/LightingWindow.cs @@ -99,7 +99,7 @@ void OnSelectionChange() static internal void RepaintSceneAndGameViews() { SceneView.RepaintAll(); - PreviewEditorWindow.RepaintAll(); + PlayModeView.RepaintAll(); } void OnGUI() diff --git a/Editor/Mono/SceneModeWindows/NavigationWindow.cs b/Editor/Mono/SceneModeWindows/NavigationWindow.cs index 732f6c9bbd..fb4334dbfb 100644 --- a/Editor/Mono/SceneModeWindows/NavigationWindow.cs +++ b/Editor/Mono/SceneModeWindows/NavigationWindow.cs @@ -454,7 +454,7 @@ public void OnBecameInvisible() static void RepaintSceneAndGameViews() { SceneView.RepaintAll(); - PreviewEditorWindow.RepaintAll(); + PlayModeView.RepaintAll(); } public void OnSceneViewGUI(SceneView sceneView) diff --git a/Editor/Mono/SceneView/SceneView.cs b/Editor/Mono/SceneView/SceneView.cs index 96a09aabac..3e40811a97 100644 --- a/Editor/Mono/SceneView/SceneView.cs +++ b/Editor/Mono/SceneView/SceneView.cs @@ -916,10 +916,10 @@ internal override void SetSearchFilter(string searchFilter, SearchMode mode, boo internal void OnLostFocus() { // don't bleed our scene view rendering into game view - var previewWindow = PreviewEditorWindow.GetMainPreviewWindow(); - if (previewWindow && previewWindow.m_Parent != null && m_Parent != null && previewWindow.m_Parent == m_Parent) + var playModeView = PlayModeView.GetMainPlayModeView(); + if (playModeView && playModeView.m_Parent != null && m_Parent != null && playModeView.m_Parent == m_Parent) { - previewWindow.m_Parent.backgroundValid = false; + playModeView.m_Parent.backgroundValid = false; } if (s_LastActiveSceneView == this) @@ -3655,9 +3655,9 @@ static void ShowCompileErrorNotification() internal static void ShowSceneViewPlayModeSaveWarning() { // In this case, we want to explicitly try the GameView before passing it on to whatever notificationView we have - var preview = (PreviewEditorWindow)WindowLayout.FindEditorWindowOfType(typeof(PreviewEditorWindow)); - if (preview != null && preview.hasFocus) - preview.ShowNotification(EditorGUIUtility.TrTextContent("You must exit play mode to save the scene!")); + var playModeView = (PlayModeView)WindowLayout.FindEditorWindowOfType(typeof(PlayModeView)); + if (playModeView != null && playModeView.hasFocus) + playModeView.ShowNotification(EditorGUIUtility.TrTextContent("You must exit play mode to save the scene!")); else ShowNotification("You must exit play mode to save the scene!"); } diff --git a/Editor/Mono/Scripting/ScriptCompilation/CompilationPipeline.cs b/Editor/Mono/Scripting/ScriptCompilation/CompilationPipeline.cs index 440d3ed406..b3a69805f0 100644 --- a/Editor/Mono/Scripting/ScriptCompilation/CompilationPipeline.cs +++ b/Editor/Mono/Scripting/ScriptCompilation/CompilationPipeline.cs @@ -501,5 +501,15 @@ internal static string GetAssemblyDefinitionFilePathFromScriptPath(EditorCompila return null; } } + + public static void RequestScriptCompilation() + { + RequestScriptCompilation(EditorCompilationInterface.Instance); + } + + internal static void RequestScriptCompilation(EditorCompilation editorCompilation) + { + editorCompilation.DirtyAllScripts(); + } } } diff --git a/Editor/Mono/Scripting/ScriptCompilation/EditorCompilation.cs b/Editor/Mono/Scripting/ScriptCompilation/EditorCompilation.cs index 985432b05a..d02934ec5e 100644 --- a/Editor/Mono/Scripting/ScriptCompilation/EditorCompilation.cs +++ b/Editor/Mono/Scripting/ScriptCompilation/EditorCompilation.cs @@ -939,7 +939,7 @@ public Exception[] SetAllCustomScriptAssemblyJsonContents(string[] paths, string HashSet predefinedAssemblyNames = null; // To check if a path prefix is already being used we use a Dictionary where the key is the prefix and the value is the file path. - var prefixToFilePathLookup = customScriptAssemblyReferences.ToDictionary(x => x.PathPrefix, x => new List(){ x.FilePath }, StringComparer.OrdinalIgnoreCase); + var prefixToFilePathLookup = customScriptAssemblyReferences.ToDictionary(x => x.PathPrefix, x => new List() { x.FilePath }, StringComparer.OrdinalIgnoreCase); ClearCompilationSetupErrorFlags(CompilationSetupErrorFlags.loadError); @@ -2116,35 +2116,39 @@ private static void SetTargetAssemblyDefines(EditorBuildRules.TargetAssembly tar for (int i = 0; i < targetAssemblyVersionDefines.Count; i++) { - if (!assetPathVersionMetaDatas.ContainsKey(targetAssemblyVersionDefines[i].name)) + var targetAssemblyVersionDefine = targetAssemblyVersionDefines[i]; + if (!assetPathVersionMetaDatas.ContainsKey(targetAssemblyVersionDefine.name)) { continue; } - if (string.IsNullOrEmpty(targetAssemblyVersionDefines[i].expression)) + if (string.IsNullOrEmpty(targetAssemblyVersionDefine.expression)) { - var define = targetAssemblyVersionDefines[i].define; + var define = targetAssemblyVersionDefine.define; if (!string.IsNullOrEmpty(define)) { defines[populatedVersionDefinesCount] = define; - populatedVersionDefinesCount++; + ++populatedVersionDefinesCount; } continue; } - var versionDefineExpression = semVersionRangesFactory.GetExpression(targetAssemblyVersionDefines[i].expression); - var assetPathVersionMetaData = assetPathVersionMetaDatas[targetAssemblyVersionDefines[i].name]; - var semVersion = SemVersionParser.Parse(assetPathVersionMetaData); - - if (versionDefineExpression.IsValid(semVersion)) + try { - var define = targetAssemblyVersionDefines[i].define; - if (!string.IsNullOrEmpty(define)) + var versionDefineExpression = semVersionRangesFactory.GetExpression(targetAssemblyVersionDefine.expression); + var assetPathVersionMetaData = assetPathVersionMetaDatas[targetAssemblyVersionDefine.name]; + var semVersion = SemVersionParser.Parse(assetPathVersionMetaData); + if (versionDefineExpression.IsValid(semVersion)) { - defines[populatedVersionDefinesCount] = define; - populatedVersionDefinesCount++; + defines[populatedVersionDefinesCount] = targetAssemblyVersionDefine.define; + ++populatedVersionDefinesCount; } } + catch (Exception e) + { + var asset = AssetDatabase.LoadAssetAtPath(EditorCompilationInterface.Instance.FindCustomTargetAssemblyFromTargetAssembly(targetAssembly).FilePath); + UnityEngine.Debug.LogException(e, asset); + } } Array.Resize(ref defines, populatedVersionDefinesCount); diff --git a/Editor/Mono/Scripting/ScriptCompilation/SemVersionRangesFactory.cs b/Editor/Mono/Scripting/ScriptCompilation/SemVersionRangesFactory.cs index c0bf19defb..94ec2fc804 100644 --- a/Editor/Mono/Scripting/ScriptCompilation/SemVersionRangesFactory.cs +++ b/Editor/Mono/Scripting/ScriptCompilation/SemVersionRangesFactory.cs @@ -6,9 +6,9 @@ namespace UnityEditor.Scripting.ScriptCompilation { - internal class SemVersionRangesFactory + class SemVersionRangesFactory { - private SemVersionRanges m_SemVersionRanges; + SemVersionRanges m_SemVersionRanges; public SemVersionRangesFactory() { diff --git a/Editor/Mono/VisualStudioIntegration/UnityVSSupport.cs b/Editor/Mono/VisualStudioIntegration/UnityVSSupport.cs index 394ea49a36..7c08b48b3d 100644 --- a/Editor/Mono/VisualStudioIntegration/UnityVSSupport.cs +++ b/Editor/Mono/VisualStudioIntegration/UnityVSSupport.cs @@ -336,7 +336,7 @@ public static void ScriptEditorChanged(string editorPath) // We reload the domain because selecting a different editor requires loading a different UnityVS Initialize(editorPath); - InternalEditorUtility.RequestScriptReload(); + EditorUtility.RequestScriptReload(); } public static string GetAboutWindowLabel() diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000000..2303b574a4 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1 @@ +For terms of use, see https://unity3d.com/legal/licenses/Unity_Reference_Only_License diff --git a/Modules/AssetDatabase/Editor/ScriptBindings/AssetDatabase.bindings.cs b/Modules/AssetDatabase/Editor/ScriptBindings/AssetDatabase.bindings.cs index 6740f25314..1d87e64c84 100644 --- a/Modules/AssetDatabase/Editor/ScriptBindings/AssetDatabase.bindings.cs +++ b/Modules/AssetDatabase/Editor/ScriptBindings/AssetDatabase.bindings.cs @@ -385,8 +385,6 @@ internal extern static string assetFolderGUID extern internal static bool IsV1Enabled(); [FreeFunction("AssetDatabase::IsV2Enabled")] extern internal static bool IsV2Enabled(); - [FreeFunction("AssetDatabase::IsOnDemandModeEnabled")] - extern internal static bool IsOnDemandModeEnabled(); [FreeFunction("AssetDatabase::CloseCachedFiles")] extern internal static void CloseCachedFiles(); diff --git a/Modules/AssetDatabase/Editor/ScriptBindings/AssetDatabaseExperimental.bindings.cs b/Modules/AssetDatabase/Editor/ScriptBindings/AssetDatabaseExperimental.bindings.cs index b7a42b491d..fcad2f6947 100644 --- a/Modules/AssetDatabase/Editor/ScriptBindings/AssetDatabaseExperimental.bindings.cs +++ b/Modules/AssetDatabase/Editor/ScriptBindings/AssetDatabaseExperimental.bindings.cs @@ -3,6 +3,8 @@ // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; +using System.Runtime.InteropServices; + using System.Collections.Generic; using System.Linq; using UnityEngine; @@ -12,11 +14,42 @@ namespace UnityEditor.Experimental { + public enum OnDemandState + { + Unavailable = 0, + Processing = 1, + Downloading = 2, + Available = 3 + } + + [NativeHeader("Modules/AssetDatabase/Editor/Public/AssetDatabaseTypes.h")] + [RequiredByNativeCode] + [StructLayout(LayoutKind.Sequential)] + public struct OnDemandProgress + { + public OnDemandState state; + public float progress; + } + [NativeHeader("Modules/AssetDatabase/Editor/V2/AssetDatabaseCounters.h")] [NativeHeader("Modules/AssetDatabase/Editor/V2/V1Compatibility.h")] [NativeHeader("Modules/AssetDatabase/Editor/Public/AssetDatabaseExperimental.h")] public sealed partial class AssetDatabaseExperimental { + public enum OnDemandMode + { + Off = 0, + Lazy = 1, + Background = 2 + } + + public enum ImportSyncMode + { + Block = 0, + Queue = 1, + Poll = 2 + } + public struct AssetDatabaseCounters { public struct Counter @@ -85,16 +118,22 @@ public void ResetDeltas() [FreeFunction("ImportCountersResetDeltas")] private extern static void ImportCountersResetDeltas(); - private extern static Hash128 GetArtifactHash_Internal_Guid_SelectImporter(string guid); - private extern static Hash128 GetArtifactHash_Internal_Guid(string guid, Type importerType); + public extern static OnDemandMode ActiveOnDemandMode + { + [FreeFunction("GetOnDemandModeV2")] get; + [FreeFunction("SetOnDemandModeV2")] set; + } + private extern static Hash128 GetArtifactHash_Internal_Guid_SelectImporter(string guid, ImportSyncMode mode); + private extern static Hash128 GetArtifactHash_Internal_Guid(string guid, Type importerType, ImportSyncMode mode); + private extern static Hash128[] GetArtifactHashes_Internal_Guids_SelectImporter(string[] guid, ImportSyncMode mode); - [uei.ExcludeFromDocs] public static Hash128 GetArtifactHash(string guid) { return GetArtifactHash(guid, null); } - public static Hash128 GetArtifactHash(string guid, [uei.DefaultValue("null")] Type importerType) + [uei.ExcludeFromDocs] public static Hash128 GetArtifactHash(string guid, ImportSyncMode mode = ImportSyncMode.Block) { return GetArtifactHash(guid, null, mode); } + public static Hash128 GetArtifactHash(string guid, [uei.DefaultValue("null")] Type importerType, ImportSyncMode mode = ImportSyncMode.Block) { if (importerType == null) - return GetArtifactHash_Internal_Guid_SelectImporter(guid); + return GetArtifactHash_Internal_Guid_SelectImporter(guid, mode); else - return GetArtifactHash_Internal_Guid(guid, importerType); + return GetArtifactHash_Internal_Guid(guid, importerType, mode); } public static bool GetArtifactPaths(Hash128 hash, out string[] paths) @@ -105,8 +144,26 @@ public static bool GetArtifactPaths(Hash128 hash, out string[] paths) return success; } + public static Hash128[] GetArtifactHashes(string[] guids, ImportSyncMode mode = ImportSyncMode.Block) + { + return GetArtifactHashes_Internal_Guids_SelectImporter(guids, mode); + } + extern private static string[] GetArtifactPathsImpl(Hash128 hash, out bool success); + private extern static OnDemandProgress GetOnDemandArtifactProgress_Internal_SelectImporter(string guid); + private extern static OnDemandProgress GetOnDemandArtifactProgress_Internal(string guid, Type importerType); + + public static OnDemandProgress GetOnDemandArtifactProgress(string guid) + { + return GetOnDemandArtifactProgress_Internal_SelectImporter(guid); + } + + public static OnDemandProgress GetOnDemandArtifactProgress(string guid, Type importerType) + { + return GetOnDemandArtifactProgress_Internal(guid, importerType); + } + [RequiredByNativeCode] static string[] OnSourceAssetsModified(string[] changedAssets, string[] addedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) { diff --git a/Modules/AssetPipelineEditor/AssetPostprocessors/ModelImporterPostProcessor.cs b/Modules/AssetPipelineEditor/AssetPostprocessors/ModelImporterPostProcessor.cs index 9f65f30847..6e3c1bfbd3 100644 --- a/Modules/AssetPipelineEditor/AssetPostprocessors/ModelImporterPostProcessor.cs +++ b/Modules/AssetPipelineEditor/AssetPostprocessors/ModelImporterPostProcessor.cs @@ -17,7 +17,7 @@ internal class ModelImporterPostProcessor : AssetPostprocessor static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromPath) { - if (AssetDatabase.IsOnDemandModeEnabled()) + if (UnityEditor.Experimental.AssetDatabaseExperimental.ActiveOnDemandMode != UnityEditor.Experimental.AssetDatabaseExperimental.OnDemandMode.Off) { // This PostProcessAllAssets will forcefully import everything in on-demand mode // We need to find a better way of filtering on asset type without forcing an import diff --git a/Modules/ParticleSystemEditor/ParticleEffectUI.cs b/Modules/ParticleSystemEditor/ParticleEffectUI.cs index afbf72e559..bfe5dceb1f 100644 --- a/Modules/ParticleSystemEditor/ParticleEffectUI.cs +++ b/Modules/ParticleSystemEditor/ParticleEffectUI.cs @@ -367,7 +367,7 @@ public void Clear() SetShowOnlySelectedMode(false); - PreviewEditorWindow.RepaintAll(); + PlayModeView.RepaintAll(); SceneView.RepaintAll(); } diff --git a/Modules/ParticleSystemEditor/ParticleSystemWindow.cs b/Modules/ParticleSystemEditor/ParticleSystemWindow.cs index 1a1f894890..24dcefe3d2 100644 --- a/Modules/ParticleSystemEditor/ParticleSystemWindow.cs +++ b/Modules/ParticleSystemEditor/ParticleSystemWindow.cs @@ -171,7 +171,7 @@ void InitEffectUI() Clear(); Repaint(); SceneView.RepaintAll(); - PreviewEditorWindow.RepaintAll(); + PlayModeView.RepaintAll(); } } diff --git a/Modules/ProfilerEditor/ProfilerWindow/ProfilerWindow.cs b/Modules/ProfilerEditor/ProfilerWindow/ProfilerWindow.cs index 4eccb101ad..e7c41164c3 100644 --- a/Modules/ProfilerEditor/ProfilerWindow/ProfilerWindow.cs +++ b/Modules/ProfilerEditor/ProfilerWindow/ProfilerWindow.cs @@ -209,7 +209,7 @@ public ProfilerProperty CreateProperty() public ProfilerProperty CreateProperty(int sortType) { int targetedFrame = GetActiveVisibleFrameIndex(); - if (targetedFrame < ProfilerDriver.lastFrameIndex - ProfilerDriver.maxHistoryLength) + if (targetedFrame < ProfilerDriver.lastFrameIndex - ProfilerUserSettings.frameCount) { return null; } @@ -339,7 +339,7 @@ void OnToggleCPUChartSeries(bool wasToggled) { if (wasToggled) { - int historyLength = ProfilerDriver.maxHistoryLength - 1; + int historyLength = ProfilerUserSettings.frameCount; int firstEmptyFrame = ProfilerDriver.lastFrameIndex - historyLength; int firstFrame = Mathf.Max(ProfilerDriver.firstFrameIndex, firstEmptyFrame); @@ -623,7 +623,7 @@ private static void UpdateChartGrid(float timeMax, ChartViewData data) private void UpdateCharts() { - int historyLength = ProfilerDriver.maxHistoryLength - 1; + int historyLength = ProfilerUserSettings.frameCount; int firstEmptyFrame = ProfilerDriver.lastFrameIndex - historyLength; int firstFrame = Mathf.Max(ProfilerDriver.firstFrameIndex, firstEmptyFrame); @@ -764,7 +764,7 @@ internal static void UpdateSingleChart(ProfilerChart chart, int firstEmptyFrame, ProfilerDriver.GetStatisticsAvailable(chart.m_Area, firstEmptyFrame, chart.m_Data.dataAvailable); if (chart is UISystemProfilerChart) - ((UISystemProfilerChart)chart).Update(firstFrame, ProfilerDriver.maxHistoryLength - 1); + ((UISystemProfilerChart)chart).Update(firstFrame, ProfilerUserSettings.frameCount); } void AddAreaClick(object userData, string[] options, int selected) @@ -1187,7 +1187,7 @@ internal static bool SetEditorDeepProfiling(bool deep) if (doApply) { ProfilerDriver.deepProfiling = deep; - InternalEditorUtility.RequestScriptReload(); + EditorUtility.RequestScriptReload(); } return doApply; diff --git a/Modules/ProfilerEditor/Public/ProfilerAPI.bindings.cs b/Modules/ProfilerEditor/Public/ProfilerAPI.bindings.cs index 9aa3ab8863..e40919c13a 100644 --- a/Modules/ProfilerEditor/Public/ProfilerAPI.bindings.cs +++ b/Modules/ProfilerEditor/Public/ProfilerAPI.bindings.cs @@ -116,13 +116,7 @@ public static partial class ProfilerDriver static public extern int lastFrameIndex {[NativeMethod("GetLastFrameIndex")] get; } [StaticAccessor("profiling::GetProfilerSessionPtr()->GetProfilerHistory()", StaticAccessorType.Arrow)] - static public extern int maxHistoryLength - { - [NativeMethod("GetMaxFrameHistoryLength")] - get; - [NativeMethod("SetMaxFrameHistoryLength")] - set; - } + static internal extern void SetMaxFrameHistoryLength(int frameCount); [StaticAccessor("profiling::GetProfilerSessionPtr()->GetProfilerHistory()", StaticAccessorType.Arrow)] static public extern string selectedPropertyPath diff --git a/Modules/ProfilerEditor/Public/ProfilerSettings.cs b/Modules/ProfilerEditor/Public/ProfilerSettings.cs index 8390286378..66d3d3cbfb 100644 --- a/Modules/ProfilerEditor/Public/ProfilerSettings.cs +++ b/Modules/ProfilerEditor/Public/ProfilerSettings.cs @@ -28,7 +28,10 @@ public static int frameCount get { if (m_FrameCount == 0) + { m_FrameCount = EditorPrefs.GetInt(k_FrameCountSettingKey, kMinFrameCount); + ProfilerDriver.SetMaxFrameHistoryLength(m_FrameCount); + } return m_FrameCount; } @@ -38,7 +41,7 @@ public static int frameCount { m_FrameCount = value; EditorPrefs.SetInt(k_FrameCountSettingKey, value); - ProfilerDriver.maxHistoryLength = value; + ProfilerDriver.SetMaxFrameHistoryLength(value); if (settingsChanged != null) settingsChanged.Invoke(); diff --git a/Modules/TextCore/Managed/TextGenerator.cs b/Modules/TextCore/Managed/TextGenerator.cs index 568aa34c2d..e963abbca0 100644 --- a/Modules/TextCore/Managed/TextGenerator.cs +++ b/Modules/TextCore/Managed/TextGenerator.cs @@ -497,8 +497,8 @@ void Prepare(TextGenerationSettings generationSettings, TextInfo textInfo) { Profiler.BeginSample("TextGenerator.Prepare"); // TODO: Find a way for GetPaddingForMaterial to not allocate - // TODO: Needs review of these values. - m_Padding = generationSettings.extraPadding ? 5.5f : 1.5f; + // TODO: Hard coded padding value is temporary change to avoid clipping of text geometry with small point size. + m_Padding = 6.0f; // generationSettings.extraPadding ? 5.5f : 1.5f; m_IsMaskingEnabled = false; diff --git a/Modules/UMPE/Editor/DataService.cs b/Modules/UMPE/Editor/DataService.cs index bcc4fd27f8..d4b6a54fbb 100644 --- a/Modules/UMPE/Editor/DataService.cs +++ b/Modules/UMPE/Editor/DataService.cs @@ -65,7 +65,7 @@ private static void InitializeSlave() Debug.Log($"Slave need to refresh the following assets: {String.Join(", ", paths)}"); AssetDatabase.Refresh(); if (paths.Any(p => p.EndsWith(".cs"))) - InternalEditorUtility.RequestScriptReload(); + EditorUtility.RequestScriptReload(); InternalEditorUtility.RepaintAllViews(); return paths; }); diff --git a/Modules/VREditor/Mono/Oculus/VRCustomOptionsOculus.cs b/Modules/VREditor/Mono/Oculus/VRCustomOptionsOculus.cs index 3ca57aae9d..e2e4609dc8 100644 --- a/Modules/VREditor/Mono/Oculus/VRCustomOptionsOculus.cs +++ b/Modules/VREditor/Mono/Oculus/VRCustomOptionsOculus.cs @@ -14,10 +14,14 @@ internal class VRCustomOptionsOculus : VRCustomOptions static GUIContent s_SharedDepthBufferLabel = EditorGUIUtility.TextContent("Shared Depth Buffer|Enable depth buffer submission to allow for overlay depth occlusion, etc."); static GUIContent s_DashSupportLabel = EditorGUIUtility.TextContent("Dash Support|If enabled, pressing the home button brings up Dash, otherwise it brings up the older universal menu."); static GUIContent s_LowOverheadModeLabel = EditorGUIUtility.TextContent("Low Overhead Mode|If enabled, the GLES graphics driver will bypass validation code, potentially running faster."); + static GUIContent s_ProtectedContextLabel = EditorGUIUtility.TextContent("Protected Context|If enabled, the Oculus SDK will create a protected graphics context. Has a slight overhead; only use if necessary."); + static GUIContent s_V2SigningLabel = EditorGUIUtility.TextContent("V2 Signing (Quest)|Enable APK v2 signing if building for Oculus Quest. Disable v2 signing if building for Gear VR or Oculus Go."); SerializedProperty m_SharedDepthBuffer; SerializedProperty m_DashSupport; SerializedProperty m_LowOverheadMode; + SerializedProperty m_ProtectedContext; + SerializedProperty m_V2Signing; public override void Initialize(SerializedObject settings) { @@ -30,6 +34,8 @@ public override void Initialize(SerializedObject settings, string propertyName) m_SharedDepthBuffer = FindPropertyAssert("sharedDepthBuffer"); m_DashSupport = FindPropertyAssert("dashSupport"); m_LowOverheadMode = FindPropertyAssert("lowOverheadMode"); + m_ProtectedContext = FindPropertyAssert("protectedContext"); + m_V2Signing = FindPropertyAssert("v2Signing"); } public override Rect Draw(BuildTargetGroup target, Rect rect) @@ -70,6 +76,28 @@ public override Rect Draw(BuildTargetGroup target, Rect rect) m_LowOverheadMode.boolValue = boolValue; } EditorGUI.EndProperty(); + rect.y += rect.height + EditorGUIUtility.standardVerticalSpacing; + + rect.height = EditorGUIUtility.singleLineHeight; + label = EditorGUI.BeginProperty(rect, s_ProtectedContextLabel, m_ProtectedContext); + EditorGUI.BeginChangeCheck(); + boolValue = EditorGUI.Toggle(rect, label, m_ProtectedContext.boolValue); + if (EditorGUI.EndChangeCheck()) + { + m_ProtectedContext.boolValue = boolValue; + } + EditorGUI.EndProperty(); + rect.y += rect.height + EditorGUIUtility.standardVerticalSpacing; + + rect.height = EditorGUIUtility.singleLineHeight; + label = EditorGUI.BeginProperty(rect, s_V2SigningLabel, m_V2Signing); + EditorGUI.BeginChangeCheck(); + boolValue = EditorGUI.Toggle(rect, label, m_V2Signing.boolValue); + if (EditorGUI.EndChangeCheck()) + { + m_V2Signing.boolValue = boolValue; + } + EditorGUI.EndProperty(); } rect.y += rect.height + EditorGUIUtility.standardVerticalSpacing; @@ -85,7 +113,7 @@ public override float GetHeight(BuildTargetGroup target) } else { - return (EditorGUIUtility.singleLineHeight * 1.0f) + (EditorGUIUtility.standardVerticalSpacing * 2.0f); + return (EditorGUIUtility.singleLineHeight * 3.0f) + (EditorGUIUtility.standardVerticalSpacing * 4.0f); } } } diff --git a/Modules/VREditor/ScriptBindings/VREditor.bindings.cs b/Modules/VREditor/ScriptBindings/VREditor.bindings.cs index b9f915178d..95cbde8c3d 100644 --- a/Modules/VREditor/ScriptBindings/VREditor.bindings.cs +++ b/Modules/VREditor/ScriptBindings/VREditor.bindings.cs @@ -75,6 +75,22 @@ public static extern bool lowOverheadMode [NativeMethod("SetOculusLowOverheadModeEnabled")] set; } + + public static extern bool protectedContext + { + [NativeMethod("GetOculusProtectedContextEnabled")] + get; + [NativeMethod("SetOculusProtectedContextEnabled")] + set; + } + + public static extern bool v2Signing + { + [NativeMethod("GetOculusV2SigningEnabled")] + get; + [NativeMethod("SetOculusV2SigningEnabled")] + set; + } } [NativeHeader("Runtime/Misc/PlayerSettings.h")] @@ -114,6 +130,18 @@ public static bool lowOverheadMode get { return UnityEditorInternal.PlayerSettingsOculus.lowOverheadMode; } set { UnityEditorInternal.PlayerSettingsOculus.lowOverheadMode = value; } } + + public static bool protectedContext + { + get { return UnityEditorInternal.PlayerSettingsOculus.protectedContext; } + set { UnityEditorInternal.PlayerSettingsOculus.protectedContext = value; } + } + + public static bool v2Signing + { + get { return UnityEditorInternal.PlayerSettingsOculus.v2Signing; } + set { UnityEditorInternal.PlayerSettingsOculus.v2Signing = value; } + } } } diff --git a/Projects/CSharp/UnityEditor.csproj b/Projects/CSharp/UnityEditor.csproj index aa3683236c..3e458e3bd6 100644 --- a/Projects/CSharp/UnityEditor.csproj +++ b/Projects/CSharp/UnityEditor.csproj @@ -2683,6 +2683,9 @@ Editor\Mono\PlatformSupport\ReorderableTextureList.cs + + Editor\Mono\PlayModeView\PlayModeView.cs + Editor\Mono\Playables\PlayableOutputEditorExtensions.cs @@ -2770,9 +2773,6 @@ Editor\Mono\PresetLibraries\ScriptableObjectSaveLoadHelper.cs - - Editor\Mono\Preview\PreviewEditorWindow.cs - Editor\Mono\ProjectBrowser\SavedSearchFilter.cs diff --git a/README.md b/README.md index b9e537408e..0fda493fee 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## Unity 2019.3.0a12 C# reference source code +## Unity 2019.3.0b1 C# reference source code The C# part of the Unity engine and editor source code. May be used for reference purposes only. diff --git a/Runtime/2D/Common/ScriptBindings/SpriteDataAccess.bindings.cs b/Runtime/2D/Common/ScriptBindings/SpriteDataAccess.bindings.cs index 02c2e4c3a1..53dcb51cf5 100644 --- a/Runtime/2D/Common/ScriptBindings/SpriteDataAccess.bindings.cs +++ b/Runtime/2D/Common/ScriptBindings/SpriteDataAccess.bindings.cs @@ -173,6 +173,8 @@ public static void SetBones(this Sprite sprite, SpriteBone[] src) extern private static SpriteBone[] GetBoneInfo([NotNull] Sprite sprite); extern private static void SetBoneData([NotNull] Sprite sprite, SpriteBone[] src); + extern internal static int GetPrimaryVertexStreamSize(Sprite sprite); + extern internal static AtomicSafetyHandle GetSafetyHandle([NotNull] this Sprite sprite); } @@ -180,10 +182,21 @@ public static void SetBones(this Sprite sprite, SpriteBone[] src) [NativeHeader("Runtime/Graphics/Mesh/SpriteRenderer.h")] public static class SpriteRendererDataAccessExtensions { + internal unsafe static void SetDeformableBuffer(this SpriteRenderer spriteRenderer, NativeArray src) + { + if (spriteRenderer.sprite == null) + throw new ArgumentException(String.Format("spriteRenderer does not have a valid sprite set.")); + + if (src.Length != SpriteDataAccessExtensions.GetPrimaryVertexStreamSize(spriteRenderer.sprite)) + throw new InvalidOperationException(String.Format("custom sprite vertex data size must match sprite asset's vertex data size {0} {1}", src.Length, SpriteDataAccessExtensions.GetPrimaryVertexStreamSize(spriteRenderer.sprite))); + + SetDeformableBuffer(spriteRenderer, src.GetUnsafeReadOnlyPtr(), src.Length); + } + internal unsafe static void SetDeformableBuffer(this SpriteRenderer spriteRenderer, NativeArray src) { if (spriteRenderer.sprite == null) - throw new InvalidOperationException(String.Format("spriteRenderer does not have a valid sprite set.")); + throw new ArgumentException(String.Format("spriteRenderer does not have a valid sprite set.")); if (src.Length != spriteRenderer.sprite.GetVertexCount()) throw new InvalidOperationException(String.Format("The src length {0} must match the vertex count of source Sprite {1}.", src.Length, spriteRenderer.sprite.GetVertexCount())); diff --git a/Runtime/2D/Common/ScriptBindings/Sprites.bindings.cs b/Runtime/2D/Common/ScriptBindings/Sprites.bindings.cs index 6a2d31d97f..4cfca0e0ee 100644 --- a/Runtime/2D/Common/ScriptBindings/Sprites.bindings.cs +++ b/Runtime/2D/Common/ScriptBindings/Sprites.bindings.cs @@ -52,6 +52,7 @@ public enum SpriteSortPoint } [StructLayout(LayoutKind.Sequential)] + [Serializable] public struct SecondarySpriteTexture { public string name; diff --git a/Runtime/Profiler/ScriptBindings/ProfilerMarker.bindings.cs b/Runtime/Profiler/ScriptBindings/ProfilerMarker.bindings.cs index b6d578170d..4330155d63 100644 --- a/Runtime/Profiler/ScriptBindings/ProfilerMarker.bindings.cs +++ b/Runtime/Profiler/ScriptBindings/ProfilerMarker.bindings.cs @@ -10,12 +10,13 @@ using Unity.Profiling.LowLevel; using UnityEngine.Bindings; using UnityEngine.Scripting; +using Object = UnityEngine.Object; namespace Unity.Profiling { - [NativeHeader("Runtime/Profiler/ScriptBindings/ProfilerMarker.bindings.h")] [UsedByNativeCode] [StructLayout(LayoutKind.Sequential)] + [NativeHeader("Runtime/Profiler/ScriptBindings/ProfilerMarker.bindings.h")] public struct ProfilerMarker { [NativeDisableUnsafePtrRestriction] @@ -25,7 +26,7 @@ public struct ProfilerMarker [MethodImpl(256)] public ProfilerMarker(string name) { - m_Ptr = Internal_Create(name, MarkerFlags.Default); + m_Ptr = Internal_Create(name, (ushort)MarkerFlags.Default); } [MethodImpl(256)] @@ -37,7 +38,7 @@ public void Begin() [MethodImpl(256)] [Conditional("ENABLE_PROFILER")] - public void Begin(UnityEngine.Object contextUnityObject) + public void Begin(Object contextUnityObject) { Internal_BeginWithObject(m_Ptr, contextUnityObject); } @@ -82,20 +83,24 @@ public AutoScope Auto() } [ThreadSafe] - [NativeConditional("ENABLE_PROFILER", "NULL")] - static extern IntPtr Internal_Create(string name, MarkerFlags flags); + [NativeConditional("ENABLE_PROFILER")] + internal static extern IntPtr Internal_Create(string name, ushort flags); + + [ThreadSafe] + [NativeConditional("ENABLE_PROFILER")] + internal static extern void Internal_Begin(IntPtr markerPtr); [ThreadSafe] [NativeConditional("ENABLE_PROFILER")] - static extern void Internal_Begin(IntPtr markerPtr); + internal static extern void Internal_BeginWithObject(IntPtr markerPtr, UnityEngine.Object contextUnityObject); [ThreadSafe] [NativeConditional("ENABLE_PROFILER")] - static extern void Internal_BeginWithObject(IntPtr markerPtr, UnityEngine.Object contextUnityObject); + internal static extern void Internal_End(IntPtr markerPtr); [ThreadSafe] [NativeConditional("ENABLE_PROFILER")] - static extern void Internal_End(IntPtr markerPtr); + internal static extern unsafe void Internal_Emit(IntPtr markerPtr, ushort eventType, int metadataCount, void* metadata); [ThreadSafe] [NativeConditional("ENABLE_PROFILER")] diff --git a/Runtime/Profiler/ScriptBindings/ProfilerMarkerUtils.cs b/Runtime/Profiler/ScriptBindings/ProfilerMarkerUtils.cs index 60585858da..b8cf65af67 100644 --- a/Runtime/Profiler/ScriptBindings/ProfilerMarkerUtils.cs +++ b/Runtime/Profiler/ScriptBindings/ProfilerMarkerUtils.cs @@ -3,11 +3,7 @@ // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; -using System.Diagnostics; using System.Runtime.InteropServices; -using Unity.Collections.LowLevel.Unsafe; -using UnityEngine.Bindings; -using UnityEngine.Scripting; namespace Unity.Profiling.LowLevel { @@ -24,4 +20,36 @@ public enum MarkerFlags Warning = 1 << 4, } + + internal enum MarkerEventType : ushort + { + Begin = 0, + End = 1, + }; + + internal enum MarkerEventDataType : byte + { + None = 0, + InstanceId = 1, + Int32 = 2, + UInt32 = 3, + Int64 = 4, + UInt64 = 5, + Float = 6, + Double = 7, + String = 8, + String16 = 9, + Vec3 = 10, + Blob8 = 11 + }; + + [StructLayout(LayoutKind.Explicit, Size = 16)] + internal unsafe struct MarkerEventData // Metadata parameter. Must be in sync with UnityProfilerMarkerData + { + [FieldOffset(0)] public byte type; + [FieldOffset(1)] public byte reserved0; + [FieldOffset(2)] public ushort reserved1; + [FieldOffset(4)] public uint size; + [FieldOffset(8)] public void* ptr; + }; } diff --git a/artifacts/generated/OldBindingsForCsProjects/Editor/InternalUtilityBindings.gen.cs b/artifacts/generated/OldBindingsForCsProjects/Editor/InternalUtilityBindings.gen.cs index 3e13ebe30b..a06f35726f 100644 --- a/artifacts/generated/OldBindingsForCsProjects/Editor/InternalUtilityBindings.gen.cs +++ b/artifacts/generated/OldBindingsForCsProjects/Editor/InternalUtilityBindings.gen.cs @@ -81,6 +81,7 @@ interface IHierarchyProperty bool Skip(int count, int[] expanded); int CountRemaining(int[] expanded); + int GetInstanceIDIfImported(); } @@ -312,6 +313,10 @@ public extern GUID[] dynamicDependencies [System.Runtime.CompilerServices.MethodImplAttribute((System.Runtime.CompilerServices.MethodImplOptions)0x1000)] extern public int CountRemaining (int[] expanded) ; + [UnityEngine.Scripting.GeneratedByOldBindingsGeneratorAttribute] // Temporarily necessary for bindings migration + [System.Runtime.CompilerServices.MethodImplAttribute((System.Runtime.CompilerServices.MethodImplOptions)0x1000)] + extern public int GetInstanceIDIfImported () ; + public extern Texture2D icon { [UnityEngine.Scripting.GeneratedByOldBindingsGeneratorAttribute] // Temporarily necessary for bindings migration From b6738cbada6a2f5590303ad9f60631b0e67a59bb Mon Sep 17 00:00:00 2001 From: Unity Technologies Date: Fri, 6 Sep 2019 17:16:21 +0000 Subject: [PATCH 02/32] Unity 2019.3.0b2 C# reference source code --- .../Deprecated/EditorGUIExt.cs | 3 +- Editor/Mono/Animation/ZoomableArea.cs | 6 +- Editor/Mono/AssetDatabase/AssetDatabase.cs | 3 +- Editor/Mono/AssetModificationProcessor.cs | 22 +- .../AssetImportContext.bindings.cs | 2 + Editor/Mono/CodeEditor/CodeEditor.cs | 7 +- .../CodeEditor/DefaultExternalCodeEditor.cs | 8 + .../CodeEditor/ExternalEditor.bindings.cs | 15 + Editor/Mono/ContainerWindow.cs | 4 +- Editor/Mono/EditorGUI.cs | 49 +- Editor/Mono/EditorGUIUtility.cs | 1 + Editor/Mono/EditorMode/ModeService.cs | 82 ++-- Editor/Mono/EditorResources.bindings.cs | 1 + Editor/Mono/EditorResources.cs | 62 +-- Editor/Mono/GUI/AppStatusBar.cs | 2 +- Editor/Mono/GUI/DockArea.cs | 4 +- Editor/Mono/GUI/EditorStyles.cs | 2 +- Editor/Mono/GUI/FoldoutHeader.cs | 18 +- Editor/Mono/GUI/MainView.cs | 28 +- Editor/Mono/GUI/Toolbar.cs | 9 +- .../GUI/TreeView/AssetsTreeViewDataSource.cs | 26 +- Editor/Mono/GUI/TreeView/AssetsTreeViewGUI.cs | 20 + Editor/Mono/GUI/WindowLayout.cs | 7 +- .../AssemblyDefinitionImporterInspector.cs | 47 +- Editor/Mono/Inspector/EditorElement.cs | 8 +- Editor/Mono/Inspector/GameObjectInspector.cs | 5 + Editor/Mono/Inspector/InspectorWindow.cs | 1 + .../PlayerSettingsEditor.cs | 21 +- Editor/Mono/Inspector/PropertyDrawerCache.cs | 2 +- .../PlayerConnection/AttachToPlayerGUI.cs | 8 +- Editor/Mono/ObjectListArea.cs | 3 +- Editor/Mono/PlayerSettings.bindings.cs | 12 +- .../PreferencesSettingsProviders.cs | 3 +- Editor/Mono/ProjectBrowserColumnOne.cs | 33 +- .../ProjectWindow/CachedFilteredHierachy.cs | 2 - .../Mono/ProjectWindow/ProjectWindowUtil.cs | 4 +- .../ScriptAttributeGUI/PropertyHandler.cs | 8 +- .../ScriptAttributeUtility.cs | 117 ++++- .../CSharpNamespaceParser.cs | 12 +- .../DefineConstraintsHelper.cs | 61 ++- .../Scripting/ScriptCompilation/SemVersion.cs | 8 +- Editor/Mono/SerializedProperty.bindings.cs | 111 +++++ Editor/Mono/Settings/SettingsService.cs | 7 +- Editor/Mono/SyncProject.cs | 14 +- .../UIElements/Controls/BindingExtensions.cs | 14 +- .../UIElements/Controls/ListViewBindings.cs | 4 +- Editor/Mono/UIElements/Controls/PopupField.cs | 4 + .../Mono/VersionControl/Common/VCProvider.cs | 42 +- .../Mono/VersionControl/UI/VCMenuPending.cs | 2 +- .../Mono/VersionControl/UI/VCMenuProject.cs | 2 +- .../lib/mono/4.5/Facades/netstandard.dll | Bin 84992 -> 84992 bytes .../ScriptBindings/AssetDatabase.bindings.cs | 34 +- Modules/GraphViewEditor/Views/GraphView.cs | 4 +- Modules/IMGUI/GUIStyle.cs | 2 +- Modules/IMGUI/GUIUtility.cs | 8 + .../Services/AssetStore/AssetStoreClient.cs | 463 ++++++------------ .../AssetStore/AssetStoreFetchedInfo.cs | 220 +++++++++ .../AssetStore/AssetStoreLocalInfo.cs | 46 ++ .../Services/AssetStore/AssetStorePackage.cs | 137 ++++-- .../AssetStore/AssetStorePackageVersion.cs | 377 +++----------- .../Services/AssetStore/AssetStoreRestAPI.cs | 36 +- .../AssetStore/AssetStoreVersionList.cs | 48 ++ .../Services/Common/PackageExtensions.cs | 35 ++ .../Services/Common/PlaceholderPackage.cs | 49 +- .../Common/PlaceholderPackageVersion.cs | 17 +- .../Services/Common/PlaceholderVersionList.cs | 36 ++ .../Services/Interfaces/IAssetStoreClient.cs | 5 +- .../Services/Interfaces/IAssetStoreRestAPI.cs | 2 +- .../Editor/Services/Interfaces/IPackage.cs | 10 +- .../Services/Interfaces/IPackageVersion.cs | 15 +- .../Editor/Services/Interfaces/IUpmClient.cs | 10 +- .../Services/Interfaces/IVersionList.cs | 30 ++ .../Services/Packages/PackageDatabase.cs | 24 +- .../Services/Packages/PackageFiltering.cs | 57 +-- .../{PackageOrigin.cs => PackageProgress.cs} | 11 +- .../Editor/Services/Packages/PackageState.cs | 3 + .../Editor/Services/Packages/PackageTag.cs | 27 +- .../Editor/Services/Packages/PackageType.cs | 18 + .../Editor/Services/Upm/UpmAddOperation.cs | 3 +- .../Editor/Services/Upm/UpmBaseOperation.cs | 3 +- .../Editor/Services/Upm/UpmClient.cs | 186 +++++-- .../Editor/Services/Upm/UpmEmbedOperation.cs | 3 +- .../Editor/Services/Upm/UpmPackage.cs | 183 ++----- .../Editor/Services/Upm/UpmPackageDocs.cs | 23 +- .../Editor/Services/Upm/UpmPackageVersion.cs | 86 ++-- .../Editor/Services/Upm/UpmRemoveOperation.cs | 3 +- .../Editor/Services/Upm/UpmSearchOperation.cs | 6 +- .../Editor/Services/Upm/UpmVersionList.cs | 152 ++++++ .../Editor/UI/Common/DictionaryExtensions.cs | 51 ++ .../PackageManagerUI/Editor/UI/Common/Page.cs | 28 +- .../Editor/UI/Common/PageManager.cs | 2 +- .../Editor/UI/PackageDependencies.cs | 5 +- .../Editor/UI/PackageDetails.cs | 229 ++++----- .../PackageManagerUI/Editor/UI/PackageItem.cs | 37 +- .../PackageManagerUI/Editor/UI/PackageList.cs | 17 +- .../Editor/UI/PackageManagerWindow.cs | 10 +- .../ProfilerRoleProvider.cs | 273 +++++------ .../ProfilerWindow/ProfilerWindow.cs | 39 +- .../ProfilerEditor/Public/ProfilerSettings.cs | 28 ++ .../Public/ProfilerSettingsProvider.cs | 25 + .../UIElements/UIElementsUtility.bindings.cs | 15 +- .../ScriptBindings/SpatialMapping.bindings.cs | 71 ++- Projects/CSharp/UnityEditor.csproj | 34 +- README.md | 2 +- 104 files changed, 2552 insertions(+), 1622 deletions(-) create mode 100644 Editor/Mono/CodeEditor/ExternalEditor.bindings.cs create mode 100644 Modules/PackageManagerUI/Editor/Services/AssetStore/AssetStoreFetchedInfo.cs create mode 100644 Modules/PackageManagerUI/Editor/Services/AssetStore/AssetStoreLocalInfo.cs create mode 100644 Modules/PackageManagerUI/Editor/Services/AssetStore/AssetStoreVersionList.cs create mode 100644 Modules/PackageManagerUI/Editor/Services/Common/PackageExtensions.cs create mode 100644 Modules/PackageManagerUI/Editor/Services/Common/PlaceholderVersionList.cs create mode 100644 Modules/PackageManagerUI/Editor/Services/Interfaces/IVersionList.cs rename Modules/PackageManagerUI/Editor/Services/Packages/{PackageOrigin.cs => PackageProgress.cs} (62%) create mode 100644 Modules/PackageManagerUI/Editor/Services/Packages/PackageType.cs create mode 100644 Modules/PackageManagerUI/Editor/Services/Upm/UpmVersionList.cs create mode 100644 Modules/PackageManagerUI/Editor/UI/Common/DictionaryExtensions.cs diff --git a/Editor/Mono/Animation/AnimationWindow/Deprecated/EditorGUIExt.cs b/Editor/Mono/Animation/AnimationWindow/Deprecated/EditorGUIExt.cs index a6e2691b3f..eef0565036 100644 --- a/Editor/Mono/Animation/AnimationWindow/Deprecated/EditorGUIExt.cs +++ b/Editor/Mono/Animation/AnimationWindow/Deprecated/EditorGUIExt.cs @@ -47,8 +47,7 @@ static bool DoRepeatButton(Rect position, GUIContent content, GUIStyle style, Fo } return false; case EventType.Repaint: - style.Draw(position, content, id); - // Handles.Repaint (); + style.Draw(position, content, id, false, position.Contains(Event.current.mousePosition)); return id == GUIUtility.hotControl && position.Contains(Event.current.mousePosition); } return false; diff --git a/Editor/Mono/Animation/ZoomableArea.cs b/Editor/Mono/Animation/ZoomableArea.cs index 3c1858328a..d653c63abc 100644 --- a/Editor/Mono/Animation/ZoomableArea.cs +++ b/Editor/Mono/Animation/ZoomableArea.cs @@ -258,12 +258,12 @@ public Styles(bool minimalGUI) if (minimalGUI) { visualSliderWidth = 0; - sliderWidth = 12; + sliderWidth = 13; } else { - visualSliderWidth = 12; - sliderWidth = 12; + visualSliderWidth = 13; + sliderWidth = 13; } } diff --git a/Editor/Mono/AssetDatabase/AssetDatabase.cs b/Editor/Mono/AssetDatabase/AssetDatabase.cs index 712d81c13c..e26d48c55f 100644 --- a/Editor/Mono/AssetDatabase/AssetDatabase.cs +++ b/Editor/Mono/AssetDatabase/AssetDatabase.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using UnityEngine.Scripting; +using uei = UnityEngine.Internal; namespace UnityEditor { @@ -56,7 +57,7 @@ private static void Internal_CallImportPackageFailed(string packageName, string importPackageFailed(packageName, errorMessage); } - public static void IsOpenForEdit(string[] assetOrMetaFilePaths, List outNotEditablePaths, StatusQueryOptions statusQueryOptions = StatusQueryOptions.UseCachedIfPossible) + public static void IsOpenForEdit(string[] assetOrMetaFilePaths, List outNotEditablePaths, [uei.DefaultValue("StatusQueryOptions.UseCachedIfPossible")] StatusQueryOptions statusQueryOptions = StatusQueryOptions.UseCachedIfPossible) { if (assetOrMetaFilePaths == null) throw new ArgumentNullException(nameof(assetOrMetaFilePaths)); diff --git a/Editor/Mono/AssetModificationProcessor.cs b/Editor/Mono/AssetModificationProcessor.cs index ec42dbf5d3..357f558378 100644 --- a/Editor/Mono/AssetModificationProcessor.cs +++ b/Editor/Mono/AssetModificationProcessor.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using UnityEngine; using UnityEditor.VersionControl; using UnityEditorInternal; @@ -274,11 +275,24 @@ static MethodInfo[] GetIsOpenForEditMethods() return isOpenForEditMethods; } - static bool IsAssetInReadOnlyFolder(string assetPath) + static bool IsPathNotEditable(string assetPath) { + // read-only asset locations (e.g. shared packages) are considered not editable bool rootFolder, readOnly; bool validPath = AssetDatabase.GetAssetFolderInfo(assetPath, out rootFolder, out readOnly); - return validPath && readOnly; + if (validPath && readOnly) + return true; + + // paths under Library are considered to be editable, even if they are not versioned + if (assetPath.StartsWith("Library/", true, CultureInfo.InvariantCulture)) + return false; + + // other paths that are not know to asset database, and not versioned, are considered + // not editable + if (!Provider.PathIsVersioned(assetPath)) + return true; + + return false; } static bool IsOpenForEditViaScriptCallbacks(string assetPath, ref string message) @@ -301,7 +315,7 @@ internal static bool IsOpenForEdit(string assetPath, out string message, StatusQ if (string.IsNullOrEmpty(assetPath)) return true; // treat empty/null paths as editable (might be under Library folders etc.) - if (IsAssetInReadOnlyFolder(assetPath)) + if (IsPathNotEditable(assetPath)) return false; if (!AssetModificationHook.IsOpenForEdit(assetPath, out message, statusOptions)) return false; @@ -322,7 +336,7 @@ internal static void IsOpenForEdit(string[] assetOrMetaFilePaths, List o { if (string.IsNullOrEmpty(path)) continue; // treat empty/null paths as editable (might be under Library folders etc.) - if (IsAssetInReadOnlyFolder(path)) + if (IsPathNotEditable(path)) { outNotEditablePaths.Add(path); continue; diff --git a/Editor/Mono/AssetPipeline/AssetImportContext.bindings.cs b/Editor/Mono/AssetPipeline/AssetImportContext.bindings.cs index 96d819c81a..cd013b7b80 100644 --- a/Editor/Mono/AssetPipeline/AssetImportContext.bindings.cs +++ b/Editor/Mono/AssetPipeline/AssetImportContext.bindings.cs @@ -30,6 +30,8 @@ public class AssetImportContext AssetImportContext() {} public extern string assetPath { get; internal set; } + public extern string GetResultPath(string extension); + public extern BuildTarget selectedBuildTarget { get; } extern void LogMessage(string msg, string file, int line, UnityEngine.Object obj, bool isAnError); diff --git a/Editor/Mono/CodeEditor/CodeEditor.cs b/Editor/Mono/CodeEditor/CodeEditor.cs index 2d7004c744..d93b97fce8 100644 --- a/Editor/Mono/CodeEditor/CodeEditor.cs +++ b/Editor/Mono/CodeEditor/CodeEditor.cs @@ -30,7 +30,7 @@ public struct Installation } [RequiredByNativeCode] - static bool OpenFileAtLineColumn(string path, int line, int column) + static bool OpenProject(string path, int line, int column) { return Editor.Current.OpenProject(path, line, column); } @@ -156,6 +156,11 @@ public static void Unregister(IExternalCodeEditor externalCodeEditor) public static string CurrentEditorInstallation => Editor.EditorInstallation.Path; + public static bool OSOpenFile(string appPath, string arguments) + { + return ExternalEditor.OSOpenFileWithArgument(appPath, arguments); + } + public static string ParseArgument(string arguments, string path, int line, int column) { var newArgument = arguments.Replace("$(ProjectPath)", QuoteForProcessStart(Directory.GetParent(Application.dataPath).FullName)); diff --git a/Editor/Mono/CodeEditor/DefaultExternalCodeEditor.cs b/Editor/Mono/CodeEditor/DefaultExternalCodeEditor.cs index e35b8f24a4..c3f390794e 100644 --- a/Editor/Mono/CodeEditor/DefaultExternalCodeEditor.cs +++ b/Editor/Mono/CodeEditor/DefaultExternalCodeEditor.cs @@ -12,6 +12,8 @@ namespace UnityEditor internal class DefaultExternalCodeEditor : IExternalCodeEditor { static readonly GUIContent k_ResetArguments = EditorGUIUtility.TrTextContent("Reset argument"); + static bool IsOSX => Application.platform == RuntimePlatform.OSXEditor; + string m_ChosenInstallation; const string k_ArgumentKey = "kScriptEditorArgs"; const string k_DefaultArgument = "$(File)"; @@ -86,6 +88,12 @@ public void Initialize(string editorInstallationPath) public bool OpenProject(string path, int line, int column) { string applicationPath = EditorPrefs.GetString("kScriptsDefaultApp"); + + if (IsOSX) + { + return CodeEditor.OSOpenFile(applicationPath, CodeEditor.ParseArgument(Arguments, path, line, column)); + } + var process = new Process { StartInfo = new ProcessStartInfo diff --git a/Editor/Mono/CodeEditor/ExternalEditor.bindings.cs b/Editor/Mono/CodeEditor/ExternalEditor.bindings.cs new file mode 100644 index 0000000000..4e94506f0d --- /dev/null +++ b/Editor/Mono/CodeEditor/ExternalEditor.bindings.cs @@ -0,0 +1,15 @@ +// Unity C# reference source +// Copyright (c) Unity Technologies. For terms of use, see +// https://unity3d.com/legal/licenses/Unity_Reference_Only_License + +using UnityEngine.Bindings; + +namespace Unity.CodeEditor +{ + [NativeHeader("Editor/Platform/Interface/ExternalEditor.h")] + internal class ExternalEditor + { + [FreeFunction("PlatformSpecificOpenFileAtLine")] + internal static extern bool OSOpenFileWithArgument(string appPath, string arguments); + } +} diff --git a/Editor/Mono/ContainerWindow.cs b/Editor/Mono/ContainerWindow.cs index 15b6e96207..8cefa3be83 100644 --- a/Editor/Mono/ContainerWindow.cs +++ b/Editor/Mono/ContainerWindow.cs @@ -337,6 +337,8 @@ public SplitView rootSplitView { if (m_ShowMode == (int)ShowMode.MainWindow && rootView && rootView.children.Length == 3) return rootView.children[1] as SplitView; + if (m_ShowMode == (int)ShowMode.MainWindow && rootView && rootView.children.Length == 2) + return rootView.children[0] as SplitView; else return rootView as SplitView; } @@ -364,7 +366,7 @@ public void HandleWindowDecorationEnd(Rect windowPosition) public void HandleWindowDecorationStart(Rect windowPosition) { - bool hasTitleBar = (windowPosition.y == 0 && showMode != ShowMode.Utility && !isPopup); + bool hasTitleBar = (windowPosition.y == 0 && (showMode != ShowMode.Utility && showMode != ShowMode.MainWindow) && !isPopup); if (!hasTitleBar) return; diff --git a/Editor/Mono/EditorGUI.cs b/Editor/Mono/EditorGUI.cs index 6b78ada58f..3744dac63d 100644 --- a/Editor/Mono/EditorGUI.cs +++ b/Editor/Mono/EditorGUI.cs @@ -4867,12 +4867,19 @@ internal static void DoInspectorTitlebar(Rect position, int id, bool foldout, Ob { GUIStyle textStyle = EditorStyles.inspectorTitlebarText; GUIStyle iconButtonStyle = EditorStyles.iconButton; + Event evt = Event.current; + bool pressed = GUIUtility.hotControl == id; + bool hasFocus = GUIUtility.HasKeyFocus(id); + bool hovered = position.Contains(evt.mousePosition); Rect iconRect = GetIconRect(position, baseStyle); Rect settingsRect = GetSettingsRect(position, baseStyle, iconButtonStyle); Rect textRect = GetTextRect(position, iconRect, settingsRect, baseStyle, textStyle); - Event evt = Event.current; + if (evt.type == EventType.Repaint) + { + baseStyle.Draw(position, GUIContent.none, hovered, pressed, foldout, hasFocus); + } bool isAddedComponentAndEventIsRepaint = false; Component comp = targetObjs[0] as Component; @@ -4999,8 +5006,7 @@ internal static void DoInspectorTitlebar(Rect position, int id, bool foldout, Ob } break; case EventType.Repaint: - baseStyle.Draw(position, GUIContent.none, id, foldout, position.Contains(Event.current.mousePosition)); - textStyle.Draw(textRect, EditorGUIUtility.TempContent(ObjectNames.GetInspectorTitle(targetObjs[0])), id, foldout, textRect.Contains(Event.current.mousePosition)); + textStyle.Draw(textRect, EditorGUIUtility.TempContent(ObjectNames.GetInspectorTitle(targetObjs[0])), hovered, pressed, foldout, hasFocus); if (EditorGUIUtility.comparisonViewMode == EditorGUIUtility.ComparisonViewMode.None) { EditorStyles.optionsButtonStyle.Draw(settingsRect, GUIContent.none, id, foldout, settingsRect.Contains(Event.current.mousePosition)); @@ -5158,15 +5164,38 @@ internal static bool FoldoutTitlebar(Rect position, GUIContent label, bool foldo // Important to get controlId for the foldout first, so it gets keyboard focus before the toggle does. int id = GUIUtility.GetControlID(s_TitlebarHash, FocusType.Keyboard, position); - if (Event.current.type == EventType.Repaint) + switch (Event.current.type) { - GUIStyle foldoutStyle = EditorStyles.foldout; - Rect textRect = new Rect(position.x + baseStyle.padding.left + (skipIconSpacing ? 0 : (kInspTitlebarIconWidth + kInspTitlebarSpacing)), position.y + baseStyle.padding.top, EditorGUIUtility.labelWidth, kInspTitlebarIconWidth); + case EventType.KeyDown: + if (GUIUtility.keyboardControl == id) + { + KeyCode kc = Event.current.keyCode; + if (kc == KeyCode.LeftArrow && foldout || (kc == KeyCode.RightArrow && foldout == false)) + { + foldout = !foldout; + GUI.changed = true; + Event.current.Use(); + return foldout; + } + } - baseStyle.Draw(position, GUIContent.none, id, foldout); - foldoutStyle.Draw(GetInspectorTitleBarObjectFoldoutRenderRect(position, baseStyle), GUIContent.none, id, foldout); - position = baseStyle.padding.Remove(position); - textStyle.Draw(textRect, EditorGUIUtility.TempContent(label.text), id, foldout); + break; + case EventType.Repaint: + GUIStyle foldoutStyle = EditorStyles.foldout; + Rect textRect = + new Rect( + position.x + baseStyle.padding.left + + (skipIconSpacing ? 0 : (kInspTitlebarIconWidth + kInspTitlebarSpacing)), + position.y + baseStyle.padding.top, EditorGUIUtility.labelWidth, kInspTitlebarIconWidth); + bool hovered = position.Contains(Event.current.mousePosition); + baseStyle.Draw(position, GUIContent.none, id, foldout, hovered); + foldoutStyle.Draw(GetInspectorTitleBarObjectFoldoutRenderRect(position, baseStyle), GUIContent.none, + id, foldout, hovered); + position = baseStyle.padding.Remove(position); + textStyle.Draw(textRect, EditorGUIUtility.TempContent(label.text), id, foldout, hovered); + break; + default: + break; } return EditorGUIInternal.DoToggleForward(IndentedRect(position), id, foldout, GUIContent.none, GUIStyle.none); diff --git a/Editor/Mono/EditorGUIUtility.cs b/Editor/Mono/EditorGUIUtility.cs index 0cac3e680f..80f6fdb8ec 100644 --- a/Editor/Mono/EditorGUIUtility.cs +++ b/Editor/Mono/EditorGUIUtility.cs @@ -98,6 +98,7 @@ internal static Material GUITextureBlitSceneGUIMaterial static EditorGUIUtility() { GUISkin.m_SkinChanged += SkinChanged; + s_HasCurrentWindowKeyFocusFunc = HasCurrentWindowKeyFocus; } internal static void RepaintCurrentWindow() diff --git a/Editor/Mono/EditorMode/ModeService.cs b/Editor/Mono/EditorMode/ModeService.cs index 721ff77368..0507bb93ca 100644 --- a/Editor/Mono/EditorMode/ModeService.cs +++ b/Editor/Mono/EditorMode/ModeService.cs @@ -83,6 +83,7 @@ public struct ModeChangedArgs public static string currentId => currentIndex == -1 ? k_DefaultModeId : modes[currentIndex].id; public static int currentIndex { get; private set; } private static ModeEntry[] modes { get; set; } = new ModeEntry[0]; + internal static bool hasSwitchableModes { get; private set; } public static event Action modeChanged; @@ -290,48 +291,67 @@ private static void LoadModes(bool checkStartupMode = false) EditorApplication.delayCall += () => RaiseModeChanged(-1, currentIndex); } - private static void ScanModes() + private static void FillModeData(string path, Dictionary modesData) { - var modesData = new Dictionary { [k_DefaultModeId] = new Dictionary { [k_LabelSectionName] = "Default" } }; - var modeFilePaths = AssetDatabase.GetAllAssetPaths() - .Where(IsEditorModeDescriptor) - .OrderBy(path => - new FileInfo(path).Length); - foreach (var modeFilePath in modeFilePaths) + try { - try + var json = SJSON.Load(path); + foreach (var rawModeId in json.Keys) { - var json = SJSON.Load(modeFilePath); - - foreach (var rawModeId in json.Keys) + var modeId = ((string)rawModeId).ToLower(); + if (IsValidModeId(modeId)) { - var modeId = ((string)rawModeId).ToLower(); - if (IsValidModeId(modeId)) - { - if (modesData.ContainsKey(modeId)) - modesData[modeId] = JsonUtils.DeepMerge(modesData[modeId] as JSONObject, json[modeId] as JSONObject); - else - modesData[modeId] = json[modeId]; - } + object modeData = null; + if (modesData.TryGetValue(modeId, out modeData)) + modesData[modeId] = JsonUtils.DeepMerge(modeData as JSONObject, json[modeId] as JSONObject); else - { - Debug.LogWarning($"Invalid Mode Id: {modeId} contains non alphanumeric characters."); - } + modesData[modeId] = json[modeId]; + } + else + { + Debug.LogWarning($"Invalid Mode Id: {modeId} contains non alphanumeric characters."); } } - catch (Exception ex) - { - Debug.LogError($"[ModeService] Error while parsing mode file {modeFilePath}.\n{ex}"); - } + } + catch (Exception ex) + { + Debug.LogError($"[ModeService] Error while parsing mode file {path}.\n{ex}"); + } + } + + internal static void ScanModes() + { + var modesData = new Dictionary { [k_DefaultModeId] = new Dictionary { [k_LabelSectionName] = "Default" } }; + + var builtinModeFile = Path.Combine(EditorApplication.applicationContentsPath, "Resources/default.mode"); + FillModeData(builtinModeFile, modesData); + + var modeDescriptors = AssetDatabase.EnumerateAllAssets(new SearchFilter + { + searchArea = SearchFilter.SearchArea.InPackagesOnly, + classNames = new[] { nameof(ModeDescriptor) }, + skipHidden = true, + showAllHits = true + }); + while (modeDescriptors.MoveNext()) + { + var md = modeDescriptors.Current.pptrValue as ModeDescriptor; + if (md == null) + continue; + FillModeData(md.path, modesData); } modes = new ModeEntry[modesData.Keys.Count]; modes[0] = CreateEntry(k_DefaultModeId, (JSONObject)modesData[k_DefaultModeId]); var modeIndex = 1; + hasSwitchableModes = false; foreach (var modeId in modesData.Keys) { if (modeId == k_DefaultModeId) continue; - modes[modeIndex] = CreateEntry(modeId, (JSONObject)modesData[modeId]); + var modeFields = (JSONObject)modesData[modeId]; + modes[modeIndex] = CreateEntry(modeId, modeFields); + hasSwitchableModes |= !JsonUtils.JsonReadBoolean(modeFields, "builtin"); modeIndex++; } } @@ -346,13 +366,6 @@ private static ModeEntry CreateEntry(string modeId, JSONObject data) }; } - private static bool IsEditorModeDescriptor(string path) - { - var pathLowerCased = path.ToLower(); - // Limit the authoring of editor modes to Unity packages for now. - return pathLowerCased.StartsWith("packages/com.unity") && pathLowerCased.EndsWith(".mode"); - } - private static void SetModeIndex(int modeIndex) { currentIndex = Math.Max(0, Math.Min(modeIndex, modeCount - 1)); @@ -516,7 +529,8 @@ private static void OnModeChangeLayouts(ModeChangedArgs args) } } - WindowLayout.ReloadWindowLayoutMenu(); + if (HasCapability(ModeCapability.LayoutWindowMenu, true)) + WindowLayout.ReloadWindowLayoutMenu(); } private static void OnModeChangeUpdate(ModeChangedArgs args) diff --git a/Editor/Mono/EditorResources.bindings.cs b/Editor/Mono/EditorResources.bindings.cs index 4533e14a07..0b469adc9b 100644 --- a/Editor/Mono/EditorResources.bindings.cs +++ b/Editor/Mono/EditorResources.bindings.cs @@ -22,6 +22,7 @@ partial class EditorResources [NativeProperty("k_GeneratedIconsPath", true, TargetType.Field)] public static extern string generatedIconsPath { get; } [NativeProperty("k_FolderIconName", true, TargetType.Field)] public static extern string folderIconName { get; } [NativeProperty("k_EmptyFolderIconName", true, TargetType.Field)] public static extern string emptyFolderIconName { get; } + [NativeProperty("k_OpenedFolderIconName", true, TargetType.Field)] internal static extern string openedFolderIconName { get; } [NativeProperty("k_EditorDefaultResourcesPath", true, TargetType.Field)] public static extern string editorDefaultResourcesPath { get; } [NativeProperty("k_LibraryBundlePath", true, TargetType.Field)] public static extern string libraryBundlePath { get; } diff --git a/Editor/Mono/EditorResources.cs b/Editor/Mono/EditorResources.cs index 59e449a0ef..113b74b171 100644 --- a/Editor/Mono/EditorResources.cs +++ b/Editor/Mono/EditorResources.cs @@ -50,10 +50,7 @@ private static bool IsEditorStyleSheet(string path) internal static string GetDefaultFont() { - if (Application.platform == RuntimePlatform.WindowsEditor) - return "Verdana"; - else - return "Roboto"; + return "Inter"; } internal static string GetCurrentFont() @@ -84,9 +81,9 @@ internal static Font GetSmallFont() } else { - if (currentFont == "Roboto") + if (currentFont == "Inter") { - s_SmallFont = EditorGUIUtility.LoadRequired("Fonts/roboto/Roboto-Small.ttf") as Font; + s_SmallFont = EditorGUIUtility.LoadRequired("Fonts/Inter/Inter-Small.ttf") as Font; } else if (currentFont == "Lucida Grande") { @@ -137,9 +134,9 @@ internal static Font GetBoldFont() } else { - if (currentFont == "Roboto") + if (currentFont == "Inter") { - s_BoldFont = EditorGUIUtility.LoadRequired("Fonts/roboto/Roboto-Bold.ttf") as Font; + s_BoldFont = EditorGUIUtility.LoadRequired("Fonts/Inter/Inter-Bold.ttf") as Font; } else if (currentFont == "Lucida Grande") { @@ -165,7 +162,7 @@ internal static List GetSupportedFonts() if (Application.platform == RuntimePlatform.WindowsEditor) { - s_SupportedFonts.Add("Segoe UI"); + s_SupportedFonts.Add("Verdana"); } foreach (var builtinFont in EditorResources.builtInFonts.Keys) @@ -188,7 +185,7 @@ internal static Dictionary builtInFonts { s_BuiltInFonts = new Dictionary { - ["Roboto"] = "Fonts/roboto/Roboto-Regular.ttf" + ["Inter"] = "Fonts/Inter/Inter-Regular.ttf" }; if (Application.platform != RuntimePlatform.WindowsEditor) @@ -230,36 +227,41 @@ private static List GetDefaultStyleCatalogPaths() return catalogFiles; } - [UsedImplicitly, RequiredByNativeCode] internal static void BuildCatalog() { - s_StyleCatalog = new StyleCatalog(); - s_RefreshGlobalStyleCatalog = false; + using (new PerformanceTracker(nameof(BuildCatalog))) + { + s_StyleCatalog = new StyleCatalog(); + s_RefreshGlobalStyleCatalog = false; - var paths = GetDefaultStyleCatalogPaths(); - foreach (var editorUssPath in AssetDatabase.GetAllAssetPaths().Where(IsEditorStyleSheet)) - paths.Add(editorUssPath); + var paths = GetDefaultStyleCatalogPaths(); + foreach (var editorUssPath in AssetDatabase.FindAssets("t:StyleSheet").Select(AssetDatabase.GUIDToAssetPath).Where(IsEditorStyleSheet)) + paths.Add(editorUssPath); - Console.WriteLine($"Building style catalogs ({paths.Count})\r\n\t{String.Join("\r\n\t", paths.ToArray())}"); - styleCatalog.Load(paths); + Console.WriteLine($"Building style catalogs ({paths.Count})\r\n\t{String.Join("\r\n\t", paths.ToArray())}"); + styleCatalog.Load(paths); + } } internal static void RefreshSkin() { - if (!CanEnableExtendedStyles()) - return; + using (new PerformanceTracker(nameof(RefreshSkin))) + { + if (!CanEnableExtendedStyles()) + return; - GUIStyle.onDraw = StylePainter.DrawStyle; + GUIStyle.onDraw = StylePainter.DrawStyle; - // Update gui skin style layouts - var skin = GUIUtility.GetDefaultSkin(); - if (skin != null) - { - // TODO: Emit OnStyleCatalogLoaded - if (Path.GetFileName(Path.GetDirectoryName(Application.dataPath)) == "editor_resources") - ConverterUtils.ResetSkinToPristine(skin, EditorGUIUtility.isProSkin ? SkinTarget.Dark : SkinTarget.Light); - skin.font = GetNormalFont(); - UpdateGUIStyleProperties(skin); + // Update gui skin style layouts + var skin = GUIUtility.GetDefaultSkin(); + if (skin != null) + { + // TODO: Emit OnStyleCatalogLoaded + if (Path.GetFileName(Path.GetDirectoryName(Application.dataPath)) == "editor_resources") + ConverterUtils.ResetSkinToPristine(skin, EditorGUIUtility.isProSkin ? SkinTarget.Dark : SkinTarget.Light); + skin.font = GetNormalFont(); + UpdateGUIStyleProperties(skin); + } } } diff --git a/Editor/Mono/GUI/AppStatusBar.cs b/Editor/Mono/GUI/AppStatusBar.cs index 1995ed7344..9cc38375e8 100644 --- a/Editor/Mono/GUI/AppStatusBar.cs +++ b/Editor/Mono/GUI/AppStatusBar.cs @@ -172,7 +172,7 @@ protected override void OldOnGUI() } else if (Unsupported.IsDeveloperMode()) { - GUI.Label(new Rect(progressBarHorizontalPosition - 200 - spacing, 0, 200, barHeight), m_MiniMemoryOverview, EditorStyles.progressBarText); + GUI.Label(new Rect(progressBarHorizontalPosition - 300 - spacing, 0, 300, barHeight), m_MiniMemoryOverview, EditorStyles.progressBarText); EditorGUIUtility.CleanCache(m_MiniMemoryOverview); } } diff --git a/Editor/Mono/GUI/DockArea.cs b/Editor/Mono/GUI/DockArea.cs index 8668e6d428..ac7f84946f 100644 --- a/Editor/Mono/GUI/DockArea.cs +++ b/Editor/Mono/GUI/DockArea.cs @@ -1077,7 +1077,9 @@ protected override void OldOnGUI() // Call reset GUI state as first thing so GUI.color is correct when drawing window decoration. EditorGUIUtility.ResetGUIState(); - Rect maximizedViewRect = position; + Rect maximizedViewRect = Rect.zero; + + maximizedViewRect.size = position.size; maximizedViewRect = Styles.background.margin.Remove(maximizedViewRect); Rect backRect = new Rect(maximizedViewRect.x + 1, maximizedViewRect.y, maximizedViewRect.width - 2, DockArea.kTabHeight); diff --git a/Editor/Mono/GUI/EditorStyles.cs b/Editor/Mono/GUI/EditorStyles.cs index 6f760d6f53..97eca0bcc0 100644 --- a/Editor/Mono/GUI/EditorStyles.cs +++ b/Editor/Mono/GUI/EditorStyles.cs @@ -492,7 +492,7 @@ private void InitSharedStyles() { padding = new RectOffset( InspectorWindow.kInspectorPaddingLeft, - InspectorWindow.kInspectorPaddingRight, 0, 0) + InspectorWindow.kInspectorPaddingRight, InspectorWindow.kInspectorPaddingTop, 0) }; // For the full width margins, use padding from right side in both sides, diff --git a/Editor/Mono/GUI/FoldoutHeader.cs b/Editor/Mono/GUI/FoldoutHeader.cs index 564f1793b9..7b9bb0d12c 100644 --- a/Editor/Mono/GUI/FoldoutHeader.cs +++ b/Editor/Mono/GUI/FoldoutHeader.cs @@ -33,6 +33,7 @@ public static void EndFoldoutHeaderGroup() public sealed partial class EditorGUI { static bool s_FoldoutHeaderGroupActive; + private static readonly int s_FoldoutHeaderHash = "FoldoutHeader".GetHashCode(); public static bool BeginFoldoutHeaderGroup(Rect position, bool foldout, string content, [DefaultValue("EditorStyles.foldoutHeader")] GUIStyle style = null, Action menuAction = null, GUIStyle menuIcon = null) @@ -46,6 +47,7 @@ public static bool BeginFoldoutHeaderGroup(Rect position, bool foldout, GUIConte if (EditorGUIUtility.hierarchyMode) { position.xMin -= EditorStyles.inspectorDefaultMargins.padding.left - EditorStyles.inspectorDefaultMargins.padding.right; + position.xMax += EditorStyles.inspectorDefaultMargins.padding.right; } if (style == null) @@ -72,8 +74,22 @@ public static bool BeginFoldoutHeaderGroup(Rect position, bool foldout, GUIConte menuAction.Invoke(menuRect); Event.current.Use(); } + int id = GUIUtility.GetControlID(s_FoldoutHeaderHash, FocusType.Keyboard, position); - foldout = GUI.Toggle(position, foldout, content, style); + if (Event.current.type == EventType.KeyDown && GUIUtility.keyboardControl == id) + { + KeyCode kc = Event.current.keyCode; + if (kc == KeyCode.LeftArrow && foldout || (kc == KeyCode.RightArrow && foldout == false)) + { + foldout = !foldout; + GUI.changed = true; + Event.current.Use(); + } + } + else + { + foldout = EditorGUIInternal.DoToggleForward(position, id, foldout, content, style); + } // Menu icon if (menuAction != null && Event.current.type == EventType.Repaint) diff --git a/Editor/Mono/GUI/MainView.cs b/Editor/Mono/GUI/MainView.cs index 1f41368a2a..cf3be9b500 100644 --- a/Editor/Mono/GUI/MainView.cs +++ b/Editor/Mono/GUI/MainView.cs @@ -10,7 +10,7 @@ internal class MainView : View, ICleanuppable { const float kStatusbarHeight = 20; - private static readonly Vector2 kMinSize = new Vector2(950, 300); + private static readonly Vector2 kMinSize = new Vector2(875, 300); private static readonly Vector2 kMaxSize = new Vector2(10000, 10000); void OnEnable() @@ -23,22 +23,36 @@ protected override void SetPosition(Rect newPos) base.SetPosition(newPos); if (children.Length == 0) return; - Toolbar t = (Toolbar)children[0]; - children[0].position = new Rect(0, 0, newPos.width, t.CalcHeight()); if (children.Length > 2) { + // toolbar - dock area view - status bar + Toolbar t = (Toolbar)children[0]; + children[0].position = new Rect(0, 0, newPos.width, t.CalcHeight()); children[1].position = new Rect(0, t.CalcHeight(), newPos.width, newPos.height - t.CalcHeight() - children[2].position.height); children[2].position = new Rect(0, newPos.height - children[2].position.height, newPos.width, children[2].position.height); } + else + { + // dock area view - status bar + children[0].position = new Rect(0, 0, newPos.width, newPos.height - children[1].position.height); + children[1].position = new Rect(0, newPos.height - children[1].position.height, newPos.width, children[1].position.height); + } } protected override void ChildrenMinMaxChanged() { - if (children.Length == 3) + if (children.Length == 3 && children[0] is Toolbar) { + // toolbar - dock area view - status bar Toolbar t = (Toolbar)children[0]; - var min = new Vector2(kMinSize.x, Mathf.Max(kMinSize.y, t.CalcHeight() + kStatusbarHeight + children[1].minSize.y)); - SetMinMaxSizes(min, kMaxSize); + var min = new Vector2(minSize.x, Mathf.Max(minSize.y, t.CalcHeight() + kStatusbarHeight + children[1].minSize.y)); + SetMinMaxSizes(min, maxSize); + } + else if (children.Length == 2) + { + // dock area view - status bar + var min = new Vector2(minSize.x, Mathf.Max(minSize.y, kStatusbarHeight + children[1].minSize.y)); + SetMinMaxSizes(min, maxSize); } base.ChildrenMinMaxChanged(); } @@ -65,7 +79,7 @@ public void Cleanup() // If we only have one child left, this means all views have been dragged out. // So we resize the window to be just the toolbar // On windows, this might need some special handling for the main menu - if (children[1].children.Length == 0) + if (children.Length == 3 && children[1].children.Length == 0) { Rect r = window.position; Toolbar t = (Toolbar)children[0]; diff --git a/Editor/Mono/GUI/Toolbar.cs b/Editor/Mono/GUI/Toolbar.cs index afcfa91635..0495e65995 100644 --- a/Editor/Mono/GUI/Toolbar.cs +++ b/Editor/Mono/GUI/Toolbar.cs @@ -200,8 +200,7 @@ void ReserveWidthRight(float width, ref Rect pos) protected override void OldOnGUI() { - const float space = 10; - const float largeSpace = 20; + const float space = 8; const float standardButtonWidth = 32; const float dropdownWidth = 80; const float playPauseStopWidth = 140; @@ -222,12 +221,12 @@ protected override void OldOnGUI() ReserveWidthRight(standardButtonWidth * s_ShownToolIcons.Length, ref pos); DoToolButtons(EditorToolGUI.GetThickArea(pos)); - ReserveWidthRight(largeSpace, ref pos); + ReserveWidthRight(space, ref pos); int playModeControlsStart = Mathf.RoundToInt((position.width - playPauseStopWidth) / 2); pos.x += pos.width; - pos.width = (playModeControlsStart - pos.x) - largeSpace; + pos.width = (playModeControlsStart - pos.x) - space; DoToolSettings(EditorToolGUI.GetThickArea(pos)); // Position centered controls. @@ -286,7 +285,7 @@ protected override void OldOnGUI() subToolbar.OnGUI(EditorToolGUI.GetThinArea(pos)); } - if (ModeService.modeCount > 1 && Unsupported.IsDeveloperBuild()) + if (Unsupported.IsDeveloperBuild() && ModeService.hasSwitchableModes) { EditorGUI.BeginChangeCheck(); ReserveWidthLeft(space, ref pos); diff --git a/Editor/Mono/GUI/TreeView/AssetsTreeViewDataSource.cs b/Editor/Mono/GUI/TreeView/AssetsTreeViewDataSource.cs index fd034f8987..3900cc18eb 100644 --- a/Editor/Mono/GUI/TreeView/AssetsTreeViewDataSource.cs +++ b/Editor/Mono/GUI/TreeView/AssetsTreeViewDataSource.cs @@ -164,7 +164,6 @@ public override void FetchData() } m_Rows = new List(m_Roots.Count * 256); - Texture2D emptyFolderIcon = EditorGUIUtility.FindTexture(EditorResources.emptyFolderIconName); Texture2D folderIcon = EditorGUIUtility.FindTexture(EditorResources.folderIconName); var assetsInstanceIDs = AssetDatabase.GetMainAssetOrInProgressProxyInstanceID("Assets"); var projectPath = Path.GetFileName(Directory.GetCurrentDirectory()); @@ -245,10 +244,7 @@ public override void FetchData() else item = new NonFolderTreeItem(property.guid, property.GetInstanceIDIfImported(), depth + subDepth, null, property.name); - if (property.isFolder && !property.hasChildren) - item.icon = emptyFolderIcon; - else - item.icon = property.icon; + item.icon = property.icon; if (property.hasChildren) { @@ -524,7 +520,15 @@ internal interface IAssetTreeViewItem string Guid { get; } } - internal class RootTreeItem : TreeViewItem + internal abstract class FolderTreeItemBase : TreeViewItem + { + protected FolderTreeItemBase(int id, int depth, TreeViewItem parent, string displayName) + : base(id, depth, parent, displayName) + { + } + } + + internal class RootTreeItem : FolderTreeItemBase { public RootTreeItem(int id, int depth, TreeViewItem parent, string displayName) : base(id, depth, parent, displayName) @@ -532,7 +536,15 @@ public RootTreeItem(int id, int depth, TreeViewItem parent, string displayName) } } - class FolderTreeItem : TreeViewItem, IAssetTreeViewItem + internal class PackageTreeItem : FolderTreeItemBase + { + public PackageTreeItem(int id, int depth, TreeViewItem parent, string displayName) + : base(id, depth, parent, displayName) + { + } + } + + internal class FolderTreeItem : FolderTreeItemBase, IAssetTreeViewItem { public string Guid { get; } diff --git a/Editor/Mono/GUI/TreeView/AssetsTreeViewGUI.cs b/Editor/Mono/GUI/TreeView/AssetsTreeViewGUI.cs index a2e7afe610..84577e4451 100644 --- a/Editor/Mono/GUI/TreeView/AssetsTreeViewGUI.cs +++ b/Editor/Mono/GUI/TreeView/AssetsTreeViewGUI.cs @@ -8,6 +8,7 @@ using UnityEditorInternal.VersionControl; using UnityEditor.VersionControl; using System.Collections.Generic; +using UnityEditor.Experimental; using UnityEngine.Assertions; namespace UnityEditor @@ -17,6 +18,17 @@ internal class AssetsTreeViewGUI : TreeViewGUI static bool s_VCEnabled; const float k_IconOverlayPadding = 7f; + internal static ScalableGUIContent s_OpenFolderIcon = new ScalableGUIContent(null, null, EditorResources.openedFolderIconName); + + internal static Texture2D openFolderTexture + { + get + { + GUIContent folderContent = s_OpenFolderIcon; + return folderContent.image as Texture2D; + } + } + internal delegate void OnAssetIconDrawDelegate(Rect iconRect, string guid); internal static event OnAssetIconDrawDelegate postAssetIconDrawCallback = null; @@ -134,6 +146,14 @@ protected override Texture GetIconForItem(TreeViewItem item) string path = AssetDatabase.GetAssetPath(item.id); icon = AssetDatabase.GetCachedIcon(path); } + + AssetsTreeViewDataSource.FolderTreeItemBase folderItem = item as AssetsTreeViewDataSource.FolderTreeItemBase; + + if (folderItem != null && m_TreeView.data.IsExpanded(folderItem)) + { + icon = openFolderTexture; + } + return icon; } diff --git a/Editor/Mono/GUI/WindowLayout.cs b/Editor/Mono/GUI/WindowLayout.cs index 0ab12b4f77..93dfc45fe9 100644 --- a/Editor/Mono/GUI/WindowLayout.cs +++ b/Editor/Mono/GUI/WindowLayout.cs @@ -174,8 +174,11 @@ static WindowLayout() { EditorApplication.delayCall += () => { - ReloadWindowLayoutMenu(); - EditorUtility.Internal_UpdateAllMenus(); + if (ModeService.HasCapability(ModeCapability.LayoutWindowMenu, true)) + { + ReloadWindowLayoutMenu(); + EditorUtility.Internal_UpdateAllMenus(); + } }; } diff --git a/Editor/Mono/Inspector/AssemblyDefinitionImporterInspector.cs b/Editor/Mono/Inspector/AssemblyDefinitionImporterInspector.cs index 8d0ca0f266..5e68b70220 100644 --- a/Editor/Mono/Inspector/AssemblyDefinitionImporterInspector.cs +++ b/Editor/Mono/Inspector/AssemblyDefinitionImporterInspector.cs @@ -41,6 +41,8 @@ internal class Styles public static readonly GUIContent loadError = EditorGUIUtility.TrTextContent("Load error"); public static readonly GUIContent expressionOutcome = EditorGUIUtility.TrTextContent("Expression outcome", "Shows the mathematical equation that your Expression represents."); public static readonly GUIContent noEngineReferences = EditorGUIUtility.TrTextContent("No Engine References", "When enabled, references to UnityEngine/UnityEditor will not be added when compiling this assembly."); + public static readonly GUIContent validDefineConstraint = new GUIContent(EditorGUIUtility.FindTexture("TestPassed"), L10n.Tr("Define constraint is valid.")); + public static readonly GUIContent invalidDefineConstraint = new GUIContent(EditorGUIUtility.FindTexture("TestFailed"), L10n.Tr("Define constraint is invalid.")); } GUIStyle m_TextStyle; @@ -177,6 +179,32 @@ public override void OnInspectorGUI() GUILayout.Space(10f); GUILayout.Label(Styles.defineConstraints, EditorStyles.boldLabel); + if (m_DefineConstraints.serializedProperty.arraySize > 0) + { + var defineConstraintsValid = true; + for (var i = 0; i < m_DefineConstraints.serializedProperty.arraySize && defineConstraintsValid; ++i) + { + var defineConstraint = m_DefineConstraints.serializedProperty.GetArrayElementAtIndex(i).FindPropertyRelative("name").stringValue; + if (!DefineConstraintsHelper.IsDefineConstraintValid(m_Defines, defineConstraint)) + { + defineConstraintsValid = false; + } + } + var constraintValidityRect = new Rect(GUILayoutUtility.GetLastRect()); + constraintValidityRect.x += constraintValidityRect.width - 23; + if (defineConstraintsValid) + { + constraintValidityRect.width = Styles.validDefineConstraint.image.width; + constraintValidityRect.height = Styles.validDefineConstraint.image.height; + EditorGUI.LabelField(constraintValidityRect, Styles.validDefineConstraint); + } + else + { + constraintValidityRect.width = Styles.invalidDefineConstraint.image.width; + constraintValidityRect.height = Styles.invalidDefineConstraint.image.height; + EditorGUI.LabelField(constraintValidityRect, Styles.invalidDefineConstraint); + } + } m_DefineConstraints.DoLayoutList(); GUILayout.Label(Styles.references, EditorStyles.boldLabel); @@ -368,9 +396,9 @@ private void DrawDefineConstraintListElement(Rect rect, int index, bool isactive rect.height -= EditorGUIUtility.standardVerticalSpacing; - var textFieldRect = new Rect(rect.x, rect.y + 1, rect.width - ReorderableList.Defaults.dragHandleWidth, rect.height); + var textFieldRect = new Rect(rect.x, rect.y + 1, rect.width - ReorderableList.Defaults.dragHandleWidth + 1, rect.height); - var validRect = new Rect(rect.width + ReorderableList.Defaults.dragHandleWidth + 1, rect.y + 1, ReorderableList.Defaults.dragHandleWidth, rect.height); + var constraintValidityRect = new Rect(rect.width + ReorderableList.Defaults.dragHandleWidth + ReorderableList.Defaults.dragHandleWidth / 2f - Styles.invalidDefineConstraint.image.width / 2f, rect.y , ReorderableList.Defaults.dragHandleWidth, rect.height); string noValue = L10n.Tr("(Missing)"); @@ -382,9 +410,18 @@ private void DrawDefineConstraintListElement(Rect rect, int index, bool isactive if (m_Defines != null) { - EditorGUI.BeginDisabled(true); - EditorGUI.Toggle(validRect, DefineConstraintsHelper.IsDefineConstraintValid(m_Defines, defineConstraint.stringValue)); - EditorGUI.EndDisabled(); + if (DefineConstraintsHelper.IsDefineConstraintValid(m_Defines, defineConstraint.stringValue)) + { + constraintValidityRect.width = Styles.validDefineConstraint.image.width; + constraintValidityRect.height = Styles.validDefineConstraint.image.height; + EditorGUI.LabelField(constraintValidityRect, Styles.validDefineConstraint); + } + else + { + constraintValidityRect.width = Styles.invalidDefineConstraint.image.width; + constraintValidityRect.height = Styles.invalidDefineConstraint.image.height; + EditorGUI.LabelField(constraintValidityRect, Styles.invalidDefineConstraint); + } } if (!string.IsNullOrEmpty(textFieldValue) && textFieldValue != noValue) diff --git a/Editor/Mono/Inspector/EditorElement.cs b/Editor/Mono/Inspector/EditorElement.cs index ab614d245c..2914b77671 100644 --- a/Editor/Mono/Inspector/EditorElement.cs +++ b/Editor/Mono/Inspector/EditorElement.cs @@ -226,6 +226,8 @@ void HeaderOnGUI() if (wasVisible != IsElementVisible(m_InspectorElement)) { SetElementVisible(m_InspectorElement, wasVisible); + + m_Footer.style.marginTop = wasVisible ? 0 : -kFooterDefaultHeight; } var multiEditingSupported = inspectorWindow.IsMultiEditingSupported(editor, target); @@ -333,13 +335,7 @@ Rect DrawEditorSmallHeader(Object target, bool wasVisible) using (new EditorGUI.DisabledScope(!currentEditor.IsEnabled())) { - // Woraround: Temporarily adjust the top padding to 2px in order to vertically center the content of the titlebar. - // This is because there is a 3px bottom margin used to be able to pick and drag a component section - var oldPadding = EditorStyles.inspectorTitlebar.padding.top; - EditorStyles.inspectorTitlebar.padding.top = 2; - bool isVisible = EditorGUILayout.InspectorTitlebar(wasVisible, currentEditor); - EditorStyles.inspectorTitlebar.padding.top = oldPadding; if (wasVisible != isVisible) { diff --git a/Editor/Mono/Inspector/GameObjectInspector.cs b/Editor/Mono/Inspector/GameObjectInspector.cs index ed38c9bd9c..b90e10aec5 100644 --- a/Editor/Mono/Inspector/GameObjectInspector.cs +++ b/Editor/Mono/Inspector/GameObjectInspector.cs @@ -234,6 +234,11 @@ private static bool ShowMixedStaticEditorFlags(StaticEditorFlags mask) return countedBits > 0 && countedBits != numFlags; } + public override bool UseDefaultMargins() + { + return false; + } + protected override void OnHeaderGUI() { bool enabledTemp = GUI.enabled; diff --git a/Editor/Mono/Inspector/InspectorWindow.cs b/Editor/Mono/Inspector/InspectorWindow.cs index 25e7a32321..3d7360238b 100644 --- a/Editor/Mono/Inspector/InspectorWindow.cs +++ b/Editor/Mono/Inspector/InspectorWindow.cs @@ -48,6 +48,7 @@ internal InspectorMode inspectorMode const float kAddComponentButtonHeight = 45f; internal const int kInspectorPaddingLeft = 8 + 10; internal const int kInspectorPaddingRight = 4; + internal const int kInspectorPaddingTop = 4; internal const float kEditorElementPaddingBottom = 2f; const float k_MinAreaAbovePreview = 130; diff --git a/Editor/Mono/Inspector/PlayerSettingsEditor/PlayerSettingsEditor.cs b/Editor/Mono/Inspector/PlayerSettingsEditor/PlayerSettingsEditor.cs index e9faef7ed0..1a26aa3e3a 100644 --- a/Editor/Mono/Inspector/PlayerSettingsEditor/PlayerSettingsEditor.cs +++ b/Editor/Mono/Inspector/PlayerSettingsEditor/PlayerSettingsEditor.cs @@ -139,7 +139,7 @@ class SettingsContent public static readonly GUIContent dynamicBatching = EditorGUIUtility.TrTextContent("Dynamic Batching"); public static readonly GUIContent graphicsJobsNonExperimental = EditorGUIUtility.TrTextContent("Graphics Jobs"); public static readonly GUIContent graphicsJobsExperimental = EditorGUIUtility.TrTextContent("Graphics Jobs (Experimental)"); - public static readonly GUIContent graphicsJobsMode = EditorGUIUtility.TrTextContent("Graphics Jobs Mode*"); + public static readonly GUIContent graphicsJobsMode = EditorGUIUtility.TrTextContent("Graphics Jobs Mode"); public static readonly GUIContent applicationBuildNumber = EditorGUIUtility.TrTextContent("Build"); public static readonly GUIContent appleDeveloperTeamID = EditorGUIUtility.TrTextContent("iOS Developer Team ID", "Developers can retrieve their Team ID by visiting the Apple Developer site under Account > Membership."); public static readonly GUIContent useOnDemandResources = EditorGUIUtility.TrTextContent("Use on-demand resources*"); @@ -195,7 +195,8 @@ class SettingsContent public static string undoChangedIconString { get { return LocalizationDatabase.GetLocalizedString("Changed Icon"); } } public static string undoChangedGraphicsAPIString { get { return LocalizationDatabase.GetLocalizedString("Changed Graphics API Settings"); } } public static string undoChangedScriptingDefineString { get { return LocalizationDatabase.GetLocalizedString("Changed Scripting Define Settings"); } } - public static string undoChangedGraphicsJobsString { get { return LocalizationDatabase.GetLocalizedString("Changed Graphics Jobs Settings"); } } + public static string undoChangedGraphicsJobsString { get { return LocalizationDatabase.GetLocalizedString("Changed Graphics Jobs Setting"); } } + public static string undoChangedGraphicsJobModeString { get { return LocalizationDatabase.GetLocalizedString("Changed Graphics Job Mode Setting"); } } } // Icon layout constants @@ -1646,9 +1647,10 @@ private void OtherSectionRenderingGUI(BuildPlatform platform, BuildTargetGroup t // no need for a drop down popup for XBoxOne // also if XboxOneD3D12 is selected as GraphicsAPI, then we want to set graphics jobs and disable the user option GraphicsDeviceType[] gfxAPIs = PlayerSettings.GetGraphicsAPIs(platform.defaultTarget); + GraphicsJobMode newGfxJobMode = GraphicsJobMode.Legacy; if (gfxAPIs[0] == GraphicsDeviceType.XboxOneD3D12) { - PlayerSettings.graphicsJobMode = GraphicsJobMode.Native; + newGfxJobMode = GraphicsJobMode.Native; graphicsJobsOptionEnabled = false; if (graphicsJobs == false) { @@ -1657,10 +1659,7 @@ private void OtherSectionRenderingGUI(BuildPlatform platform, BuildTargetGroup t newGraphicsJobs = true; } } - else - { - PlayerSettings.graphicsJobMode = GraphicsJobMode.Legacy; - } + PlayerSettings.SetGraphicsJobModeForPlatform(platform.defaultTarget, newGfxJobMode); graphicsJobsModeOptionEnabled = false; } EditorGUI.BeginChangeCheck(); @@ -1696,13 +1695,15 @@ private void OtherSectionRenderingGUI(BuildPlatform platform, BuildTargetGroup t if (gfxJobModesSupported) { + EditorGUI.BeginChangeCheck(); using (new EditorGUI.DisabledScope(!graphicsJobsModeOptionEnabled)) { - GraphicsJobMode currGfxJobMode = PlayerSettings.graphicsJobMode; + GraphicsJobMode currGfxJobMode = PlayerSettings.GetGraphicsJobModeForPlatform(platform.defaultTarget); GraphicsJobMode newGfxJobMode = BuildEnumPopup(SettingsContent.graphicsJobsMode, currGfxJobMode, m_GfxJobModeValues, m_GfxJobModeNames); - if (newGfxJobMode != currGfxJobMode) + if (EditorGUI.EndChangeCheck() && (newGfxJobMode != currGfxJobMode)) { - PlayerSettings.graphicsJobMode = newGfxJobMode; + Undo.RecordObject(target, SettingsContent.undoChangedGraphicsJobModeString); + PlayerSettings.SetGraphicsJobModeForPlatform(platform.defaultTarget, newGfxJobMode); } } } diff --git a/Editor/Mono/Inspector/PropertyDrawerCache.cs b/Editor/Mono/Inspector/PropertyDrawerCache.cs index 2a0a64eb61..b64e2f3ace 100644 --- a/Editor/Mono/Inspector/PropertyDrawerCache.cs +++ b/Editor/Mono/Inspector/PropertyDrawerCache.cs @@ -32,7 +32,7 @@ private static int GetPropertyHash(SerializedProperty property) return 0; // For efficiency, ignore indices inside brackets [] in order to make array elements share handlers. - int key = property.serializedObject.targetObject.GetInstanceID() ^ property.hashCodeForPropertyPathWithoutArrayIndex; + int key = property.serializedObject.targetObject.GetInstanceID() ^ property.hashCodeForPropertyPath; if (property.propertyType == SerializedPropertyType.ObjectReference) { key ^= property.objectReferenceInstanceIDValue; diff --git a/Editor/Mono/Networking/PlayerConnection/AttachToPlayerGUI.cs b/Editor/Mono/Networking/PlayerConnection/AttachToPlayerGUI.cs index d8a39190c8..df9422f8f4 100644 --- a/Editor/Mono/Networking/PlayerConnection/AttachToPlayerGUI.cs +++ b/Editor/Mono/Networking/PlayerConnection/AttachToPlayerGUI.cs @@ -118,7 +118,7 @@ public string connectionName get { string name = ProfilerDriver.GetConnectionIdentifier(ProfilerDriver.connectedProfiler); - if (m_EditorModeTargetState.HasValue && name.StartsWith(k_EditorConnectionName)) + if (m_EditorModeTargetState.HasValue && name.Contains(k_EditorConnectionName)) { if (m_EditorModeTargetConnectionStatus(EditorConnectionTarget.MainEditorProcessEditmode)) name = Content.Editmode.text; @@ -177,7 +177,7 @@ private static void SuccessfullyConnectedToPlayer(string player, EditorConnectio } else { - if (player.StartsWith(k_EditorConnectionName)) + if (player.Contains(k_EditorConnectionName)) { // if e.g. the console or the memory profiler connects to the Editor, the profiler should switch to PlayMode profiling, not to Editmode profiling // especially since falling back onto the Editor is the default. @@ -261,9 +261,9 @@ void AddAvailablePlayerConnections(GenericMenu menuOptions, ref bool hasOpenConn } if (enabled) { - if (m_EditorModeTargetState.HasValue && name.StartsWith(k_EditorConnectionName)) + if (m_EditorModeTargetState.HasValue && name.Contains(k_EditorConnectionName)) { - menuOptions.AddItem(Content.Playmode, isConnected && m_EditorModeTargetConnectionStatus(EditorConnectionTarget.MainEditorProcessPlaymode), () => + menuOptions.AddItem(Content.Playmode, isConnected && !m_EditorModeTargetConnectionStatus(EditorConnectionTarget.MainEditorProcessPlaymode), () => { ProfilerDriver.connectedProfiler = guid; SuccessfullyConnectedToPlayer(connectionName, EditorConnectionTarget.MainEditorProcessPlaymode); diff --git a/Editor/Mono/ObjectListArea.cs b/Editor/Mono/ObjectListArea.cs index 0f6fae4ae5..000647191c 100644 --- a/Editor/Mono/ObjectListArea.cs +++ b/Editor/Mono/ObjectListArea.cs @@ -428,7 +428,8 @@ void RequeryAssetStore() void ClearAssetStoreGroups() { - EndRename(true); + if (!GetCreateAssetUtility().IsCreatingNewAsset()) + EndRename(true); m_Groups.Clear(); m_Groups.Add(m_LocalAssets); m_StoreAssets.Clear(); diff --git a/Editor/Mono/PlayerSettings.bindings.cs b/Editor/Mono/PlayerSettings.bindings.cs index 39c6319e8e..11e88ea9fa 100644 --- a/Editor/Mono/PlayerSettings.bindings.cs +++ b/Editor/Mono/PlayerSettings.bindings.cs @@ -978,7 +978,17 @@ public static bool graphicsJobs [StaticAccessor("PlayerSettingsBindings", StaticAccessorType.DoubleColon)] internal static extern void SetGraphicsJobsForPlatform(BuildTarget platform, bool graphicsJobs); - public static extern GraphicsJobMode graphicsJobMode { get; set; } + public static GraphicsJobMode graphicsJobMode + { + get { return GetGraphicsJobModeForPlatform(EditorUserBuildSettings.activeBuildTarget); } + set { SetGraphicsJobModeForPlatform(EditorUserBuildSettings.activeBuildTarget, value); } + } + + [StaticAccessor("PlayerSettingsBindings", StaticAccessorType.DoubleColon)] + internal static extern GraphicsJobMode GetGraphicsJobModeForPlatform(BuildTarget platform); + + [StaticAccessor("PlayerSettingsBindings", StaticAccessorType.DoubleColon)] + internal static extern void SetGraphicsJobModeForPlatform(BuildTarget platform, GraphicsJobMode gfxJobMode); [Obsolete("GetPlatformVuforiaEnabled(BuildTargetGroup targetGroup) has been deprecated. Use vuforiaEnabled instead.")] [StaticAccessor("PlayerSettingsBindings", StaticAccessorType.DoubleColon)] diff --git a/Editor/Mono/PreferencesWindow/PreferencesSettingsProviders.cs b/Editor/Mono/PreferencesWindow/PreferencesSettingsProviders.cs index 2c91f3308b..a8c62590ec 100644 --- a/Editor/Mono/PreferencesWindow/PreferencesSettingsProviders.cs +++ b/Editor/Mono/PreferencesWindow/PreferencesSettingsProviders.cs @@ -550,8 +550,7 @@ private void ShowGeneral(string searchContext) // Refresh skin to get new font Unsupported.ClearSkinCache(); - EditorResources.BuildCatalog(); - EditorUtility.RequestScriptReload(); + InternalEditorUtility.RequestScriptReload(); InternalEditorUtility.RepaintAllViews(); } } diff --git a/Editor/Mono/ProjectBrowserColumnOne.cs b/Editor/Mono/ProjectBrowserColumnOne.cs index 7656301614..9d4fd78feb 100644 --- a/Editor/Mono/ProjectBrowserColumnOne.cs +++ b/Editor/Mono/ProjectBrowserColumnOne.cs @@ -112,7 +112,17 @@ bool IsVisibleRootNode(TreeViewItem item) protected override Texture GetIconForItem(TreeViewItem item) { if (item != null && item.icon != null) - return item.icon; + { + var icon = item.icon; + AssetsTreeViewDataSource.FolderTreeItemBase folderItem = item as AssetsTreeViewDataSource.FolderTreeItemBase; + + if (folderItem != null && m_TreeView.data.IsExpanded(folderItem)) + { + icon = openFolderTexture; + } + + return icon; + } SearchFilterTreeItem searchFilterItem = item as SearchFilterTreeItem; if (searchFilterItem != null) @@ -265,6 +275,8 @@ public override void FetchData() m_RootItem = new TreeViewItem(0, 0, null, "Invisible Root Item"); SetExpanded(m_RootItem, true); // ensure always visible + Texture2D folderIcon = EditorGUIUtility.FindTexture(EditorResources.folderIconName); + // We want three roots: Favorites, Assets, and Saved Filters List visibleRoots = new List(); @@ -272,18 +284,16 @@ public override void FetchData() int assetsFolderInstanceID = AssetDatabase.GetMainAssetOrInProgressProxyInstanceID("Assets"); int depth = 0; string displayName = "Assets"; //CreateDisplayName (assetsFolderInstanceID); - TreeViewItem assetRootItem = new TreeViewItem(assetsFolderInstanceID, depth, m_RootItem, displayName); + AssetsTreeViewDataSource.RootTreeItem assetRootItem = new AssetsTreeViewDataSource.RootTreeItem(assetsFolderInstanceID, depth, m_RootItem, displayName); + assetRootItem.icon = folderIcon; ReadAssetDatabase("Assets", assetRootItem, depth + 1); // Fetch packages folder displayName = PackageManager.Folders.GetPackagesPath(); - TreeViewItem packagesRootItem = new TreeViewItem(ProjectBrowser.kPackagesFolderInstanceId, depth, m_RootItem, displayName); + AssetsTreeViewDataSource.RootTreeItem packagesRootItem = new AssetsTreeViewDataSource.RootTreeItem(ProjectBrowser.kPackagesFolderInstanceId, depth, m_RootItem, displayName); depth++; - Texture2D folderIcon = EditorGUIUtility.FindTexture(EditorResources.folderIconName); - Texture2D emptyFolderIcon = EditorGUIUtility.FindTexture(EditorResources.emptyFolderIconName); - - packagesRootItem.icon = emptyFolderIcon; + packagesRootItem.icon = folderIcon; var packages = PackageManagerUtilityInternal.GetAllVisiblePackages(skipHiddenPackages); foreach (var package in packages) @@ -291,10 +301,10 @@ public override void FetchData() var packageFolderInstanceId = AssetDatabase.GetMainAssetOrInProgressProxyInstanceID(package.assetPath); displayName = !string.IsNullOrEmpty(package.displayName) ? package.displayName : package.name; - TreeViewItem packageItem = new TreeViewItem(packageFolderInstanceId, depth, packagesRootItem, displayName); + AssetsTreeViewDataSource.PackageTreeItem packageItem = new AssetsTreeViewDataSource.PackageTreeItem(packageFolderInstanceId, depth, packagesRootItem, displayName); + packageItem.icon = folderIcon; packagesRootItem.AddChild(packageItem); ReadAssetDatabase(package.assetPath, packageItem, depth + 1); - packagesRootItem.icon = folderIcon; } // Fetch saved filters @@ -328,15 +338,14 @@ private void ReadAssetDatabase(string assetFolderRootPath, TreeViewItem parent, property.Reset(); Texture2D folderIcon = EditorGUIUtility.FindTexture(EditorResources.folderIconName); - Texture2D emptyFolderIcon = EditorGUIUtility.FindTexture(EditorResources.emptyFolderIconName); List allFolders = new List(); while (property.Next(null)) { if (property.isFolder) { - TreeViewItem folderItem = new TreeViewItem(property.instanceID, baseDepth + property.depth, null, property.name); - folderItem.icon = property.hasChildren ? folderIcon : emptyFolderIcon; + AssetsTreeViewDataSource.FolderTreeItem folderItem = new AssetsTreeViewDataSource.FolderTreeItem(property.guid, property.instanceID, baseDepth + property.depth, null, property.name); + folderItem.icon = folderIcon; allFolders.Add(folderItem); } } diff --git a/Editor/Mono/ProjectWindow/CachedFilteredHierachy.cs b/Editor/Mono/ProjectWindow/CachedFilteredHierachy.cs index 99ae81b1ec..ab112805c5 100644 --- a/Editor/Mono/ProjectWindow/CachedFilteredHierachy.cs +++ b/Editor/Mono/ProjectWindow/CachedFilteredHierachy.cs @@ -187,8 +187,6 @@ void CopyPropertyData(ref FilterResult result, HierarchyProperty property) // Otherwise, don't - as this may cause Textures to load unintendedly (e.g if we have 3000 search results we do not want to load icons before needed when rendering) if (!property.isMainRepresentation) result.icon = property.icon; - else if (property.isFolder && !property.hasChildren) - result.icon = EditorGUIUtility.FindTexture(EditorResources.emptyFolderIconName); else result.icon = null; diff --git a/Editor/Mono/ProjectWindow/ProjectWindowUtil.cs b/Editor/Mono/ProjectWindow/ProjectWindowUtil.cs index 6aa10d95f0..07c6cf3c49 100644 --- a/Editor/Mono/ProjectWindow/ProjectWindowUtil.cs +++ b/Editor/Mono/ProjectWindow/ProjectWindowUtil.cs @@ -259,14 +259,14 @@ public static void CreateAsset(Object asset, string pathName) // Create a folder public static void CreateFolder() { - StartNameEditingIfProjectWindowExists(0, ScriptableObject.CreateInstance(), "New Folder", EditorGUIUtility.IconContent(EditorResources.emptyFolderIconName).image as Texture2D, null); + StartNameEditingIfProjectWindowExists(0, ScriptableObject.CreateInstance(), "New Folder", EditorGUIUtility.IconContent(EditorResources.folderIconName).image as Texture2D, null); } internal static void CreateFolderWithTemplates(string defaultName, params string[] templates) { var endNameEditAction = ScriptableObject.CreateInstance(); endNameEditAction.templates = templates; - StartNameEditingIfProjectWindowExists(0, endNameEditAction, defaultName, EditorGUIUtility.IconContent(EditorResources.emptyFolderIconName).image as Texture2D, null); + StartNameEditingIfProjectWindowExists(0, endNameEditAction, defaultName, EditorGUIUtility.IconContent(EditorResources.folderIconName).image as Texture2D, null); } public static void CreateScene() diff --git a/Editor/Mono/ScriptAttributeGUI/PropertyHandler.cs b/Editor/Mono/ScriptAttributeGUI/PropertyHandler.cs index 4ccb6a8b45..cce7ce545c 100644 --- a/Editor/Mono/ScriptAttributeGUI/PropertyHandler.cs +++ b/Editor/Mono/ScriptAttributeGUI/PropertyHandler.cs @@ -43,7 +43,7 @@ public bool empty } } - public void HandleAttribute(PropertyAttribute attribute, FieldInfo field, Type propertyType) + public void HandleAttribute(SerializedProperty property, PropertyAttribute attribute, FieldInfo field, Type propertyType) { if (attribute is TooltipAttribute) { @@ -63,12 +63,12 @@ public void HandleAttribute(PropertyAttribute attribute, FieldInfo field, Type p } // Look for its drawer type of this attribute - HandleDrawnType(attribute.GetType(), propertyType, field, attribute); + HandleDrawnType(property, attribute.GetType(), propertyType, field, attribute); } - public void HandleDrawnType(Type drawnType, Type propertyType, FieldInfo field, PropertyAttribute attribute) + public void HandleDrawnType(SerializedProperty property, Type drawnType, Type propertyType, FieldInfo field, PropertyAttribute attribute) { - Type drawerType = ScriptAttributeUtility.GetDrawerTypeForType(drawnType); + Type drawerType = ScriptAttributeUtility.GetDrawerTypeForPropertyAndType(property, drawnType); // If we found a drawer type, instantiate the drawer, cache it, and return it. if (drawerType != null) diff --git a/Editor/Mono/ScriptAttributeGUI/ScriptAttributeUtility.cs b/Editor/Mono/ScriptAttributeGUI/ScriptAttributeUtility.cs index 95448db5da..311e189856 100644 --- a/Editor/Mono/ScriptAttributeGUI/ScriptAttributeUtility.cs +++ b/Editor/Mono/ScriptAttributeGUI/ScriptAttributeUtility.cs @@ -116,6 +116,15 @@ private static void BuildDrawerTypeForTypeDictionary() } } + /// + /// Builds the drawer cache and checks for the drawer cache for a statically + /// defined drawer for a given type. + /// NOTE: The world 'statically' in this context means that what is being + /// looked up is only what is in the cache, which might not play well with + /// Managed References types (where the dynamic type matters). + /// + /// + /// internal static Type GetDrawerTypeForType(Type type) { if (s_DrawerTypeForType == null) @@ -126,6 +135,7 @@ internal static Type GetDrawerTypeForType(Type type) if (drawerType.drawer != null) return drawerType.drawer; + // // now check for base generic versions of the drawers... if (type.IsGenericType) s_DrawerTypeForType.TryGetValue(type.GetGenericTypeDefinition(), out drawerType); @@ -133,6 +143,46 @@ internal static Type GetDrawerTypeForType(Type type) return drawerType.drawer; } + /// + /// Does the same thing as 'GetDrawerTypeForType' (with the same side effect of building the cache) + /// but also plays well with Managed References. If the property that is used as a reference for the drawer + /// query is of a managed reference type, the class parents are also looked up as fallbacks. + /// + /// + /// + /// The custom property drawer type or 'null' if not found. + internal static Type GetDrawerTypeForPropertyAndType(SerializedProperty property, Type type) + { + // As a side effect this also builds the drawer cache dict + var staticDrawerType = GetDrawerTypeForType(type); + if (staticDrawerType != null) + return staticDrawerType; + + // Special case for managed references. + // The custom property drawers for those are defined with 'useForChildren=false' + // (otherwise the dynamic type is not taking into account in the custom property + // drawer resolution) so even if 's_DrawerTypeForType' is built (based on static types) + // we have to check base types for custom property drawers manually. + // Managed references with no drawers should properly try to fallback + if (property.propertyType == SerializedPropertyType.ManagedReference) + { + DrawerKeySet drawerTypes; + + FieldInfo foundField = null; + for (Type currentType = type; foundField == null && currentType != null; currentType = currentType.BaseType) + { + s_DrawerTypeForType.TryGetValue(currentType, out drawerTypes); + if (drawerTypes.drawer != null) + { + s_DrawerTypeForType.Add(type, drawerTypes); + return drawerTypes.drawer; + } + } + } + + return null; + } + private static List GetFieldAttributes(FieldInfo field) { if (field == null) @@ -145,7 +195,14 @@ private static List GetFieldAttributes(FieldInfo field) return null; } - internal static FieldInfo GetFieldInfoFromProperty(SerializedProperty property, out Type type) + /// + /// Returns the field info and field type for the property. The types are based on the + /// static field definition. + /// + /// + /// + /// + internal static FieldInfo GetFieldInfoAndStaticTypeFromProperty(SerializedProperty property, out Type type) { var classType = GetScriptTypeFromProperty(property); if (classType == null) @@ -156,6 +213,60 @@ internal static FieldInfo GetFieldInfoFromProperty(SerializedProperty property, return GetFieldInfoFromPropertyPath(classType, property.propertyPath, out type); } + /// + /// Returns the field info and type for the property. Contrary to GetFieldInfoAndStaticTypeFromProperty, + /// when confronted with a managed reference the dynamic instance type is returned. + /// + /// + /// + /// + internal static FieldInfo GetFieldInfoFromProperty(SerializedProperty property, out Type type) + { + var fieldInfo = GetFieldInfoAndStaticTypeFromProperty(property, out type); + if (fieldInfo == null) + return fieldInfo; + + // Managed references are a special case, we need to override the static type + // returned by 'GetFieldInfoFromPropertyPath' for custom property handler matching + // by the dynamic type of the instance. + if (property.propertyType == SerializedPropertyType.ManagedReference) + { + Type managedReferenceInstanceType; + + // Try to get a Type instance for the managed reference + if (GetTypeFromManagedReferenceFullTypeName(property.managedReferenceFullTypename, out managedReferenceInstanceType)) + { + type = managedReferenceInstanceType; + } + + // We keep the fallback to the field type returned by 'GetFieldInfoFromPropertyPath'. + } + + return fieldInfo; + } + + /// + /// Create a Type instance from the managed reference full type name description. + /// The expected format for the typename string is the one returned by SerializedProperty.managedReferenceFullTypename. + /// + /// + /// + /// + internal static bool GetTypeFromManagedReferenceFullTypeName(string managedReferenceFullTypename, out Type managedReferenceInstanceType) + { + managedReferenceInstanceType = null; + + var parts = managedReferenceFullTypename.Split(' '); + if (parts.Length == 2) + { + var assemblyPart = parts[0]; + var nsClassnamePart = parts[1]; + managedReferenceInstanceType = Type.GetType($"{nsClassnamePart}, {assemblyPart}"); + } + + return managedReferenceInstanceType != null; + } + private static Type GetScriptTypeFromProperty(SerializedProperty property) { if (property.serializedObject.targetObject != null) @@ -305,12 +416,12 @@ internal static PropertyHandler GetHandler(SerializedProperty property) if (attributes != null) { for (int i = attributes.Count - 1; i >= 0; i--) - handler.HandleAttribute(attributes[i], field, propertyType); + handler.HandleAttribute(property, attributes[i], field, propertyType); } // Field has no CustomPropertyDrawer attribute with matching drawer so look for default drawer for field type if (!handler.hasPropertyDrawer && propertyType != null) - handler.HandleDrawnType(propertyType, propertyType, field, null); + handler.HandleDrawnType(property, propertyType, propertyType, field, null); if (handler.empty) { diff --git a/Editor/Mono/Scripting/ScriptCompilation/CSharpNamespaceParser.cs b/Editor/Mono/Scripting/ScriptCompilation/CSharpNamespaceParser.cs index edd8731646..c5c0c0c0bd 100644 --- a/Editor/Mono/Scripting/ScriptCompilation/CSharpNamespaceParser.cs +++ b/Editor/Mono/Scripting/ScriptCompilation/CSharpNamespaceParser.cs @@ -241,7 +241,7 @@ static bool CheckForNamespaceModification(Stack> namespaceScope { foreach (var tuple in namespaceScopeStack) { - if (tuple.Item1 && tuple.Item2 == stackCount) + if (tuple.Item1 && (stackCount == -1 || tuple.Item2 == stackCount)) return true; } return false; @@ -261,11 +261,11 @@ static string ReduceCodeAndCheckForNamespacesModification(string source, string foreach (var s in split) { - // Check for new namespace deeper than top level of directives + // Check for new namespace declarations, when we have are inside of multiple #ifs, + // and also have seen previous namespace declarations (likely namespace modification). if (k_Namespace.IsMatch(s)) { - if (stack.Count > 1) - namespaceModificationFound = true; + namespaceModificationFound |= (stack.Count > 1 && CheckForNamespaceModification(namespaceScopeStack, -1)); foundNamespace = true; } if (s.IndexOf("{", StringComparison.Ordinal) >= 0) @@ -291,7 +291,7 @@ static string ReduceCodeAndCheckForNamespacesModification(string source, string var directive = match.Groups[1].Value; if (directive == "else") { - namespaceModificationFound = CheckForNamespaceModification(namespaceScopeStack, stack.Count); + namespaceModificationFound |= CheckForNamespaceModification(namespaceScopeStack, stack.Count); var elseEmitting = stack.Peek().Item2; stack.Pop(); stack.Push(new Tuple(elseEmitting, false)); @@ -316,7 +316,7 @@ static string ReduceCodeAndCheckForNamespacesModification(string source, string } else if (directive == "elif") { - namespaceModificationFound = CheckForNamespaceModification(namespaceScopeStack, stack.Count); + namespaceModificationFound |= CheckForNamespaceModification(namespaceScopeStack, stack.Count); var evalResult = true; var elseEmitting = stack.Peek().Item2; stack.Pop(); diff --git a/Editor/Mono/Scripting/ScriptCompilation/DefineConstraintsHelper.cs b/Editor/Mono/Scripting/ScriptCompilation/DefineConstraintsHelper.cs index 5af84d5a28..22cdef182b 100644 --- a/Editor/Mono/Scripting/ScriptCompilation/DefineConstraintsHelper.cs +++ b/Editor/Mono/Scripting/ScriptCompilation/DefineConstraintsHelper.cs @@ -2,18 +2,21 @@ // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License -using System; using System.Collections.Generic; using System.Linq; +using System.Text.RegularExpressions; using UnityEngine.Scripting; namespace UnityEditor.Scripting.ScriptCompilation { - internal static class DefineConstraintsHelper + static class DefineConstraintsHelper { public const string Not = "!"; public const string Or = "||"; + public static readonly char[] k_ValidWhitespaces = { ' ', '\t' }; + public static readonly char[] k_Whitespaces = { ' ', '\t', '\r', '\n' }; + [RequiredByNativeCode] public static bool IsDefineConstraintsCompatible(string[] defines, string[] defineConstraints) { @@ -40,10 +43,26 @@ static void GetDefineConstraintsValidity(string[] defines, string[] defineConstr internal static bool IsDefineConstraintValid(string[] defines, string defineConstraints) { - var splitDefines = new HashSet(defineConstraints.Split(new[] { Or }, StringSplitOptions.RemoveEmptyEntries)); + // Split by "||" (OR) and keep it in the resulting array + var splitDefines = Regex.Split(defineConstraints, "(\\|\\|)"); + + // Trim what we consider valid space characters + for (var i = 0; i < splitDefines.Length; ++i) + { + splitDefines[i] = splitDefines[i].Trim(k_ValidWhitespaces); + } + + // Check for consecutive Or + for (var i = 0; i < splitDefines.Length; ++i) + { + if (splitDefines[i] == Or && (i < splitDefines.Length - 1 && splitDefines[i + 1] == Or)) + { + return false; + } + } - var notExpectedDefines = new HashSet(splitDefines.Where(x => x.StartsWith(Not)).Select(x => x.Substring(1))); - var expectedDefines = new HashSet(splitDefines.Where(x => !x.StartsWith(Not))); + var notExpectedDefines = new HashSet(splitDefines.Where(x => x.StartsWith(Not.ToString()) && x != Or).Select(x => x.Substring(1))); + var expectedDefines = new HashSet(splitDefines.Where(x => !x.StartsWith(Not.ToString()) && x != Or)); if (defines == null) { @@ -51,9 +70,26 @@ internal static bool IsDefineConstraintValid(string[] defines, string defineCons { return false; } + return true; } + foreach (var define in expectedDefines) + { + if (!IsTokenValid(define)) + { + return false; + } + } + + foreach (var define in notExpectedDefines) + { + if (!IsTokenValid(define)) + { + return false; + } + } + if (expectedDefines.Overlaps(notExpectedDefines)) { var complement = new HashSet(expectedDefines); @@ -73,5 +109,20 @@ internal static bool IsDefineConstraintValid(string[] defines, string defineCons return expectedDefines.Any(defines.Contains) || !notExpectedDefines.Any(defines.Contains); } + + static bool IsTokenValid(string token) + { + if (string.IsNullOrEmpty(token)) + { + return false; + } + + if (k_Whitespaces.Any(token.Contains)) + { + return false; + } + + return true; + } } } diff --git a/Editor/Mono/Scripting/ScriptCompilation/SemVersion.cs b/Editor/Mono/Scripting/ScriptCompilation/SemVersion.cs index 2e02640757..c8332c2b62 100644 --- a/Editor/Mono/Scripting/ScriptCompilation/SemVersion.cs +++ b/Editor/Mono/Scripting/ScriptCompilation/SemVersion.cs @@ -129,7 +129,10 @@ private static int CompareExtension(string current, string other, bool lower = f if (currentPartIsNumber && otherPartIsNumber) { result = currentNumber.CompareTo(otherNumber); - if (result != 0) return currentPart.CompareTo(otherNumber); + if (result != 0) + { + return result; + } } else { @@ -145,10 +148,11 @@ private static int CompareExtension(string current, string other, bool lower = f result = string.CompareOrdinal(currentPart, otherPart); if (result != 0) + { return result; + } } } - return currentParts.Length.CompareTo(otherParts.Length); } diff --git a/Editor/Mono/SerializedProperty.bindings.cs b/Editor/Mono/SerializedProperty.bindings.cs index 4bbb965dac..4907078bac 100644 --- a/Editor/Mono/SerializedProperty.bindings.cs +++ b/Editor/Mono/SerializedProperty.bindings.cs @@ -9,6 +9,8 @@ using UnityEngine.Bindings; using UnityObject = UnityEngine.Object; +using System.Reflection; +using System.Text.RegularExpressions; namespace UnityEditor { @@ -74,6 +76,9 @@ public enum SerializedPropertyType // Bounds with int property. BoundsInt = 23, + + // Managed reference property. + ManagedReference = 24, } [NativeHeader("Editor/Src/Utility/SerializedProperty.h")] @@ -487,6 +492,28 @@ public string propertyPath [NativeName("GetPropertyPath")] private extern string GetPropertyPathInternal(); + /// + /// This is a more generic and less specialized form as the one below + /// 'hashCodeForPropertyPathWithoutArrayIndex' that assumes that + /// we dont have managed references. + /// + internal int hashCodeForPropertyPath + { + get + { + Verify(); + + // For managed references we cannot ignore the array index since + // instances might change from index to index. + if (propertyType == SerializedPropertyType.ManagedReference) + { + return GetHashCodeForPropertyPathInternal(); + } + + return hashCodeForPropertyPathWithoutArrayIndex; + } + } + internal int hashCodeForPropertyPathWithoutArrayIndex { get @@ -499,6 +526,9 @@ internal int hashCodeForPropertyPathWithoutArrayIndex [NativeName("GetHasCodeForPropertyPathWithoutArrayIndex")] private extern int GetHasCodeForPropertyPathWithoutArrayIndexInternal(); + [NativeName("GetHashCodeForPropertyPath")] + private extern int GetHashCodeForPropertyPathInternal(); + // Is this property editable? (RO) public bool editable { @@ -863,6 +893,87 @@ public UnityObject objectReferenceValue } } + // Value of an object reference property. + public object managedReferenceValue + { + set + { + if (propertyType != SerializedPropertyType.ManagedReference) + { + throw new System.InvalidOperationException( + $"Attempting to set the managed reference value on a SerializedProperty that is set to a '{this.type}'"); + } + + // Make sure that the underlying base type is compatible with the current object + Type type; + var fieldInfo = UnityEditor.ScriptAttributeUtility.GetFieldInfoAndStaticTypeFromProperty(this, out type); + var propertyBaseType = type; + + if (!propertyBaseType.IsAssignableFrom(value.GetType())) + { + throw new System.InvalidOperationException( + $"Cannot assign an object of type '{value.GetType().Name}' to a managed reference with a base type of '{propertyBaseType.Name}': types are not compatible"); + } + + Verify(VerifyFlags.IteratorNotAtEnd); + SetManagedReferenceValueInternal(value); + } + // No getter for managed reference since it adds a few notions that might be hard to reason about from the user's perspective. + // A serializedobject is a serialized version of a UnityObject. A serialized property is a view on that serialized object. + // All operations and all that a serialized knows about are around that serialzied view of the managed world. + // Managed references in the context of SO/SP does not have the same semantics as C# references. All the references are + // serialized properly but the original C# reference is lost in the process and cannot be retrieved unless e.g. cached locally + // on the C# side (i.e. in this file). BUT this would not world properly or involve tricky reconstruction with domain reloads + // after which all those C# references would have to be reconstructed etc. + // So for now we dont allow getting a managed reference. + + // If we ever wanted to add one though, the approach would be to add an explicit setting that takes an reference ID and an object instance as + // input, this would allow serialized properties to edit relations between managed references. We would also need a method to get + // an instance from a managed reference id along with a nice safe API around it. + } + + // Dynamic type for the current managed reference. + public string managedReferenceFullTypename + { + get + { + if (propertyType != SerializedPropertyType.ManagedReference) + { + throw new System.InvalidOperationException( + $"Attempting to get the managed reference full typename on a SerializedProperty that is set to a '{this.type}'"); + } + if (serializedObject.targetObject == null) + { + return null; + } + return GetManagedReferenceFullTypeNameInternal(); + } + } + + // Static type for the current managed reference. + public string managedReferenceFieldTypename + { + get + { + if (propertyType != SerializedPropertyType.ManagedReference) + { + throw new System.InvalidOperationException( + $"Attempting to get the managed reference full typename on a SerializedProperty that is set to a '{this.type}'"); + } + + Type type; + var fieldInfo = UnityEditor.ScriptAttributeUtility.GetFieldInfoAndStaticTypeFromProperty(this, out type); + + return $"{type.Assembly.GetName().Name} {type.FullName.Replace("+", "/")}"; + } + } + + [NativeName("GetManagedReferenceFullTypeName")] + private extern string GetManagedReferenceFullTypeNameInternal(); + + [NativeName("SetManagedReferenceValue")] + private extern void SetManagedReferenceValueInternal(object value); + [NativeName("GetPPtrValue")] private extern UnityObject GetPPtrValueInternal(); diff --git a/Editor/Mono/Settings/SettingsService.cs b/Editor/Mono/Settings/SettingsService.cs index af49bc0371..958acd9a7b 100644 --- a/Editor/Mono/Settings/SettingsService.cs +++ b/Editor/Mono/Settings/SettingsService.cs @@ -32,8 +32,11 @@ public static void NotifySettingsProviderChanged() const string k_ProjectSettings = "Edit/Project Settings"; static SettingsService() { - EditorApplication.update -= CheckProjectSettings; - EditorApplication.update += CheckProjectSettings; + if (Unity.MPE.ProcessService.level == Unity.MPE.ProcessLevel.UMP_MASTER) + { + EditorApplication.update -= CheckProjectSettings; + EditorApplication.update += CheckProjectSettings; + } } internal static event Action settingsProviderChanged; diff --git a/Editor/Mono/SyncProject.cs b/Editor/Mono/SyncProject.cs index 7a7a2438e6..6808dac155 100644 --- a/Editor/Mono/SyncProject.cs +++ b/Editor/Mono/SyncProject.cs @@ -12,6 +12,7 @@ using UnityEngine; using UnityEngine.Scripting; using UnityEditorInternal; +using Unity.CodeEditor; namespace UnityEditor { @@ -42,10 +43,14 @@ public VisualStudioPath(string path, string edition = "") [InitializeOnLoad] internal partial class SyncVS : AssetPostprocessor { + static bool s_Enabled = false; static bool s_AlreadySyncedThisDomainReload; static SyncVS() { + s_Enabled = Unity.MPE.ProcessService.level == Unity.MPE.ProcessLevel.UMP_MASTER; + if (!s_Enabled) + return; Synchronizer = new SolutionSynchronizer(Directory.GetParent(Application.dataPath).FullName, new SolutionSynchronizationSettings()); try { @@ -160,7 +165,11 @@ public void OnActiveBuildTargetChanged(BuildTarget oldTarget, BuildTarget newTar [RequiredByNativeCode] public static void SyncVisualStudioProjectIfItAlreadyExists() { - if (Synchronizer.SolutionExists()) + if (!s_Enabled) + return; + + #pragma warning disable 618 + if (Synchronizer.SolutionExists() && ScriptEditorUtility.GetScriptEditorFromPath(CodeEditor.CurrentEditorInstallation) != ScriptEditorUtility.ScriptEditor.Other) { Synchronizer.Sync(); } @@ -174,6 +183,9 @@ public static void PostprocessSyncProject( string[] movedAssets, string[] movedFromAssetPaths) { + if (!s_Enabled) + return; + Synchronizer.SyncIfNeeded(addedAssets.Union(deletedAssets.Union(movedAssets.Union(movedFromAssetPaths))), importedAssets); } diff --git a/Editor/Mono/UIElements/Controls/BindingExtensions.cs b/Editor/Mono/UIElements/Controls/BindingExtensions.cs index 9821996507..b7909040e9 100644 --- a/Editor/Mono/UIElements/Controls/BindingExtensions.cs +++ b/Editor/Mono/UIElements/Controls/BindingExtensions.cs @@ -115,7 +115,14 @@ public static void Unbind(this VisualElement element) public static SerializedProperty BindProperty(this IBindable field, SerializedObject obj) { - return BindPropertyWithParent(field, new SerializedObjectUpdateWrapper(obj), null); + var property = obj?.FindProperty(field.bindingPath); + + if (property != null) + { + Bind(field as VisualElement, new SerializedObjectUpdateWrapper(obj), null); + } + + return property; } public static void BindProperty(this IBindable field, SerializedProperty property) @@ -125,7 +132,8 @@ public static void BindProperty(this IBindable field, SerializedProperty propert throw new ArgumentNullException(nameof(property)); } - DoBindProperty(field, new SerializedObjectUpdateWrapper(property.serializedObject), property); + field.bindingPath = property.propertyPath; + Bind(field as VisualElement, new SerializedObjectUpdateWrapper(property.serializedObject), null); } private static void DoBindProperty(IBindable field, SerializedObjectUpdateWrapper obj, SerializedProperty property) @@ -916,7 +924,7 @@ protected void UpdateFieldIsAttached() else { //we're not dealing with VisualElement - if (!isFieldAttached) + if (m_Field != null && !isFieldAttached) { isFieldAttached = true; ResetCachedValues(); diff --git a/Editor/Mono/UIElements/Controls/ListViewBindings.cs b/Editor/Mono/UIElements/Controls/ListViewBindings.cs index 56706556bd..005f432291 100644 --- a/Editor/Mono/UIElements/Controls/ListViewBindings.cs +++ b/Editor/Mono/UIElements/Controls/ListViewBindings.cs @@ -43,12 +43,12 @@ protected void SetBinding(ListView listView, if (listView.makeItem == null) { - listView.makeItem = MakeListViewItem; + listView.makeItem = () => MakeListViewItem(); } if (listView.bindItem == null) { - listView.bindItem = BindListViewItem; + listView.bindItem = (v, i) => BindListViewItem(v, i); } listView.itemsSource = m_DataList; diff --git a/Editor/Mono/UIElements/Controls/PopupField.cs b/Editor/Mono/UIElements/Controls/PopupField.cs index 3d31175b02..548b56162a 100644 --- a/Editor/Mono/UIElements/Controls/PopupField.cs +++ b/Editor/Mono/UIElements/Controls/PopupField.cs @@ -89,6 +89,10 @@ public int index public new static readonly string labelUssClassName = ussClassName + "__label"; public new static readonly string inputUssClassName = ussClassName + "__input"; + public PopupField() + : this(null) + {} + public PopupField(string label = null) : base(label) { diff --git a/Editor/Mono/VersionControl/Common/VCProvider.cs b/Editor/Mono/VersionControl/Common/VCProvider.cs index 76266a378d..c6754c414d 100644 --- a/Editor/Mono/VersionControl/Common/VCProvider.cs +++ b/Editor/Mono/VersionControl/Common/VCProvider.cs @@ -184,12 +184,22 @@ static private Task CheckCallbackAndCheckout(AssetList assets, CheckoutMode mode return Internal_Checkout(consolidatedAssetList.ToArray(), mode, changeset); } - static public Task Checkout(AssetList assets, CheckoutMode mode, ChangeSet changeset = null) + static public Task Checkout(AssetList assets, CheckoutMode mode) + { + return Checkout(assets, mode, null); + } + + static public Task Checkout(AssetList assets, CheckoutMode mode, ChangeSet changeset) { return CheckCallbackAndCheckout(assets, mode, changeset); } - static public Task Checkout(string[] assets, CheckoutMode mode, ChangeSet changeset = null) + static public Task Checkout(string[] assets, CheckoutMode mode) + { + return Checkout(assets, mode, null); + } + + static public Task Checkout(string[] assets, CheckoutMode mode, ChangeSet changeset) { var assetList = new AssetList(); foreach (var path in assets) @@ -201,7 +211,12 @@ static public Task Checkout(string[] assets, CheckoutMode mode, ChangeSet change return CheckCallbackAndCheckout(assetList, mode, changeset); } - static public Task Checkout(Object[] assets, CheckoutMode mode, ChangeSet changeset = null) + static public Task Checkout(Object[] assets, CheckoutMode mode) + { + return Checkout(assets, mode, null); + } + + static public Task Checkout(Object[] assets, CheckoutMode mode, ChangeSet changeset) { var assetList = new AssetList(); foreach (var o in assets) @@ -222,7 +237,12 @@ static public bool CheckoutIsValid(Asset asset, CheckoutMode mode) return Internal_CheckoutIsValid(new Asset[] { asset }, mode); } - static public Task Checkout(Asset asset, CheckoutMode mode, ChangeSet changeset = null) + static public Task Checkout(Asset asset, CheckoutMode mode) + { + return Checkout(asset, mode, null); + } + + static public Task Checkout(Asset asset, CheckoutMode mode, ChangeSet changeset) { var assetList = new AssetList(); assetList.Add(asset); @@ -230,7 +250,12 @@ static public Task Checkout(Asset asset, CheckoutMode mode, ChangeSet changeset return CheckCallbackAndCheckout(assetList, mode, changeset); } - static public Task Checkout(string asset, CheckoutMode mode, ChangeSet changeset = null) + static public Task Checkout(string asset, CheckoutMode mode) + { + return Checkout(asset, mode, null); + } + + static public Task Checkout(string asset, CheckoutMode mode, ChangeSet changeset) { var assetList = new AssetList(); assetList.Add(GetAssetByPath(asset)); @@ -238,7 +263,12 @@ static public Task Checkout(string asset, CheckoutMode mode, ChangeSet changeset return CheckCallbackAndCheckout(assetList, mode, changeset); } - static public Task Checkout(UnityEngine.Object asset, CheckoutMode mode, ChangeSet changeset = null) + static public Task Checkout(UnityEngine.Object asset, CheckoutMode mode) + { + return Checkout(asset, mode, null); + } + + static public Task Checkout(UnityEngine.Object asset, CheckoutMode mode, ChangeSet changeset) { var path = AssetDatabase.GetAssetPath(asset); var vcasset = GetAssetByPath(path); diff --git a/Editor/Mono/VersionControl/UI/VCMenuPending.cs b/Editor/Mono/VersionControl/UI/VCMenuPending.cs index f324d0a51a..3b839ea279 100644 --- a/Editor/Mono/VersionControl/UI/VCMenuPending.cs +++ b/Editor/Mono/VersionControl/UI/VCMenuPending.cs @@ -37,7 +37,7 @@ static void Revert(int userData) //[MenuItem ("CONTEXT/Pending/Revert Unchanged", true, 201)] static bool RevertUnchangedTest(int userData) { - return Provider.RevertIsValid(ListControl.FromID(userData).SelectedAssets, RevertMode.Normal); + return Provider.RevertIsValid(ListControl.FromID(userData).SelectedAssets, RevertMode.Unchanged); } //[MenuItem ("CONTEXT/Pending/Revert Unchanged", false, 201)] diff --git a/Editor/Mono/VersionControl/UI/VCMenuProject.cs b/Editor/Mono/VersionControl/UI/VCMenuProject.cs index 5bd45a8543..efce119452 100644 --- a/Editor/Mono/VersionControl/UI/VCMenuProject.cs +++ b/Editor/Mono/VersionControl/UI/VCMenuProject.cs @@ -153,7 +153,7 @@ static void Revert(ShortcutArguments args) static bool RevertUnchangedTest(MenuCommand cmd) { AssetList selected = Provider.GetAssetListFromSelection(); - return Provider.enabled && Provider.RevertIsValid(selected, RevertMode.Normal); + return Provider.enabled && Provider.RevertIsValid(selected, RevertMode.Unchanged); } // Called from native class VCSAssetMenuHandler as "Assets/Version Control/Revert Unchanged" menu handler diff --git a/External/MonoBleedingEdge/builds/monodistribution/lib/mono/4.5/Facades/netstandard.dll b/External/MonoBleedingEdge/builds/monodistribution/lib/mono/4.5/Facades/netstandard.dll index 73897a6699cd532c5d41a77b2dc1985ccd825867..9350e84c4aa8cd85d1264c30601947772ed7cbcd 100644 GIT binary patch literal 84992 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

K{sU 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 diff --git a/Modules/AssetDatabase/Editor/ScriptBindings/AssetDatabase.bindings.cs b/Modules/AssetDatabase/Editor/ScriptBindings/AssetDatabase.bindings.cs index 1d87e64c84..71fa85c1c0 100644 --- a/Modules/AssetDatabase/Editor/ScriptBindings/AssetDatabase.bindings.cs +++ b/Modules/AssetDatabase/Editor/ScriptBindings/AssetDatabase.bindings.cs @@ -292,72 +292,66 @@ public static void ExportPackage(string assetPathName, string fileName, ExportPa extern internal static string GetUniquePathNameAtSelectedPath(string fileName); - [System.Obsolete("AssetDatabase.IsOpenForEdit without StatusQueryOptions has been deprecated. Use the version with StatusQueryOptions instead. This will always request the cached status (StatusQueryOptions.UseCachedIfPossible)")] - public static bool IsOpenForEdit(UnityEngine.Object assetObject) + [uei.ExcludeFromDocs] public static bool IsOpenForEdit(UnityEngine.Object assetObject) { return IsOpenForEdit(assetObject, StatusQueryOptions.UseCachedIfPossible); } - public static bool IsOpenForEdit(UnityEngine.Object assetObject, StatusQueryOptions StatusQueryOptions) + public static bool IsOpenForEdit(UnityEngine.Object assetObject, [uei.DefaultValue("StatusQueryOptions.UseCachedIfPossible")] StatusQueryOptions statusOptions) { string assetPath = GetAssetOrScenePath(assetObject); - return IsOpenForEdit(assetPath, StatusQueryOptions); + return IsOpenForEdit(assetPath, statusOptions); } - [System.Obsolete("AssetDatabase.IsOpenForEdit without StatusQueryOptions has been deprecated. Use the version with StatusQueryOptions instead. This will always request the cached status (StatusQueryOptions.UseCachedIfPossible)")] - public static bool IsOpenForEdit(string assetOrMetaFilePath) + [uei.ExcludeFromDocs] public static bool IsOpenForEdit(string assetOrMetaFilePath) { return IsOpenForEdit(assetOrMetaFilePath, StatusQueryOptions.UseCachedIfPossible); } - public static bool IsOpenForEdit(string assetOrMetaFilePath, StatusQueryOptions StatusQueryOptions) + public static bool IsOpenForEdit(string assetOrMetaFilePath, [uei.DefaultValue("StatusQueryOptions.UseCachedIfPossible")] StatusQueryOptions statusOptions) { string message; - return IsOpenForEdit(assetOrMetaFilePath, out message, StatusQueryOptions); + return IsOpenForEdit(assetOrMetaFilePath, out message, statusOptions); } - [System.Obsolete("AssetDatabase.IsOpenForEdit without StatusQueryOptions has been deprecated. Use the version with StatusQueryOptions instead. This will always request the cached status (StatusQueryOptions.UseCachedIfPossible)")] - public static bool IsOpenForEdit(UnityEngine.Object assetObject, out string message) + [uei.ExcludeFromDocs] public static bool IsOpenForEdit(UnityEngine.Object assetObject, out string message) { return IsOpenForEdit(assetObject, out message, StatusQueryOptions.UseCachedIfPossible); } - public static bool IsOpenForEdit(UnityEngine.Object assetObject, out string message, StatusQueryOptions statusOptions) + public static bool IsOpenForEdit(UnityEngine.Object assetObject, out string message, [uei.DefaultValue("StatusQueryOptions.UseCachedIfPossible")] StatusQueryOptions statusOptions) { string assetPath = GetAssetOrScenePath(assetObject); return IsOpenForEdit(assetPath, out message, statusOptions); } - [System.Obsolete("AssetDatabase.IsOpenForEdit without StatusQueryOptions has been deprecated. Use the version with StatusQueryOptions instead. This will always request the cached status (StatusQueryOptions.UseCachedIfPossible)")] - public static bool IsOpenForEdit(string assetOrMetaFilePath, out string message) + [uei.ExcludeFromDocs] public static bool IsOpenForEdit(string assetOrMetaFilePath, out string message) { return IsOpenForEdit(assetOrMetaFilePath, out message, StatusQueryOptions.UseCachedIfPossible); } - public static bool IsOpenForEdit(string assetOrMetaFilePath, out string message, StatusQueryOptions statusOptions) + public static bool IsOpenForEdit(string assetOrMetaFilePath, out string message, [uei.DefaultValue("StatusQueryOptions.UseCachedIfPossible")] StatusQueryOptions statusOptions) { return AssetModificationProcessorInternal.IsOpenForEdit(assetOrMetaFilePath, out message, statusOptions); } - [System.Obsolete("AssetDatabase.IsMetaFileOpenForEdit without StatusQueryOptions has been deprecated. Use the version with StatusQueryOptions instead. This will always request the cached status (StatusQueryOptions.UseCachedIfPossible)")] - public static bool IsMetaFileOpenForEdit(UnityEngine.Object assetObject) + [uei.ExcludeFromDocs] public static bool IsMetaFileOpenForEdit(UnityEngine.Object assetObject) { return IsMetaFileOpenForEdit(assetObject, StatusQueryOptions.UseCachedIfPossible); } - public static bool IsMetaFileOpenForEdit(UnityEngine.Object assetObject, StatusQueryOptions statusOptions) + public static bool IsMetaFileOpenForEdit(UnityEngine.Object assetObject, [uei.DefaultValue("StatusQueryOptions.UseCachedIfPossible")] StatusQueryOptions statusOptions) { string message; return IsMetaFileOpenForEdit(assetObject, out message, statusOptions); } - [System.Obsolete("AssetDatabase.IsMetaFileOpenForEdit without StatusQueryOptions has been deprecated. Use the version with StatusQueryOptions instead. This will always request the cached status (StatusQueryOptions.UseCachedIfPossible)")] - public static bool IsMetaFileOpenForEdit(UnityEngine.Object assetObject, out string message) + [uei.ExcludeFromDocs] public static bool IsMetaFileOpenForEdit(UnityEngine.Object assetObject, out string message) { return IsMetaFileOpenForEdit(assetObject, out message, StatusQueryOptions.UseCachedIfPossible); } - public static bool IsMetaFileOpenForEdit(UnityEngine.Object assetObject, out string message, StatusQueryOptions statusOptions) + public static bool IsMetaFileOpenForEdit(UnityEngine.Object assetObject, out string message, [uei.DefaultValue("StatusQueryOptions.UseCachedIfPossible")] StatusQueryOptions statusOptions) { string assetPath = GetAssetOrScenePath(assetObject); string metaPath = AssetDatabase.GetTextMetaFilePathFromAssetPath(assetPath); diff --git a/Modules/GraphViewEditor/Views/GraphView.cs b/Modules/GraphViewEditor/Views/GraphView.cs index dd158284be..56a637b861 100644 --- a/Modules/GraphViewEditor/Views/GraphView.cs +++ b/Modules/GraphViewEditor/Views/GraphView.cs @@ -283,9 +283,9 @@ protected GraphView() Font graphViewFont = EditorGUIUtility.LoadRequired("GraphView/DummyFont(LucidaGrande).ttf") as Font; if (Application.platform == RuntimePlatform.WindowsEditor) - graphViewFont.fontNames = new string[] { "Segoe UI", "Helvetica Neue", "Helvetica", "Arial", "Verdana" }; + graphViewFont.fontNames = new string[] { "Verdana" }; else if (Application.platform == RuntimePlatform.OSXEditor) - graphViewFont.fontNames = new string[] { "Helvetica Neue", "Lucida Grande" }; + graphViewFont.fontNames = new string[] { "Lucida Grande" }; m_FontsOverridden = true; } diff --git a/Modules/IMGUI/GUIStyle.cs b/Modules/IMGUI/GUIStyle.cs index 2a3c86689b..3869ebc7ba 100644 --- a/Modules/IMGUI/GUIStyle.cs +++ b/Modules/IMGUI/GUIStyle.cs @@ -273,7 +273,7 @@ public void Draw(Rect position, GUIContent content, int controlID, bool on) public void Draw(Rect position, GUIContent content, int controlID, bool on, bool hover) { - Draw(position, content, controlID, hover, false, on, controlID == GUIUtility.keyboardControl); + Draw(position, content, controlID, hover, GUIUtility.hotControl == controlID, on, GUIUtility.HasKeyFocus(controlID)); } private void Draw(Rect position, GUIContent content, int controlId, bool isHover, bool isActive, bool on, bool hasKeyboardFocus) diff --git a/Modules/IMGUI/GUIUtility.cs b/Modules/IMGUI/GUIUtility.cs index 08268a5516..e0d0f51bd2 100644 --- a/Modules/IMGUI/GUIUtility.cs +++ b/Modules/IMGUI/GUIUtility.cs @@ -120,6 +120,14 @@ public static int keyboardControl } } + internal static Func s_HasCurrentWindowKeyFocusFunc; + + internal static bool HasKeyFocus(int controlID) + { + return controlID == GUIUtility.keyboardControl && + (s_HasCurrentWindowKeyFocusFunc != null ? s_HasCurrentWindowKeyFocusFunc() : true); + } + //*undocumented* public static void ExitGUI() { diff --git a/Modules/PackageManagerUI/Editor/Services/AssetStore/AssetStoreClient.cs b/Modules/PackageManagerUI/Editor/Services/AssetStore/AssetStoreClient.cs index 03faf25f5c..d5132e5482 100644 --- a/Modules/PackageManagerUI/Editor/Services/AssetStore/AssetStoreClient.cs +++ b/Modules/PackageManagerUI/Editor/Services/AssetStore/AssetStoreClient.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Linq; using UnityEngine; -using AssetStorePackageInfo = UnityEditor.PackageInfo; namespace UnityEditor.PackageManager.UI.AssetStore { @@ -21,6 +20,7 @@ internal class AssetStoreClientInternal : ScriptableSingleton> onPackagesChanged = delegate {}; + public event Action onPackageVersionUpdated = delegate {}; public event Action onDownloadProgress = delegate {}; public event Action onListOperationStart = delegate {}; @@ -35,57 +35,34 @@ internal class AssetStoreClientInternal : ScriptableSingleton m_Downloads = new Dictionary(); - private Dictionary m_UpdateDetails = new Dictionary(); + private Dictionary m_FetchedInfos = new Dictionary(); - private HashSet m_PackageDetailsFetched = new HashSet(); + private Dictionary m_LocalInfos = new Dictionary(); [SerializeField] - private string[] m_SerializedUpdateDetailKeys = new string[0]; - - [SerializeField] - private PackageState[] m_SerializedUpdateDetailValues = new PackageState[0]; + private DownloadProgress[] m_SerializedDownloads = new DownloadProgress[0]; [SerializeField] - private DownloadProgress[] m_SerializedDownloads = new DownloadProgress[0]; + private FetchedInfo[] m_SerializedFetchedInfos = new FetchedInfo[0]; [SerializeField] - private long[] m_SerializedPackageDetailsFetched; + private LocalInfo[] m_SerializedLocalInfos = new LocalInfo[0]; [SerializeField] private bool m_SetupDone; public void OnAfterDeserialize() { - m_Downloads.Clear(); - foreach (var p in m_SerializedDownloads) - { - m_Downloads[p.packageId] = p; - } - - m_UpdateDetails.Clear(); - for (var i = 0; i < m_SerializedUpdateDetailKeys.Length; i++) - { - m_UpdateDetails[m_SerializedUpdateDetailKeys[i]] = m_SerializedUpdateDetailValues[i]; - } - - m_PackageDetailsFetched = new HashSet(m_SerializedPackageDetailsFetched); + m_Downloads = m_SerializedDownloads.ToDictionary(d => d.packageId, d => d); + m_FetchedInfos = m_SerializedFetchedInfos.ToDictionary(info => info.id, info => info); + m_LocalInfos = m_SerializedLocalInfos.ToDictionary(info => info.id, info => info); } public void OnBeforeSerialize() { m_SerializedDownloads = m_Downloads.Values.ToArray(); - - m_SerializedUpdateDetailKeys = new string[m_UpdateDetails.Count]; - m_SerializedUpdateDetailValues = new PackageState[m_UpdateDetails.Count]; - var i = 0; - foreach (var kp in m_UpdateDetails) - { - m_SerializedUpdateDetailKeys[i] = kp.Key; - m_SerializedUpdateDetailValues[i] = kp.Value; - i++; - } - - m_SerializedPackageDetailsFetched = m_PackageDetailsFetched.ToArray(); + m_SerializedFetchedInfos = m_FetchedInfos.Values.ToArray(); + m_SerializedLocalInfos = m_LocalInfos.Values.ToArray(); } public void Fetch(long productId) @@ -96,25 +73,19 @@ public void Fetch(long productId) return; } + RefreshLocalInfos(); + var id = productId.ToString(); - var localPackages = GetLocalPackages(); - if (localPackages.ContainsKey(id)) - RefreshProductUpdateDetails(new Dictionary { { id, localPackages[id] } }, () => { FetchInternal(localPackages, productId); }); - else - FetchInternal(localPackages, productId); - } + var localInfo = m_LocalInfos.Get(id); + if (localInfo?.updateInfoFetched == false) + RefreshProductUpdateDetails(new[] { localInfo }); - private void FetchInternal(IDictionary localPackages, long productID) - { // create a placeholder before fetching data from the cloud for the first time - if (!m_PackageDetailsFetched.Contains(productID)) - { - onPackagesChanged?.Invoke(new[] { new PlaceholderPackage(productID.ToString(), PackageTag.AssetStore) }); - } + if (!m_FetchedInfos.ContainsKey(productId.ToString())) + onPackagesChanged?.Invoke(new[] { new PlaceholderPackage(productId.ToString(), PackageType.AssetStore) }); - FetchDetailsInternal(new[] { productID }, localPackages); - - onProductFetched?.Invoke(productID); + FetchDetails(new[] { productId }); + onProductFetched?.Invoke(productId); } public void List(int offset, int limit, string searchText = "", bool fetchDetails = true) @@ -127,15 +98,11 @@ public void List(int offset, int limit, string searchText = "", bool fetchDetail onListOperationStart?.Invoke(); - var localPackages = GetLocalPackages(); + RefreshLocalInfos(); + if (offset == 0) - RefreshProductUpdateDetails(localPackages, () => { ListInternal(localPackages, offset, limit, searchText, fetchDetails); }); - else - ListInternal(localPackages, offset, limit, searchText, fetchDetails); - } + RefreshProductUpdateDetails(); - private void ListInternal(IDictionary localPackages, int offset, int limit, string searchText, bool fetchDetails) - { AssetStoreRestAPI.instance.GetProductIDList(offset, limit, searchText, productList => { if (!productList.isValid) @@ -161,11 +128,11 @@ private void ListInternal(IDictionary localPackag var placeholderPackages = new List(); - foreach (var product in productList.list) + foreach (var productId in productList.list) { // create a placeholder before fetching data from the cloud for the first time - if (!m_PackageDetailsFetched.Contains(product)) - placeholderPackages.Add(new PlaceholderPackage(product.ToString(), PackageTag.AssetStore)); + if (!m_FetchedInfos.ContainsKey(productId.ToString())) + placeholderPackages.Add(new PlaceholderPackage(productId.ToString(), PackageType.AssetStore, PackageTag.None, PackageProgress.Refreshing)); } if (placeholderPackages.Any()) @@ -174,16 +141,11 @@ private void ListInternal(IDictionary localPackag onListOperationFinish?.Invoke(); if (fetchDetails) - FetchDetailsInternal(productList.list, localPackages); + FetchDetails(productList.list); }); } public void FetchDetails(IEnumerable packageIds) - { - FetchDetailsInternal(packageIds, GetLocalPackages()); - } - - private void FetchDetailsInternal(IEnumerable packageIds, IDictionary localPackages) { var countProduct = packageIds.Count(); if (countProduct == 0) @@ -195,54 +157,31 @@ private void FetchDetailsInternal(IEnumerable packageIds, IDictionary { - AssetStorePackage package; - object error; - if (!productDetail.TryGetValue("errorMessage", out error)) + AssetStorePackage package = null; + var error = productDetail.GetString("errorMessage"); + if (string.IsNullOrEmpty(error)) { - AssetStorePackageInfo localPackage; - if (localPackages.TryGetValue(id.ToString(), out localPackage)) - { - productDetail["localPath"] = localPackage.packagePath; - } + var fetchedInfo = FetchedInfo.ParseFetchedInfo(id.ToString(), productDetail); + if (fetchedInfo == null) + package = new AssetStorePackage(id.ToString(), new Error(NativeErrorCode.Unknown, "Error parsing product details.")); else { - productDetail["localPath"] = string.Empty; - } - - package = new AssetStorePackage(id.ToString(), productDetail); - if (m_UpdateDetails.ContainsKey(package.uniqueId)) - { - package.SetState(m_UpdateDetails[package.uniqueId]); - } - - if (package.state == PackageState.Outdated && !string.IsNullOrEmpty(localPackage.packagePath)) - { - package.m_FetchedVersion.localPath = string.Empty; - - try - { - var info = new AssetStorePackageVersion.SpecificVersionInfo(); - var item = Json.Deserialize(localPackage.jsonInfo) as Dictionary; - info.versionId = item["version_id"] as string; - info.versionString = item["version"] as string; - info.publishedDate = item["pubdate"] as string; - info.supportedVersion = item["unity_version"] as string; - - var installedVersion = new AssetStorePackageVersion(id.ToString(), productDetail, info); - installedVersion.localPath = localPackage.packagePath; - - package.AddVersion(installedVersion); - } - catch (Exception) + var oldFetchedInfo = m_FetchedInfos.Get(fetchedInfo.id); + if (oldFetchedInfo == null || oldFetchedInfo.versionId != fetchedInfo.versionId || oldFetchedInfo.versionString != fetchedInfo.versionString) { + if (string.IsNullOrEmpty(fetchedInfo.packageName)) + package = new AssetStorePackage(fetchedInfo, m_LocalInfos.Get(fetchedInfo.id)); + else + UpmClient.instance.FetchForProduct(fetchedInfo.id, fetchedInfo.packageName); + m_FetchedInfos[fetchedInfo.id] = fetchedInfo; } } - m_PackageDetailsFetched.Add(id); } else - package = new AssetStorePackage(id.ToString(), new Error(NativeErrorCode.Unknown, error as string)); + package = new AssetStorePackage(id.ToString(), new Error(NativeErrorCode.Unknown, error)); - onPackagesChanged?.Invoke(new[] { package }); + if (package != null) + onPackagesChanged?.Invoke(new[] { package }); countProduct--; if (countProduct == 0) @@ -251,178 +190,12 @@ private void FetchDetailsInternal(IEnumerable packageIds, IDictionary packages) - { - if (packages == null || !packages.Any() || !ApplicationUtil.instance.isUserLoggedIn) - return; - - var localInfos = new Dictionary(); - var localPackages = AssetStoreUtils.instance.GetLocalPackageList(); - foreach (var p in localPackages) - { - if (!string.IsNullOrEmpty(p.jsonInfo)) - { - var item = Json.Deserialize(p.jsonInfo) as Dictionary; - if (item != null && item.ContainsKey("id") && item["id"] is string) - { - localInfos[(string)item["id"]] = new AssetStorePackageVersion.SpecificVersionInfo - { - packagePath = p.packagePath, - versionString = item.ContainsKey("version") && item["version"] is string? (string)item["version"] : string.Empty, - versionId = item.ContainsKey("version_id") && item["version_id"] is string? (string)item["version_id"] : string.Empty, - publishedDate = item.ContainsKey("pubdate") && item["pubdate"] is string? (string)item["pubdate"] : string.Empty, - supportedVersion = item.ContainsKey("unity_version") && item["unity_version"] is string? (string)item["unity_version"] : string.Empty - }; - } - } - } - - var updatedPackages = new List(); - foreach (var package in packages) - { - var assetStorePackage = package as AssetStorePackage; - if (assetStorePackage == null) - continue; - - AssetStorePackageVersion.SpecificVersionInfo localInfo; - localInfos.TryGetValue(assetStorePackage.uniqueId, out localInfo); - - var packageChanged = false; - if (localInfo == null) - { - if (assetStorePackage.installedVersion != null) - { - assetStorePackage.m_FetchedVersion.localPath = string.Empty; - if (assetStorePackage.m_FetchedVersion != assetStorePackage.m_LocalVersion) - { - assetStorePackage.RemoveVersion(assetStorePackage.m_LocalVersion); - } - - assetStorePackage.SetState(PackageState.UpToDate); - packageChanged = true; - } - } - else if (assetStorePackage.installedVersion == null || localInfo.versionString != assetStorePackage.installedVersion.versionString) - { - if (assetStorePackage.m_FetchedVersion.versionString == localInfo.versionString) - { - assetStorePackage.m_FetchedVersion.localPath = localInfo.packagePath; - if (assetStorePackage.m_LocalVersion != assetStorePackage.m_FetchedVersion) - { - assetStorePackage.RemoveVersion(assetStorePackage.m_LocalVersion); - } - - assetStorePackage.SetState(PackageState.UpToDate); - } - else if (assetStorePackage.m_LocalVersion.versionString == localInfo.versionString) - { - assetStorePackage.m_FetchedVersion.localPath = string.Empty; - assetStorePackage.m_LocalVersion.localPath = localInfo.packagePath; - assetStorePackage.SetState(PackageState.Outdated); - } - else - { - assetStorePackage.m_FetchedVersion.localPath = string.Empty; - assetStorePackage.AddVersion(new AssetStorePackageVersion(assetStorePackage.m_FetchedVersion, localInfo)); - assetStorePackage.m_LocalVersion.localPath = localInfo.packagePath; - assetStorePackage.SetState(PackageState.Outdated); - } - - packageChanged = true; - } - - if (packageChanged) - { - if (m_UpdateDetails.ContainsKey(assetStorePackage.uniqueId)) - m_UpdateDetails[assetStorePackage.uniqueId] = assetStorePackage.state; - - updatedPackages.Add(package); - } - } - - if (updatedPackages.Any()) - onPackagesChanged?.Invoke(updatedPackages); - } - - public void Refresh(IPackage package) + public void RefreshLocal() { if (!ApplicationUtil.instance.isUserLoggedIn) return; - var assetStorePackage = package as AssetStorePackage; - if (assetStorePackage == null) - return; - - AssetStorePackageVersion.SpecificVersionInfo localInfo = null; - var localPackage = AssetStoreUtils.instance.GetLocalPackageList().FirstOrDefault(p => - { - if (!string.IsNullOrEmpty(p.jsonInfo)) - { - var item = Json.Deserialize(p.jsonInfo) as Dictionary; - if (item != null && item.ContainsKey("id") && item["id"] is string && package.uniqueId == (string)item["id"]) - { - localInfo = new AssetStorePackageVersion.SpecificVersionInfo - { - versionString = item.ContainsKey("version") && item["version"] is string? (string)item["version"] : string.Empty, - versionId = item.ContainsKey("version_id") && item["version_id"] is string? (string)item["version_id"] : string.Empty, - publishedDate = item.ContainsKey("pubdate") && item["pubdate"] is string? (string)item["pubdate"] : string.Empty, - supportedVersion = item.ContainsKey("unity_version") && item["unity_version"] is string? (string)item["unity_version"] : string.Empty - }; - return true; - } - } - return false; - }); - - var packageChanged = false; - if (localInfo == null) - { - if (assetStorePackage.installedVersion != null) - { - assetStorePackage.m_FetchedVersion.localPath = string.Empty; - if (assetStorePackage.m_FetchedVersion != assetStorePackage.m_LocalVersion) - { - assetStorePackage.RemoveVersion(assetStorePackage.m_LocalVersion); - } - - assetStorePackage.SetState(PackageState.UpToDate); - packageChanged = true; - } - } - else if (assetStorePackage.installedVersion == null || localInfo.versionString != assetStorePackage.installedVersion.versionString) - { - if (assetStorePackage.m_FetchedVersion.versionString == localInfo.versionString) - { - assetStorePackage.m_FetchedVersion.localPath = localPackage.packagePath; - if (assetStorePackage.m_LocalVersion != assetStorePackage.m_FetchedVersion) - { - assetStorePackage.RemoveVersion(assetStorePackage.m_LocalVersion); - } - assetStorePackage.SetState(PackageState.UpToDate); - } - else if (assetStorePackage.m_LocalVersion.versionString == localInfo.versionString) - { - assetStorePackage.m_FetchedVersion.localPath = string.Empty; - assetStorePackage.m_LocalVersion.localPath = localPackage.packagePath; - assetStorePackage.SetState(PackageState.Outdated); - } - else - { - assetStorePackage.m_FetchedVersion.localPath = string.Empty; - assetStorePackage.AddVersion(new AssetStorePackageVersion(assetStorePackage.m_FetchedVersion, localInfo)); - assetStorePackage.m_LocalVersion.localPath = localPackage.packagePath; - assetStorePackage.SetState(PackageState.Outdated); - } - - packageChanged = true; - } - - if (packageChanged) - { - if (m_UpdateDetails.ContainsKey(assetStorePackage.uniqueId)) - m_UpdateDetails[assetStorePackage.uniqueId] = assetStorePackage.state; - onPackagesChanged?.Invoke(new[] { package }); - } + RefreshLocalInfos(); } public bool IsAnyDownloadInProgress() @@ -540,6 +313,37 @@ public void OnDownloadProgress(string packageId, string message, ulong bytes, ul onDownloadProgress?.Invoke(progress); } + private void OnProductPackageChanged(string productId, IPackage package) + { + var fetchedInfo = m_FetchedInfos.Get(productId); + if (fetchedInfo != null) + { + var assetStorePackage = new AssetStorePackage(fetchedInfo, package as UpmPackage); + onPackagesChanged?.Invoke(new[] { assetStorePackage }); + } + } + + private void OnProductPackageVersionUpdated(string productId, IPackageVersion version) + { + var upmVersion = version as UpmPackageVersion; + var fetchedInfo = m_FetchedInfos.Get(productId); + if (upmVersion != null && fetchedInfo != null) + upmVersion.UpdateFetchedInfo(fetchedInfo); + onPackageVersionUpdated?.Invoke(productId, version); + } + + private void OnProductPackageFetchError(string productId, Error error) + { + var fetchedInfo = m_FetchedInfos.Get(productId); + if (fetchedInfo != null) + { + var assetStorePackage = new AssetStorePackage(fetchedInfo); + var assetStorePackageVersion = assetStorePackage.versionList.primary as AssetStorePackageVersion; + assetStorePackageVersion.SetUpmPackageFetchError(error); + onPackagesChanged?.Invoke(new[] { assetStorePackage }); + } + } + public void Setup() { System.Diagnostics.Debug.Assert(!m_SetupDone); @@ -550,6 +354,10 @@ public void Setup() { AssetStoreUtils.instance.RegisterDownloadDelegate(this); } + + UpmClient.instance.onProductPackageChanged += OnProductPackageChanged; + UpmClient.instance.onProductPackageVersionUpdated += OnProductPackageVersionUpdated; + UpmClient.instance.onProductPackageFetchError += OnProductPackageFetchError; } public void Clear() @@ -559,12 +367,18 @@ public void Clear() AssetStoreUtils.instance.UnRegisterDownloadDelegate(this); ApplicationUtil.instance.onUserLoginStateChange -= OnUserLoginStateChange; + UpmClient.instance.onProductPackageChanged -= OnProductPackageChanged; + UpmClient.instance.onProductPackageVersionUpdated -= OnProductPackageVersionUpdated; + UpmClient.instance.onProductPackageFetchError -= OnProductPackageFetchError; } public void Reset() { - m_UpdateDetails.Clear(); - m_PackageDetailsFetched.Clear(); + m_LocalInfos.Clear(); + m_FetchedInfos.Clear(); + + m_SerializedLocalInfos = new LocalInfo[0]; + m_SerializedFetchedInfos = new FetchedInfo[0]; } private void OnUserLoginStateChange(bool loggedIn) @@ -573,6 +387,8 @@ private void OnUserLoginStateChange(bool loggedIn) { AssetStoreUtils.instance.UnRegisterDownloadDelegate(this); AbortAllDownloads(); + Reset(); + UpmClient.instance.ResetProductCache(); } else { @@ -590,50 +406,89 @@ public void AbortAllDownloads() AssetStoreDownloadOperation.instance.AbortDownloadPackageAsync(download); } - private void RefreshProductUpdateDetails(IDictionary localPackages, Action doneCallbackAction) + public void RefreshProductUpdateDetails(IEnumerable localInfos = null) { - var needsUpdateDetail = localPackages.Where(kp => m_UpdateDetails[kp.Key] == PackageState.UpToDate); - if (!needsUpdateDetail.Any()) - { - doneCallbackAction?.Invoke(); - } - else + localInfos = localInfos ?? m_LocalInfos.Values.Where(info => !info.updateInfoFetched); + if (!localInfos.Any()) + return; + + AssetStoreRestAPI.instance.GetProductUpdateDetail(localInfos, updateDetails => { - var list = needsUpdateDetail.Select(kp => kp.Value).ToList(); - AssetStoreRestAPI.instance.GetProductUpdateDetail(list, updateDetails => + if (updateDetails.ContainsKey("errorMessage")) + return; + + var results = updateDetails.GetList>("results"); + if (results == null) + return; + + foreach (var updateDetail in results) { - object error; - if (!updateDetails.TryGetValue("errorMessage", out error)) + var id = updateDetail.GetString("id"); + var localInfo = m_LocalInfos.Get(id); + if (localInfo != null) { - var results = updateDetails["results"] as List; - foreach (var item in results) + localInfo.updateInfoFetched = true; + var newValue = updateDetail.Get("can_update", 0L) != 0L; + if (localInfo.canUpdate != newValue) { - var updateDetail = item as IDictionary; - var canUpdate = (updateDetail["can_update"] is long? (long)updateDetail["can_update"] : 0) != 0; - m_UpdateDetails[updateDetail["id"] as string] = canUpdate ? PackageState.Outdated : PackageState.UpToDate; + localInfo.canUpdate = newValue; + OnLocalInfoChanged(localInfo); } } - - doneCallbackAction?.Invoke(); - }); - } + } + }); } - private IDictionary GetLocalPackages() + private void RefreshLocalInfos() { - var localPackages = new Dictionary(); - foreach (var package in AssetStoreUtils.instance.GetLocalPackageList()) + var infos = AssetStoreUtils.instance.GetLocalPackageList(); + var oldLocalInfos = m_LocalInfos; + m_LocalInfos = new Dictionary(); + foreach (var info in infos) { - var item = Json.Deserialize(package.jsonInfo) as Dictionary; - if (item != null && item.ContainsKey("id") && item["id"] is string) + var parsedInfo = LocalInfo.ParseLocalInfo(info); + var id = parsedInfo?.id; + if (string.IsNullOrEmpty(id)) + continue; + + var oldInfo = oldLocalInfos.Get(id); + if (oldInfo != null) { - var packageId = (string)item["id"]; - localPackages[packageId] = package; - if (!m_UpdateDetails.ContainsKey(packageId)) - m_UpdateDetails[packageId] = PackageState.UpToDate; + oldLocalInfos.Remove(oldInfo.id); + + if (oldInfo.versionId == parsedInfo.versionId && + oldInfo.versionString == parsedInfo.versionString && + oldInfo.packagePath == parsedInfo.packagePath) + { + m_LocalInfos[id] = oldInfo; + continue; + } } + + m_LocalInfos[id] = parsedInfo; + OnLocalInfoChanged(parsedInfo); } - return localPackages; + + foreach (var info in oldLocalInfos.Values) + OnLocalInfoRemoved(info); + } + + private void OnLocalInfoChanged(LocalInfo localInfo) + { + var fetchedInfo = m_FetchedInfos.Get(localInfo.id); + if (fetchedInfo == null) + return; + var package = new AssetStorePackage(fetchedInfo, localInfo); + onPackagesChanged?.Invoke(new[] { package }); + } + + private void OnLocalInfoRemoved(LocalInfo localInfo) + { + var fetchedInfo = m_FetchedInfos.Get(localInfo.id); + if (fetchedInfo == null) + return; + var package = new AssetStorePackage(fetchedInfo, (LocalInfo)null); + onPackagesChanged?.Invoke(new[] { package }); } } } diff --git a/Modules/PackageManagerUI/Editor/Services/AssetStore/AssetStoreFetchedInfo.cs b/Modules/PackageManagerUI/Editor/Services/AssetStore/AssetStoreFetchedInfo.cs new file mode 100644 index 0000000000..7cc1d375c7 --- /dev/null +++ b/Modules/PackageManagerUI/Editor/Services/AssetStore/AssetStoreFetchedInfo.cs @@ -0,0 +1,220 @@ +// Unity C# reference source +// Copyright (c) Unity Technologies. For terms of use, see +// https://unity3d.com/legal/licenses/Unity_Reference_Only_License + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; + +namespace UnityEditor.PackageManager.UI.AssetStore +{ + [Serializable] + internal class FetchedInfo + { + public string id; + public string packageName; + public string description; + public string author; + public string publisherId; + public string category; + public string versionString; + public string versionId; + public string publishedDate; + public string displayName; + public string state; + + public List supportedVersions; + public List images; + public List links; + public List sizeInfos; + + public static FetchedInfo ParseFetchedInfo(string productId, IDictionary productDetail) + { + if (string.IsNullOrEmpty(productId) || productDetail == null || !productDetail.Any()) + return null; + return new FetchedInfo(productId, productDetail); + } + + private FetchedInfo(string productId, IDictionary productDetail) + { + id = productId; + description = CleanUpHtml(productDetail.GetString("description")) ?? string.Empty; + + var publisher = productDetail.GetDictionary("productPublisher"); + if (publisher != null) + { + if (publisher.GetString("url") == "http://unity3d.com") + author = "Unity Technologies Inc."; + else + author = publisher.GetString("name") ?? L10n.Tr("Unknown publisher"); + publisherId = publisher.GetString("externalRef") ?? string.Empty; + } + else + { + author = string.Empty; + publisherId = string.Empty; + } + + packageName = productDetail.GetString("packageName") ?? string.Empty; + category = productDetail.GetDictionary("category")?.GetString("name") ?? string.Empty; + + var versionInfo = productDetail.GetDictionary("version"); + if (versionInfo != null) + { + versionString = versionInfo.GetString("name"); + versionId = versionInfo.GetString("id"); + publishedDate = versionInfo.GetString("publishedDate"); + } + + displayName = productDetail.GetString("displayName"); + + supportedVersions = productDetail.GetList("supportedUnityVersions")?.ToList(); + + state = productDetail.GetString("state"); + + images = GetImagesFromProductDetails(productDetail); + links = GetLinksFromProductDetails(productDetail); + sizeInfos = GetSizeInfoFromProductDetails(productDetail); + } + + private static string CleanUpHtml(string source) + { + if (string.IsNullOrEmpty(source)) + return source; + + source = source.Replace("
", "\n"); + + var array = new char[source.Length]; + var arrayIndex = 0; + var inside = false; + + foreach (var c in source.ToCharArray()) + { + if (c == '<') + inside = true; + else if (c == '>') + inside = false; + else + { + if (!inside) + array[arrayIndex++] = c; + } + } + + var text = new string(array, 0, arrayIndex); + text = Regex.Replace(text, @"&#x?\d+;", ""); + text = text.Replace(" ", " "); + text = text.Replace("<", "<"); + text = text.Replace(">", ">"); + text = text.Replace("&", "&"); + text = text.Replace(""", "\""); + text = text.Replace("'", "'"); + text = Regex.Replace(text, @"[\n\r]+", "\n"); + text = text.Trim(' ', '\r', '\n', '\t'); + + return text; + } + + private static List GetImagesFromProductDetails(IDictionary productDetail) + { + var result = new List(); + var mainImageThumbnailUrl = productDetail.GetDictionary("mainImage")?.GetString("url"); + if (!string.IsNullOrEmpty(mainImageThumbnailUrl)) + { + mainImageThumbnailUrl = mainImageThumbnailUrl.Replace("//d2ujflorbtfzji.cloudfront.net/", "//assetstorev1-prd-cdn.unity3d.com/"); + result.Add(new PackageImage + { + type = PackageImage.ImageType.Main, + thumbnailUrl = "http:" + mainImageThumbnailUrl, + url = string.Empty + }); + } + + var images = productDetail.GetList>("images") ?? Enumerable.Empty>(); + foreach (var image in images) + { + var type = image?.GetString("type"); + if (string.IsNullOrEmpty(type)) + continue; + + var imageType = PackageImage.ImageType.Screenshot; + var thumbnailUrl = image.GetString("thumbnailUrl"); + thumbnailUrl = thumbnailUrl.Replace("//d2ujflorbtfzji.cloudfront.net/", "//assetstorev1-prd-cdn.unity3d.com/"); + + if (type == "sketchfab") + imageType = PackageImage.ImageType.Sketchfab; + else if (type == "youtube") + imageType = PackageImage.ImageType.Youtube; + + var imageUrl = image.GetString("imageUrl"); + if (imageType == PackageImage.ImageType.Screenshot) + imageUrl = "http:" + imageUrl; + + result.Add(new PackageImage + { + type = imageType, + thumbnailUrl = "http:" + thumbnailUrl, + url = imageUrl + }); + } + return result; + } + + private static List GetLinksFromProductDetails(IDictionary productDetail) + { + var result = new List(); + + var slug = productDetail.GetString("slug") ?? productDetail.GetString("id"); + result.Add(GetPackageLink("View in the Asset Store", $"/packages/p/{slug}")); + + var publisher = productDetail.GetDictionary("productPublisher"); + if (publisher != null) + { + var url = publisher.GetString("url"); + if (!string.IsNullOrEmpty(url) && Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute)) + result.Add(GetPackageLink("Publisher Website", url)); + + var supportUrl = publisher.GetString("supportUrl"); + if (!string.IsNullOrEmpty(supportUrl) && Uri.IsWellFormedUriString(supportUrl, UriKind.RelativeOrAbsolute)) + result.Add(GetPackageLink("Publisher Support", supportUrl)); + } + return result; + } + + private static List GetSizeInfoFromProductDetails(IDictionary productDetail) + { + var result = new List(); + var uploads = productDetail.GetDictionary("uploads"); + if (uploads != null) + { + foreach (var key in uploads.Keys) + { + var simpleVersion = Regex.Replace(key, @"(?\d+)\.(?\d+).(?\d+)[abfp].+", "${major}.${minor}.${patch}"); + SemVersion version; + if (SemVersion.TryParse(simpleVersion.Trim(), out version)) + { + var info = uploads.GetDictionary(key); + var assetCount = info.GetString("assetCount") ?? string.Empty; + var downloadSize = info.GetString("downloadSize") ?? string.Empty; + + result.Add(new PackageSizeInfo + { + supportedUnityVersion = version, + assetCount = string.IsNullOrEmpty(assetCount) ? 0 : ulong.Parse(assetCount), + downloadSize = string.IsNullOrEmpty(downloadSize) ? 0 : ulong.Parse(downloadSize) + }); + } + } + } + return result; + } + + private static PackageLink GetPackageLink(string name, string url) + { + if (!url.StartsWith("http:", StringComparison.InvariantCulture) && !url.StartsWith("https:", StringComparison.InvariantCulture)) + url = AssetStoreUtils.instance.assetStoreUrl + url; + return new PackageLink { name = name, url = url }; + } + } +} diff --git a/Modules/PackageManagerUI/Editor/Services/AssetStore/AssetStoreLocalInfo.cs b/Modules/PackageManagerUI/Editor/Services/AssetStore/AssetStoreLocalInfo.cs new file mode 100644 index 0000000000..545be15273 --- /dev/null +++ b/Modules/PackageManagerUI/Editor/Services/AssetStore/AssetStoreLocalInfo.cs @@ -0,0 +1,46 @@ +// Unity C# reference source +// Copyright (c) Unity Technologies. For terms of use, see +// https://unity3d.com/legal/licenses/Unity_Reference_Only_License + +using System; +using System.Collections.Generic; + +namespace UnityEditor.PackageManager.UI.AssetStore +{ + [Serializable] + internal class LocalInfo + { + public string id; + public string versionString; + public string versionId; + public string publishedDate; + public string supportedVersion; + public string packagePath; + + public bool updateInfoFetched; + public bool canUpdate; + + public static LocalInfo ParseLocalInfo(UnityEditor.PackageInfo localInfo) + { + if (string.IsNullOrEmpty(localInfo.jsonInfo)) + return null; + + var jsonInfo = Json.Deserialize(localInfo.jsonInfo) as Dictionary; + var id = jsonInfo?.GetString("id"); + if (string.IsNullOrEmpty(id)) + return null; + + return new LocalInfo + { + id = id, + packagePath = localInfo.packagePath ?? string.Empty, + versionString = jsonInfo.GetString("version") ?? string.Empty, + versionId = jsonInfo.GetString("version_id") ?? string.Empty, + publishedDate = jsonInfo.GetString("pubdate") ?? string.Empty, + supportedVersion = jsonInfo.GetString("unity_version") ?? string.Empty, + updateInfoFetched = false, + canUpdate = false + }; + } + } +} diff --git a/Modules/PackageManagerUI/Editor/Services/AssetStore/AssetStorePackage.cs b/Modules/PackageManagerUI/Editor/Services/AssetStore/AssetStorePackage.cs index b4c71e64f8..f431e86e37 100644 --- a/Modules/PackageManagerUI/Editor/Services/AssetStore/AssetStorePackage.cs +++ b/Modules/PackageManagerUI/Editor/Services/AssetStore/AssetStorePackage.cs @@ -13,88 +13,139 @@ namespace UnityEditor.PackageManager.UI.AssetStore internal class AssetStorePackage : IPackage { [SerializeField] - private string m_ProductId; + private string m_Name; + public string name => m_Name; - public string name => string.Empty; + [SerializeField] + private string m_ProductId; public string uniqueId => m_ProductId; - public string displayName => m_Versions.FirstOrDefault()?.displayName; + public string displayName => versions.FirstOrDefault()?.displayName; [SerializeField] - private List m_Versions; + private AssetStoreVersionList m_VersionList; - internal AssetStorePackageVersion m_InstalledVersion => m_Versions.FirstOrDefault(pv => pv.isAvailableOnDisk); - internal AssetStorePackageVersion m_FetchedVersion => m_Versions.FirstOrDefault(); - internal AssetStorePackageVersion m_LocalVersion => m_Versions.LastOrDefault(); + [SerializeField] + private UpmVersionList m_UpmVersionList; - public IEnumerable versions => m_Versions.Cast(); - public IEnumerable keyVersions => m_Versions.Cast(); - public IPackageVersion installedVersion => m_InstalledVersion; - public IPackageVersion latestVersion => m_FetchedVersion; - public IPackageVersion latestPatch => latestVersion; - public IPackageVersion recommendedVersion => latestVersion; - public IPackageVersion primaryVersion => installedVersion ?? latestVersion; + public IVersionList versionList => string.IsNullOrEmpty(name) ? m_VersionList as IVersionList : m_UpmVersionList as IVersionList; [SerializeField] - private PackageState m_State; - public PackageState state => m_State; - - public void SetState(PackageState state) - { - m_State = state; - } + private PackageProgress m_Progress; + public PackageProgress progress => m_Progress; public bool isDiscoverable => true; [SerializeField] private List m_Errors; - public IEnumerable errors => m_Errors; + // Combined errors for this package or any version. + // Stop lookup after first error encountered on a version to save time not looking up redundant errors. + public IEnumerable errors => (versions.Select(v => v.errors).FirstOrDefault(e => e?.Any() ?? false) ?? new List()).Concat(m_Errors); - public void AddError(Error error) + [SerializeField] + private PackageType m_Type; + public bool Is(PackageType type) { - m_Errors?.Add(error); + return (m_Type & type) != 0; } - public void ClearErrors() - { - m_Errors?.Clear(); - } + [SerializeField] + private List m_Images; + [SerializeField] + private List m_Links; + + public IEnumerable images => m_Images; + + public IEnumerable links => m_Links; + + public IEnumerable versions => versionList?.all; - public void AddVersion(AssetStorePackageVersion version) + public IEnumerable keyVersions => versionList?.key; + + public IPackageVersion installedVersion => versionList?.installed; + + public IPackageVersion latestVersion => versionList?.latest; + + public IPackageVersion latestPatch => versionList?.latestPatch; + + public IPackageVersion recommendedVersion => versionList?.recommended; + + public IPackageVersion primaryVersion => versionList?.primary; + + public void AddError(Error error) { - m_Versions.Add(version); + m_Errors?.Add(error); } - public void RemoveVersion(AssetStorePackageVersion version) + public void ClearErrors() { - m_Versions.Remove(version); + m_Errors?.Clear(); } public AssetStorePackage(string productId, Error error) { m_Errors = new List { error }; - m_State = PackageState.Error; + m_Progress = PackageProgress.None; + m_Type = PackageType.AssetStore; + m_Name = string.Empty; m_ProductId = productId; - m_Versions = new List(); + + m_Images = new List(); + m_Links = new List(); + m_VersionList = new AssetStoreVersionList(); + m_UpmVersionList = new UpmVersionList(); } - public AssetStorePackage(string productId, IDictionary productDetail) + public AssetStorePackage(FetchedInfo fetchedInfo, LocalInfo localInfo = null) { m_Errors = new List(); - m_State = PackageState.UpToDate; - m_ProductId = productId; - m_Versions = new List(); - try + m_Progress = PackageProgress.None; + m_Type = PackageType.AssetStore; + m_Name = string.Empty; + m_ProductId = fetchedInfo?.id.ToString(); + m_Images = fetchedInfo?.images ?? new List(); + m_Links = fetchedInfo?.links ?? new List(); + m_VersionList = new AssetStoreVersionList(); + m_UpmVersionList = new UpmVersionList(); + + if (string.IsNullOrEmpty(fetchedInfo?.id) || string.IsNullOrEmpty(fetchedInfo?.versionId)) + { + AddError(new Error(NativeErrorCode.Unknown, "Invalid product details.")); + } + else if (localInfo == null) { - m_Versions.Add(new AssetStorePackageVersion(productId, productDetail)); + m_VersionList.AddVersion(new AssetStorePackageVersion(fetchedInfo)); } - catch (Exception e) + else { - m_Errors.Add(new Error(NativeErrorCode.Unknown, e.Message)); - m_State = PackageState.Error; + m_VersionList.AddVersion(new AssetStorePackageVersion(fetchedInfo, localInfo)); + if (localInfo.canUpdate && (localInfo.versionId != fetchedInfo.versionId || localInfo.versionString != fetchedInfo.versionString)) + m_VersionList.AddVersion(new AssetStorePackageVersion(fetchedInfo)); } } + public AssetStorePackage(FetchedInfo fetchedInfo, UpmPackage package) + { + m_Errors = new List(); + m_Progress = PackageProgress.None; + m_Type = PackageType.AssetStore; + m_Name = package?.name ?? string.Empty; + m_ProductId = fetchedInfo?.id.ToString(); + + m_Images = fetchedInfo?.images ?? new List(); + m_Links = fetchedInfo?.links ?? new List(); + m_VersionList = new AssetStoreVersionList(); + + m_UpmVersionList = package?.versionList as UpmVersionList ?? new UpmVersionList(); + foreach (var version in m_UpmVersionList.all.Cast()) + version.UpdateFetchedInfo(fetchedInfo); + + if (string.IsNullOrEmpty(fetchedInfo?.id) || string.IsNullOrEmpty(fetchedInfo?.versionId)) + AddError(new Error(NativeErrorCode.Unknown, "Invalid product details.")); + else if (string.IsNullOrEmpty(package?.name)) + AddError(new Error(NativeErrorCode.Unknown, "Invalid package info.")); + } + public IPackage Clone() { return (IPackage)MemberwiseClone(); diff --git a/Modules/PackageManagerUI/Editor/Services/AssetStore/AssetStorePackageVersion.cs b/Modules/PackageManagerUI/Editor/Services/AssetStore/AssetStorePackageVersion.cs index fdd7c3d4d7..bdd0782e14 100644 --- a/Modules/PackageManagerUI/Editor/Services/AssetStore/AssetStorePackageVersion.cs +++ b/Modules/PackageManagerUI/Editor/Services/AssetStore/AssetStorePackageVersion.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Text.RegularExpressions; @@ -14,22 +15,11 @@ namespace UnityEditor.PackageManager.UI.AssetStore [Serializable] internal class AssetStorePackageVersion : IPackageVersion { - public class SpecificVersionInfo - { - public string versionString; - public string versionId; - public string publishedDate; - public string supportedVersion; - public string packagePath; - } - [SerializeField] private string m_PackageUniqueId; [SerializeField] private string m_DisplayName; [SerializeField] - private string m_Type; - [SerializeField] private string m_Author; [SerializeField] private string m_Description; @@ -40,7 +30,7 @@ public class SpecificVersionInfo [SerializeField] private SemVersion m_Version; [SerializeField] - private DateTime m_PublishedDate; + private long m_PublishedDateTicks; [SerializeField] private string m_PublisherId; [SerializeField] @@ -56,32 +46,48 @@ public class SpecificVersionInfo [SerializeField] private SemVersion m_SupportedUnityVersion; [SerializeField] - private List m_Images; - [SerializeField] private List m_SizeInfos; [SerializeField] - private List m_Links; - [SerializeField] private PackageTag m_Tag; public string name => string.Empty; public string displayName => m_DisplayName; - public string type => m_Type; - public string author => m_Author; + public string authorLink => $"{AssetStoreUtils.instance.assetStoreUrl}/publishers/{publisherId}"; + public string description => m_Description; public string category => m_Category; + private Dictionary m_CategoryLinks; + public IDictionary categoryLinks + { + get + { + if (m_CategoryLinks == null) + { + m_CategoryLinks = new Dictionary(); + var categories = m_Category.Split('/'); + var parentCategory = "/"; + foreach (var category in categories) + { + var lower = category.ToLower(CultureInfo.InvariantCulture); + var url = $"{AssetStoreUtils.instance.assetStoreUrl}{parentCategory}{lower}"; + parentCategory += lower + "/"; + m_CategoryLinks[category] = url; + } + } + return m_CategoryLinks; + } + } + public string packageUniqueId => m_PackageUniqueId; public string uniqueId => m_VersionId; - public PackageSource source => PackageSource.Unknown; - public IEnumerable errors => m_Errors; public IEnumerable samples => Enumerable.Empty(); @@ -94,7 +100,7 @@ public SemVersion version set { m_Version = value; } } - public DateTime? publishedDate => m_PublishedDate; + public DateTime? publishedDate => m_PublishedDateTicks == 0 ? (DateTime?)null : new DateTime(m_PublishedDateTicks, DateTimeKind.Utc); public string publisherId => m_PublisherId; @@ -108,8 +114,6 @@ public SemVersion version public bool isFullyFetched => true; - public bool isUserVisible => true; - public bool isAvailableOnDisk => m_IsAvailableOnDisk; public bool isVersionLocked => true; @@ -146,314 +150,75 @@ public string versionId public IEnumerable supportedVersions => m_SupportedUnityVersions; - public IEnumerable images => m_Images; - public IEnumerable sizes => m_SizeInfos; - public IEnumerable links => m_Links; - public bool HasTag(PackageTag tag) { return (m_Tag & tag) == tag; } - public AssetStorePackageVersion(AssetStorePackageVersion other, SpecificVersionInfo localInfo = null) + public AssetStorePackageVersion(FetchedInfo fetchedInfo, LocalInfo localInfo = null) { - m_PackageUniqueId = other.m_PackageUniqueId; - m_DisplayName = other.m_DisplayName; - m_Type = other.m_Type; - m_Author = other.m_Author; - m_Description = other.m_Description; - m_Category = other.m_Category; - m_Errors = other.m_Errors; - m_Version = other.m_Version; - m_PublishedDate = other.m_PublishedDate; - m_PublisherId = other.m_PublisherId; - m_IsAvailableOnDisk = other.m_IsAvailableOnDisk; - m_LocalPath = other.m_LocalPath; - m_VersionString = other.m_VersionString; - m_VersionId = other.m_VersionId; - m_SupportedUnityVersions = other.m_SupportedUnityVersions; - m_SupportedUnityVersion = other.m_SupportedUnityVersion; - m_Images = other.m_Images; - m_SizeInfos = other.m_SizeInfos; - m_Links = other.m_Links; - m_Tag = other.m_Tag; - - if (localInfo != null) - { - m_VersionString = localInfo.versionString; - m_VersionId = localInfo.versionId; - - SemVersion semVer; - if (!SemVersion.TryParse(m_VersionString.Trim(), out semVer)) - { - semVer = new SemVersion(0); - } - m_Version = semVer; - - m_PublishedDate = DateTime.Parse(localInfo.publishedDate); - - var simpleVersion = Regex.Replace(localInfo.supportedVersion, @"(?\d+)\.(?\d+).(?\d+)[abfp].+", "${major}.${minor}.${patch}"); - SemVersion.TryParse(simpleVersion.Trim(), out m_SupportedUnityVersion); - } - } - - public AssetStorePackageVersion(string productId, IDictionary productDetail, SpecificVersionInfo localInfo = null) - { - if (productDetail == null) - { - throw new ArgumentNullException(nameof(productDetail)); - } + if (fetchedInfo == null) + throw new ArgumentNullException(nameof(fetchedInfo)); m_Errors = new List(); - m_Type = "assetstore"; - m_Tag = PackageTag.AssetStore; - m_PackageUniqueId = productId; - - try - { - var description = productDetail.ContainsKey("description") ? productDetail["description"] as string : string.Empty; - m_Description = CleanUpHtml(description); - - var publisher = new Dictionary(); - if (productDetail.ContainsKey("productPublisher")) - { - publisher = productDetail["productPublisher"] as Dictionary; - if (publisher.ContainsKey("url") && publisher["url"] is string && (string)publisher["url"] == "http://unity3d.com") - m_Author = "Unity Technologies Inc."; - else - m_Author = publisher.ContainsKey("name") ? publisher["name"] as string : L10n.Tr("Unknown publisher"); - - m_PublisherId = publisher.ContainsKey("externalRef") ? publisher["externalRef"] as string : string.Empty; - } - else - { - m_Author = string.Empty; - m_PublisherId = string.Empty; - } + m_Tag = PackageTag.Downloadable | PackageTag.Importable; + m_PackageUniqueId = fetchedInfo.id.ToString(); - m_Category = string.Empty; - if (productDetail.ContainsKey("category")) - { - var categoryInfo = productDetail["category"] as IDictionary; - m_Category = categoryInfo["name"] as string; - } + m_Description = fetchedInfo.description; + m_Author = fetchedInfo.author; + m_PublisherId = fetchedInfo.publisherId; - if (localInfo != null) - { - m_VersionString = localInfo.versionString; - m_VersionId = localInfo.versionId; + m_Category = fetchedInfo.category; - SemVersion semVer; - if (!SemVersion.TryParse(m_VersionString.Trim(), out semVer)) - { - semVer = new SemVersion(0); - } - m_Version = semVer; + m_VersionString = localInfo?.versionString ?? fetchedInfo.versionString ?? string.Empty; + m_VersionId = localInfo?.versionId ?? fetchedInfo.versionId ?? string.Empty; + m_Version = SemVersion.TryParse(m_VersionString.Trim(), out m_Version) ? m_Version : new SemVersion(0); - m_PublishedDate = DateTime.Parse(localInfo.publishedDate); - } - else if (productDetail.ContainsKey("version")) - { - var versionInfo = productDetail["version"] as IDictionary; - m_VersionString = versionInfo["name"] as string; - m_VersionId = versionInfo["id"] as string; - SemVersion semVer; - if (!SemVersion.TryParse(m_VersionString.Trim(), out semVer)) - { - semVer = new SemVersion(0); - } - m_Version = semVer; + var publishDateString = localInfo?.publishedDate ?? fetchedInfo.publishedDate ?? string.Empty; + m_PublishedDateTicks = !string.IsNullOrEmpty(publishDateString) ? DateTime.Parse(publishDateString).Ticks : 0; + m_DisplayName = !string.IsNullOrEmpty(fetchedInfo.displayName) ? fetchedInfo.displayName : $"Package {m_PackageUniqueId}@{m_VersionId}"; - if (versionInfo.ContainsKey("publishedDate")) - { - var date = versionInfo["publishedDate"] as string; - m_PublishedDate = DateTime.Parse(date); - } - else - { - m_PublishedDate = new DateTime(); - } - } - else - { - m_VersionString = string.Empty; - m_VersionId = string.Empty; - m_Version = new SemVersion(0); - } - - m_DisplayName = productDetail.ContainsKey("displayName") ? productDetail["displayName"] as string : $"Package {m_PackageUniqueId}@{m_VersionId}"; - - m_SupportedUnityVersions = new List(); - if (productDetail.ContainsKey("supportedUnityVersions")) - { - var supportedVersions = productDetail["supportedUnityVersions"] as IList; - foreach (var supportedVersion in supportedVersions.Where(v => v is string)) - { - SemVersion version; - if (SemVersion.TryParse(supportedVersion as string, out version)) - m_SupportedUnityVersions.Add(version); - } - - m_SupportedUnityVersions.Sort((left, right) => left.CompareByPrecedence(right)); - } - - if (localInfo != null) - { - var simpleVersion = Regex.Replace(localInfo.supportedVersion, @"(?\d+)\.(?\d+).(?\d+)[abfp].+", "${major}.${minor}.${patch}"); - SemVersion.TryParse(simpleVersion.Trim(), out m_SupportedUnityVersion); - } - else - { - m_SupportedUnityVersion = m_SupportedUnityVersions.LastOrDefault(); - } - - m_Images = new List(); - if (productDetail.ContainsKey("mainImage")) - { - var mainImage = productDetail["mainImage"] as IDictionary; - var thumbnailUrl = mainImage["url"] as string; - thumbnailUrl = thumbnailUrl.Replace("//d2ujflorbtfzji.cloudfront.net/", "//assetstorev1-prd-cdn.unity3d.com/"); - m_Images.Add(new PackageImage - { - type = PackageImage.ImageType.Main, - thumbnailUrl = "http:" + thumbnailUrl, - url = string.Empty - }); - } - - if (productDetail.ContainsKey("images")) - { - var images = productDetail["images"] as IList; - foreach (var image in images) - { - var imageInfo = image as IDictionary; - var type = imageInfo["type"] as string; - if (string.IsNullOrEmpty(type)) - continue; - - var imageType = PackageImage.ImageType.Screenshot; - var thumbnailUrl = imageInfo["thumbnailUrl"] as string; - thumbnailUrl = thumbnailUrl.Replace("//d2ujflorbtfzji.cloudfront.net/", "//assetstorev1-prd-cdn.unity3d.com/"); - - if (type == "sketchfab") - imageType = PackageImage.ImageType.Sketchfab; - else if (type == "youtube") - imageType = PackageImage.ImageType.Youtube; - - var imageUrl = imageInfo["imageUrl"] as string; - if (imageType == PackageImage.ImageType.Screenshot) - imageUrl = "http:" + imageUrl; - - m_Images.Add(new PackageImage - { - type = imageType, - thumbnailUrl = "http:" + thumbnailUrl, - url = imageUrl - }); - } - } - - m_SizeInfos = new List(); - if (productDetail.ContainsKey("uploads")) - { - var uploads = productDetail["uploads"] as IDictionary; - foreach (var key in uploads.Keys) - { - var simpleVersion = Regex.Replace(key, @"(?\d+)\.(?\d+).(?\d+)[abfp].+", "${major}.${minor}.${patch}"); - SemVersion version; - if (SemVersion.TryParse(simpleVersion.Trim(), out version)) - { - var info = uploads[key] as IDictionary; - var assetCount = info["assetCount"] as string; - var downloadSize = info["downloadSize"] as string; - - m_SizeInfos.Add(new PackageSizeInfo - { - supportedUnityVersion = version, - assetCount = string.IsNullOrEmpty(assetCount) ? 0 : ulong.Parse(assetCount), - downloadSize = string.IsNullOrEmpty(downloadSize) ? 0 : ulong.Parse(downloadSize) - }); - } - } - - m_SizeInfos.Sort((left, right) => left.supportedUnityVersion.CompareByPrecedence(right.supportedUnityVersion)); - } - - m_Links = new List(); - - var slug = productDetail.ContainsKey("slug") ? productDetail["slug"] as string : m_PackageUniqueId; - m_Links.Add(new PackageLink {name = "View in the Asset Store", url = $"/packages/p/{slug}"}); - - if (publisher.ContainsKey("url")) - { - var url = publisher["url"] as string; - if (!string.IsNullOrEmpty(url) && Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute)) - m_Links.Add(new PackageLink {name = "Publisher Web Site", url = url}); - } - - if (publisher.ContainsKey("supportUrl")) - { - var url = publisher["supportUrl"] as string; - if (!string.IsNullOrEmpty(url) && Uri.IsWellFormedUriString(url, UriKind.RelativeOrAbsolute)) - m_Links.Add(new PackageLink {name = "Publisher Support", url = url}); - } - - if (productDetail.ContainsKey("state")) + m_SupportedUnityVersions = new List(); + if (fetchedInfo.supportedVersions?.Any() ?? false) + { + foreach (var supportedVersion in fetchedInfo.supportedVersions) { - var state = productDetail["state"] as string; - if (state.Equals("published", StringComparison.InvariantCultureIgnoreCase)) - m_Tag |= PackageTag.Published; - else if (state.Equals("deprecated", StringComparison.InvariantCultureIgnoreCase)) - m_Tag |= PackageTag.Deprecated; + SemVersion version; + if (SemVersion.TryParse(supportedVersion as string, out version)) + m_SupportedUnityVersions.Add(version); } + m_SupportedUnityVersions.Sort((left, right) => left.CompareByPrecedence(right)); + } - m_LocalPath = productDetail.ContainsKey("localPath") ? productDetail["localPath"] as string : string.Empty; - m_IsAvailableOnDisk = !string.IsNullOrEmpty(m_LocalPath) && File.Exists(m_LocalPath); + if (localInfo != null) + { + var simpleVersion = Regex.Replace(localInfo.supportedVersion, @"(?\d+)\.(?\d+).(?\d+)[abfp].+", "${major}.${minor}.${patch}"); + SemVersion.TryParse(simpleVersion.Trim(), out m_SupportedUnityVersion); } - catch (Exception e) + else { - m_Errors.Add(new Error(NativeErrorCode.Unknown, e.Message)); + m_SupportedUnityVersion = m_SupportedUnityVersions.LastOrDefault(); } - } - - private static string CleanUpHtml(string source) - { - if (string.IsNullOrEmpty(source)) - return source; - source = source.Replace("
", "\n"); + m_SizeInfos = new List(fetchedInfo.sizeInfos); + m_SizeInfos.Sort((left, right) => left.supportedUnityVersion.CompareByPrecedence(right.supportedUnityVersion)); - var array = new char[source.Length]; - var arrayIndex = 0; - var inside = false; + var state = fetchedInfo.state ?? string.Empty; + if (state.Equals("published", StringComparison.InvariantCultureIgnoreCase)) + m_Tag |= PackageTag.Published; + else if (state.Equals("deprecated", StringComparison.InvariantCultureIgnoreCase)) + m_Tag |= PackageTag.Deprecated; - foreach (var c in source.ToCharArray()) - { - if (c == '<') - inside = true; - else if (c == '>') - inside = false; - else - { - if (!inside) - array[arrayIndex++] = c; - } - } + m_LocalPath = localInfo?.packagePath ?? string.Empty; + m_IsAvailableOnDisk = !string.IsNullOrEmpty(m_LocalPath) && File.Exists(m_LocalPath); + } - var text = new string(array, 0, arrayIndex); - text = Regex.Replace(text, @"&#x?\d+;", ""); - text = text.Replace(" ", " "); - text = text.Replace("<", "<"); - text = text.Replace(">", ">"); - text = text.Replace("&", "&"); - text = text.Replace(""", "\""); - text = text.Replace("'", "'"); - text = Regex.Replace(text, @"[\n\r]+", "\n"); - text = text.Trim(' ', '\r', '\n', '\t'); - - return text; + public void SetUpmPackageFetchError(Error error) + { + m_Errors.Add(error); + m_Tag &= ~(PackageTag.Downloadable | PackageTag.Importable); } } } diff --git a/Modules/PackageManagerUI/Editor/Services/AssetStore/AssetStoreRestAPI.cs b/Modules/PackageManagerUI/Editor/Services/AssetStore/AssetStoreRestAPI.cs index c06a5b252a..581f0b5032 100644 --- a/Modules/PackageManagerUI/Editor/Services/AssetStore/AssetStoreRestAPI.cs +++ b/Modules/PackageManagerUI/Editor/Services/AssetStore/AssetStoreRestAPI.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Linq; using UnityEditor.Connect; namespace UnityEditor.PackageManager.UI.AssetStore @@ -219,7 +220,7 @@ public void GetDownloadDetail(long productID, Action doneCa }); } - public void GetProductUpdateDetail(List localPackages, Action> doneCallbackAction) + public void GetProductUpdateDetail(IEnumerable localInfos, Action> doneCallbackAction) { AssetStoreOAuth.instance.FetchUserInfo(userInfo => { @@ -231,7 +232,7 @@ public void GetProductUpdateDetail(List localPackages, return; } - if (localPackages?.Count == 0) + if (localInfos == null || !localInfos.Any()) { doneCallbackAction?.Invoke(new Dictionary()); return; @@ -239,35 +240,14 @@ public void GetProductUpdateDetail(List localPackages, var packageList = new List>(); - foreach (var product in localPackages) + foreach (var info in localInfos) { var dictData = new Dictionary(); - dictData["local_path"] = product.packagePath; - - try - { - var localPackageJson = Json.Deserialize(product.jsonInfo) as Dictionary; - if (localPackageJson == null) - { - var ret = new Dictionary(); - ret["errorMessage"] = "Failed to parse JSON in local package"; - doneCallbackAction?.Invoke(ret); - return; - } - - dictData["id"] = localPackageJson["id"] as string; - dictData["version"] = localPackageJson["version"] as string; - dictData["version_id"] = localPackageJson["version_id"] as string; - } - catch (Exception e) - { - Dictionary ret = new Dictionary(); - ret["errorMessage"] = e.Message; - doneCallbackAction?.Invoke(ret); - return; - } - + dictData["local_path"] = info?.packagePath ?? string.Empty; + dictData["id"] = info?.id ?? string.Empty; + dictData["version"] = info?.versionString ?? string.Empty; + dictData["version_id"] = info?.versionId ?? string.Empty; packageList.Add(dictData); } diff --git a/Modules/PackageManagerUI/Editor/Services/AssetStore/AssetStoreVersionList.cs b/Modules/PackageManagerUI/Editor/Services/AssetStore/AssetStoreVersionList.cs new file mode 100644 index 0000000000..9160d7034f --- /dev/null +++ b/Modules/PackageManagerUI/Editor/Services/AssetStore/AssetStoreVersionList.cs @@ -0,0 +1,48 @@ +// Unity C# reference source +// Copyright (c) Unity Technologies. For terms of use, see +// https://unity3d.com/legal/licenses/Unity_Reference_Only_License + +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEditor.PackageManager.UI.AssetStore; + +namespace UnityEditor.PackageManager.UI +{ + [Serializable] + internal class AssetStoreVersionList : IVersionList + { + private List m_Versions; + + public IEnumerable all => m_Versions.Cast(); + + public IEnumerable key => m_Versions.Cast(); + + public IPackageVersion installed => null; + + public IPackageVersion latest => m_Versions.LastOrDefault(); + + public IPackageVersion latestPatch => latest; + + public IPackageVersion importAvailable => m_Versions.FirstOrDefault(v => v.isAvailableOnDisk); + + public IPackageVersion recommended => latest; + + public IPackageVersion primary => importAvailable ?? latest; + + public AssetStoreVersionList() + { + m_Versions = new List(); + } + + public void AddVersion(AssetStorePackageVersion version) + { + m_Versions.Add(version); + } + + public void RemoveVersion(AssetStorePackageVersion version) + { + m_Versions.Remove(version); + } + } +} diff --git a/Modules/PackageManagerUI/Editor/Services/Common/PackageExtensions.cs b/Modules/PackageManagerUI/Editor/Services/Common/PackageExtensions.cs new file mode 100644 index 0000000000..7b7ed868bf --- /dev/null +++ b/Modules/PackageManagerUI/Editor/Services/Common/PackageExtensions.cs @@ -0,0 +1,35 @@ +// Unity C# reference source +// Copyright (c) Unity Technologies. For terms of use, see +// https://unity3d.com/legal/licenses/Unity_Reference_Only_License + +using System.Linq; + +namespace UnityEditor.PackageManager.UI +{ + internal static class PackageExtensions + { + public static PackageState GetState(this IPackage package) + { + if (package.progress != PackageProgress.None) + return PackageState.InProgress; + + if (package.errors.Any()) + return PackageState.Error; + + var primary = package.primaryVersion; + if (primary.HasTag(PackageTag.InDevelopment)) + return PackageState.InDevelopment; + + if (primary != package.recommendedVersion) + return PackageState.Outdated; + + if (package.versionList.importAvailable != null) + return PackageState.ImportAvailable; + + if (package.installedVersion != null) + return PackageState.Installed; + + return PackageState.UpToDate; + } + } +} diff --git a/Modules/PackageManagerUI/Editor/Services/Common/PlaceholderPackage.cs b/Modules/PackageManagerUI/Editor/Services/Common/PlaceholderPackage.cs index b6457318d6..e1cdaf1833 100644 --- a/Modules/PackageManagerUI/Editor/Services/Common/PlaceholderPackage.cs +++ b/Modules/PackageManagerUI/Editor/Services/Common/PlaceholderPackage.cs @@ -18,27 +18,42 @@ internal class PlaceholderPackage : IPackage public string name => string.Empty; public string uniqueId => m_UniqueId; - public string displayName => m_Version.displayName; + public string displayName => versions.FirstOrDefault().displayName; [SerializeField] - private PlaceholderPackageVersion m_Version; + private PlaceholderVersionList m_VersionList; - public IEnumerable versions => new[] { m_Version }; - public IEnumerable keyVersions => new[] { m_Version }; - public IPackageVersion installedVersion => null; - public IPackageVersion latestVersion => m_Version; - public IPackageVersion latestPatch => m_Version; - public IPackageVersion recommendedVersion => m_Version; - public IPackageVersion primaryVersion => m_Version; + public IVersionList versionList => m_VersionList; [SerializeField] - private PackageState m_State; - public PackageState state => m_State; + private PackageProgress m_Progress; + public PackageProgress progress => m_Progress; + + [SerializeField] + private PackageType m_Type; public bool isDiscoverable => true; public IEnumerable errors => Enumerable.Empty(); + public IEnumerable images => Enumerable.Empty(); + + public IEnumerable links => Enumerable.Empty(); + + public IEnumerable versions => versionList?.all; + + public IEnumerable keyVersions => versionList?.key; + + public IPackageVersion installedVersion => versionList?.installed; + + public IPackageVersion latestVersion => versionList?.latest; + + public IPackageVersion latestPatch => versionList?.latestPatch; + + public IPackageVersion recommendedVersion => versionList?.recommended; + + public IPackageVersion primaryVersion => versionList?.primary; + public void AddError(Error error) { } @@ -47,11 +62,17 @@ public void ClearErrors() { } - public PlaceholderPackage(string uniqueId, PackageTag tag = PackageTag.None, PackageSource source = PackageSource.Unknown, PackageState state = PackageState.InProgress) + public bool Is(PackageType type) + { + return (m_Type & type) != 0; + } + + public PlaceholderPackage(string uniqueId, PackageType type = PackageType.None, PackageTag tag = PackageTag.None, PackageProgress progress = PackageProgress.None) { + m_Type = type; m_UniqueId = uniqueId; - m_State = state; - m_Version = new PlaceholderPackageVersion(uniqueId, uniqueId, tag, source); + m_Progress = progress; + m_VersionList = new PlaceholderVersionList(new PlaceholderPackageVersion(uniqueId, uniqueId, tag)); } public IPackage Clone() diff --git a/Modules/PackageManagerUI/Editor/Services/Common/PlaceholderPackageVersion.cs b/Modules/PackageManagerUI/Editor/Services/Common/PlaceholderPackageVersion.cs index e7f41f62cc..757380742e 100644 --- a/Modules/PackageManagerUI/Editor/Services/Common/PlaceholderPackageVersion.cs +++ b/Modules/PackageManagerUI/Editor/Services/Common/PlaceholderPackageVersion.cs @@ -21,20 +21,18 @@ internal class PlaceholderPackageVersion : IPackageVersion public string uniqueId => m_UniqueId; public string name => string.Empty; - [SerializeField] - private PackageSource m_Source; - public PackageSource source => m_Source; - public string displayName => string.Empty; - public string type => string.Empty; - public string author => string.Empty; + public string authorLink => string.Empty; + public string description => string.Empty; public string category => string.Empty; + public IDictionary categoryLinks => null; + public IEnumerable errors => Enumerable.Empty(); public IEnumerable samples => Enumerable.Empty(); @@ -57,8 +55,6 @@ internal class PlaceholderPackageVersion : IPackageVersion public bool isFullyFetched => true; - public bool isUserVisible => true; - public bool isAvailableOnDisk => false; public bool isVersionLocked => true; @@ -77,12 +73,8 @@ internal class PlaceholderPackageVersion : IPackageVersion public IEnumerable supportedVersions => Enumerable.Empty(); - public IEnumerable images => Enumerable.Empty(); - public IEnumerable sizes => Enumerable.Empty(); - public IEnumerable links => Enumerable.Empty(); - public SemVersion supportedVersion => null; [SerializeField] @@ -97,7 +89,6 @@ public PlaceholderPackageVersion(string packageUniqueId, string uniqueId, Packag m_PackageUniqueId = packageUniqueId; m_UniqueId = uniqueId; m_Tag = tag; - m_Source = source; } } } diff --git a/Modules/PackageManagerUI/Editor/Services/Common/PlaceholderVersionList.cs b/Modules/PackageManagerUI/Editor/Services/Common/PlaceholderVersionList.cs new file mode 100644 index 0000000000..f600a45793 --- /dev/null +++ b/Modules/PackageManagerUI/Editor/Services/Common/PlaceholderVersionList.cs @@ -0,0 +1,36 @@ +// Unity C# reference source +// Copyright (c) Unity Technologies. For terms of use, see +// https://unity3d.com/legal/licenses/Unity_Reference_Only_License + +using System; +using System.Collections.Generic; + +namespace UnityEditor.PackageManager.UI +{ + [Serializable] + internal class PlaceholderVersionList : IVersionList + { + private PlaceholderPackageVersion[] m_Versions; + + public IEnumerable all => m_Versions; + + public IEnumerable key => m_Versions; + + public IPackageVersion installed => null; + + public IPackageVersion latest => m_Versions[0]; + + public IPackageVersion latestPatch => m_Versions[0]; + + public IPackageVersion importAvailable => null; + + public IPackageVersion recommended => m_Versions[0]; + + public IPackageVersion primary => m_Versions[0]; + + public PlaceholderVersionList(PlaceholderPackageVersion version) + { + m_Versions = new[] { version }; + } + } +} diff --git a/Modules/PackageManagerUI/Editor/Services/Interfaces/IAssetStoreClient.cs b/Modules/PackageManagerUI/Editor/Services/Interfaces/IAssetStoreClient.cs index fbf89a41b0..72f7dd3a81 100644 --- a/Modules/PackageManagerUI/Editor/Services/Interfaces/IAssetStoreClient.cs +++ b/Modules/PackageManagerUI/Editor/Services/Interfaces/IAssetStoreClient.cs @@ -15,6 +15,7 @@ internal interface IAssetStoreClient event Action onProductFetched; event Action> onPackagesChanged; + event Action onPackageVersionUpdated; event Action onDownloadProgress; @@ -32,9 +33,7 @@ internal interface IAssetStoreClient void FetchDetails(IEnumerable packageIds); - void Refresh(IPackage package); - - void Refresh(IEnumerable packages); + void RefreshLocal(); bool IsAnyDownloadInProgress(); diff --git a/Modules/PackageManagerUI/Editor/Services/Interfaces/IAssetStoreRestAPI.cs b/Modules/PackageManagerUI/Editor/Services/Interfaces/IAssetStoreRestAPI.cs index 1bf7816ad6..e539b13af0 100644 --- a/Modules/PackageManagerUI/Editor/Services/Interfaces/IAssetStoreRestAPI.cs +++ b/Modules/PackageManagerUI/Editor/Services/Interfaces/IAssetStoreRestAPI.cs @@ -38,6 +38,6 @@ internal interface IAssetStoreRestAPI void GetDownloadDetail(long productID, Action doneCallbackAction); - void GetProductUpdateDetail(List localPackages, Action> doneCallbackAction); + void GetProductUpdateDetail(IEnumerable localInfos, Action> doneCallbackAction); } } diff --git a/Modules/PackageManagerUI/Editor/Services/Interfaces/IPackage.cs b/Modules/PackageManagerUI/Editor/Services/Interfaces/IPackage.cs index aa6c026660..2a8d726da7 100644 --- a/Modules/PackageManagerUI/Editor/Services/Interfaces/IPackage.cs +++ b/Modules/PackageManagerUI/Editor/Services/Interfaces/IPackage.cs @@ -14,6 +14,8 @@ internal interface IPackage string displayName { get; } + IVersionList versionList { get; } + IEnumerable versions { get; } IEnumerable keyVersions { get; } @@ -31,7 +33,13 @@ internal interface IPackage // it will be the default that will be displayed if no versions are selected IPackageVersion primaryVersion { get; } - PackageState state { get; } + PackageProgress progress { get; } + + IEnumerable images { get; } + + IEnumerable links { get; } + + bool Is(PackageType type); bool isDiscoverable { get; } diff --git a/Modules/PackageManagerUI/Editor/Services/Interfaces/IPackageVersion.cs b/Modules/PackageManagerUI/Editor/Services/Interfaces/IPackageVersion.cs index a401bd1087..6c6b6d16f9 100644 --- a/Modules/PackageManagerUI/Editor/Services/Interfaces/IPackageVersion.cs +++ b/Modules/PackageManagerUI/Editor/Services/Interfaces/IPackageVersion.cs @@ -13,21 +13,20 @@ internal interface IPackageVersion string displayName { get; } - string type { get; } - string author { get; } + string authorLink { get; } + string description { get; } string category { get; } + IDictionary categoryLinks { get; } + string packageUniqueId { get; } string uniqueId { get; } - // TODO: Might need to create a wrapper `PackageSource` to account for AssetStore package info. - PackageSource source { get; } - IEnumerable errors { get; } IEnumerable samples { get; } @@ -51,8 +50,6 @@ internal interface IPackageVersion // A version is fully fetched when the information isn't derived from another version (therefore may be inaccurate) bool isFullyFetched { get; } - bool isUserVisible { get; } - bool isAvailableOnDisk { get; } bool isVersionLocked { get; } @@ -73,12 +70,8 @@ internal interface IPackageVersion IEnumerable supportedVersions { get; } - IEnumerable images { get; } - IEnumerable sizes { get; } - IEnumerable links { get; } - EntitlementsInfo entitlements { get; } } } diff --git a/Modules/PackageManagerUI/Editor/Services/Interfaces/IUpmClient.cs b/Modules/PackageManagerUI/Editor/Services/Interfaces/IUpmClient.cs index e8fda42ffb..05ddacc77a 100644 --- a/Modules/PackageManagerUI/Editor/Services/Interfaces/IUpmClient.cs +++ b/Modules/PackageManagerUI/Editor/Services/Interfaces/IUpmClient.cs @@ -16,8 +16,10 @@ internal interface IUpmClient event Action onEmbedOperation; event Action> onPackagesChanged; - - event Action onPackageVersionUpdated; + event Action onProductPackageChanged; + event Action onPackageVersionUpdated; + event Action onProductPackageVersionUpdated; + event Action onProductPackageFetchError; bool isAddRemoveOrEmbedInProgress { get; } @@ -28,6 +30,8 @@ internal interface IUpmClient void SearchAll(bool offlineMode = false); void ExtraFetch(string packageId); + void FetchForProduct(string productId, string packageName); + void List(bool offlineMode = false); void AddById(string packageId); void AddByPath(string path); @@ -43,5 +47,7 @@ internal interface IUpmClient void Clear(); void Reset(); + + void ResetProductCache(); } } diff --git a/Modules/PackageManagerUI/Editor/Services/Interfaces/IVersionList.cs b/Modules/PackageManagerUI/Editor/Services/Interfaces/IVersionList.cs new file mode 100644 index 0000000000..ae0f347335 --- /dev/null +++ b/Modules/PackageManagerUI/Editor/Services/Interfaces/IVersionList.cs @@ -0,0 +1,30 @@ +// Unity C# reference source +// Copyright (c) Unity Technologies. For terms of use, see +// https://unity3d.com/legal/licenses/Unity_Reference_Only_License + +using System.Collections.Generic; + +namespace UnityEditor.PackageManager.UI +{ + internal interface IVersionList + { + IEnumerable all { get; } + + IEnumerable key { get; } + + IPackageVersion installed { get; } + + IPackageVersion latest { get; } + + IPackageVersion latestPatch { get; } + + IPackageVersion importAvailable { get; } + + // the recommended version to install or update to + IPackageVersion recommended { get; } + + // the primary version is most important version that we want to show to the user + // it will be the default that will be displayed if no versions are selected + IPackageVersion primary { get; } + } +} diff --git a/Modules/PackageManagerUI/Editor/Services/Packages/PackageDatabase.cs b/Modules/PackageManagerUI/Editor/Services/Packages/PackageDatabase.cs index 5c632119e4..726eff87a7 100644 --- a/Modules/PackageManagerUI/Editor/Services/Packages/PackageDatabase.cs +++ b/Modules/PackageManagerUI/Editor/Services/Packages/PackageDatabase.cs @@ -207,6 +207,7 @@ public void Setup() UpmClient.instance.Setup(); AssetStore.AssetStoreClient.instance.onPackagesChanged += OnPackagesChanged; + AssetStore.AssetStoreClient.instance.onPackageVersionUpdated += OnUpmPackageVersionUpdated; AssetStore.AssetStoreClient.instance.onDownloadProgress += OnDownloadProgress; AssetStore.AssetStoreClient.instance.onListOperationStart += OnAssetStoreOperationStart; AssetStore.AssetStoreClient.instance.onListOperationFinish += OnAssetStoreOperationFinish; @@ -214,9 +215,6 @@ public void Setup() AssetStore.AssetStoreClient.instance.Setup(); ApplicationUtil.instance.onUserLoginStateChange += OnUserLoginStateChange; - - //if (m_RefreshedOnce) - // Refresh(RefreshOptions.Purchased | RefreshOptions.OfflineMode); } public void Clear() @@ -235,6 +233,7 @@ public void Clear() UpmClient.instance.Clear(); AssetStore.AssetStoreClient.instance.onPackagesChanged -= OnPackagesChanged; + AssetStore.AssetStoreClient.instance.onPackageVersionUpdated -= OnUpmPackageVersionUpdated; AssetStore.AssetStoreClient.instance.onDownloadProgress -= OnDownloadProgress; AssetStore.AssetStoreClient.instance.onListOperationStart -= OnAssetStoreOperationStart; AssetStore.AssetStoreClient.instance.onListOperationFinish -= OnAssetStoreOperationFinish; @@ -275,7 +274,7 @@ private void OnDownloadProgress(DownloadProgress progress) if (progress.state == DownloadProgress.State.Completed) { - AssetStore.AssetStoreClient.instance.Refresh(package); + AssetStore.AssetStoreClient.instance.RefreshLocal(); } onDownloadProgress?.Invoke(package, progress); @@ -455,14 +454,15 @@ private void OnListOrSearchOperationFinalized(IOperation operation) onRefreshOperationFinish(operation is UpmListOperation ? PackageFilterTab.Local : PackageFilterTab.All); } - private void OnUpmPackageVersionUpdated(IPackageVersion version) + private void OnUpmPackageVersionUpdated(string packageUniqueId, IPackageVersion version) { - var upmPackage = GetPackage(version.packageInfo.name) as UpmPackage; - if (upmPackage != null) + var package = GetPackage(packageUniqueId); + var upmVersions = package?.versions as UpmVersionList; + if (upmVersions != null) { - var packagePreUpdate = upmPackage.Clone(); - upmPackage.UpdateVersion(version as UpmPackageVersion); - onPackagesChanged?.Invoke(k_EmptyList, k_EmptyList, new[] { packagePreUpdate }, new[] { upmPackage }); + var packagePreUpdate = package.Clone(); + upmVersions.UpdateVersion(version as UpmPackageVersion); + onPackagesChanged?.Invoke(k_EmptyList, k_EmptyList, new[] { packagePreUpdate }, new[] { package }); } } @@ -487,7 +487,7 @@ public void Uninstall(IPackage package) { if (package.installedVersion == null) return; - UpmClient.instance.RemoveByName(package.uniqueId); + UpmClient.instance.RemoveByName(package.name); } public void Embed(IPackageVersion packageVersion) @@ -501,7 +501,7 @@ public void RemoveEmbedded(IPackage package) { if (package.installedVersion == null) return; - UpmClient.instance.RemoveEmbeddedByName(package.uniqueId); + UpmClient.instance.RemoveEmbeddedByName(package.name); } public void FetchExtraInfo(IPackageVersion version) diff --git a/Modules/PackageManagerUI/Editor/Services/Packages/PackageFiltering.cs b/Modules/PackageManagerUI/Editor/Services/Packages/PackageFiltering.cs index e0449c1939..35872bae9d 100644 --- a/Modules/PackageManagerUI/Editor/Services/Packages/PackageFiltering.cs +++ b/Modules/PackageManagerUI/Editor/Services/Packages/PackageFiltering.cs @@ -18,15 +18,13 @@ internal static bool FilterByTab(IPackage package, PackageFilterTab tab) switch (tab) { case PackageFilterTab.Modules: - return package.versions.Any(v => v.HasTag(PackageTag.BuiltIn) && !v.HasTag(PackageTag.AssetStore)); + return package.Is(PackageType.BuiltIn); case PackageFilterTab.All: - return package.versions.Any(v => !v.HasTag(PackageTag.BuiltIn) && !v.HasTag(PackageTag.AssetStore)) - && (package.isDiscoverable || (package.installedVersion?.isDirectDependency ?? false)); + return package.Is(PackageType.Installable) && (package.isDiscoverable || (package.installedVersion?.isDirectDependency ?? false)); case PackageFilterTab.Local: - return package.versions.Any(v => !v.HasTag(PackageTag.BuiltIn) && !v.HasTag(PackageTag.AssetStore)) - && (package.installedVersion?.isDirectDependency ?? false); + return !package.Is(PackageType.BuiltIn) && (package.installedVersion?.isDirectDependency ?? false); case PackageFilterTab.AssetStore: - return ApplicationUtil.instance.isUserLoggedIn && (package.primaryVersion?.HasTag(PackageTag.AssetStore) ?? false); + return ApplicationUtil.instance.isUserLoggedIn && package.Is(PackageType.AssetStore); case PackageFilterTab.InDevelopment: return package.installedVersion?.HasTag(PackageTag.InDevelopment) ?? false; default: @@ -34,12 +32,12 @@ internal static bool FilterByTab(IPackage package, PackageFilterTab tab) } } - internal static bool FilterByText(IPackageVersion version, string text) + internal static bool FilterByText(IPackage package, IPackageVersion version, string text) { if (string.IsNullOrEmpty(text)) return true; - if (version == null) + if (package == null || version == null) return false; if (version.name.IndexOf(text, StringComparison.CurrentCultureIgnoreCase) >= 0) @@ -48,39 +46,24 @@ internal static bool FilterByText(IPackageVersion version, string text) if (!string.IsNullOrEmpty(version.displayName) && version.displayName.IndexOf(text, StringComparison.CurrentCultureIgnoreCase) >= 0) return true; - if (!version.HasTag(PackageTag.BuiltIn)) - { - var prerelease = text.StartsWith("-") ? text.Substring(1) : text; - if (version.version != null && version.version.Prerelease.IndexOf(prerelease, StringComparison.CurrentCultureIgnoreCase) >= 0) - return true; - - if (version.version.StripTag().StartsWith(text, StringComparison.CurrentCultureIgnoreCase)) - return true; + var prerelease = text.StartsWith("-") ? text.Substring(1) : text; + if (version.version != null && version.version.Prerelease.IndexOf(prerelease, StringComparison.CurrentCultureIgnoreCase) >= 0) + return true; - if (version.HasTag(PackageTag.Preview)) - { - if (PackageTag.Preview.ToString().IndexOf(text, StringComparison.CurrentCultureIgnoreCase) >= 0) - return true; - } + if (version.HasTag(PackageTag.Preview) && PackageTag.Preview.ToString().IndexOf(text, StringComparison.CurrentCultureIgnoreCase) >= 0) + return true; - if (version.HasTag(PackageTag.Verified)) - { - if (PackageTag.Verified.ToString().IndexOf(text, StringComparison.CurrentCultureIgnoreCase) >= 0) - return true; - } + if (version.HasTag(PackageTag.Verified) && PackageTag.Verified.ToString().IndexOf(text, StringComparison.CurrentCultureIgnoreCase) >= 0) + return true; - if (version.HasTag(PackageTag.Core)) - { - if (PackageTag.BuiltIn.ToString().IndexOf(text, StringComparison.CurrentCultureIgnoreCase) >= 0) - return true; - } - } + if (version.version.StripTag().StartsWith(text, StringComparison.CurrentCultureIgnoreCase)) + return true; - if (version.HasTag(PackageTag.AssetStore)) + if (!string.IsNullOrEmpty(version.category)) { - var words = text.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries); - var categories = version.category?.Split('/'); - if (categories != null && words.All(word => word.Length >= 2 && categories.Any(category => category.StartsWith(word, StringComparison.CurrentCultureIgnoreCase)))) + var words = text.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + var categories = version.category.Split('/'); + if (words.All(word => word.Length >= 2 && categories.Any(category => category.StartsWith(word, StringComparison.CurrentCultureIgnoreCase)))) return true; } @@ -133,7 +116,7 @@ public bool FilterByCurrentSearchText(IPackage package) var trimText = currentSearchText.Trim(' ', '\t'); trimText = Regex.Replace(trimText, @"[ ]{2,}", " "); - return string.IsNullOrEmpty(trimText) || FilterByText(package.primaryVersion, trimText); + return string.IsNullOrEmpty(trimText) || FilterByText(package, package.primaryVersion, trimText); } public bool FilterByCurrentTab(IPackage package) diff --git a/Modules/PackageManagerUI/Editor/Services/Packages/PackageOrigin.cs b/Modules/PackageManagerUI/Editor/Services/Packages/PackageProgress.cs similarity index 62% rename from Modules/PackageManagerUI/Editor/Services/Packages/PackageOrigin.cs rename to Modules/PackageManagerUI/Editor/Services/Packages/PackageProgress.cs index b6e474079c..161b6ec74c 100644 --- a/Modules/PackageManagerUI/Editor/Services/Packages/PackageOrigin.cs +++ b/Modules/PackageManagerUI/Editor/Services/Packages/PackageProgress.cs @@ -4,11 +4,12 @@ namespace UnityEditor.PackageManager.UI { - internal enum PackageOrigin + internal enum PackageProgress { - Unknown, - Builtin, - Registry, - AssetStore, + None, + Refreshing, + Downloading, + Installing, + Removing } } diff --git a/Modules/PackageManagerUI/Editor/Services/Packages/PackageState.cs b/Modules/PackageManagerUI/Editor/Services/Packages/PackageState.cs index f1fef1c45b..fa52b886d0 100644 --- a/Modules/PackageManagerUI/Editor/Services/Packages/PackageState.cs +++ b/Modules/PackageManagerUI/Editor/Services/Packages/PackageState.cs @@ -7,6 +7,9 @@ namespace UnityEditor.PackageManager.UI internal enum PackageState { UpToDate, + Installed, + ImportAvailable, + InDevelopment, Outdated, InProgress, Error diff --git a/Modules/PackageManagerUI/Editor/Services/Packages/PackageTag.cs b/Modules/PackageManagerUI/Editor/Services/Packages/PackageTag.cs index 3afe5ca83f..4387b2c796 100644 --- a/Modules/PackageManagerUI/Editor/Services/Packages/PackageTag.cs +++ b/Modules/PackageManagerUI/Editor/Services/Packages/PackageTag.cs @@ -11,19 +11,26 @@ internal enum PackageTag : uint { None = 0, - // package source/origin + // package type InDevelopment = 1 << 0, Local = 1 << 1, Git = 1 << 2, - BuiltIn = 1 << 3, - Core = 1 << 4, - AssetStore = 1 << 5, - Published = 1 << 6, - Deprecated = 1 << 7, + Bundled = 1 << 3, + BuiltIn = 1 << 4, - // preview status - Verified = 1 << 10, // the recommended version if major version > 0 - Preview = 1 << 11, // with `preview`, `preview.x` tag or with `0` as major version - Release = 1 << 12 // no pre-release tag & major version > 0 + // attributes + VersionLocked = 1 << 8, + Installable = 1 << 9, + Removable = 1 << 10, + Downloadable = 1 << 11, + Importable = 1 << 12, + Embeddable = 1 << 13, + + // status + Published = 1 << 16, + Deprecated = 1 << 17, + Verified = 1 << 18, // the recommended version if major version > 0 + Preview = 1 << 19, // with `preview`, `preview.x` tag or with `0` as major version + Release = 1 << 20 // no pre-release tag & major version > 0 } } diff --git a/Modules/PackageManagerUI/Editor/Services/Packages/PackageType.cs b/Modules/PackageManagerUI/Editor/Services/Packages/PackageType.cs new file mode 100644 index 0000000000..5e1b3319bf --- /dev/null +++ b/Modules/PackageManagerUI/Editor/Services/Packages/PackageType.cs @@ -0,0 +1,18 @@ +// Unity C# reference source +// Copyright (c) Unity Technologies. For terms of use, see +// https://unity3d.com/legal/licenses/Unity_Reference_Only_License + +using System; + +namespace UnityEditor.PackageManager.UI +{ + [Flags] + internal enum PackageType + { + None = 0, + + Installable = 1 << 0, + BuiltIn = 1 << 1, + AssetStore = 1 << 2 + } +} diff --git a/Modules/PackageManagerUI/Editor/Services/Upm/UpmAddOperation.cs b/Modules/PackageManagerUI/Editor/Services/Upm/UpmAddOperation.cs index 72ad1a3588..b29662c734 100644 --- a/Modules/PackageManagerUI/Editor/Services/Upm/UpmAddOperation.cs +++ b/Modules/PackageManagerUI/Editor/Services/Upm/UpmAddOperation.cs @@ -14,11 +14,12 @@ internal class UpmAddOperation : UpmBaseOperation public override string specialUniqueId { get { return m_SpecialUniqueId; } } - public void Add(string packageId) + public void Add(string packageId, string packageUniqueId = null) { m_PackageId = packageId; m_PackageName = string.Empty; m_SpecialUniqueId = string.Empty; + m_PackageUniqueId = packageUniqueId ?? packageName; Start(); } diff --git a/Modules/PackageManagerUI/Editor/Services/Upm/UpmBaseOperation.cs b/Modules/PackageManagerUI/Editor/Services/Upm/UpmBaseOperation.cs index b3371585c1..cf8b91c655 100644 --- a/Modules/PackageManagerUI/Editor/Services/Upm/UpmBaseOperation.cs +++ b/Modules/PackageManagerUI/Editor/Services/Upm/UpmBaseOperation.cs @@ -30,7 +30,8 @@ public string packageName protected string m_PackageId = string.Empty; public string packageId { get { return m_PackageId; } } - public string packageUniqueId { get { return packageName; } } + protected string m_PackageUniqueId = string.Empty; + public string packageUniqueId { get { return m_PackageUniqueId; } } public string versionUniqueId { get { return packageId; } } public virtual string specialUniqueId { get { return string.Empty; } } diff --git a/Modules/PackageManagerUI/Editor/Services/Upm/UpmClient.cs b/Modules/PackageManagerUI/Editor/Services/Upm/UpmClient.cs index 426386f2c7..199eb86a13 100644 --- a/Modules/PackageManagerUI/Editor/Services/Upm/UpmClient.cs +++ b/Modules/PackageManagerUI/Editor/Services/Upm/UpmClient.cs @@ -26,7 +26,11 @@ internal class UpmClientInternal : ScriptableSingleton, IUpmC public event Action onEmbedOperation = delegate {}; public event Action> onPackagesChanged = delegate {}; - public event Action onPackageVersionUpdated = delegate {}; + public event Action onProductPackageChanged = delegate {}; + + public event Action onPackageVersionUpdated = delegate {}; + public event Action onProductPackageVersionUpdated = delegate {}; + public event Action onProductPackageFetchError = delegate {}; private UpmSearchOperation m_SearchOperation; private UpmSearchOperation m_SearchOfflineOperation; @@ -51,6 +55,11 @@ private PackageInfo GetInstalledPackageInfo(string packageName) return m_InstalledPackageInfos.TryGetValue(packageName, out result) ? result : null; } + private Dictionary m_ProductPackageInfos = new Dictionary(); + + // the mapping between package name (key) to asset store product id (value) + private Dictionary m_ProductIdMap = new Dictionary(); + private readonly Dictionary m_ExtraFetchOperations = new Dictionary(); private Dictionary> m_ExtraPackageInfo = new Dictionary>(); private void AddExtraPackageInfo(PackageInfo packageInfo) @@ -67,7 +76,10 @@ private void AddExtraPackageInfo(PackageInfo packageInfo) // arrays created to help serialize dictionaries private PackageInfo[] m_SerializedInstalledPackageInfos; private PackageInfo[] m_SerializedSearchPackageInfos; + private PackageInfo[] m_SerializedProductPackageInfos; private PackageInfo[] m_SerializedExtraPackageInfos; + private string[] m_SerializedProductIdMapKeys; + private string[] m_SerializedProductIdMapValues; [SerializeField] private bool m_SetupDone; @@ -108,7 +120,8 @@ public void AddById(string packageId) { if (isAddRemoveOrEmbedInProgress) return; - m_AddOperation.Add(packageId); + var packageName = packageId.Split(new[] { '@' }, 2)[0]; + m_AddOperation.Add(packageId, m_ProductIdMap.Get(packageName)); SetupAddOperation(); } @@ -210,7 +223,7 @@ public void EmbedByName(string packageName) { if (isAddRemoveOrEmbedInProgress) return; - m_EmbedOperation.Embed(packageName); + m_EmbedOperation.Embed(packageName, m_ProductIdMap.Get(packageName)); m_EmbedOperation.onProcessResult += OnProcessAddResult; m_EmbedOperation.onOperationError += error => Debug.LogError($"Error embedding package: {m_EmbedOperation.packageName}."); onEmbedOperation(m_EmbedOperation); @@ -220,7 +233,7 @@ public void RemoveByName(string packageName) { if (isAddRemoveOrEmbedInProgress) return; - m_RemoveOperation.Remove(packageName); + m_RemoveOperation.Remove(packageName, m_ProductIdMap.Get(packageName)); SetupRemoveOperation(); } @@ -300,28 +313,72 @@ private void OnProcessSearchAllResult(SearchRequest request, bool offlineMode) OnPackageInfosUpdated(FindUpdatedPackageInfos(oldSearchResult, searchResult)); } - public void ExtraFetch(string packageIdOrName) + public void ExtraFetch(string packageId) + { + ExtraFetchInternal(packageId); + } + + private void ExtraFetchInternal(string packageIdOrName, string productId = null) { if (m_ExtraFetchOperations.ContainsKey(packageIdOrName)) return; var operation = new UpmSearchOperation(); - operation.Search(packageIdOrName); - operation.onProcessResult += OnProcessExtraFetchResult; - operation.onOperationError += error => Debug.LogError($"Error searching for package {packageIdOrName} online."); + operation.Search(packageIdOrName, productId); + operation.onProcessResult += (requst) => OnProcessExtraFetchResult(requst, productId); + operation.onOperationError += (error) => OnProcessExtraFetchError(error, productId); + operation.onOperationFinalized += () => OnExtraFetchFinalized(packageIdOrName); m_ExtraFetchOperations[packageIdOrName] = operation; } - private void OnProcessExtraFetchResult(SearchRequest request) + private void OnProcessExtraFetchResult(SearchRequest request, string productId = null) { var packageInfo = request.Result.FirstOrDefault(); - AddExtraPackageInfo(packageInfo); - m_ExtraFetchOperations.Remove(packageInfo.packageId); + if (!string.IsNullOrEmpty(productId)) + { + var oldInfo = m_ProductPackageInfos.Get(packageInfo.name); + // remove the created package that's created before asset store info was fetched + if (oldInfo == null && m_InstalledPackageInfos.ContainsKey(packageInfo.name)) + onPackagesChanged(new[] { CreateUpmPackage(null, null, packageInfo.name) }); - // only trigger the call when the package is not installed, as installed version always have the most up-to-date package info - var installedPackageInfo = GetInstalledPackageInfo(packageInfo.name); - if (installedPackageInfo?.packageId != packageInfo.packageId) - onPackageVersionUpdated(new UpmPackageVersion(packageInfo, false)); + if (oldInfo == null || IsDifferent(oldInfo, packageInfo)) + { + m_ProductPackageInfos[packageInfo.name] = packageInfo; + OnPackageInfosUpdated(request.Result.Take(1)); + } + } + else + { + AddExtraPackageInfo(packageInfo); + + // only trigger the call when the package is not installed, as installed version always have the most up-to-date package info + var installedPackageInfo = m_InstalledPackageInfos.Get(packageInfo.name); + if (installedPackageInfo?.packageId != packageInfo.packageId) + { + productId = m_ProductIdMap.Get(packageInfo.name); + if (string.IsNullOrEmpty(productId)) + onPackageVersionUpdated?.Invoke(packageInfo.name, new UpmPackageVersion(packageInfo, false)); + else + onProductPackageVersionUpdated?.Invoke(productId, new UpmPackageVersion(packageInfo, false)); + } + } + } + + private void OnProcessExtraFetchError(Error error, string productId = null) + { + if (!string.IsNullOrEmpty(productId)) + onProductPackageFetchError?.Invoke(productId, error); + } + + private void OnExtraFetchFinalized(string packageIdOrName) + { + m_ExtraFetchOperations.Remove(packageIdOrName); + } + + public void FetchForProduct(string productId, string packageName) + { + m_ProductIdMap[packageName] = productId; + ExtraFetchInternal(packageName, productId); } private void OnPackageInfosUpdated(IEnumerable packageInfos) @@ -330,58 +387,85 @@ private void OnPackageInfosUpdated(IEnumerable packageInfos) return; var upmPackages = new List(); + var productPackages = new List(); var showPreview = PackageManagerPrefs.instance.showPreviewPackages; foreach (var p in packageInfos) { - var installedInfo = GetInstalledPackageInfo(p.name); - var searchInfo = GetSearchPackageInfo(p.name); - - upmPackages.Add(CreateUpmPackage(searchInfo, installedInfo, p.name)); + var productId = m_ProductIdMap.Get(p.name); + var installedInfo = m_InstalledPackageInfos.Get(p.name); + if (string.IsNullOrEmpty(productId)) + upmPackages.Add(CreateUpmPackage(m_SearchPackageInfos.Get(p.name), installedInfo, p.name)); + else + productPackages.Add(CreateUpmPackage(m_ProductPackageInfos.Get(p.name), installedInfo, p.name)); } - foreach (var package in upmPackages) + foreach (var package in upmPackages.Concat(productPackages)) { - if (!showPreview && ShouldPreviewsBeRemoved(package)) + if (!showPreview && ShouldPreviewsBeRemoved(package.versionList)) RemovePreviewVersions(package); - UpdateExtraPackageInfos(package); + UpdateExtraPackageInfos(package.name, package.versionList); } if (upmPackages.Any()) onPackagesChanged(upmPackages.Cast()); + + foreach (var package in productPackages) + onProductPackageChanged?.Invoke(m_ProductIdMap.Get(package.name), package); } private void OnShowPreviewPackagesChanged(bool showPreview) { var updatedUpmPackages = new List(); + var updatedProductPackages = new List(); foreach (var installedInfo in m_InstalledPackageInfos.Values) { - var package = CreateUpmPackage(GetSearchPackageInfo(installedInfo.name), installedInfo); - if (ShouldPreviewsBeRemoved(package)) - updatedUpmPackages.Add(package); + var productId = m_ProductIdMap.Get(installedInfo.name); + if (string.IsNullOrEmpty(productId)) + { + var package = CreateUpmPackage(m_SearchPackageInfos.Get(installedInfo.name), installedInfo); + if (ShouldPreviewsBeRemoved(package.versionList)) + updatedUpmPackages.Add(package); + } + else + { + var package = CreateUpmPackage(m_ProductPackageInfos.Get(installedInfo.name), installedInfo); + if (ShouldPreviewsBeRemoved(package.versionList)) + updatedProductPackages.Add(package); + } } foreach (var searchInfo in m_SearchPackageInfos.Values.Where(p => !m_InstalledPackageInfos.ContainsKey(p.name))) { var package = CreateUpmPackage(searchInfo, null); - if (ShouldPreviewsBeRemoved(package)) + if (ShouldPreviewsBeRemoved(package.versionList)) updatedUpmPackages.Add(package); } - foreach (var package in updatedUpmPackages) + foreach (var productPackageInfo in m_ProductPackageInfos.Values.Where(p => !m_InstalledPackageInfos.ContainsKey(p.name))) + { + var package = CreateUpmPackage(productPackageInfo, null); + if (ShouldPreviewsBeRemoved(package.versionList)) + updatedProductPackages.Add(package); + } + + foreach (var package in updatedUpmPackages.Concat(updatedProductPackages)) { if (!showPreview) RemovePreviewVersions(package); - UpdateExtraPackageInfos(package); + UpdateExtraPackageInfos(package.name, package.versionList); } if (updatedUpmPackages.Any()) - onPackagesChanged(updatedUpmPackages.Cast()); + onPackagesChanged?.Invoke(updatedUpmPackages.Cast()); + + foreach (var package in updatedProductPackages) + onProductPackageChanged?.Invoke(m_ProductIdMap.Get(package.name), package); } private UpmPackage CreateUpmPackage(PackageInfo searchInfo, PackageInfo installedInfo, string packageName = null) { if (searchInfo == null && installedInfo == null) - return new UpmPackage(packageName, Enumerable.Empty(), false); + return new UpmPackage(packageName, false, PackageType.Installable); UpmPackage result; if (searchInfo == null) @@ -395,15 +479,15 @@ private UpmPackage CreateUpmPackage(PackageInfo searchInfo, PackageInfo installe return result; } - private void UpdateExtraPackageInfos(UpmPackage package) + private void UpdateExtraPackageInfos(string packageName, IVersionList versions) { - if (!package.versions.Any()) + if (!versions.all.Any()) return; Dictionary extraVersions; - if (m_ExtraPackageInfo.TryGetValue(package.name, out extraVersions)) + if (m_ExtraPackageInfo.TryGetValue(packageName, out extraVersions)) { - foreach (var version in package.versions.Cast()) + foreach (var version in versions.all.Cast()) { if (version.isFullyFetched) continue; @@ -415,14 +499,14 @@ private void UpdateExtraPackageInfos(UpmPackage package) // if the primary version is not fully fetched, trigger an extra fetch automatically right away to get results early // since the primary version's display name is used in the package list - var primaryVersion = package.primaryVersion; + var primaryVersion = versions.primary; if (!primaryVersion.isFullyFetched) ExtraFetch(primaryVersion.uniqueId); } - private static bool IsPreviewInstalled(UpmPackage package) + private static bool IsPreviewInstalled(IVersionList versions) { - return (!package.installedVersion?.HasTag(PackageTag.Release)) ?? false; + return (!versions.installed?.HasTag(PackageTag.Release)) ?? false; } private static bool IsPreviewInstalled(PackageInfo packageInfo) @@ -432,11 +516,11 @@ private static bool IsPreviewInstalled(PackageInfo packageInfo) } // check if the preview versions be filtered out if the user have `show previews` turned off - private static bool ShouldPreviewsBeRemoved(UpmPackage package) + private static bool ShouldPreviewsBeRemoved(IVersionList versions) { - if (IsPreviewInstalled(package)) + if (IsPreviewInstalled(versions)) return false; - return package.versions.Any(v => !v.HasTag(PackageTag.Release)); + return versions.all.Any(v => !v.HasTag(PackageTag.Release)); } private static void RemovePreviewVersions(UpmPackage package) @@ -477,7 +561,10 @@ public void OnBeforeSerialize() { m_SerializedInstalledPackageInfos = m_InstalledPackageInfos.Values.ToArray(); m_SerializedSearchPackageInfos = m_SearchPackageInfos.Values.ToArray(); + m_SerializedProductPackageInfos = m_ProductPackageInfos.Values.ToArray(); m_SerializedExtraPackageInfos = m_ExtraPackageInfo.Values.SelectMany(p => p.Values).ToArray(); + m_SerializedProductIdMapKeys = m_ProductIdMap.Keys.ToArray(); + m_SerializedProductIdMapValues = m_ProductIdMap.Values.ToArray(); } public void OnAfterDeserialize() @@ -488,8 +575,13 @@ public void OnAfterDeserialize() foreach (var p in m_SerializedSearchPackageInfos) m_SearchPackageInfos[p.name] = p; + m_ProductPackageInfos = m_SerializedProductPackageInfos.ToDictionary(p => p.name, p => p); + foreach (var p in m_SerializedExtraPackageInfos) AddExtraPackageInfo(p); + + for (var i = 0; i < m_SerializedProductIdMapKeys.Length; i++) + m_ProductIdMap[m_SerializedProductIdMapKeys[i]] = m_SerializedProductIdMapValues[i]; } public void OnEnable() @@ -522,10 +614,24 @@ public void Reset() m_InstalledPackageInfos.Clear(); m_SearchPackageInfos.Clear(); m_ExtraPackageInfo.Clear(); + m_ProductIdMap.Clear(); + m_ExtraFetchOperations.Clear(); m_SerializedInstalledPackageInfos = new PackageInfo[0]; m_SerializedSearchPackageInfos = new PackageInfo[0]; m_SerializedExtraPackageInfos = new PackageInfo[0]; + + ResetProductCache(); + } + + public void ResetProductCache() + { + m_ProductPackageInfos.Clear(); + m_ProductIdMap.Clear(); + + m_SerializedProductPackageInfos = new PackageInfo[0]; + m_SerializedProductIdMapKeys = new string[0]; + m_SerializedProductIdMapValues = new string[0]; } } } diff --git a/Modules/PackageManagerUI/Editor/Services/Upm/UpmEmbedOperation.cs b/Modules/PackageManagerUI/Editor/Services/Upm/UpmEmbedOperation.cs index e4eec82dbe..b60b006983 100644 --- a/Modules/PackageManagerUI/Editor/Services/Upm/UpmEmbedOperation.cs +++ b/Modules/PackageManagerUI/Editor/Services/Upm/UpmEmbedOperation.cs @@ -8,9 +8,10 @@ namespace UnityEditor.PackageManager.UI { internal class UpmEmbedOperation : UpmBaseOperation { - public void Embed(string packageName) + public void Embed(string packageName, string packageUniqueId = null) { m_PackageName = packageName; + m_PackageUniqueId = packageUniqueId ?? packageName; Start(); } diff --git a/Modules/PackageManagerUI/Editor/Services/Upm/UpmPackage.cs b/Modules/PackageManagerUI/Editor/Services/Upm/UpmPackage.cs index 2b66a50fc4..23ebc39cbe 100644 --- a/Modules/PackageManagerUI/Editor/Services/Upm/UpmPackage.cs +++ b/Modules/PackageManagerUI/Editor/Services/Upm/UpmPackage.cs @@ -15,91 +15,25 @@ internal class UpmPackage : IPackage public string name { get { return m_Name; } } public string uniqueId { get { return m_Name; } } - public string displayName { get { return m_Versions.First().displayName; } } - - private PackageState m_State; - public PackageState state { get { return m_State; } } + public string displayName { get { return versions.First().displayName; } } private bool m_IsDiscoverable; public bool isDiscoverable { get { return m_IsDiscoverable; } } - private List m_Versions; - public IEnumerable versions { get { return m_Versions.Cast(); } } + private UpmVersionList m_VersionList; + public IVersionList versionList => m_VersionList; - public IEnumerable keyVersions - { - get - { - // Get key versions -- Latest, Verified, LatestPatch, Installed. - var keyVersions = new HashSet(); - - var installed = installedVersion; - var latestRelease = m_Versions.LastOrDefault(v => v.HasTag(PackageTag.Release)); - var verifiedVersion = m_Versions.FirstOrDefault(v => v.HasTag(PackageTag.Verified)); - keyVersions.Add(installed); - keyVersions.Add(latestRelease); - keyVersions.Add(verifiedVersion); - keyVersions.Add(latestPatch); - keyVersions.Add(recommendedVersion); - if (installed == null && latestRelease == null) - keyVersions.Add(latestVersion); - return keyVersions.Where(v => v != null).OrderBy(package => package.version); - } - } + public IEnumerable keyVersions => versionList?.key; - // keeping the index makes it easier to find newer versions - private int m_InstalledIndex; - public IPackageVersion installedVersion { get { return m_InstalledIndex < 0 ? null : m_Versions[m_InstalledIndex]; } } + public IPackageVersion installedVersion => versionList?.installed; - public IPackageVersion latestPatch - { - get - { - if (m_InstalledIndex < 0) - return null; - - var installed = m_Versions[m_InstalledIndex].version; - for (var i = m_Versions.Count - 1; i > m_InstalledIndex; --i) - { - if (m_Versions[i].version.IsPatchOf(installed)) - return m_Versions[i]; - } - return null; - } - } + public IPackageVersion latestPatch => versionList?.latestPatch; - public IPackageVersion latestVersion { get { return m_Versions.Last(); } } + public IPackageVersion latestVersion => versionList?.latest; - public IPackageVersion recommendedVersion - { - get - { - // Override with current when it's version locked - var installed = installedVersion; - if (installed != null && installed.isVersionLocked) - return installed; - - // Only try to find recommended version in versions newer than the installed version - var newerVersions = installed == null ? m_Versions : m_Versions.Skip(m_InstalledIndex + 1).SkipWhile(v => v.version <= installed.version); - - var verifiedVersion = newerVersions.FirstOrDefault(v => v.HasTag(PackageTag.Verified)); - if (verifiedVersion != null) - return verifiedVersion; - - var latestRelease = newerVersions.LastOrDefault(v => v.HasTag(PackageTag.Release)); - if (latestRelease != null && (installed == null || !installed.HasTag(PackageTag.Verified))) - return latestRelease; - - var latestPreview = newerVersions.LastOrDefault(package => package.HasTag(PackageTag.Preview)); - if (latestPreview != null && (installed == null || installed.HasTag(PackageTag.Preview))) - return latestPreview; - - // Show current if it exists, otherwise latest user visible, and then otherwise show the absolute latest - return installed ?? latestVersion; - } - } + public IPackageVersion recommendedVersion => versionList?.recommended; - public IPackageVersion primaryVersion { get { return installedVersion ?? recommendedVersion; } } + public IPackageVersion primaryVersion => versionList?.primary; // errors on the package level (not just about a particular version) List m_UpmErrors; @@ -108,100 +42,51 @@ public IPackageVersion recommendedVersion // Stop lookup after first error encountered on a version to save time not looking up redundant errors. public IEnumerable errors => (versions.Select(v => v.errors).FirstOrDefault(e => e?.Any() ?? false) ?? new List()).Concat(m_UpmErrors); - public UpmPackage(string name, IEnumerable versions, bool isDiscoverable) - { - Initialize(name, versions, isDiscoverable); - } + public IEnumerable images => Enumerable.Empty(); - public UpmPackage(PackageInfo info, bool isInstalled, bool isDiscoverable) - { - var mainVersion = new UpmPackageVersion(info, isInstalled); + public IEnumerable links => Enumerable.Empty(); + + public IEnumerable versions => versionList?.all; - var versions = info.versions.compatible.Select(v => new UpmPackageVersion(info, false, v, mainVersion.displayName)).ToList(); - AddToSortedVersions(versions, mainVersion); - Initialize(info.name, versions, isDiscoverable); + private PackageProgress m_Progress; + public PackageProgress progress => m_Progress; + + private PackageType m_Type; + public bool Is(PackageType type) + { + return (m_Type & type) != 0; } - private void Initialize(string name, IEnumerable versions, bool isDiscoverable) + public UpmPackage(string name, bool isDiscoverable, PackageType type = PackageType.None) { + m_Progress = PackageProgress.None; m_Name = name; - m_Versions = versions.ToList(); m_IsDiscoverable = isDiscoverable; - + m_VersionList = new UpmVersionList(); m_UpmErrors = new List(); - - SetInstalledVersion(m_Versions.FindIndex(v => v.isInstalled)); + m_Type = type; } - private void SetInstalledVersion(int newInstalledIndex) + public UpmPackage(PackageInfo info, bool isInstalled, bool isDiscoverable) { - m_InstalledIndex = newInstalledIndex; - m_State = PackageState.UpToDate; - if (m_Versions.Any(v => v.errors.Any())) - m_State = PackageState.Error; - else if (m_InstalledIndex >= 0 && !recommendedVersion.isInstalled) - m_State = PackageState.Outdated; + m_Progress = PackageProgress.None; + m_Name = info.name; + m_UpmErrors = new List(); + m_IsDiscoverable = isDiscoverable; + m_VersionList = new UpmVersionList(info, isInstalled); + m_Type = primaryVersion.HasTag(PackageTag.BuiltIn) ? PackageType.BuiltIn : PackageType.Installable; } internal void UpdateVersions(IEnumerable updatedVersions) { - Initialize(name, updatedVersions, isDiscoverable); - } - - internal void UpdateVersion(UpmPackageVersion version) - { - for (var i = 0; i < m_Versions.Count; ++i) - { - if (m_Versions[i].uniqueId != version.uniqueId) - continue; - m_Versions[i] = version; - return; - } - } - - private static int AddToSortedVersions(List sortedVersions, UpmPackageVersion versionToAdd) - { - for (var i = 0; i < sortedVersions.Count; ++i) - { - if (sortedVersions[i].version.CompareByPrecedence(versionToAdd.version) < 0) - continue; - // note that the difference between this and the previous function is that - // two upm package versions could have the the same version but different package id - if (sortedVersions[i].uniqueId == versionToAdd.uniqueId) - { - sortedVersions[i] = versionToAdd; - return i; - } - sortedVersions.Insert(i, versionToAdd); - return i; - } - sortedVersions.Add(versionToAdd); - return sortedVersions.Count - 1; + m_VersionList = new UpmVersionList(updatedVersions); + m_UpmErrors.Clear(); } // This function is only used to update the object, not to actually perform the add operation public void AddInstalledVersion(UpmPackageVersion newVersion) { - if (m_InstalledIndex >= 0) - { - m_Versions[m_InstalledIndex].isInstalled = false; - if (m_Versions[m_InstalledIndex].HasTag(PackageTag.Git | PackageTag.Local | PackageTag.InDevelopment)) - m_Versions.RemoveAt(m_InstalledIndex); - } - newVersion.isInstalled = true; - SetInstalledVersion(AddToSortedVersions(m_Versions, newVersion)); - } - - // This function is only used to update the object, not to actually perform the remove operation - public void RemoveInstalledVersion() - { - if (m_InstalledIndex >= 0) - { - m_Versions[m_InstalledIndex].isInstalled = false; - if (m_Versions[m_InstalledIndex].HasTag(PackageTag.Git | PackageTag.Local | PackageTag.InDevelopment)) - m_Versions.RemoveAt(m_InstalledIndex); - SetInstalledVersion(-1); - } + m_VersionList.AddInstalledVersion(newVersion); } public void AddError(Error error) diff --git a/Modules/PackageManagerUI/Editor/Services/Upm/UpmPackageDocs.cs b/Modules/PackageManagerUI/Editor/Services/Upm/UpmPackageDocs.cs index f73274d070..29b69b2489 100644 --- a/Modules/PackageManagerUI/Editor/Services/Upm/UpmPackageDocs.cs +++ b/Modules/PackageManagerUI/Editor/Services/Upm/UpmPackageDocs.cs @@ -96,14 +96,11 @@ public static string GetDocumentationUrl(IPackageVersion version, bool offline = if (offline) return GetOfflineDocumentationUrl(upmVersion); - if (upmVersion.source == PackageSource.BuiltIn) + if (upmVersion.HasTag(PackageTag.BuiltIn) && !string.IsNullOrEmpty(upmVersion.description)) { - if (!string.IsNullOrEmpty(upmVersion.description)) - { - var split = SplitBuiltinDescription(upmVersion); - if (split.Length > 1) - return split[1]; - } + var split = SplitBuiltinDescription(upmVersion); + if (split.Length > 1) + return split[1]; } return $"http://docs.unity3d.com/Packages/{upmVersion.shortVersionId}/index.html"; } @@ -157,9 +154,19 @@ private static string GetOfflineLicensesUrl(UpmPackageVersion version) return string.Empty; } + public static bool HasDocs(IPackageVersion version) + { + return (version as UpmPackageVersion) != null; + } + public static bool HasChangelog(IPackageVersion version) { - return version != null && !version.HasTag(PackageTag.BuiltIn) && string.IsNullOrEmpty(GetPackageUrlRedirect(version)); + return (version as UpmPackageVersion) != null && !version.HasTag(PackageTag.BuiltIn) && string.IsNullOrEmpty(GetPackageUrlRedirect(version)); + } + + public static bool HasLicenses(IPackageVersion version) + { + return (version as UpmPackageVersion) != null && !version.HasTag(PackageTag.BuiltIn); } } } diff --git a/Modules/PackageManagerUI/Editor/Services/Upm/UpmPackageVersion.cs b/Modules/PackageManagerUI/Editor/Services/Upm/UpmPackageVersion.cs index 1b9fae77af..4e18be3174 100644 --- a/Modules/PackageManagerUI/Editor/Services/Upm/UpmPackageVersion.cs +++ b/Modules/PackageManagerUI/Editor/Services/Upm/UpmPackageVersion.cs @@ -7,6 +7,7 @@ using System.Globalization; using System.IO; using System.Linq; +using UnityEditorInternal; using UnityEngine; namespace UnityEditor.PackageManager.UI @@ -22,6 +23,9 @@ internal class UpmPackageVersion : IPackageVersion public string name { get { return m_PackageInfo.name; } } public string type { get { return m_PackageInfo.type; } } public string category { get { return m_PackageInfo.category; } } + + public IDictionary categoryLinks => null; + public IEnumerable errors => m_PackageInfo.errors.Concat(entitlementsError != null ? new List { entitlementsError } : new List()); public bool isDirectDependency { get { return isFullyFetched && m_PackageInfo.isDirectDependency; } } @@ -34,14 +38,14 @@ internal class UpmPackageVersion : IPackageVersion private string m_PackageId; public string uniqueId { get { return m_PackageId; } } - public string packageUniqueId { get { return name; } } - - private PackageSource m_Source; - public PackageSource source { get { return m_Source; } } + private string m_PackageUniqueId; + public string packageUniqueId => m_PackageUniqueId; private string m_Author; public string author { get { return m_Author; } } + public string authorLink => string.Empty; + private string m_DisplayName; public string displayName { get { return m_DisplayName; } } @@ -116,12 +120,10 @@ public bool isInstalled set { m_IsInstalled = value; - m_Source = m_PackageInfo.source == PackageSource.BuiltIn || m_IsInstalled ? m_PackageInfo.source : PackageSource.Registry; + RefreshTags(); } } - public bool isUserVisible { get { return isInstalled || HasTag(PackageTag.Release | PackageTag.Preview | PackageTag.Verified | PackageTag.Core); } } - private string m_Description; public string description { get { return !string.IsNullOrEmpty(m_Description) ? m_Description : m_PackageInfo.description; } } @@ -132,28 +134,13 @@ public bool HasTag(PackageTag tag) return (m_Tag & tag) != 0; } - public bool isVersionLocked - { - get { return source == PackageSource.Embedded || source == PackageSource.Git || source == PackageSource.BuiltIn; } - } + public bool isVersionLocked => HasTag(PackageTag.VersionLocked); - public bool canBeRemoved - { - get { return source != PackageSource.Unknown; } - } + public bool canBeRemoved => HasTag(PackageTag.Removable); - public bool canBeEmbedded - { - get - { - return isInstalled && isDirectDependency && (source == PackageSource.Registry || HasTag(PackageTag.Core)); - } - } + public bool canBeEmbedded => HasTag(PackageTag.Embeddable); - private bool hasPathInId - { - get { return source == PackageSource.Local || source == PackageSource.Embedded || source == PackageSource.LocalTarball; } - } + public bool installedFromPath => HasTag(PackageTag.Local | PackageTag.InDevelopment | PackageTag.Git); public bool isAvailableOnDisk { @@ -162,7 +149,8 @@ public bool isAvailableOnDisk public string shortVersionId { get { return FormatPackageId(name, version.ShortVersion()); } } - public DateTime? publishedDate { get { return m_PackageInfo.datePublished; } } + private long m_PublishedDateTicks; + public DateTime? publishedDate => m_PublishedDateTicks == 0 ? m_PackageInfo.datePublished : new DateTime(m_PublishedDateTicks, DateTimeKind.Utc); public string publisherId => m_Author; @@ -183,17 +171,14 @@ public string localPath public IEnumerable supportedVersions => Enumerable.Empty(); - public IEnumerable images => Enumerable.Empty(); - public IEnumerable sizes => Enumerable.Empty(); - public IEnumerable links => Enumerable.Empty(); - public UpmPackageVersion(PackageInfo packageInfo, bool isInstalled, SemVersion version, string displayName) { m_Version = version; m_DisplayName = displayName; m_IsInstalled = isInstalled; + m_PackageUniqueId = packageInfo.name; UpdatePackageInfo(packageInfo); } @@ -207,14 +192,19 @@ internal void UpdatePackageInfo(PackageInfo newPackageInfo) { m_IsFullyFetched = m_Version == newPackageInfo.version; m_PackageInfo = newPackageInfo; - m_Source = m_PackageInfo.source == PackageSource.BuiltIn || m_IsInstalled ? m_PackageInfo.source : PackageSource.Registry; + m_PackageUniqueId = m_PackageInfo.name; RefreshTags(); + // For core packages, or packages that are bundled with Unity without being published, use Unity's build date + m_PublishedDateTicks = 0; + if (HasTag(PackageTag.Bundled) && m_PackageInfo.datePublished == null) + m_PublishedDateTicks = new DateTime(1970, 1, 1).Ticks + InternalEditorUtility.GetUnityVersionDate() * TimeSpan.TicksPerSecond; + m_Author = string.IsNullOrEmpty(m_PackageInfo.author.name) && m_PackageInfo.name.StartsWith(k_UnityPrefix) ? "Unity Technologies Inc." : m_PackageInfo.author.name; - if (m_Source == PackageSource.BuiltIn) + if (HasTag(PackageTag.BuiltIn)) m_Description = UpmPackageDocs.SplitBuiltinDescription(this)[0]; // reset sample parse status on package info update, such that the sample list gets regenerated @@ -224,7 +214,7 @@ internal void UpdatePackageInfo(PackageInfo newPackageInfo) { m_DisplayName = GetDisplayName(m_PackageInfo); m_PackageId = m_PackageInfo.packageId; - if (hasPathInId) + if (installedFromPath) m_PackageId = m_PackageId.Replace("\\", "/"); } else @@ -233,24 +223,38 @@ internal void UpdatePackageInfo(PackageInfo newPackageInfo) } } + internal void UpdateFetchedInfo(AssetStore.FetchedInfo fetchedInfo) + { + m_PackageUniqueId = fetchedInfo.id; + + // override version info with product info + m_DisplayName = fetchedInfo.displayName; + m_Description = fetchedInfo.description; + } + private void RefreshTags() { - switch (m_Source) + // in the case of git/local packages, we always assume that the non-installed versions are from the registry + var source = m_PackageInfo.source == PackageSource.BuiltIn || m_IsInstalled ? m_PackageInfo.source : PackageSource.Registry; + switch (source) { case PackageSource.BuiltIn: - m_Tag = type.Equals("module") ? PackageTag.BuiltIn : PackageTag.Core; + m_Tag = PackageTag.Bundled | PackageTag.VersionLocked; + if (m_PackageInfo.type == "module") + m_Tag |= PackageTag.BuiltIn; break; case PackageSource.Embedded: - m_Tag = PackageTag.InDevelopment; + m_Tag = PackageTag.InDevelopment | PackageTag.VersionLocked; break; case PackageSource.Local: + case PackageSource.LocalTarball: m_Tag = PackageTag.Local; break; case PackageSource.Git: - m_Tag = PackageTag.Git; + m_Tag = PackageTag.Git | PackageTag.VersionLocked; break; case PackageSource.Unknown: @@ -260,10 +264,14 @@ private void RefreshTags() break; } + m_Tag |= PackageTag.Installable | PackageTag.Removable; + if (isInstalled && isDirectDependency && !installedFromPath && !HasTag(PackageTag.BuiltIn)) + m_Tag |= PackageTag.Embeddable; + if (m_Version.IsRelease()) { m_Tag |= PackageTag.Release; - if (m_Version == m_PackageInfo.versions.verified && !HasTag(PackageTag.InDevelopment | PackageTag.Local | PackageTag.Git)) + if (m_Version == m_PackageInfo.versions.verified && !installedFromPath) m_Tag |= PackageTag.Verified; } else diff --git a/Modules/PackageManagerUI/Editor/Services/Upm/UpmRemoveOperation.cs b/Modules/PackageManagerUI/Editor/Services/Upm/UpmRemoveOperation.cs index 6778dc34fe..63265d30db 100644 --- a/Modules/PackageManagerUI/Editor/Services/Upm/UpmRemoveOperation.cs +++ b/Modules/PackageManagerUI/Editor/Services/Upm/UpmRemoveOperation.cs @@ -10,9 +10,10 @@ namespace UnityEditor.PackageManager.UI [Serializable] internal class UpmRemoveOperation : UpmBaseOperation { - public void Remove(string packageName) + public void Remove(string packageName, string packageUniqueId = null) { m_PackageName = packageName; + m_PackageUniqueId = packageUniqueId ?? packageName; Start(); } diff --git a/Modules/PackageManagerUI/Editor/Services/Upm/UpmSearchOperation.cs b/Modules/PackageManagerUI/Editor/Services/Upm/UpmSearchOperation.cs index 820f423b87..88c7c0d90b 100644 --- a/Modules/PackageManagerUI/Editor/Services/Upm/UpmSearchOperation.cs +++ b/Modules/PackageManagerUI/Editor/Services/Upm/UpmSearchOperation.cs @@ -42,18 +42,20 @@ public void SearchAllOffline(long timestamp) Start(); } - public void Search(string packageNameOrId) + public void Search(string packageNameOrId, string productId = null) { m_OfflineMode = false; SetPackageNameOrId(packageNameOrId); + m_PackageUniqueId = productId ?? packageName; Start(); } - public void SearchOffline(string packageNameOrId, long timestamp) + public void SearchOffline(string packageNameOrId, long timestamp, string productId = null) { m_OfflineMode = true; m_Timestamp = timestamp; SetPackageNameOrId(packageNameOrId); + m_PackageUniqueId = productId ?? packageName; Start(); } diff --git a/Modules/PackageManagerUI/Editor/Services/Upm/UpmVersionList.cs b/Modules/PackageManagerUI/Editor/Services/Upm/UpmVersionList.cs new file mode 100644 index 0000000000..8ea6d47f89 --- /dev/null +++ b/Modules/PackageManagerUI/Editor/Services/Upm/UpmVersionList.cs @@ -0,0 +1,152 @@ +// Unity C# reference source +// Copyright (c) Unity Technologies. For terms of use, see +// https://unity3d.com/legal/licenses/Unity_Reference_Only_License + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace UnityEditor.PackageManager.UI +{ + [Serializable] + internal class UpmVersionList : IVersionList + { + private List m_Versions; + public IEnumerable all => m_Versions.Cast(); + + public IEnumerable key + { + get + { + // Get key versions -- Latest, Verified, LatestPatch, Installed. + var keyVersions = new HashSet(); + + var installed = this.installed; + var latestRelease = m_Versions.LastOrDefault(v => v.HasTag(PackageTag.Release)); + var verifiedVersion = m_Versions.FirstOrDefault(v => v.HasTag(PackageTag.Verified)); + keyVersions.Add(installed); + keyVersions.Add(latestRelease); + keyVersions.Add(verifiedVersion); + keyVersions.Add(latestPatch); + keyVersions.Add(recommended); + if (installed == null && latestRelease == null) + keyVersions.Add(latest); + return keyVersions.Where(v => v != null).OrderBy(package => package.version); + } + } + + private int m_InstalledIndex; + public IPackageVersion installed { get { return m_InstalledIndex < 0 ? null : m_Versions[m_InstalledIndex]; } } + + public IPackageVersion latestPatch + { + get + { + if (m_InstalledIndex < 0) + return null; + + var installed = m_Versions[m_InstalledIndex].version; + for (var i = m_Versions.Count - 1; i > m_InstalledIndex; --i) + { + if (m_Versions[i].version.IsPatchOf(installed)) + return m_Versions[i]; + } + return null; + } + } + + public IPackageVersion latest => m_Versions.Last(); + + public IPackageVersion recommended + { + get + { + // Override with current when it's version locked + var installed = this.installed; + if (installed?.HasTag(PackageTag.VersionLocked) ?? false) + return installed; + + // Only try to find recommended version in versions newer than the installed version + var newerVersions = installed == null ? m_Versions : m_Versions.Skip(m_InstalledIndex + 1).SkipWhile(v => v.version <= installed.version); + + var verifiedVersion = newerVersions.FirstOrDefault(v => v.HasTag(PackageTag.Verified)); + if (verifiedVersion != null) + return verifiedVersion; + + var latestRelease = newerVersions.LastOrDefault(v => v.HasTag(PackageTag.Release)); + if (latestRelease != null && (installed == null || !installed.HasTag(PackageTag.Verified))) + return latestRelease; + + var latestPreview = newerVersions.LastOrDefault(package => package.HasTag(PackageTag.Preview)); + if (latestPreview != null && (installed == null || installed.HasTag(PackageTag.Preview))) + return latestPreview; + + // Show current if it exists, otherwise latest user visible, and then otherwise show the absolute latest + return installed ?? latest; + } + } + + public IPackageVersion primary => installed ?? recommended; + + public IPackageVersion importAvailable => null; + + internal void UpdateVersion(UpmPackageVersion version) + { + for (var i = 0; i < m_Versions.Count; ++i) + { + if (m_Versions[i].uniqueId != version.uniqueId) + continue; + m_Versions[i] = version; + return; + } + } + + // This function is only used to update the object, not to actually perform the add operation + public void AddInstalledVersion(UpmPackageVersion newVersion) + { + if (m_InstalledIndex >= 0) + { + m_Versions[m_InstalledIndex].isInstalled = false; + if (m_Versions[m_InstalledIndex].installedFromPath) + m_Versions.RemoveAt(m_InstalledIndex); + } + newVersion.isInstalled = true; + m_InstalledIndex = AddToSortedVersions(m_Versions, newVersion); + } + + private static int AddToSortedVersions(List sortedVersions, UpmPackageVersion versionToAdd) + { + for (var i = 0; i < sortedVersions.Count; ++i) + { + if (sortedVersions[i].version.CompareByPrecedence(versionToAdd.version) < 0) + continue; + // note that the difference between this and the previous function is that + // two upm package versions could have the the same version but different package id + if (sortedVersions[i].uniqueId == versionToAdd.uniqueId) + { + sortedVersions[i] = versionToAdd; + return i; + } + sortedVersions.Insert(i, versionToAdd); + return i; + } + sortedVersions.Add(versionToAdd); + return sortedVersions.Count - 1; + } + + public UpmVersionList(IEnumerable versions = null) + { + m_Versions = versions?.ToList() ?? new List(); + m_InstalledIndex = m_Versions.FindIndex(v => v.isInstalled); + } + + public UpmVersionList(PackageInfo info, bool isInstalled) + { + var mainVersion = new UpmPackageVersion(info, isInstalled); + m_Versions = info.versions.compatible.Select(v => new UpmPackageVersion(info, false, v, mainVersion.displayName)).ToList(); + AddToSortedVersions(m_Versions, mainVersion); + + m_InstalledIndex = m_Versions.FindIndex(v => v.isInstalled); + } + } +} diff --git a/Modules/PackageManagerUI/Editor/UI/Common/DictionaryExtensions.cs b/Modules/PackageManagerUI/Editor/UI/Common/DictionaryExtensions.cs new file mode 100644 index 0000000000..c0209ec1b1 --- /dev/null +++ b/Modules/PackageManagerUI/Editor/UI/Common/DictionaryExtensions.cs @@ -0,0 +1,51 @@ +// Unity C# reference source +// Copyright (c) Unity Technologies. For terms of use, see +// https://unity3d.com/legal/licenses/Unity_Reference_Only_License + +using System.Collections.Generic; +using System.Linq; + +namespace UnityEditor.PackageManager.UI +{ + internal static class DictionaryExtensions + { + public static T Get(this IDictionary dict, string key) where T : class + { + object result; + return dict.TryGetValue(key, out result) ? result as T : null; + } + + public static T Get(this IDictionary dict, string key, T fallbackValue = default(T)) where T : struct + { + object result; + return dict.TryGetValue(key, out result) ? (T)result : fallbackValue; + } + + public static T Get(this IDictionary dict, string key) where T : class + { + T result; + return dict.TryGetValue(key, out result) ? result : null; + } + + public static T Get(this IDictionary dict, string key, T fallbackValue = default(T)) where T : struct + { + T result; + return dict.TryGetValue(key, out result) ? result : fallbackValue; + } + + public static IDictionary GetDictionary(this IDictionary dict, string key) + { + return Get>(dict, key); + } + + public static IEnumerable GetList(this IDictionary dict, string key) + { + return Get>(dict, key)?.OfType(); + } + + public static string GetString(this IDictionary dict, string key) + { + return Get(dict, key); + } + } +} diff --git a/Modules/PackageManagerUI/Editor/UI/Common/Page.cs b/Modules/PackageManagerUI/Editor/UI/Common/Page.cs index faf3e53234..8e819c548a 100644 --- a/Modules/PackageManagerUI/Editor/UI/Common/Page.cs +++ b/Modules/PackageManagerUI/Editor/UI/Common/Page.cs @@ -224,8 +224,8 @@ public void RebuildList(IEnumerable addOrUpdateList = null, IEnumerabl m_PackageVisualStateLookup = newLookupTable; } - if (addOrUpdateList != null && removeList != null) - onPageUpdate?.Invoke(addOrUpdateList, removeList); + if (addOrUpdateList != null || removeList != null) + onPageUpdate?.Invoke(addOrUpdateList ?? Enumerable.Empty(), removeList ?? Enumerable.Empty()); FilterBySearchText(); } @@ -291,8 +291,24 @@ public void OnProductListFetched(ProductList productList, bool fetchDetailsCalle return; } + var rebuildList = PackageFiltering.instance.currentFilterTab == PackageFilterTab.AssetStore; + + HashSet removed = null; + List added = null; if (productList.startIndex == 0) { + if (rebuildList) + { + removed = new HashSet(targetList.list); + added = new List(); + foreach (var id in productList.list) + { + if (removed.Contains(id)) + removed.Remove(id); + else + added.Add(id); + } + } // override the result if the new list starts from index 0 (meaning it's a refresh) targetList.list = productList.list; targetList.total = productList.total; @@ -302,6 +318,8 @@ public void OnProductListFetched(ProductList productList, bool fetchDetailsCalle { // append the result if it is the next page targetList.list.AddRange(productList.list); + if (rebuildList) + added = productList.list; } else { @@ -316,10 +334,12 @@ public void OnProductListFetched(ProductList productList, bool fetchDetailsCalle if (!fetchDetailsCalled && productList.list.Any()) AssetStore.AssetStoreClient.instance.FetchDetails(productList.list); - if (PackageFiltering.instance.currentFilterTab == PackageFilterTab.AssetStore) + if (rebuildList) { m_IsAlreadyFetched = true; - RebuildList(); + var addedPackages = added?.Select(id => PackageDatabase.instance.GetPackage(id.ToString())); + var removedPackages = removed?.Select(id => PackageDatabase.instance.GetPackage(id.ToString())); + RebuildList(addedPackages, removedPackages); } } diff --git a/Modules/PackageManagerUI/Editor/UI/Common/PageManager.cs b/Modules/PackageManagerUI/Editor/UI/Common/PageManager.cs index 5202a0fbd4..f18c883b7a 100644 --- a/Modules/PackageManagerUI/Editor/UI/Common/PageManager.cs +++ b/Modules/PackageManagerUI/Editor/UI/Common/PageManager.cs @@ -223,7 +223,7 @@ public void Refresh(RefreshOptions options) if ((options & RefreshOptions.Purchased) != 0) AssetStore.AssetStoreClient.instance.List(0, k_DefaultPageSize, string.Empty); if ((options & RefreshOptions.PurchasedOffline) != 0) - AssetStore.AssetStoreClient.instance.Refresh(PackageDatabase.instance.assetStorePackages); + AssetStore.AssetStoreClient.instance.RefreshLocal(); } public void Fetch(string uniqueId) diff --git a/Modules/PackageManagerUI/Editor/UI/PackageDependencies.cs b/Modules/PackageManagerUI/Editor/UI/PackageDependencies.cs index c3172d6629..7b295ba659 100644 --- a/Modules/PackageManagerUI/Editor/UI/PackageDependencies.cs +++ b/Modules/PackageManagerUI/Editor/UI/PackageDependencies.cs @@ -51,7 +51,10 @@ private string BuildStatusText(DependencyInfo dependency) public void SetDependencies(DependencyInfo[] dependencies) { - if (dependencies == null || dependencies.Length == 0) + var showDependency = PackageManagerPrefs.instance.showPackageDependencies && dependencies != null; + UIUtils.SetElementDisplay(this, showDependency); + + if (!showDependency || dependencies.Length == 0) { ClearDependencies(); return; diff --git a/Modules/PackageManagerUI/Editor/UI/PackageDetails.cs b/Modules/PackageManagerUI/Editor/UI/PackageDetails.cs index 9315ff9e85..fbd92d854d 100644 --- a/Modules/PackageManagerUI/Editor/UI/PackageDetails.cs +++ b/Modules/PackageManagerUI/Editor/UI/PackageDetails.cs @@ -71,7 +71,6 @@ internal enum PackageAction PackageTag.Local, PackageTag.Git, PackageTag.Preview, - PackageTag.AssetStore, PackageTag.Deprecated }; @@ -113,7 +112,7 @@ public PackageDetails() detailDesc.RegisterCallback(DescriptionGeometryChangeEvent); - GetTagLabel(PackageTag.Verified).text = ApplicationUtil.instance.shortUnityVersion + " verified"; + GetTagLabel(PackageTag.Verified.ToString()).text = ApplicationUtil.instance.shortUnityVersion + " verified"; } private void OnEditorSelectionChanged() @@ -158,7 +157,7 @@ public void OnEnable() PageManager.instance.onPageRebuild += page => OnSelectionChanged(PageManager.instance.GetSelectedVersion()); PageManager.instance.onSelectionChanged += OnSelectionChanged; - PackageManagerPrefs.instance.onShowDependenciesChanged += (value) => RefreshDependencies(value); + PackageManagerPrefs.instance.onShowDependenciesChanged += (value) => RefreshDependencies(); // manually call the callback function once on initialization to refresh the UI OnSelectionChanged(PageManager.instance.GetSelectedVersion()); @@ -214,10 +213,8 @@ internal void OnSelectionChanged(IPackageVersion version) SetPackage(null); } - private void RefreshDependencies(bool? dependenciesVisibility = null) + private void RefreshDependencies() { - if (dependenciesVisibility != null) - UIUtils.SetElementDisplay(dependencies, (bool)dependenciesVisibility); dependencies.SetDependencies(displayVersion?.dependencies); } @@ -252,15 +249,14 @@ private void SetDisplayVersion(IPackageVersion version) } else { - var isBuiltIn = displayVersion.HasTag(PackageTag.BuiltIn); - var isAssetStorePackage = displayVersion.HasTag(PackageTag.AssetStore); + var isBuiltIn = package.Is(PackageType.BuiltIn); SetUpdateVisibility(true); detailTitle.text = displayVersion.displayName; - UIUtils.SetElementDisplay(detailNameContainer, !isAssetStorePackage); - detailName.text = displayVersion.name; + UIUtils.SetElementDisplay(detailNameContainer, !string.IsNullOrEmpty(package.name)); + detailName.text = package.name; RefreshLinks(); @@ -269,15 +265,16 @@ private void SetDisplayVersion(IPackageVersion version) RefreshCategories(); detailVersion.text = $"Version {displayVersion.version.StripTag()}"; - if (isAssetStorePackage && displayVersion.versionString != displayVersion.version.ToString()) + if (displayVersion.versionString != displayVersion.version.ToString()) { detailVersion.text = $"Version {displayVersion.versionString}"; } foreach (var tag in k_VisibleTags) - UIUtils.SetElementDisplay(GetTagLabel(tag), displayVersion.HasTag(tag)); + UIUtils.SetElementDisplay(GetTagLabel(tag.ToString()), displayVersion.HasTag(tag)); + UIUtils.SetElementDisplay(GetTagLabel(PackageType.AssetStore.ToString()), package.Is(PackageType.AssetStore)); - UIUtils.SetElementDisplay(editButton, displayVersion.isInstalled && !displayVersion.HasTag(PackageTag.BuiltIn)); + UIUtils.SetElementDisplay(editButton, displayVersion.isInstalled && !isBuiltIn); sampleList.SetPackageVersion(displayVersion); @@ -290,7 +287,7 @@ private void SetDisplayVersion(IPackageVersion version) UIUtils.SetElementDisplay(customContainer, true); RefreshExtensions(displayVersion); - RefreshDependencies(PackageManagerPrefs.instance.showPackageDependencies); + RefreshDependencies(); RefreshSupportedUnityVersions(); @@ -311,12 +308,12 @@ private void SetDisplayVersion(IPackageVersion version) private void DescriptionGeometryChangeEvent(GeometryChangedEvent evt) { - if (displayVersion == null) + // only hide lengthly description when there are images to be displayed + if (!(package?.images.Any() ?? false)) return; - var isAssetStorePackage = displayVersion.HasTag(PackageTag.AssetStore); var lineHeight = detailDesc.computedStyle.unityFont.value.lineHeight + 2; - if (isAssetStorePackage && !UIUtils.IsElementVisible(detailDescMore) && !UIUtils.IsElementVisible(detailDescLess) && + if (!UIUtils.IsElementVisible(detailDescMore) && !UIUtils.IsElementVisible(detailDescLess) && evt.newRect.height > lineHeight * 4) { UIUtils.SetElementDisplay(detailDescMore, true); @@ -340,8 +337,7 @@ private void RefreshAuthor() UIUtils.SetElementDisplay(detailAuthorContainer, !string.IsNullOrEmpty(displayVersion.author)); if (!string.IsNullOrEmpty(displayVersion.author)) { - var isAssetStorePackage = displayVersion.HasTag(PackageTag.AssetStore); - if (isAssetStorePackage) + if (!string.IsNullOrEmpty(displayVersion.authorLink)) { UIUtils.SetElementDisplay(detailAuthorText, false); UIUtils.SetElementDisplay(detailAuthorLink, true); @@ -358,94 +354,55 @@ private void RefreshAuthor() private void RefreshPublishedDate() { - var isAssetStorePackage = displayVersion.HasTag(PackageTag.AssetStore); - if (isAssetStorePackage) - { - detailDate.text = (displayVersion.publishedDate != null) ? $"{displayVersion.publishedDate.Value:MMMM dd, yyyy}" : string.Empty; - } - else - { - // If the package details is not enabled, don't update the date yet as we are fetching new information - if (enabledSelf) - { - detailDate.text = string.Empty; - - // In Development packages are not published, so we do not show any published date - if (displayVersion != null && !displayVersion.HasTag(PackageTag.InDevelopment)) - { - if (displayVersion.publishedDate != null) - detailDate.text = $"{displayVersion.publishedDate.Value:MMMM dd, yyyy}"; - else if (displayVersion.HasTag(PackageTag.Core) || displayVersion.isInstalled) - { - // For core packages, or installed packages that are bundled with Unity without being published, use Unity's build date - var unityBuildDate = new DateTime(1970, 1, 1, 0, 0, 0, 0); - unityBuildDate = unityBuildDate.AddSeconds(InternalEditorUtility.GetUnityVersionDate()); - detailDate.text = $"{unityBuildDate:MMMM dd, yyyy}"; - } - } - } - } - + // If the package details is not enabled, don't update the date yet as we are fetching new information + if (enabledSelf) + detailDate.text = displayVersion.publishedDate != null ? $"{displayVersion.publishedDate.Value:MMMM dd, yyyy}" : string.Empty; UIUtils.SetElementDisplay(detailDateContainer, !string.IsNullOrEmpty(detailDate.text)); } private void RefreshCategories() { - var isAssetStorePackage = displayVersion.HasTag(PackageTag.AssetStore); - UIUtils.SetElementDisplay(detailCategories, isAssetStorePackage && !string.IsNullOrEmpty(displayVersion.category)); - detailCategories.Clear(); + var categoryLinks = displayVersion?.categoryLinks; + UIUtils.SetElementDisplay(detailCategories, categoryLinks != null); - if (isAssetStorePackage) + detailCategories.Clear(); + if (categoryLinks != null) { - var categories = displayVersion.category.Split('/'); - var parentCategory = "/"; - foreach (var category in categories) + foreach (var item in categoryLinks) { - var lower = category.ToLower(CultureInfo.InvariantCulture); - var url = $"{AssetStoreUtils.instance.assetStoreUrl}{parentCategory}{lower}"; + var category = item.Key; + var url = item.Value; detailCategories.Add(new Button(() => { ApplicationUtil.instance.OpenURL(url); }) - {text = category, classList = {"category", "unity-button", "link"}}); - parentCategory += lower + "/"; + { text = category, classList = { "category", "unity-button", "link" } }); } } } private void RefreshLinks() { - var isBuiltIn = displayVersion.HasTag(PackageTag.BuiltIn); - var isAssetStorePackage = displayVersion.HasTag(PackageTag.AssetStore); detailLinks.Clear(); - if (!isAssetStorePackage) - { - UIUtils.SetElementDisplay(detailLinksContainer, true); - detailLinks.Add(new Button(ViewDocClick) { text = "View documentation", classList = { "unity-button", "link" }}); - - if (UpmPackageDocs.HasChangelog(displayVersion)) - detailLinks.Add(new Button(ViewChangelogClick) { text = "View changelog", classList = { "unity-button", "link" } }); - - if (!isBuiltIn) - detailLinks.Add(new Button(ViewLicensesClick) { text = "View licenses", classList = { "unity-button", "link" } }); - } - else + // add links from the package + foreach (var link in package.links) { - UIUtils.SetElementDisplay(detailLinksContainer, displayVersion.links.Any()); - foreach (var link in displayVersion.links) + detailLinks.Add(new Button(() => { ApplicationUtil.instance.OpenURL(link.url); }) { - var tooltip = link.url; - var url = link.url; - if (!url.StartsWith("http:", StringComparison.InvariantCulture) && !url.StartsWith("https:", StringComparison.InvariantCulture)) - { - url = AssetStoreUtils.instance.assetStoreUrl + url; - tooltip = string.Empty; - } - detailLinks.Add(new Button(() => { ApplicationUtil.instance.OpenURL(url); }) - { - text = link.name, - tooltip = tooltip, - classList = { "unity-button", "link" } - }); - } + text = link.name, + tooltip = link.url, + classList = { "unity-button", "link" } + }); } + + // add links related to the upm version + if (UpmPackageDocs.HasDocs(displayVersion)) + detailLinks.Add(new Button(ViewDocClick) { text = "View documentation", classList = { "unity-button", "link" } }); + + if (UpmPackageDocs.HasChangelog(displayVersion)) + detailLinks.Add(new Button(ViewChangelogClick) { text = "View changelog", classList = { "unity-button", "link" } }); + + if (UpmPackageDocs.HasLicenses(displayVersion)) + detailLinks.Add(new Button(ViewLicensesClick) { text = "View licenses", classList = { "unity-button", "link" } }); + + UIUtils.SetElementDisplay(detailLinksContainer, detailLinks.childCount != 0); } private void RefreshSupportedUnityVersions() @@ -483,16 +440,16 @@ private void ClearSupportingImages() private void RefreshSupportingImages() { - UIUtils.SetElementDisplay(detailImagesContainer, displayVersion.images.Any()); + UIUtils.SetElementDisplay(detailImagesContainer, package.images.Any()); ClearSupportingImages(); if (s_LoadingTexture == null) s_LoadingTexture = (Texture2D)EditorGUIUtility.LoadRequired("Icons/UnityLogo.png"); long id; - if (long.TryParse(displayVersion.uniqueId, out id)) + if (long.TryParse(package.uniqueId, out id)) { - foreach (var packageImage in displayVersion.images) + foreach (var packageImage in package.images) { var image = new Label {classList = {"image"}}; @@ -507,7 +464,7 @@ private void RefreshSupportingImages() AssetStoreDownloadOperation.instance.DownloadImageAsync(id, packageImage.thumbnailUrl, (retId, texture) => { - if (retId.ToString() == displayVersion?.uniqueId) + if (retId.ToString() == package?.uniqueId) { texture.hideFlags = HideFlags.HideAndDontSave; image.style.backgroundImage = texture; @@ -521,7 +478,16 @@ public void SetPackage(IPackage package, IPackageVersion version = null) { version = version ?? package?.primaryVersion; this.package = package; - ShowVersion(version); + + SetEnabled(true); + + if (version != null && !version.isFullyFetched) + { + SetEnabled(false); + PackageDatabase.instance.FetchExtraInfo(version); + } + + SetDisplayVersion(version); } internal void OnPackagesUpdated(IEnumerable updatedPackages) @@ -541,19 +507,6 @@ internal void OnPackagesUpdated(IEnumerable updatedPackages) } } - private void ShowVersion(IPackageVersion version) - { - SetEnabled(true); - - if (version != null && !version.isFullyFetched) - { - SetEnabled(false); - PackageDatabase.instance.FetchExtraInfo(version); - } - - SetDisplayVersion(version); - } - private void RefreshErrorDisplay() { var error = displayVersion?.errors?.FirstOrDefault() ?? package?.errors?.FirstOrDefault(); @@ -581,7 +534,8 @@ private void RefreshAddButton() { var installed = package?.installedVersion; var targetVersion = this.targetVersion; - var visibleFlag = !(installed?.isVersionLocked ?? false) && displayVersion != null && !displayVersion.HasTag(PackageTag.AssetStore) && targetVersion != null; + var installable = targetVersion?.HasTag(PackageTag.Installable) ?? false; + var visibleFlag = !(installed?.HasTag(PackageTag.VersionLocked) ?? false) && displayVersion != null && installable; if (visibleFlag) { var installInProgress = PackageDatabase.instance.IsInstallInProgress(displayVersion); @@ -608,7 +562,7 @@ private void RefreshAddButton() private void RefreshRemoveButton() { - var visibleFlag = displayVersion?.canBeRemoved ?? false; + var visibleFlag = displayVersion?.HasTag(PackageTag.Removable) ?? false; if (visibleFlag) { var installed = package?.installedVersion; @@ -629,34 +583,36 @@ private void RefreshImportAndDownloadButtons() if (displayVersion == null) return; - var isAssetStore = displayVersion.HasTag(PackageTag.AssetStore); - if (!isAssetStore) - { - UIUtils.SetElementDisplay(importButton, false); - UIUtils.SetElementDisplay(downloadButton, false); - downloadProgress.Hide(); - return; - } - - importButton.text = GetButtonText(PackageAction.Import); - - var progress = PackageDatabase.instance.GetDownloadProgress(displayVersion); - var downloadInProgress = progress != null && (progress.state == DownloadProgress.State.InProgress || progress.state == DownloadProgress.State.Started); - downloadButton.text = GetButtonText(package.state == PackageState.Outdated ? PackageAction.Upgrade : PackageAction.Download, downloadInProgress); - var enableButton = !ApplicationUtil.instance.isCompiling; + var downloadInProgress = false; - UIUtils.SetElementDisplay(importButton, true); - importButton.SetEnabled(enableButton && displayVersion.isAvailableOnDisk); - - UIUtils.SetElementDisplay(downloadButton, true); - var notOnDiskOrUpgradeAvailable = !displayVersion.isAvailableOnDisk || package.state == PackageState.Outdated; - downloadButton.SetEnabled(enableButton && notOnDiskOrUpgradeAvailable); - - if (downloadInProgress) - downloadProgress.SetProgress(progress.total == 0 ? 0 : progress.current / (float)progress.total); + var downloadable = displayVersion.HasTag(PackageTag.Downloadable); + UIUtils.SetElementDisplay(downloadButton, downloadable); + if (downloadable && downloadInProgress) + downloadProgress.Show(); else downloadProgress.Hide(); + if (downloadable) + { + var progress = PackageDatabase.instance.GetDownloadProgress(displayVersion); + var state = package.GetState(); + downloadInProgress = progress != null && (progress.state == DownloadProgress.State.InProgress || progress.state == DownloadProgress.State.Started); + downloadButton.text = GetButtonText(state == PackageState.Outdated ? PackageAction.Upgrade : PackageAction.Download, downloadInProgress); + + var enableDownloadButton = !displayVersion.isAvailableOnDisk || state == PackageState.InProgress || state == PackageState.Outdated; + downloadButton.SetEnabled(enableButton && enableDownloadButton); + + if (downloadInProgress) + downloadProgress.SetProgress(progress.total == 0 ? 0 : progress.current / (float)progress.total); + } + + var importable = displayVersion.HasTag(PackageTag.Importable); + UIUtils.SetElementDisplay(importButton, importable); + if (importable) + { + importButton.text = GetButtonText(PackageAction.Import); + importButton.SetEnabled(enableButton && displayVersion.isAvailableOnDisk); + } } private string GetButtonText(PackageAction action, bool inProgress = false, SemVersion version = null) @@ -681,10 +637,9 @@ private void DescLessClick() private void AuthorClick() { - if (displayVersion == null || !displayVersion.HasTag(PackageTag.AssetStore)) - return; - - ApplicationUtil.instance.OpenURL($"{AssetStoreUtils.instance.assetStoreUrl}/publishers/{displayVersion.publisherId}"); + var authorLink = displayVersion?.authorLink ?? string.Empty; + if (!string.IsNullOrEmpty(authorLink)) + ApplicationUtil.instance.OpenURL(authorLink); } private void UpdateClick() @@ -936,6 +891,6 @@ private void DownloadOrCancelClick() private VisualElement detailSizes { get { return cache.Get("detailSizes"); } } private VisualElement detailImagesContainer { get { return cache.Get("detailImagesContainer"); } } private VisualElement detailImages { get { return cache.Get("detailImages"); } } - internal Label GetTagLabel(PackageTag tag) { return cache.Get