From 7765d52c6cc13c363796ca00437f1a3209943991 Mon Sep 17 00:00:00 2001 From: Unity Technologies Date: Wed, 19 Jul 2023 17:48:18 +0000 Subject: [PATCH] Unity 2023.2.0b1 C# reference source code --- Editor/Mono/EditorApplication.bindings.cs | 2 +- Editor/Mono/EditorApplication.cs | 4 +- Editor/Mono/EditorGUI.cs | 3 +- .../Mono/EditorUserBuildSettings.bindings.cs | 30 +- Editor/Mono/EditorWindow.cs | 3 +- Editor/Mono/GI/Lightmapping.bindings.cs | 8 +- Editor/Mono/GUI/Actions/EditorAction.cs | 17 +- Editor/Mono/GUI/Actions/EditorActionTool.cs | 50 ++- Editor/Mono/GUI/Tools/EditorToolManager.cs | 29 ++ Editor/Mono/GameView/GameView.cs | 6 +- Editor/Mono/HandleUtility.cs | 68 ++-- .../Mono/Inspector/EditorSettingsInspector.cs | 4 +- .../GraphicsSettingsUtils.cs | 41 ++- .../Inspector/RendererLightingSettings.cs | 2 +- Editor/Mono/Inspector/SpriteFrameInspector.cs | 14 +- Editor/Mono/Inspector/TextureInspector.cs | 4 +- Editor/Mono/Menu.bindings.cs | 3 + Editor/Mono/MenuUtility.cs | 52 ++- Editor/Mono/ObjectListLocalGroup.cs | 62 +++- Editor/Mono/Overlays/Overlay.cs | 3 +- .../PrefabOverrides/PrefabOverridesWindow.cs | 5 +- Editor/Mono/Prefabs/PrefabUtility.cs | 82 ++++- .../PreferencesSettingsProviders.cs | 15 - .../ProjectWindow/AssetClipboardUtility.cs | 101 +++--- .../Mono/ProjectWindow/ProjectWindowUtil.cs | 11 +- ...GraphicsSettingsContainerPropertyDrawer.cs | 92 ----- Editor/Mono/SceneHierarchy.cs | 200 ++++++----- .../Mono/SceneModeWindows/LightingWindow.cs | 2 +- .../LightingWindowLightmapPreviewTab.cs | 2 +- .../Mono/SceneView/SceneOrientationGizmo.cs | 3 - Editor/Mono/SceneView/SceneView.cs | 5 +- .../ScriptAttributeGUI/DecoratorDrawer.cs | 2 + .../Mono/ScriptAttributeGUI/PropertyDrawer.cs | 10 +- .../ScriptAttributeGUI/PropertyHandler.cs | 39 --- .../Mono/ScriptAttributeGUI/PropertyTrait.cs | 5 - .../Bindings/BindingStyleHelpers.cs | 9 +- .../UIElements/Controls/EnumFlagsField.cs | 21 +- Editor/Mono/UIElements/Controls/MaskField.cs | 2 +- .../Mono/UIElements/Controls/PropertyField.cs | 2 + .../Mono/UIElements/EditorMenuExtensions.cs | 13 +- Modules/Accessibility/AssemblyInfo.cs | 1 + .../Bindings/AccessibilityManager.bindings.cs | 4 +- .../Accessibility/Managed/AssistiveSupport.cs | 15 + .../Hierarchy/AccessibilityHierarchy.cs | 22 ++ .../Managed/Hierarchy/AccessibilityNode.cs | 65 +++- .../Managed/Services/ServiceManager.cs | 5 +- .../Managed/AccessibilityEditor.cs | 17 + .../Managed/AccessibilityHierarchyTreeView.cs | 246 ++++++++++++++ .../AccessibilityHierarchyViewModel.cs | 266 +++++++++++++++ .../Managed/AccessibilityHierarchyViewer.cs | 160 +++++++++ .../AccessibilityHierarchyViewerWindow.cs | 60 ++++ .../Managed/SearchableLabel.cs | 88 +++++ .../Managed/TreeViewSearchBar.cs | 239 +++++++++++++ .../AndroidJNI/AndroidApplicationExitInfo.cs | 315 ++++++++++++++++++ .../ScriptBindings/AssetDatabase.bindings.cs | 3 + Modules/IMGUI/TextSelectingUtilities.cs | 2 +- .../Packages/Actions/ImportActionBase.cs | 16 +- .../Packages/Actions/ImportNewAction.cs | 4 +- .../Packages/Actions/ImportUpdateAction.cs | 4 +- .../Packages/Actions/ReImportAction.cs | 4 +- .../UI/ToolBar/LegacyFormatDropdownButton.cs | 6 +- Modules/SceneView/CameraOverlay.cs | 1 + .../ShortcutManagerWindowView.cs | 16 +- .../ShortcutProfileManager.cs | 2 +- .../TextCoreTextEngine/Managed/LinkInfo.cs | 2 +- .../Managed/TextAssets/FontAsset.cs | 45 ++- .../Managed/TextAssets/FontAssetUtilities.cs | 1 + .../Managed/TextElementInfo.cs | 2 +- .../TextGenerator/TextGenerationSettings.cs | 60 +++- .../TextGenerator/TextGeneratorLayout.cs | 8 +- .../TextGenerator/TextGeneratorParsing.cs | 16 +- .../TextGeneratorPreferredValues.cs | 5 +- .../TextGenerator/TextGeneratorPrepare.cs | 18 +- .../TextCoreTextEngine/Managed/TextHandle.cs | 16 +- .../Managed/TextMarkupTagsCommon.cs | 2 + .../Managed/TextCoreContextMenuItems.cs | 32 +- .../Managed/TextEditorResourceManager.cs | 1 + .../Library/BuilderLibraryProjectScanner.cs | 10 +- .../Core/Bindings/BindingActivationContext.cs | 2 +- .../Core/Bindings/BindingContext.cs | 4 +- .../UIElements/Core/Bindings/BindingTarget.cs | 2 +- .../Core/Bindings/BindingUpdater.cs | 4 +- .../Core/Bindings/ConverterGroup.cs | 2 +- .../Core/Bindings/ConverterGroups.cs | 2 +- .../UIElements/Core/Bindings/DataBinding.cs | 2 +- .../Core/Bindings/DataBindingManager.cs | 38 +-- .../Core/Bindings/DataBindingUtility.cs | 20 +- .../Core/Bindings/DataSourceContext.cs | 2 +- .../INotifyBindablePropertyChanged.cs | 2 +- .../Bindings/VisualTreeBindingsUpdater.cs | 6 +- .../Bindings/VisualTreeDataBindingUpdater.cs | 9 +- .../Controllers/CollectionViewController.cs | 7 +- .../UIElements/Core/Controls/BaseTreeView.cs | 1 - .../Controls/BaseVerticalCollectionView.cs | 1 + .../Core/Controls/GenericDropdownMenu.cs | 9 +- .../Core/Controls/InputField/BaseField.cs | 1 + .../MultiColumnCollectionHeader.cs | 22 +- .../MultiColumn/MultiColumnController.cs | 16 + .../MultiColumn/MultiColumnListView.cs | 22 +- .../MultiColumn/MultiColumnTreeView.cs | 22 +- .../ToggleButtonGroupState.cs | 4 +- .../UIElements/Core/Events/EventHandler.cs | 2 +- .../Core/Renderer/UIRMeshGenerator.cs | 3 + .../UIElements/Core/Text/UITKTextHandle.cs | 4 +- .../UIElements/Core/Text/UITKTextJobSystem.cs | 9 +- Modules/UIElements/Core/UXML/TemplateAsset.cs | 1 - .../Core/UXML/VisualElementAsset.cs | 18 +- .../UIElements/Core/UXML/VisualTreeAsset.cs | 11 +- .../Core/VisualElementDataBinding.cs | 6 +- .../Bindings/BindingExtensions.cs | 143 +++++--- .../Bindings/ListViewBindings.cs | 14 +- .../Debugger/DebuggerSearchBar.cs | 2 +- .../Inspector/PanelSettingsInspector.cs | 5 +- .../UIElementsViewImporter.cs | 22 +- .../UXML/UxmlAttributeConverter.cs | 2 +- .../UXML/UxmlSerializedDataDescription.cs | 46 ++- .../UXML/UxmlSerializedDataRegistry.cs | 21 +- .../VisualTreeAssetChangeTrackerUpdater.cs | 7 +- Projects/CSharp/UnityEditor.csproj | 26 +- Projects/CSharp/UnityEngine.csproj | 8 +- README.md | 2 +- Runtime/Export/Debug/Debug.bindings.cs | 3 - Runtime/Export/Graphics/GraphicsEnums.cs | 1 + .../IRenderPipelineGraphicsSettings.cs | 1 + .../RenderingCommandBuffer.bindings.cs | 33 ++ Runtime/Export/Graphics/Texture.bindings.cs | 2 + Runtime/Export/Graphics/Texture.cs | 6 + .../Export/Misc/ObjectDispatcher.bindings.cs | 92 ++--- .../RenderPipelineGlobalSettings.cs | 74 +++- ...RenderPipelineGraphicsSettingsContainer.cs | 99 ------ 130 files changed, 2737 insertions(+), 931 deletions(-) delete mode 100644 Editor/Mono/RenderPipelineGraphicsSettingsContainerPropertyDrawer.cs create mode 100644 Modules/AccessibilityEditor/Managed/AccessibilityEditor.cs create mode 100644 Modules/AccessibilityEditor/Managed/AccessibilityHierarchyTreeView.cs create mode 100644 Modules/AccessibilityEditor/Managed/AccessibilityHierarchyViewModel.cs create mode 100644 Modules/AccessibilityEditor/Managed/AccessibilityHierarchyViewer.cs create mode 100644 Modules/AccessibilityEditor/Managed/AccessibilityHierarchyViewerWindow.cs create mode 100644 Modules/AccessibilityEditor/Managed/SearchableLabel.cs create mode 100644 Modules/AccessibilityEditor/Managed/TreeViewSearchBar.cs create mode 100644 Modules/AndroidJNI/AndroidApplicationExitInfo.cs delete mode 100644 Runtime/Export/RenderPipeline/RenderPipelineGraphicsSettingsContainer.cs diff --git a/Editor/Mono/EditorApplication.bindings.cs b/Editor/Mono/EditorApplication.bindings.cs index 6cbb3c970e..b9bc3cca03 100644 --- a/Editor/Mono/EditorApplication.bindings.cs +++ b/Editor/Mono/EditorApplication.bindings.cs @@ -297,7 +297,7 @@ public static extern bool isTemporaryProject internal static extern void UpdateSceneIfNeeded(); [StaticAccessor("GetApplication()", StaticAccessorType.Dot)] - internal static extern void UpdateMainWindowTitle(); + public static extern void UpdateMainWindowTitle(); // Plays system beep sound. [FreeFunction("UnityBeep")] diff --git a/Editor/Mono/EditorApplication.cs b/Editor/Mono/EditorApplication.cs index fd54d1b3b2..b76e0398d6 100644 --- a/Editor/Mono/EditorApplication.cs +++ b/Editor/Mono/EditorApplication.cs @@ -42,7 +42,7 @@ public enum PauseState Unpaused, } - internal class ApplicationTitleDescriptor + public class ApplicationTitleDescriptor { public ApplicationTitleDescriptor(string projectName, string unityVersion, string activeSceneName, string targetName, bool codeCoverageEnabled) { @@ -282,7 +282,7 @@ public static event Action playModeStateChanged // Global contextual menus for inspector values public static SerializedPropertyCallbackFunction contextualPropertyMenu; - internal static event Action updateMainWindowTitle; + public static event Action updateMainWindowTitle; internal static string GetDefaultMainWindowTitle(ApplicationTitleDescriptor desc) { diff --git a/Editor/Mono/EditorGUI.cs b/Editor/Mono/EditorGUI.cs index 1743e82c95..f5498740cf 100644 --- a/Editor/Mono/EditorGUI.cs +++ b/Editor/Mono/EditorGUI.cs @@ -7462,9 +7462,10 @@ internal static float GetPropertyHeightInternal(SerializedProperty property, GUI return ScriptAttributeUtility.GetHandler(property).GetHeight(property, label, includeChildren); } + [Obsolete("CanCacheInspectorGUI has been deprecated and is no longer used.", false)] public static bool CanCacheInspectorGUI(SerializedProperty property) { - return ScriptAttributeUtility.GetHandler(property).CanCacheInspectorGUI(property); + return false; } internal static bool HasVisibleChildFields(SerializedProperty property, bool isUIElements = false) diff --git a/Editor/Mono/EditorUserBuildSettings.bindings.cs b/Editor/Mono/EditorUserBuildSettings.bindings.cs index 3b2a60604e..6471365e65 100644 --- a/Editor/Mono/EditorUserBuildSettings.bindings.cs +++ b/Editor/Mono/EditorUserBuildSettings.bindings.cs @@ -242,6 +242,13 @@ public enum WSABuildAndRunDeployTarget DevicePortal = 2 } + [NativeType(Header = "Editor/Src/EditorUserBuildSettings.h")] + public enum WindowsBuildAndRunDeployTarget + { + LocalMachine = 0, + DevicePortal = 2 + } + [NativeType(Header = "Editor/Src/EditorUserBuildSettings.h")] public enum WSABuildType { @@ -334,7 +341,7 @@ public enum SwitchRomCompressionType [NativeType(Header = "Editor/Src/EditorUserBuildSettings.h")] public enum QNXOsVersion { - [UnityEngine.InspectorName("Neutrino RTOS 7.0")] + [System.Obsolete("Neutrino RTOS 7.0 has been removed in 2023.2")] Neutrino70 = 0, [UnityEngine.InspectorName("Neutrino RTOS 7.1")] @@ -347,13 +354,13 @@ public enum EmbeddedArchitecture [UnityEngine.InspectorName("Arm64")] Arm64 = 0, - [UnityEngine.InspectorName("Arm32")] + [System.Obsolete("Arm32 has been removed in 2023.2")] Arm32 = 1, [UnityEngine.InspectorName("X64")] X64 = 2, - [UnityEngine.InspectorName("X86")] + [System.Obsolete("X86 has been removed in 2023.2")] X86 = 3, } @@ -363,13 +370,13 @@ public enum QNXArchitecture [UnityEngine.InspectorName("Arm64")] Arm64 = EmbeddedArchitecture.Arm64, - [UnityEngine.InspectorName("Arm32")] + [System.Obsolete("Arm32 has been removed in 2023.2")] Arm32 = EmbeddedArchitecture.Arm32, [UnityEngine.InspectorName("X64")] X64 = EmbeddedArchitecture.X64, - [UnityEngine.InspectorName("X86")] + [System.Obsolete("X86 has been removed in 2023.2")] X86 = EmbeddedArchitecture.X86, } @@ -379,13 +386,13 @@ public enum EmbeddedLinuxArchitecture [UnityEngine.InspectorName("Arm64")] Arm64 = EmbeddedArchitecture.Arm64, - [UnityEngine.InspectorName("Arm32")] + [System.Obsolete("Arm32 has been removed in 2023.2")] Arm32 = EmbeddedArchitecture.Arm32, [UnityEngine.InspectorName("X64")] X64 = EmbeddedArchitecture.X64, - [UnityEngine.InspectorName("X86")] + [System.Obsolete("X86 has been removed in 2023.2")] X86 = EmbeddedArchitecture.X86, } @@ -619,7 +626,7 @@ public static extern WebGLTextureSubtarget webGLBuildSubtarget } public static extern string webGLClientBrowserPath { get; set; } - + public static extern WebGLClientBrowserType webGLClientBrowserType { [NativeMethod("GetWebGLClientBrowserType")] @@ -764,6 +771,13 @@ public static extern WSABuildAndRunDeployTarget wsaBuildAndRunDeployTarget set; } + public static extern WindowsBuildAndRunDeployTarget windowsBuildAndRunDeployTarget + { + [NativeMethod("GetSelectedWindowsBuildAndRunDeployTarget")] + get; + [NativeMethod("SetSelectedWindowsBuildAndRunDeployTarget")] + set; + } public static extern int overrideMaxTextureSize { get; set; } public static extern Build.OverrideTextureCompression overrideTextureCompression { get; set; } diff --git a/Editor/Mono/EditorWindow.cs b/Editor/Mono/EditorWindow.cs index b55a36f03b..78d11a454d 100644 --- a/Editor/Mono/EditorWindow.cs +++ b/Editor/Mono/EditorWindow.cs @@ -337,8 +337,7 @@ internal void DrawNotification() if (warningSize.y > targetHeight) warningSize.y = targetHeight; - Rect r = new Rect((position.width - warningSize.x) * .5f, 20 + (position.height - 20 - warningSize.y) * .7f, warningSize.x, warningSize.y); - + Rect r = new Rect((position.width - warningSize.x - EditorStyles.notificationText.margin.horizontal) * .5f, 20 + (position.height - 20 - warningSize.y) * .7f, warningSize.x + EditorStyles.notificationText.margin.horizontal, warningSize.y); double time = EditorApplication.timeSinceStartup; if (time > m_FadeoutTime) GUI.color = new Color(1, 1, 1, 1 - (float)((time - m_FadeoutTime) / kWarningFadeoutTime)); diff --git a/Editor/Mono/GI/Lightmapping.bindings.cs b/Editor/Mono/GI/Lightmapping.bindings.cs index a73300a5dd..dff1226a83 100644 --- a/Editor/Mono/GI/Lightmapping.bindings.cs +++ b/Editor/Mono/GI/Lightmapping.bindings.cs @@ -140,14 +140,14 @@ public static float bounceBoost } [RequiredByNativeCode] - internal static bool GetIsInteractive() + internal static bool ShouldBakeInteractively() { - return SceneView.IsInteractiveBakingEnabled(); + return SceneView.NeedsInteractiveBaking(); } - internal static bool isInteractive + internal static bool shouldBakeInteractively { - get { return GetIsInteractive(); } + get { return ShouldBakeInteractively(); } } [RequiredByNativeCode] diff --git a/Editor/Mono/GUI/Actions/EditorAction.cs b/Editor/Mono/GUI/Actions/EditorAction.cs index 95526666be..6f1c40e610 100644 --- a/Editor/Mono/GUI/Actions/EditorAction.cs +++ b/Editor/Mono/GUI/Actions/EditorAction.cs @@ -17,9 +17,8 @@ public enum EditorActionResult public abstract class EditorAction { bool m_IsFinished; - internal event Action actionFinished; - public virtual Texture2D icon => null; + internal event Action actionFinished; public static T Start() where T : EditorAction, new() => Start(new T()); @@ -27,21 +26,17 @@ public static T Start(T action) where T : EditorAction { if (action == null) throw new ArgumentNullException(nameof(action)); + if (action.m_IsFinished) return action; - var tool = (EditorActionTool)ScriptableObject.CreateInstance(typeof(EditorActionTool), - x => - { - if (x is not EditorActionTool t) - return; - - t.action = action; - }); - ToolManager.SetActiveTool(tool); + + EditorToolManager.activeOverride = new EditorActionTool(action); + return action; } public virtual void OnSceneGUI(SceneView sceneView) {} + public void Finish(EditorActionResult result) { if (m_IsFinished) diff --git a/Editor/Mono/GUI/Actions/EditorActionTool.cs b/Editor/Mono/GUI/Actions/EditorActionTool.cs index 01083412b4..746f05cf8d 100644 --- a/Editor/Mono/GUI/Actions/EditorActionTool.cs +++ b/Editor/Mono/GUI/Actions/EditorActionTool.cs @@ -2,42 +2,58 @@ // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License +using System; using UnityEditor.EditorTools; using UnityEngine; namespace UnityEditor.Actions { - sealed class EditorActionTool : EditorTool + sealed class EditorActionTool : IDisposable { public EditorAction action { get; internal set; } + bool m_IsCancelled; - public override GUIContent toolbarIcon => new GUIContent(action?.icon); + EditorActionTool() { } - public override void OnActivated() + public EditorActionTool(EditorAction action) { - base.OnActivated(); - - if (action != null) - action.actionFinished += OnActionFinished; + this.action = action; + this.action.actionFinished += OnActionFinished; } - public override void OnToolGUI(EditorWindow window) + public void OnGUI(EditorWindow window) { - if (action != null) - action?.OnSceneGUI(window as SceneView); + if (!(window is SceneView sceneView)) + return; + + var evt = Event.current; + + if(evt.type == EventType.KeyDown && evt.keyCode == KeyCode.Escape) + { + Cancel(); + evt.Use(); + return; + } + + action?.OnSceneGUI(sceneView); } - void OnActionFinished(EditorActionResult result) + void OnActionFinished(EditorActionResult result) => Dispose(); + + public void Cancel() { - action.actionFinished -= OnActionFinished; - action = null; - DestroyImmediate(this); + m_IsCancelled = true; + Dispose(); } - public override void OnWillBeDeactivated() + public void Dispose() { - // If the tool is deactivate without being completed first - action?.Finish(EditorActionResult.Canceled); + if (action == null) + return; + action.actionFinished -= OnActionFinished; + action?.Finish(m_IsCancelled ? EditorActionResult.Canceled : EditorActionResult.Success); + action = null; + EditorToolManager.activeOverride = null; } } } diff --git a/Editor/Mono/GUI/Tools/EditorToolManager.cs b/Editor/Mono/GUI/Tools/EditorToolManager.cs index e2f8125831..1b69e5aaed 100644 --- a/Editor/Mono/GUI/Tools/EditorToolManager.cs +++ b/Editor/Mono/GUI/Tools/EditorToolManager.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Linq; +using UnityEditor.Actions; using UnityEditor.SceneManagement; using UnityEngine; using UnityObject = UnityEngine.Object; @@ -20,6 +21,8 @@ sealed class EditorToolManager : ScriptableSingleton [SerializeField] EditorTool m_ActiveTool; + EditorActionTool m_ActiveOverride; + Tool m_PreviousTool = Tool.Move; [SerializeField] @@ -166,6 +169,8 @@ internal static EditorTool activeTool s_ChangingActiveTool = true; + activeOverride = null; + ToolManager.ActiveToolWillChange(); var previous = instance.m_ActiveTool; @@ -206,6 +211,20 @@ internal static EditorTool activeTool } } + // this tool will transparently override the `OnToolGUI` method of the active tool. + // do not expose this as public API with also considering how to handle lifecycle and active tool interop. + // currently this is only used for EditorToolAction. + internal static EditorActionTool activeOverride + { + get => instance.m_ActiveOverride; + + set + { + instance.m_ActiveOverride?.Dispose(); + instance.m_ActiveOverride = value; + } + } + [Serializable] struct ComponentToolCache : ISerializationCallbackReceiver { @@ -303,6 +322,7 @@ void OnEnable() void OnDisable() { + m_ActiveOverride = null; Undo.undoRedoEvent -= UndoRedoPerformed; ActiveEditorTracker.editorTrackerRebuilt -= TrackerRebuilt; Selection.selectedObjectWasDestroyed -= SelectedObjectWasDestroyed; @@ -382,6 +402,8 @@ void RestoreCustomEditorTool() // destroy invalid custom editor tools void ClearCustomEditorTools() { + m_ActiveOverride = null; + foreach (var customEditorTool in m_ComponentTools) { if (customEditorTool.editor == m_ActiveTool) @@ -512,10 +534,17 @@ internal static void OnToolGUI(EditorWindow window) if (!IsGizmoCulledBySceneCullingMasksOrFocusedScene(activeToolContext.target)) activeToolContext.OnToolGUI(window); + if (instance.m_ActiveOverride != null) + { + instance.m_ActiveOverride.OnGUI(window); + return; + } + if (Tools.s_Hidden || instance.m_ActiveTool == null) return; var current = instance.m_ActiveTool; + if (IsGizmoCulledBySceneCullingMasksOrFocusedScene(current.target)) return; diff --git a/Editor/Mono/GameView/GameView.cs b/Editor/Mono/GameView/GameView.cs index 30f58cf6b6..0787aec904 100644 --- a/Editor/Mono/GameView/GameView.cs +++ b/Editor/Mono/GameView/GameView.cs @@ -96,8 +96,8 @@ internal static class Styles public static GUIContent gizmosContent = EditorGUIUtility.TrTextContent("Gizmos"); public static GUIContent zoomSliderContent = EditorGUIUtility.TrTextContent("Scale", "Size of the game view on the screen."); public static GUIContent vsyncContent = EditorGUIUtility.TrTextContent("VSync"); - public static GUIContent muteOffContent = EditorGUIUtility.TrIconContent("SceneviewAudio On", "Mute Audio"); - public static GUIContent muteOnContent = EditorGUIUtility.TrIconContent("SceneviewAudio", "Mute Audio"); + public static GUIContent muteOffContent = EditorGUIUtility.TrIconContent("GameViewAudio On", "Mute Audio"); + public static GUIContent muteOnContent = EditorGUIUtility.TrIconContent("GameViewAudio", "Mute Audio"); public static GUIContent shortcutsOnContent = EditorGUIUtility.TrIconContent("Keyboard", "Unity Shortcuts"); public static GUIContent shortcutsOffContent = EditorGUIUtility.TrIconContent("KeyboardShortcutsDisabled", "Unity Shortcuts"); public static GUIContent statsContent = EditorGUIUtility.TrTextContent("Stats"); @@ -106,7 +106,7 @@ internal static class Styles public static GUIContent clearEveryFrameContextMenuContent = EditorGUIUtility.TrTextContent("Clear Every Frame in Edit Mode"); public static GUIContent lowResAspectRatiosContextMenuContent = EditorGUIUtility.TrTextContent("Low Resolution Aspect Ratios"); public static GUIContent metalFrameCaptureContent = EditorGUIUtility.TrIconContent("FrameCapture", "Capture the current view and open in Xcode frame debugger"); - public static GUIContent frameDebuggerContent = EditorGUIUtility.TrIconContent("Debug_Frame_d", "Opens the Frame Debugger"); + public static GUIContent frameDebuggerContent = EditorGUIUtility.TrIconContent("Debug", "Opens the Frame Debugger"); public const string k_StatsShortcutID = "Game View/Toggle Stats"; public const string k_StatsTooltip = "View general rendering information"; diff --git a/Editor/Mono/HandleUtility.cs b/Editor/Mono/HandleUtility.cs index d1d6d45ae1..3ddd567401 100644 --- a/Editor/Mono/HandleUtility.cs +++ b/Editor/Mono/HandleUtility.cs @@ -32,8 +32,7 @@ public static float CalcLineTranslation(Vector2 src, Vector2 dest, Vector3 srcPo // The constrained direction is facing towards the camera, THATS BAD when the handle is close to the camera // The srcPosition goes through to the other side of the camera float invert = 1.0F; - Camera cam = GetCurrentCamera(); - Vector3 cameraForward = cam == null ? Vector3.forward : cam.transform.forward; + Vector3 cameraForward = Camera.current == null ? Vector3.forward : Camera.current.transform.forward; if (Vector3.Dot(constraintDir, cameraForward) < 0.0F) invert = -1.0F; @@ -42,7 +41,7 @@ public static float CalcLineTranslation(Vector2 src, Vector2 dest, Vector3 srcPo // we then parametrise the perpendicular position of dest into the line (p1-p2) Vector3 cd = constraintDir; cd.y = -cd.y; - + Camera cam = Camera.current; // if camera is null, then we are drawing in OnGUI, where y-coordinate goes top-to-bottom Vector2 p1 = cam == null ? Vector2.Scale(srcPosition, new Vector2(1f, -1f)) @@ -65,16 +64,6 @@ public static float CalcLineTranslation(Vector2 src, Vector2 dest, Vector3 srcPo return output; } - internal static Camera GetCurrentCamera() - { - var camera = Camera.current; - - if (!camera && SceneView.lastActiveSceneView != null) - camera = SceneView.lastActiveSceneView.camera; - - return camera; - } - internal static float GetParametrization(Vector2 x0, Vector2 x1, Vector2 x2) { return -(Vector2.Dot(x1 - x0, x2 - x1) / (x2 - x1).sqrMagnitude); @@ -201,7 +190,7 @@ internal static float DistanceToLineInternal(Vector3 point, Vector3 p1, Vector3 public static float DistanceToCircle(Vector3 position, float radius) { Vector2 screenCenter = WorldToGUIPoint(position); - Camera cam = GetCurrentCamera(); + Camera cam = Camera.current; if (cam) { var screenEdge = WorldToGUIPoint(position + cam.transform.right * radius); @@ -217,7 +206,7 @@ public static float DistanceToCircle(Vector3 position, float radius) public static float DistanceToCircle(CameraProjectionCache projection, Vector3 position, float radius) { Vector2 screenCenter = projection.WorldToGUIPoint(position); - Camera cam = GetCurrentCamera(); + Camera cam = Camera.current; if (cam) { var screenEdge = projection.WorldToGUIPoint(position + cam.transform.right * radius); @@ -468,7 +457,7 @@ public static Vector3 ClosestPointToArc(Vector3 center, Vector3 normal, Vector3 public static float DistanceToPolyLine(params Vector3[] points) { Matrix4x4 handleMatrix = Handles.matrix; - CameraProjectionCache cam = new CameraProjectionCache(GetCurrentCamera()); + CameraProjectionCache cam = new CameraProjectionCache(Camera.current); Vector2 mouse = Event.current.mousePosition; Vector2 p1 = cam.WorldToGUIPoint(handleMatrix.MultiplyPoint3x4(points[0])); @@ -491,7 +480,7 @@ public static float DistanceToPolyLine(params Vector3[] points) internal static float DistanceToPolyLine(Vector3[] points, bool loop, out int index) { Matrix4x4 handleMatrix = Handles.matrix; - CameraProjectionCache cam = new CameraProjectionCache(GetCurrentCamera()); + CameraProjectionCache cam = new CameraProjectionCache(Camera.current); Vector2 mouse = Event.current.mousePosition; Vector2 p1 = cam.WorldToGUIPoint(handleMatrix.MultiplyPoint3x4(points[0])); @@ -639,7 +628,7 @@ static void CalcPointCloudConvexHull(Vector3[] points, List outHull) // project point cloud into 2D GUI space var handleMatrix = Handles.matrix; - var cam = new CameraProjectionCache(GetCurrentCamera()); + var cam = new CameraProjectionCache(Camera.current); for (var i = 0; i < points.Length; ++i) points[i] = cam.WorldToGUIPoint(handleMatrix.MultiplyPoint3x4(points[i])); @@ -651,7 +640,7 @@ static void CalcPointCloudConvexHull(Vector3[] points, List outHull) static readonly List s_PointCloudConvexHull = new List(); static float DistanceToPointCloudConvexHull(params Vector3[] points) { - if (points == null || points.Length == 0 || GetCurrentCamera() == null) + if (points == null || points.Length == 0 || Camera.current == null) return float.PositiveInfinity; var mousePos = Event.current.mousePosition; @@ -703,10 +692,9 @@ internal static void BeginHandles() Handles.zTest = CompareFunction.Always; s_CustomPickDistance = kPickDistance; - Camera cam = GetCurrentCamera(); - if (null != cam) + if (null != Camera.current) { - s_PreviousCamera = cam; + s_PreviousCamera = Camera.current; } Handles.Internal_SetCurrentCamera(null); @@ -739,7 +727,7 @@ internal static void EndHandles() // Get world space size of a manipulator handle at given position. public static float GetHandleSize(Vector3 position) { - Camera cam = GetCurrentCamera(); + Camera cam = Camera.current; position = Handles.matrix.MultiplyPoint(position); if (cam) { @@ -756,11 +744,7 @@ public static float GetHandleSize(Vector3 position) static float renderingViewHeight { - get - { - Camera cam = GetCurrentCamera(); - return cam == null ? Screen.height : cam.pixelHeight; - } + get { return Camera.current == null ? Screen.height : Camera.current.pixelHeight; } } // Convert world space point to a 2D GUI position. @@ -772,7 +756,7 @@ public static Vector2 WorldToGUIPoint(Vector3 world) // Convert world space point to a 2D GUI position. public static Vector3 WorldToGUIPointWithDepth(Vector3 world) { - return WorldToGUIPointWithDepth(GetCurrentCamera(), world); + return WorldToGUIPointWithDepth(Camera.current, world); } // Convert world space point to a 2D GUI position. @@ -808,7 +792,7 @@ public static Ray GUIPointToWorldRay(Vector2 position) private static Ray GUIPointToWorldRayPrecise(Vector2 position, float startZ = float.NegativeInfinity) { - Camera camera = GetCurrentCamera(); + Camera camera = Camera.current; if (!camera && SceneView.lastActiveSceneView != null) camera = SceneView.lastActiveSceneView.camera; @@ -932,7 +916,7 @@ public static GameObject[] PickRectObjects(Rect rect) // *undocumented* public static GameObject[] PickRectObjects(Rect rect, bool selectPrefabRootsOnly) { - Camera cam = GetCurrentCamera(); + Camera cam = Camera.current; rect = EditorGUIUtility.PointsToPixels(rect); rect.x /= cam.pixelWidth; rect.width /= cam.pixelWidth; @@ -954,7 +938,7 @@ public static bool FindNearestVertex(Vector2 guiPoint, Transform[] objectsToSear public static bool FindNearestVertex(Vector2 guiPoint, Transform[] objectsToSearch, Transform[] objectsToIgnore, out Vector3 vertex, out GameObject gameObject) { - Camera cam = GetCurrentCamera(); + Camera cam = Camera.current; var screenPoint = EditorGUIUtility.PointsToPixels(guiPoint); screenPoint.y = cam.pixelRect.yMax - screenPoint.y; gameObject = Internal_FindNearestVertex(cam, screenPoint, objectsToSearch, objectsToIgnore, out vertex, out bool found); @@ -973,7 +957,7 @@ public static bool FindNearestVertex(Vector2 guiPoint, Transform[] objectsToSear public static bool FindNearestVertex(Vector2 guiPoint, Transform[] objectsToSearch, Transform[] objectsToIgnore, out Vector3 vertex) { - Camera cam = GetCurrentCamera(); + Camera cam = Camera.current; var screenPoint = EditorGUIUtility.PointsToPixels(guiPoint); screenPoint.y = cam.pixelRect.yMax - screenPoint.y; Internal_FindNearestVertex(cam, screenPoint, objectsToSearch, objectsToIgnore, out vertex, out bool found); @@ -1095,7 +1079,7 @@ internal static PickingObject PickObject(Vector2 guiPosition, List ignore = null, List filter = null) { - Camera cam = GetCurrentCamera(); + Camera cam = Camera.current; int layers = cam.cullingMask; var screenPosition = GUIPointToScreenPixelCoordinate(guiPosition); var sceneView = SceneView.lastActiveSceneView; @@ -1458,7 +1442,7 @@ static Material handleWireMaterial get { InitHandleMaterials(); - return GetCurrentCamera() ? s_HandleWireMaterial : s_HandleWireMaterial2D; + return Camera.current ? s_HandleWireMaterial : s_HandleWireMaterial2D; } } @@ -1468,7 +1452,7 @@ static Material handleDottedWireMaterial get { InitHandleMaterials(); - return GetCurrentCamera() ? s_HandleDottedWireMaterial : s_HandleDottedWireMaterial2D; + return Camera.current ? s_HandleDottedWireMaterial : s_HandleDottedWireMaterial2D; } } @@ -1505,9 +1489,8 @@ internal static void ApplyWireMaterial([DefaultValue("UnityEngine.Rendering.Comp // for any later GL.Begin calls. mat.SetFloat("_HandleZTest", (float)zTest); mat.SetPass(0); - Camera cam = GetCurrentCamera(); - int textureIndex = cam ? s_HandleWireTextureIndex : s_HandleWireTextureIndex2D; - int samplerIndex = cam ? s_HandleWireTextureSamplerIndex : s_HandleWireTextureSamplerIndex2D; + int textureIndex = Camera.current ? s_HandleWireTextureIndex : s_HandleWireTextureIndex2D; + int samplerIndex = Camera.current ? s_HandleWireTextureSamplerIndex : s_HandleWireTextureSamplerIndex2D; Internal_SetHandleWireTextureIndex(textureIndex, samplerIndex); } @@ -1525,9 +1508,8 @@ internal static void ApplyDottedWireMaterial([DefaultValue("UnityEngine.Renderin // for any later GL.Begin calls. mat.SetFloat("_HandleZTest", (float)zTest); mat.SetPass(0); - Camera cam = GetCurrentCamera(); - int textureIndex = cam ? s_HandleDottedWireTextureIndex : s_HandleDottedWireTextureIndex2D; - int samplerIndex = cam ? s_HandleDottedWireTextureSamplerIndex : s_HandleDottedWireTextureSamplerIndex2D; + int textureIndex = Camera.current ? s_HandleDottedWireTextureIndex : s_HandleDottedWireTextureIndex2D; + int samplerIndex = Camera.current ? s_HandleDottedWireTextureSamplerIndex : s_HandleDottedWireTextureSamplerIndex2D; Internal_SetHandleWireTextureIndex(textureIndex, samplerIndex); } @@ -1615,7 +1597,7 @@ static bool TryRaySnap(Ray ray, out RaycastHit resultHit) // Casts /ray/ against the scene. public static object RaySnap(Ray ray) { - Camera cam = GetCurrentCamera(); + Camera cam = Camera.current; if (cam == null) return null; diff --git a/Editor/Mono/Inspector/EditorSettingsInspector.cs b/Editor/Mono/Inspector/EditorSettingsInspector.cs index 5d400296a5..50ba889cfe 100644 --- a/Editor/Mono/Inspector/EditorSettingsInspector.cs +++ b/Editor/Mono/Inspector/EditorSettingsInspector.cs @@ -47,7 +47,7 @@ class Content public static readonly GUIContent cacheServerLearnMore = new GUIContent("Learn more...", "Go to cacheserver documentation."); public static GUIContent assetSerialization = EditorGUIUtility.TrTextContent("Asset Serialization"); - public static GUIContent textSerializeMappingsOnOneLine = EditorGUIUtility.TrTextContent("Force Serialize References On One Line", "Forces Unity to write references and other inline mappings on one line, to help reduce version control noise"); + public static GUIContent textSerializeMappingsOnOneLine = EditorGUIUtility.TrTextContent("Reduce version control noise", "Forces Unity to write references and similar YAML structures on one line, which reduces version control noise."); public static GUIContent defaultBehaviorMode = EditorGUIUtility.TrTextContent("Default Behaviour Mode"); public static GUIContent buildPipelineHeader = EditorGUIUtility.TrTextContent("Build Pipeline"); @@ -438,7 +438,7 @@ public override void OnInspectorGUI() if (m_SerializationMode.intValue != (int)SerializationMode.ForceBinary) { EditorGUI.BeginChangeCheck(); - EditorGUILayout.PropertyField(m_SerializeInlineMappingsOnOneLine); + EditorGUILayout.PropertyField(m_SerializeInlineMappingsOnOneLine, Content.textSerializeMappingsOnOneLine); if (EditorGUI.EndChangeCheck() && m_IsGlobalSettings) { EditorSettings.serializeInlineMappingsOnOneLine = m_SerializeInlineMappingsOnOneLine.boolValue; diff --git a/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsUtils.cs b/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsUtils.cs index 962575e3eb..54260ff470 100644 --- a/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsUtils.cs +++ b/Editor/Mono/Inspector/GraphicsSettingsInspectors/GraphicsSettingsUtils.cs @@ -2,7 +2,7 @@ // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License -using System; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Reflection; @@ -81,9 +81,8 @@ internal static List CollectRenderPipelineAssetsByGloba for (int i = 0; i < renderPipelineGlobalSettingsMap.arraySize; ++i) { var globalSettings = GetRenderPipelineGlobalSettingsByIndex(renderPipelineGlobalSettingsMap, i); - if (!TryCreateNewGlobalSettingsContainer(globalSettings, out var globalSettingsContainer)) - continue; - existedGlobalSettings.Add(globalSettingsContainer); + if (TryCreateNewGlobalSettingsContainer(globalSettings, out var globalSettingsContainer)) + existedGlobalSettings.Add(globalSettingsContainer); } return existedGlobalSettings; } @@ -91,28 +90,28 @@ internal static List CollectRenderPipelineAssetsByGloba internal static bool TryCreateNewGlobalSettingsContainer(RenderPipelineGlobalSettings globalSettings, out GlobalSettingsContainer globalSettingsContainer) { globalSettingsContainer = null; - if (globalSettings == null) - return false; - var settingsListInContainer = GetSettingsListFromRenderPipelineGlobalSettings(globalSettings, out var globalSettingsSO, out var settingsContainer); - if (settingsListInContainer.arraySize == 0) + if (globalSettings == null) return false; + + var globalSettingsSerializedObject = new SerializedObject(globalSettings); + var settingsContainer = globalSettingsSerializedObject.FindProperty("m_Settings"); - var globalSettingsType = globalSettings.GetType(); - if (!ExtractSupportedOnRenderPipelineAttribute(globalSettingsType, out var supportedOnRenderPipelineAttribute)) - return false; + var settingsListInContainer = settingsContainer != null + ? settingsContainer.FindPropertyRelative("m_SettingsList") + : null; - var tabName = CreateNewTabName(globalSettingsType, supportedOnRenderPipelineAttribute); - globalSettingsContainer = new GlobalSettingsContainer(tabName, supportedOnRenderPipelineAttribute.renderPipelineTypes[0], settingsContainer, globalSettingsSO); - return true; - } + if (settingsListInContainer != null && settingsListInContainer.arraySize != 0) + { + var globalSettingsType = globalSettings.GetType(); + if (ExtractSupportedOnRenderPipelineAttribute(globalSettingsType, out var supportedOnRenderPipelineAttribute)) + { + var tabName = CreateNewTabName(globalSettingsType, supportedOnRenderPipelineAttribute); + globalSettingsContainer = new GlobalSettingsContainer(tabName, supportedOnRenderPipelineAttribute.renderPipelineTypes[0], settingsContainer, globalSettingsSerializedObject); + } + } - internal static SerializedProperty GetSettingsListFromRenderPipelineGlobalSettings(RenderPipelineGlobalSettings globalSettings, out SerializedObject globalSettingsSO, out SerializedProperty settingsContainer) - { - globalSettingsSO = new SerializedObject(globalSettings); - settingsContainer = globalSettingsSO.FindProperty("m_Settings"); - var settingsListInContainer = settingsContainer.FindPropertyRelative("m_SettingsList"); - return settingsListInContainer; + return globalSettingsContainer != null; } internal static RenderPipelineGlobalSettings GetRenderPipelineGlobalSettingsByIndex(SerializedProperty srpDefaultSettings, int i) diff --git a/Editor/Mono/Inspector/RendererLightingSettings.cs b/Editor/Mono/Inspector/RendererLightingSettings.cs index abf9592f80..e0e15413d6 100644 --- a/Editor/Mono/Inspector/RendererLightingSettings.cs +++ b/Editor/Mono/Inspector/RendererLightingSettings.cs @@ -564,7 +564,7 @@ void ShowAtlasGUI(int instanceID, bool isMeshRenderer, bool useInteractiveLightB return; // Hide preview lightmaps if we aren't in preview mode - if (useInteractiveLightBakingData && !Lightmapping.isInteractive) + if (useInteractiveLightBakingData && !Lightmapping.shouldBakeInteractively) return; Hash128 contentHash = LightmapVisualizationUtility.GetBakedGITextureHash(lightmapIndex, 0, GITextureType.Baked, useInteractiveLightBakingData); diff --git a/Editor/Mono/Inspector/SpriteFrameInspector.cs b/Editor/Mono/Inspector/SpriteFrameInspector.cs index 76a5c416f7..07ae7133f3 100644 --- a/Editor/Mono/Inspector/SpriteFrameInspector.cs +++ b/Editor/Mono/Inspector/SpriteFrameInspector.cs @@ -185,14 +185,18 @@ public static Texture2D BuildPreviewTexture(Sprite sprite, Material spriteRender if (!isPolygon) { // Try to have a minimum of 64 pixels for width and height, unless requested width and height is smaller - var minWidth = Mathf.Min(64, width); - var minHeight = Mathf.Min(64, height); + var minWidth = Mathf.Min(64f, width); + var minHeight = Mathf.Min(64f, height); PreviewHelpers.AdjustWidthAndHeightForStaticPreview((int) spriteWidth, (int) spriteHeight, ref width, ref height); - // Set minimum size for width and height to prevent small previews for small sprites - width = Mathf.Max(minWidth, width); - height = Mathf.Max(minHeight, height); + // Set minimum size for width/height to prevent small previews for small sprites + if (width < minWidth && height < minHeight) + { + var ratio = Mathf.Min( minWidth / width, minHeight / height); + width = Mathf.FloorToInt(width * ratio); + height = Mathf.FloorToInt(height * ratio); + } } SavedRenderTargetState savedRTState = new SavedRenderTargetState(); diff --git a/Editor/Mono/Inspector/TextureInspector.cs b/Editor/Mono/Inspector/TextureInspector.cs index c67bb6b7ab..d12494a3c0 100644 --- a/Editor/Mono/Inspector/TextureInspector.cs +++ b/Editor/Mono/Inspector/TextureInspector.cs @@ -30,8 +30,8 @@ internal static void AdjustWidthAndHeightForStaticPreview(int textureWidth, int { // For textures larger than our wanted width and height we ensure to // keep aspect ratio of the texture and fit it to best match our wanted width and height. - float relWidth = height / (float)textureWidth; - float relHeight = width / (float)textureHeight; + float relWidth = width / (float)textureWidth; + float relHeight = height / (float)textureHeight; float scale = Mathf.Min(relHeight, relWidth); diff --git a/Editor/Mono/Menu.bindings.cs b/Editor/Mono/Menu.bindings.cs index a62b61854e..fbbf9db106 100644 --- a/Editor/Mono/Menu.bindings.cs +++ b/Editor/Mono/Menu.bindings.cs @@ -60,6 +60,9 @@ public sealed class Menu [FreeFunction("MenuController::SetMenuItemHotkey")] internal static extern void SetHotkey(string menuPath, string hotkey); + [FreeFunction("MenuController::GetMenuItemHotkey")] + internal static extern string GetHotkey(string menuPath); + [FreeFunction("MenuController::ExtractSubmenus")] internal static extern string[] ExtractSubmenus(string menuPath); diff --git a/Editor/Mono/MenuUtility.cs b/Editor/Mono/MenuUtility.cs index 340d0af9c8..ef1c15db16 100644 --- a/Editor/Mono/MenuUtility.cs +++ b/Editor/Mono/MenuUtility.cs @@ -34,9 +34,19 @@ public static void AddMenuItem(DropdownMenu menu, string menuItemPath, string co public static void AddMenuItemWithContext(DropdownMenu menu, IEnumerable context, string menuItemPath, string contextMenuPath = "") { var contextArray = ToArray(context); - bool enabled = Menu.GetEnabledWithContext(menuItemPath, contextArray); + var enabled = Menu.GetEnabledWithContext(menuItemPath, contextArray); + AddMenuItemWithContext(menu, context, enabled, menuItemPath, contextMenuPath); + } + + internal static void AddMenuItemWithContext(DropdownMenu menu, IEnumerable context, bool enabled, string menuItemPath, string contextMenuPath = "") + { + var contextArray = ToArray(context); string iconResource = Menu.GetIconResource(menuItemPath); - AddAction(menu, string.IsNullOrEmpty(contextMenuPath) ? menuItemPath : contextMenuPath, + string shortcut = Menu.GetHotkey(menuItemPath); + string path = (string.IsNullOrEmpty(contextMenuPath) ? menuItemPath : contextMenuPath) + + (string.IsNullOrEmpty(shortcut) ? "" : " " + shortcut); + + AddAction(menu, path, () => { ExecuteMenuItem(contextArray, menuItemPath); }, enabled, string.IsNullOrEmpty(iconResource) ? null : EditorGUIUtility.LoadIcon(iconResource), enabled ? string.Empty : Menu.GetDisabledTooltip(menuItemPath)); @@ -87,11 +97,18 @@ static void AddMenuItems(DropdownMenu menu, string componentName, ScriptingMenuI public static void AddClipboardEntriesTo(DropdownMenu menu) { - AddMenuItemWithContext(menu, null, "Edit/Cut", "Cut"); - AddMenuItemWithContext(menu, null, "Edit/Copy", "Copy"); - AddMenuItemWithContext(menu, null, "Edit/Paste", "Paste"); - AddMenuItemWithContext(menu, null, "Edit/Duplicate", "Duplicate"); - AddMenuItemWithContext(menu, null, "Edit/Delete", "Delete"); + var isThereGameObjectInSelection = Selection.gameObjects.Length > 0; + var isPasteEnabled = GUIUtility.systemCopyBuffer.Length > 0; + AddClipboardEntriesTo(menu, isThereGameObjectInSelection, isThereGameObjectInSelection, isPasteEnabled, isThereGameObjectInSelection, isThereGameObjectInSelection); + } + + public static void AddClipboardEntriesTo(DropdownMenu menu, bool cutEnabled, bool copyEnabled, bool pasteEnabled, bool duplicateEnabled, bool deleteEnabled) + { + AddMenuItemWithContext(menu, null, cutEnabled, "Edit/Cut", "Cut"); + AddMenuItemWithContext(menu, null, copyEnabled, "Edit/Copy", "Copy"); + AddMenuItemWithContext(menu, null, pasteEnabled, "Edit/Paste", "Paste"); + AddMenuItemWithContext(menu, null, duplicateEnabled, "Edit/Duplicate", "Duplicate"); + AddMenuItemWithContext(menu, null, deleteEnabled, "Edit/Delete", "Delete"); } public static void AddComponentEntriesTo(DropdownMenu menu) @@ -118,6 +135,7 @@ public static void AddComponentEntriesTo(DropdownMenu menu) public static void AddGameObjectEntriesTo(DropdownMenu menu) { bool hasSelectedGO = Selection.gameObjects.Length > 0; + bool hasSelectedExactlyOneGO = Selection.gameObjects.Length == 1; AddClipboardEntriesTo(menu); menu.AppendSeparator(); @@ -128,13 +146,31 @@ public static void AddGameObjectEntriesTo(DropdownMenu menu) menu.AppendSeparator(); - AddAction(menu, "Isolate", SceneVisibilityManager.ToggleIsolateSelectionShortcut, hasSelectedGO); + AddAction(menu, + SceneVisibilityState.isolation ? "Exit Isolation" : "Isolate", + SceneVisibilityManager.ToggleIsolateSelectionShortcut, + SceneVisibilityState.isolation || hasSelectedGO); menu.AppendSeparator(); AddMenuItemWithContext(menu, null, "Component/Add...", "Add Component..."); AddMenuItemWithContext(menu, null, "Assets/Properties...", "Properties..."); + // Prefab menu items that only make sense if a single object is selected. + var listOfSceneHierarchyWindows = SceneHierarchyWindow.GetAllSceneHierarchyWindows(); + if (hasSelectedExactlyOneGO && listOfSceneHierarchyWindows.Count > 0) + { + var sceneHierarchy = listOfSceneHierarchyWindows[0].sceneHierarchy; + if (sceneHierarchy is not null) + { + menu.AppendSeparator(); + var prefabIcon = PrefabUtility.GameObjectStyles.prefabIcon; + AddAction(menu, "Prefab/", null, icon: prefabIcon); + sceneHierarchy.PopulateDropdownMenuWithPrefabMenuItems(menu); + } + } + + // Component entries if (hasSelectedGO) { menu.AppendSeparator(); diff --git a/Editor/Mono/ObjectListLocalGroup.cs b/Editor/Mono/ObjectListLocalGroup.cs index 18cac0cf88..489cc06173 100644 --- a/Editor/Mono/ObjectListLocalGroup.cs +++ b/Editor/Mono/ObjectListLocalGroup.cs @@ -7,11 +7,8 @@ using UnityEditor.VersionControl; using UnityEditorInternal; using UnityEditorInternal.VersionControl; -using System.Collections; using System.Collections.Generic; -using System.Linq; using Math = System.Math; -using IndexOutOfRangeException = System.IndexOutOfRangeException; using AssetReference = UnityEditorInternal.InternalEditorUtility.AssetReference; namespace UnityEditor @@ -844,6 +841,8 @@ void DrawItem(Rect position, FilteredHierarchy.FilterResult filterItem, BuiltinR } else // Icon grid { + Texture previewImage = null; + // Get icon bool drawDropShadow = false; if (string.IsNullOrEmpty(assetReference.guid) && m_Owner.GetCreateAssetUtility().instanceID == assetReference.instanceID && m_Owner.GetCreateAssetUtility().icon != null) @@ -858,17 +857,16 @@ void DrawItem(Rect position, FilteredHierarchy.FilterResult filterItem, BuiltinR else { // Check for asset preview - Texture image = null; bool shouldGetAssetPreview = ShouldGetAssetPreview(assetReference); if (shouldGetAssetPreview) { if (assetReference.instanceID != 0) - image = AssetPreview.GetAssetPreview(assetReference.instanceID, m_Owner.GetAssetPreviewManagerID()); + previewImage = AssetPreview.GetAssetPreview(assetReference.instanceID, m_Owner.GetAssetPreviewManagerID()); else if (!string.IsNullOrEmpty(assetReference.guid)) - image = AssetPreview.GetAssetPreviewFromGUID(assetReference.guid, m_Owner.GetAssetPreviewManagerID()); + previewImage = AssetPreview.GetAssetPreviewFromGUID(assetReference.guid, m_Owner.GetAssetPreviewManagerID()); } - m_Content.image = image; + m_Content.image = previewImage; if (m_Content.image != null) drawDropShadow = true; @@ -957,9 +955,31 @@ void DrawItem(Rect position, FilteredHierarchy.FilterResult filterItem, BuiltinR if (isDropTarget) Styles.resultsLabel.Draw(new Rect(labelRect.x - 10, labelRect.y, labelRect.width + 20, labelRect.height), GUIContent.none, true, true, false, false); + + Texture2D typeIcon = null; + if (filterItem != null && previewImage != null) + { + Type type = InternalEditorUtility.GetTypeWithoutLoadingObject(filterItem.instanceID); + + if (type != typeof(Texture2D)) + { + typeIcon = filterItem.icon; + } + } + + if (builtinResource != null) + { + Type type = InternalEditorUtility.GetTypeWithoutLoadingObject(builtinResource.m_InstanceID); + + if (type != typeof(Texture2D)) + { + typeIcon = AssetPreview.GetMiniTypeThumbnail(type); + } + } + var orgClipping = Styles.resultsGridLabel.clipping; var orgAlignment = Styles.resultsLabel.alignment; - var size = Styles.resultsGridLabel.CalcSizeWithConstraints(GUIContent.Temp(labeltext), orgPosition.size); + var size = Styles.resultsGridLabel.CalcSizeWithConstraints(GUIContent.Temp(labeltext, typeIcon), orgPosition.size); size.x += Styles.resultsGridLabel.padding.horizontal; labelRect.x = orgPosition.x + (orgPosition.width - size.x) / 2.0f; labelRect.width = size.x; @@ -968,9 +988,33 @@ void DrawItem(Rect position, FilteredHierarchy.FilterResult filterItem, BuiltinR Styles.resultsGridLabel.clipping = TextClipping.Ellipsis; Styles.resultsGridLabel.alignment = TextAnchor.MiddleCenter; - Styles.resultsGridLabel.Draw(labelRect, labeltext, false, false, selected, m_Owner.HasFocus()); + Styles.resultsGridLabel.Draw(labelRect, GUIContent.Temp(labeltext, typeIcon), false, false, selected, m_Owner.HasFocus()); Styles.resultsGridLabel.clipping = orgClipping; Styles.resultsLabel.alignment = orgAlignment; + + // We only need to set the tooltip once, and not for every item. + if (labelRect.Contains(Event.current.mousePosition)) + { + string tooltip = null; + + if (filterItem != null) + { + //We use GetAssetPath to have the file extension as well + string path = AssetDatabase.GetAssetPath(filterItem.instanceID); + tooltip = path.Substring(path.LastIndexOf('/') + 1); + } + else if (builtinResource != null) + { + //We have a "None" item in the ObjectSelector that has a 0 instanceID + if (builtinResource.m_InstanceID != 0) + tooltip = builtinResource.m_Name + "\n" + "(Built-in Resource)"; + } + + if (tooltip != null) + { + GUI.Label(labelRect, GUIContent.Temp("", tooltip)); + } + } } } diff --git a/Editor/Mono/Overlays/Overlay.cs b/Editor/Mono/Overlays/Overlay.cs index 25b3939dd2..c880cbb3d4 100644 --- a/Editor/Mono/Overlays/Overlay.cs +++ b/Editor/Mono/Overlays/Overlay.cs @@ -720,7 +720,8 @@ void BuildContextMenu(ContextualMenuPopulateEvent evt) menu.AppendSeparator(); var layouts = supportedLayouts; - if ((layouts & Layout.Panel) != 0) + // Panel layout is always supported by default, we only add this option in the menu if other options are available + if ((layouts & Layout.HorizontalToolbar) != 0 || (layouts & Layout.VerticalToolbar) != 0) menu.AppendAction(L10n.Tr("Panel"), action => { layout = Layout.Panel; collapsed = false; }, GetMenuItemState(layout == Layout.Panel)); if ((layouts & Layout.HorizontalToolbar) != 0) menu.AppendAction(L10n.Tr("Horizontal"), action => { layout = Layout.HorizontalToolbar; collapsed = false; }, GetMenuItemState(layout == Layout.HorizontalToolbar)); diff --git a/Editor/Mono/Prefabs/PrefabOverrides/PrefabOverridesWindow.cs b/Editor/Mono/Prefabs/PrefabOverrides/PrefabOverridesWindow.cs index 8d3bca32e8..54888bc6a9 100644 --- a/Editor/Mono/Prefabs/PrefabOverrides/PrefabOverridesWindow.cs +++ b/Editor/Mono/Prefabs/PrefabOverrides/PrefabOverridesWindow.cs @@ -20,6 +20,7 @@ internal class PrefabOverridesWindow : PopupWindowContent RectOffset k_TreeViewPadding = new RectOffset(0, 0, 4, 4); const float k_HeaderHeight = 60f; const float k_ButtonWidth = 120; + const float k_ButtonWidthVariant = 201; const float k_ButtonWidthPadding = 10; //Padding in case the text is long to give the window a bit of margins around the buttons const float k_HeaderLeftMargin = 6; const float k_NoOverridesLabelHeight = 26f; @@ -629,7 +630,7 @@ void UpdateText(Texture assetIcon, string assetName) if (stage is PrefabStage && PrefabUtility.IsPartOfVariantPrefab(AssetDatabase.LoadAssetAtPath(stage.assetPath))) { applyAllContent = Styles.applyAllToBaseContent; - m_ApplyButtonWidth = GUI.skin.button.CalcSize(applyAllContent).x; + m_ApplyButtonWidth = k_ButtonWidthVariant; applySelectedContent = Styles.applySelectedToBaseContent; } @@ -800,7 +801,7 @@ void DrawRemoveButton(Rect rect) if (GUILayout.Button(Styles.buttonContent, EditorStyles.miniButton, GUILayout.Width(k_ButtonWidth))) { - PrefabUtility.RemovePrefabInstanceUnusedOverrides(m_InstanceOverridesInfos); + PrefabUtility.RemovePrefabInstanceUnusedOverrides(m_InstanceOverridesInfos, InteractionMode.UserAction); editorWindow.Close(); m_Owner.RefreshStatus(); GUIUtility.ExitGUI(); diff --git a/Editor/Mono/Prefabs/PrefabUtility.cs b/Editor/Mono/Prefabs/PrefabUtility.cs index e0720d1fcb..47f510dda3 100644 --- a/Editor/Mono/Prefabs/PrefabUtility.cs +++ b/Editor/Mono/Prefabs/PrefabUtility.cs @@ -372,6 +372,26 @@ public static void ApplyPrefabInstances(GameObject[] instanceRoots, InteractionM Undo.FlushTrackedObjects(); // Needs to be called after StopAssetEditing() to fix UUM-6917 } + public static void RemoveUnusedOverrides(GameObject[] prefabInstances, InteractionMode action) + { + if (prefabInstances == null) + throw new ArgumentNullException(nameof(prefabInstances)); + + HashSet rootSet = new HashSet(prefabInstances.Length); + foreach (var gameObject in prefabInstances) + { + if (gameObject == null) + throw new ArgumentException("Input array contains null elements.", nameof(prefabInstances)); + if (!PrefabUtility.IsPartOfPrefabInstance(gameObject)) + throw new ArgumentException("Input array contains objects which are not part of a Prefab instance.", nameof(prefabInstances)); + + rootSet.Add(PrefabUtility.GetOutermostPrefabInstanceRoot(gameObject)); + } + + InstanceOverridesInfo[] instanceOverridesInfos = rootSet.Select(PrefabUtility.GetPrefabInstanceOverridesInfo_Internal).ToArray(); + PrefabUtility.RemovePrefabInstanceUnusedOverrides(instanceOverridesInfos, action); + } + private static void MapObjectReferencePropertyToSourceIfApplicable(SerializedProperty property, Object prefabSourceObject) { var referencedObject = property.objectReferenceValue; @@ -3514,7 +3534,7 @@ internal static bool HavePrefabInstancesUnusedOverrides(GameObject[] gameObjects internal static InstanceOverridesInfo[] GetPrefabInstancesOverridesInfos(GameObject[] selectedGameObjects) { - if (selectedGameObjects == null || !selectedGameObjects.Any()) + if (selectedGameObjects == null || selectedGameObjects.Length == 0) return new InstanceOverridesInfo[] {}; List allInstanceMods = new List(); @@ -3634,18 +3654,46 @@ internal static bool DoRemovePrefabInstanceUnusedOverridesDialog(InstanceOverrid return false; } - internal static void RemovePrefabInstanceUnusedOverrides(InstanceOverridesInfo[] instanceOverridesInfos) + internal static void RemovePrefabInstanceUnusedOverrides(InstanceOverridesInfo[] instanceOverridesInfos, InteractionMode interactionMode = InteractionMode.UserAction) { - bool updatedEditorLog = false; - foreach (InstanceOverridesInfo ipmods in instanceOverridesInfos) - updatedEditorLog |= PrefabUtility.RemovePrefabInstanceUnusedOverrides(ipmods); + { + if (ipmods.unusedOverrideCount != 0 && interactionMode == InteractionMode.UserAction) + { + Undo.RegisterCompleteObjectUndo(ipmods.instance, "Remove unused overrides"); + } - if (updatedEditorLog) - System.Console.WriteLine(""); + PrefabUtility.RemovePrefabInstanceUnusedOverrides(ipmods, out RemovedModifications removedModificationsCount); + + if (interactionMode == InteractionMode.UserAction) + { + if (removedModificationsCount.unusedMods > 0) + { + LogRemovedPropertyOverrides(ipmods.instance, ipmods.unusedMods); + } + if (removedModificationsCount.unusedRemovedGameObjectCount > 0) + { + LogRemovedUnusedRemovedGameObjects(ipmods.instance, ipmods.unusedRemovedGameObjectCount); + } + if (removedModificationsCount.unusedRemovedComponentCount > 0) + { + LogRemovedUnusedRemovedComponents(ipmods.instance, ipmods.unusedRemovedComponentCount); + } + } + } + + if (interactionMode == InteractionMode.UserAction) + Undo.FlushTrackedObjects(); + } + + private struct RemovedModifications + { + public int unusedMods; + public int unusedRemovedGameObjectCount; + public int unusedRemovedComponentCount; } - private static bool RemovePrefabInstanceUnusedOverrides(InstanceOverridesInfo iovInfo) + private static bool RemovePrefabInstanceUnusedOverrides(InstanceOverridesInfo iovInfo, out RemovedModifications removedModificationsCount) { if (iovInfo.instance == null) throw new ArgumentNullException(nameof(iovInfo), "InstanceOverridesInfo.instance was null"); @@ -3654,30 +3702,32 @@ private static bool RemovePrefabInstanceUnusedOverrides(InstanceOverridesInfo io else if (iovInfo.usedMods == null) throw new ArgumentNullException(nameof(iovInfo), "InstanceOverridesInfo.usedMods was null"); - bool updatedEditorLog = false; - if (iovInfo.unusedOverrideCount != 0) + removedModificationsCount = new RemovedModifications { - Undo.RegisterCompleteObjectUndo(iovInfo.instance, "Remove unused overrides"); + unusedMods = iovInfo.unusedMods.Length, + unusedRemovedGameObjectCount = iovInfo.unusedRemovedGameObjectCount, + unusedRemovedComponentCount = iovInfo.unusedRemovedComponentCount + }; - if (iovInfo.unusedMods.Any()) + bool removedAny = iovInfo.unusedOverrideCount != 0; + if (removedAny) + { + if (iovInfo.unusedMods.Length > 0) { SetPropertyModifications(iovInfo.instance, iovInfo.usedMods); - updatedEditorLog |= PrefabUtility.LogRemovedPropertyOverrides(iovInfo.instance, iovInfo.unusedMods); } if (iovInfo.unusedRemovedGameObjectCount > 0) { PrefabUtility.RemoveRemovedGameObjectOverridesWhichAreNull(iovInfo.instance); - updatedEditorLog |= PrefabUtility.LogRemovedUnusedRemovedGameObjects(iovInfo.instance, iovInfo.unusedRemovedGameObjectCount); } if (iovInfo.unusedRemovedComponentCount > 0) { PrefabUtility.RemoveRemovedComponentOverridesWhichAreInvalid(iovInfo.instance); - updatedEditorLog |= PrefabUtility.LogRemovedUnusedRemovedComponents(iovInfo.instance, iovInfo.unusedRemovedComponentCount); } } - return updatedEditorLog; + return removedAny; } internal static bool LogRemovedPropertyOverrides(GameObject instance, PropertyModification[] mods) diff --git a/Editor/Mono/PreferencesWindow/PreferencesSettingsProviders.cs b/Editor/Mono/PreferencesWindow/PreferencesSettingsProviders.cs index 137e477d0d..9f2620570a 100644 --- a/Editor/Mono/PreferencesWindow/PreferencesSettingsProviders.cs +++ b/Editor/Mono/PreferencesWindow/PreferencesSettingsProviders.cs @@ -88,7 +88,6 @@ class GeneralProperties public static readonly GUIContent enableExtendedLogging = EditorGUIUtility.TrTextContent("Timestamp Editor log entries", "Adds timestamp and thread Id to Editor.log messages."); public static readonly GUIContent enableHelperBar = EditorGUIUtility.TrTextContent("Enable Helper Bar", "Enables Helper Bar in the status bar at the bottom of the main Unity Editor window."); public static readonly GUIContent enablePlayModeTooltips = EditorGUIUtility.TrTextContent("Enable PlayMode Tooltips", "Enables tooltips in the editor while in play mode."); - public static readonly GUIContent contextMenuAutoExpandDelay = EditorGUIUtility.TrTextContent("Context Menu Auto Expand Delay"); } class ExternalProperties @@ -585,7 +584,6 @@ private void ShowGeneral(string searchContext) DrawEnableHelperBar(); DrawEnableTooltipsInPlayMode(); - DrawContextMenuAutoExpandDelay(); EditorGUILayout.Space(); GUILayout.Label(GeneralProperties.hierarchyHeader, EditorStyles.boldLabel); @@ -734,19 +732,6 @@ void DrawEnableTooltipsInPlayMode() } } - void DrawContextMenuAutoExpandDelay() - { - var delay = EditorPrefs.GetFloat(EditorMenuExtensions.k_AutoExpandDelayKeyName, - EditorMenuExtensions.k_SubmenuExpandDelay); - - EditorGUI.BeginChangeCheck(); - delay = EditorGUILayout.Slider(GeneralProperties.contextMenuAutoExpandDelay, delay, 0f, 1.0f); - if (EditorGUI.EndChangeCheck()) - { - EditorPrefs.SetFloat(EditorMenuExtensions.k_AutoExpandDelayKeyName, delay); - } - } - public void ApplyChangesToPrefs(bool force = false) { if (GUI.changed || force) diff --git a/Editor/Mono/ProjectWindow/AssetClipboardUtility.cs b/Editor/Mono/ProjectWindow/AssetClipboardUtility.cs index 93b462d693..92900234ac 100644 --- a/Editor/Mono/ProjectWindow/AssetClipboardUtility.cs +++ b/Editor/Mono/ProjectWindow/AssetClipboardUtility.cs @@ -93,72 +93,55 @@ static IEnumerable PasteCopiedAssets(string destination = null) Object firstDuplicatedObjectToFail = null; List pastedObjects = new List(); - try + foreach (var item in assetClipboard) { - // StartAssetEditing begins a batch operation on the AssetDatabase - // such that we don't do any actual imports until - // AssetDatabase.StopAssetImporting is called. - // This means that while function calls are allowed to happen - // imports won't actually happen until AssetDatabase.StopAssetImporting - // is called below. - AssetDatabase.StartAssetEditing(); - - // We batch the copies here - foreach (var item in assetClipboard) - { - var asset = ObjectIdentifier.ToObject(item); - var assetPath = AssetDatabase.GetAssetPath(asset); + var asset = ObjectIdentifier.ToObject(item); + var assetPath = AssetDatabase.GetAssetPath(asset); - // if duplicating a sub-asset, then create a copy next to the main asset file - if (asset != null && !String.IsNullOrEmpty(assetPath) && AssetDatabase.IsSubAsset(asset)) + // if duplicating a sub-asset, then create a copy next to the main asset file + if (asset != null && !String.IsNullOrEmpty(assetPath) && AssetDatabase.IsSubAsset(asset)) + { + if (asset is ISubAssetNotDuplicatable || asset is GameObject) { - if (asset is ISubAssetNotDuplicatable || asset is GameObject) - { - firstDuplicatedObjectToFail = firstDuplicatedObjectToFail ? firstDuplicatedObjectToFail : asset; - continue; - } - - var extension = NativeFormatImporterUtility.GetExtensionForAsset(asset); - - // We dot sanitize or block unclean the asset filename (asset.name) - // since the assertdb will do it for us and has a whole tailored logic for that. - - // It feels wrong that the asset name (that can apparently contain any char) - // is conflated with the orthogonal notion of filename. From the user's POV - // it will force an asset dup but with mangled names if the original name contained - // "invalid chars" for filenames. - // Path.Combine is not used here to avoid blocking asset names that might - // contain chars not allowed in filenames. - if ((new HashSet(Path.GetInvalidFileNameChars())).Intersect(asset.name).Count() != 0) - { - Debug.LogWarning(string.Format("Duplicated asset name '{0}' contains invalid characters. Those will be replaced in the duplicated asset name.", asset.name)); - } - - var newPath = AssetDatabase.GenerateUniqueAssetPath( - string.Format("{0}{1}{2}.{3}", - Path.GetDirectoryName(assetPath), - Path.DirectorySeparatorChar, - asset.name, - extension) - ); - - assetPath = GetValidPath(newPath, destination); - AssetDatabase.CreateAsset(Object.Instantiate(asset), assetPath); - pastedObjects.Add(assetPath); + firstDuplicatedObjectToFail = firstDuplicatedObjectToFail ? firstDuplicatedObjectToFail : asset; + continue; } - // Otherwise duplicate the main asset file - else if (EditorUtility.IsPersistent(asset)) + + var extension = NativeFormatImporterUtility.GetExtensionForAsset(asset); + + // We dot sanitize or block unclean the asset filename (asset.name) + // since the assertdb will do it for us and has a whole tailored logic for that. + + // It feels wrong that the asset name (that can apparently contain any char) + // is conflated with the orthogonal notion of filename. From the user's POV + // it will force an asset dup but with mangled names if the original name contained + // "invalid chars" for filenames. + // Path.Combine is not used here to avoid blocking asset names that might + // contain chars not allowed in filenames. + if ((new HashSet(Path.GetInvalidFileNameChars())).Intersect(asset.name).Count() != 0) { - var newPath = AssetDatabase.GenerateUniqueAssetPath(GetValidPath(assetPath, destination)); - if (newPath.Length > 0 && AssetDatabase.CopyAsset(assetPath, newPath)) - pastedObjects.Add(newPath); + Debug.LogWarning(string.Format("Duplicated asset name '{0}' contains invalid characters. Those will be replaced in the duplicated asset name.", asset.name)); } + + var newPath = AssetDatabase.GenerateUniqueAssetPath( + string.Format("{0}{1}{2}.{3}", + Path.GetDirectoryName(assetPath), + Path.DirectorySeparatorChar, + asset.name, + extension) + ); + + assetPath = GetValidPath(newPath, destination); + AssetDatabase.CreateAsset(Object.Instantiate(asset), assetPath); + pastedObjects.Add(assetPath); + } + // Otherwise duplicate the main asset file + else if (EditorUtility.IsPersistent(asset)) + { + var newPath = AssetDatabase.GenerateUniqueAssetPath(GetValidPath(assetPath, destination)); + if (newPath.Length > 0 && AssetDatabase.CopyAsset(assetPath, newPath)) + pastedObjects.Add(newPath); } - } - finally - { - // Batch import all assets that were created & copied in the try{} block above - AssetDatabase.StopAssetEditing(); } if (firstDuplicatedObjectToFail != null) diff --git a/Editor/Mono/ProjectWindow/ProjectWindowUtil.cs b/Editor/Mono/ProjectWindow/ProjectWindowUtil.cs index 34f46ae95c..3a487fca90 100644 --- a/Editor/Mono/ProjectWindow/ProjectWindowUtil.cs +++ b/Editor/Mono/ProjectWindow/ProjectWindowUtil.cs @@ -382,7 +382,7 @@ static void CreatePrefabVariant() string sourcePath = AssetDatabase.GetAssetPath(go); string sourceDir = Path.GetDirectoryName(sourcePath).ConvertSeparatorsToUnity(); - string variantPath = GetPrefabVariantPath(sourceDir, go.name); + string variantPath = GetPrefabVariantPath(sourceDir, go); StartNameEditingIfProjectWindowExists( 0, @@ -413,7 +413,7 @@ static GameObject[] CreatePrefabVariants(GameObject[] gameObjects) { string sourcePath = AssetDatabase.GetAssetPath(go); string sourceDir = Path.GetDirectoryName(sourcePath).ConvertSeparatorsToUnity(); - string variantPath = GetPrefabVariantPath(sourceDir, go.name); + string variantPath = GetPrefabVariantPath(sourceDir, go); variantPath = AssetDatabase.GenerateUniqueAssetPath(variantPath); var variant = PrefabUtility.CreateVariant(go, variantPath); @@ -430,9 +430,12 @@ static GameObject[] CreatePrefabVariants(GameObject[] gameObjects) return createdVariants.ToArray(); } - static string GetPrefabVariantPath(string folder, string gameObjectName) + static string GetPrefabVariantPath(string folder, GameObject gameObject) { - return string.Format("{0}/{1} Variant.prefab", folder, gameObjectName); + if (PrefabUtility.IsPartOfModelPrefab(gameObject)) + return string.Format("{0}/{1}.prefab", folder, gameObject.name); + else + return string.Format("{0}/{1} Variant.prefab", folder, gameObject.name); } public static void CreateAssetWithContent(string filename, string content, Texture2D icon = null) diff --git a/Editor/Mono/RenderPipelineGraphicsSettingsContainerPropertyDrawer.cs b/Editor/Mono/RenderPipelineGraphicsSettingsContainerPropertyDrawer.cs deleted file mode 100644 index 6307133582..0000000000 --- a/Editor/Mono/RenderPipelineGraphicsSettingsContainerPropertyDrawer.cs +++ /dev/null @@ -1,92 +0,0 @@ -// 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.ComponentModel; -using System.Reflection; -using UnityEditor.UIElements; -using UnityEngine.Rendering; -using UnityEngine.UIElements; - -namespace UnityEditor.Rendering -{ - [CustomPropertyDrawer(typeof(RenderPipelineGraphicsSettingsContainer))] - class RenderPipelineGraphicsSettingsContainerPropertyDrawer : PropertyDrawer - { - //internal is for tests - internal static SortedDictionary> Categorize(SerializedProperty property) - { - SortedDictionary> categories = new(); - foreach(SerializedProperty prop in property.Copy()) - { - var type = prop.boxedValue.GetType(); - - //remove array length property - if (!typeof(IRenderPipelineGraphicsSettings).IsAssignableFrom(type)) - continue; - - var typeName = ObjectNames.NicifyVariableName(type.Name); - string name = type.GetCustomAttribute()?.Category ?? typeName; - - //sort per type in category - if (categories.TryGetValue(name, out var categoryElement)) - { - if (categoryElement.ContainsKey(typeName)) - UnityEngine.Debug.LogWarning($"{nameof(IRenderPipelineGraphicsSettings)} {typeName} is duplicated. Only showing first one."); - else - categoryElement.Add(typeName, new PropertyField(prop)); - continue; - } - - //sort per category - categories.Add(name, new SortedDictionary() - { - { typeName, new PropertyField(prop) } - }); - } - return categories; - } - - - public override VisualElement CreatePropertyGUI(SerializedProperty property) - { - var root = new VisualElement { name = "GlobalSettingsList" }; - var graphicsSettings = property.FindPropertyRelative("m_SettingsList"); - UnityEngine.Debug.Assert(graphicsSettings != null); - - foreach (var category in Categorize(graphicsSettings)) - { - var foldout = new Foldout() { text = category.Key }; - foreach(var element in category.Value) - foldout.Add(element.Value); - root.Add(foldout); - } - - return root; - } - } - - //The purpose is to remove the foldout drown from the ISRPGraphicsSetting itself, - //only if there is no dedicated CustomPropertyDrawer. - [CustomPropertyDrawer(typeof(IRenderPipelineGraphicsSettings), useForChildren: true)] - class ISRPGraphicsSettingPropertyDrawer : PropertyDrawer - { - public override VisualElement CreatePropertyGUI(SerializedProperty property) - { - VisualElement root = new(); - - bool atLeastOneChild = false; - foreach(SerializedProperty prop in property.Copy()) - { - atLeastOneChild = true; - root.Add(new PropertyField(prop)); - } - - if (!atLeastOneChild) - root.Add(new Label($"This {nameof(IRenderPipelineGraphicsSettings)} is empty.")); - - return root; - } - } -} diff --git a/Editor/Mono/SceneHierarchy.cs b/Editor/Mono/SceneHierarchy.cs index db5d4f70a7..95d344bba6 100644 --- a/Editor/Mono/SceneHierarchy.cs +++ b/Editor/Mono/SceneHierarchy.cs @@ -14,6 +14,7 @@ using UnityEditorInternal; using UnityEngine.Assertions; using System.Text; +using UnityEngine.UIElements; namespace UnityEditor { @@ -78,7 +79,7 @@ public bool hasCustomScenes const int kInvalidSceneHandle = 0; bool m_RectSelectInProgress; - TreeViewController m_TreeView; + IMGUI.Controls.TreeViewController m_TreeView; [SerializeField] TreeViewState m_TreeViewState; @@ -219,7 +220,7 @@ internal TreeViewState treeViewState get { return m_TreeViewState; } } - internal TreeViewController treeView + internal IMGUI.Controls.TreeViewController treeView { get { @@ -262,7 +263,7 @@ void Init() if (m_SortingObjects == null) SetUpSortMethodLists(); - m_TreeView = new TreeViewController(m_EditorWindow, m_TreeViewState); + m_TreeView = new IMGUI.Controls.TreeViewController(m_EditorWindow, m_TreeViewState); m_TreeView.itemDoubleClickedCallback += TreeViewItemDoubleClicked; m_TreeView.selectionChangedCallback += TreeViewSelectionChanged; m_TreeView.onGUIRowCallback += OnRowGUICallback; @@ -1159,89 +1160,8 @@ void CreateSubSceneGameObjectContextClick(GenericMenu menu, int contextClickedIt menu.AddItem(EditorGUIUtility.TrTextContent("Delete GameObject"), false, DeleteGO); } - void CreateGameObjectContextClick(GenericMenu menu, int contextClickedItemID) + void PopulateGenericMenuWithPrefabMenuItems(GenericMenu menu, int contextClickedItemID) { - bool itemIsSelected = Selection.gameObjects.Length > 0; - // If the custom parent for new objects is set we don't allow cut, copy or duplicate any ancestors of this object - // as it might be context objects (as in Prefab Mode in Context). - bool allowCutCopyAndDuplicate = m_CustomParentForNewGameObjects == null - || !IsChildOfSelectionOrSelected(m_CustomParentForNewGameObjects.parent); - - if (itemIsSelected && allowCutCopyAndDuplicate) - menu.AddItem(EditorGUIUtility.TrTextContent("Cut"), false, ClipboardUtility.CutGO); - else - menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Cut")); - if (itemIsSelected && allowCutCopyAndDuplicate) - menu.AddItem(EditorGUIUtility.TrTextContent("Copy"), false, ClipboardUtility.CopyGO); - else - menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Copy")); - if (CutBoard.CanGameObjectsBePasted() || Unsupported.CanPasteGameObjectsFromPasteboard()) - menu.AddItem(EditorGUIUtility.TrTextContent("Paste"), false, PasteGO); - else - menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Paste")); - if (ClipboardUtility.CanPasteAsChild()) - menu.AddItem(EditorGUIUtility.TrTextContent("Paste As Child"), false, ClipboardUtility.PasteGOAsChild); - else - menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Paste As Child")); - - menu.AddSeparator(""); - - if (itemIsSelected && !hasSearchFilter && m_TreeViewState.selectedIDs.Count == 1 && !GetIsNotEditable()) - menu.AddItem(EditorGUIUtility.TrTextContent("Rename"), false, RenameGO); - else - menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Rename")); - - if (itemIsSelected && allowCutCopyAndDuplicate) - menu.AddItem(EditorGUIUtility.TrTextContent("Duplicate"), false, DuplicateGO); - else - menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Duplicate")); - - if (m_CustomParentForNewGameObjects != null && IsChildOfSelectionOrSelected(m_CustomParentForNewGameObjects) || !itemIsSelected) - menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Delete")); - else - menu.AddItem(EditorGUIUtility.TrTextContent("Delete"), false, DeleteGO); - - - menu.AddSeparator(""); - - if (IsSelectChildrenAvailable()) - menu.AddItem(EditorGUIUtility.TrTextContent("Select Children"), false, SelectChildren); - else - menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Select Children")); - - menu.AddSeparator(""); - if (Selection.activeGameObject) - { - menu.AddItem(EditorGUIUtility.TrTextContent("Find References in Scene"), false, FindReferenceInScene); - } - else - { - menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Find References in Scene")); - } - - menu.AddSeparator(""); - - GameObject selectedObject = null; - if (Selection.objects.Length > 0) - selectedObject = Selection.objects[Selection.objects.Length - 1] as GameObject; - - if (Selection.count == 0 && treeView.hoveredItem == null || selectedObject && (selectedObject.name == PrefabUtility.kDummyPrefabStageRootObjectName || PrefabStageUtility.IsGameObjectThePrefabRootInAnyPrefabStage(selectedObject))) - { - menu.AddDisabledItem(Styles.setOriginLabel); - } - else if (selectedObject && (selectedObject.GetInstanceID() != GetDefaultParentForSession(selectedObject.scene.guid) || EditorSceneManager.GetActiveScene().guid != selectedObject.scene.guid)) - { - menu.AddItem(Styles.setOriginLabel, false, () => - { - SetDefaultParentObject(false); - }); - } - else - { - menu.AddItem(Styles.clearOriginLabel, false, () => { ClearDefaultParentObject(); }); - } - - // Prefab menu items that only make sense if a single object is selected. GameObject go = null; string assetPath = null; GameObject prefabAsset = null; @@ -1367,6 +1287,92 @@ void CreateGameObjectContextClick(GenericMenu menu, int contextClickedItemID) menu.AddSeparator("Prefab/"); menu.AddItem(EditorGUIUtility.TrTextContent("Prefab/Remove Unused Overrides..."), false, RemoveSelectedPrefabInstanceUnusedOverrides); } + } + + void CreateGameObjectContextClick(GenericMenu menu, int contextClickedItemID) + { + bool itemIsSelected = Selection.gameObjects.Length > 0; + // If the custom parent for new objects is set we don't allow cut, copy or duplicate any ancestors of this object + // as it might be context objects (as in Prefab Mode in Context). + bool allowCutCopyAndDuplicate = m_CustomParentForNewGameObjects == null + || !IsChildOfSelectionOrSelected(m_CustomParentForNewGameObjects.parent); + + if (itemIsSelected && allowCutCopyAndDuplicate) + menu.AddItem(EditorGUIUtility.TrTextContent("Cut"), false, ClipboardUtility.CutGO); + else + menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Cut")); + if (itemIsSelected && allowCutCopyAndDuplicate) + menu.AddItem(EditorGUIUtility.TrTextContent("Copy"), false, ClipboardUtility.CopyGO); + else + menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Copy")); + if (CutBoard.CanGameObjectsBePasted() || Unsupported.CanPasteGameObjectsFromPasteboard()) + menu.AddItem(EditorGUIUtility.TrTextContent("Paste"), false, PasteGO); + else + menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Paste")); + if (ClipboardUtility.CanPasteAsChild()) + menu.AddItem(EditorGUIUtility.TrTextContent("Paste As Child"), false, ClipboardUtility.PasteGOAsChild); + else + menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Paste As Child")); + + menu.AddSeparator(""); + + if (itemIsSelected && !hasSearchFilter && m_TreeViewState.selectedIDs.Count == 1 && !GetIsNotEditable()) + menu.AddItem(EditorGUIUtility.TrTextContent("Rename"), false, RenameGO); + else + menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Rename")); + + if (itemIsSelected && allowCutCopyAndDuplicate) + menu.AddItem(EditorGUIUtility.TrTextContent("Duplicate"), false, DuplicateGO); + else + menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Duplicate")); + + if (m_CustomParentForNewGameObjects != null && IsChildOfSelectionOrSelected(m_CustomParentForNewGameObjects) || !itemIsSelected) + menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Delete")); + else + menu.AddItem(EditorGUIUtility.TrTextContent("Delete"), false, DeleteGO); + + + menu.AddSeparator(""); + + if (IsSelectChildrenAvailable()) + menu.AddItem(EditorGUIUtility.TrTextContent("Select Children"), false, SelectChildren); + else + menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Select Children")); + + menu.AddSeparator(""); + if (Selection.activeGameObject) + { + menu.AddItem(EditorGUIUtility.TrTextContent("Find References in Scene"), false, FindReferenceInScene); + } + else + { + menu.AddDisabledItem(EditorGUIUtility.TrTextContent("Find References in Scene")); + } + + menu.AddSeparator(""); + + GameObject selectedObject = null; + if (Selection.objects.Length > 0) + selectedObject = Selection.objects[Selection.objects.Length - 1] as GameObject; + + if (Selection.count == 0 && treeView.hoveredItem == null || selectedObject && (selectedObject.name == PrefabUtility.kDummyPrefabStageRootObjectName || PrefabStageUtility.IsGameObjectThePrefabRootInAnyPrefabStage(selectedObject))) + { + menu.AddDisabledItem(Styles.setOriginLabel); + } + else if (selectedObject && (selectedObject.GetInstanceID() != GetDefaultParentForSession(selectedObject.scene.guid) || EditorSceneManager.GetActiveScene().guid != selectedObject.scene.guid)) + { + menu.AddItem(Styles.setOriginLabel, false, () => + { + SetDefaultParentObject(false); + }); + } + else + { + menu.AddItem(Styles.clearOriginLabel, false, () => { ClearDefaultParentObject(); }); + } + + // Prefab menu items that only make sense if a single object is selected. + PopulateGenericMenuWithPrefabMenuItems(menu, contextClickedItemID); GameObject[] selectedGameObjects = Selection.transforms.Select(t => t.gameObject).ToArray(); @@ -1400,6 +1406,28 @@ void CreateGameObjectContextClick(GenericMenu menu, int contextClickedItemID) } } + internal void PopulateDropdownMenuWithPrefabMenuItems(DropdownMenu menu) + { + if (m_TreeViewState.selectedIDs.Count != 1) return; + + int selectedGameObjectInstanceID = m_TreeViewState.selectedIDs[0]; + GenericMenu genericMenuForGettingPrefabItems = new GenericMenu(); + PopulateGenericMenuWithPrefabMenuItems(genericMenuForGettingPrefabItems, selectedGameObjectInstanceID); + + for (int i = 1, prefabMenuItemsCount = genericMenuForGettingPrefabItems.menuItems.Count; i < prefabMenuItemsCount; ++i) + { + GenericMenu.MenuItem menuItem = genericMenuForGettingPrefabItems.menuItems[i]; + if (menuItem.separator) + menu.AppendSeparator(menuItem.content.text); + else if (menuItem.func2 != null && menuItem.userData != null) + menu.AppendAction(menuItem.content.text, action => { menuItem.func2(menuItem.userData); }); + else if (menuItem.func != null) + menu.AppendAction(menuItem.content.text, action => { menuItem.func(); }); + else + menu.AppendAction(menuItem.content.text, action => { }, DropdownMenuAction.Status.Disabled); + } + } + private void FindReferenceInScene() { var selectedObject = Selection.activeObject; @@ -1754,7 +1782,7 @@ bool AskUserToRemovePrefabInstanceUnusedOverrides(PrefabUtility.InstanceOverride { if (PrefabUtility.DoRemovePrefabInstanceUnusedOverridesDialog(instanceOverridesInfos)) { - PrefabUtility.RemovePrefabInstanceUnusedOverrides(instanceOverridesInfos); + PrefabUtility.RemovePrefabInstanceUnusedOverrides(instanceOverridesInfos, InteractionMode.UserAction); return true; } diff --git a/Editor/Mono/SceneModeWindows/LightingWindow.cs b/Editor/Mono/SceneModeWindows/LightingWindow.cs index d045c6b5ed..31d2f958f5 100644 --- a/Editor/Mono/SceneModeWindows/LightingWindow.cs +++ b/Editor/Mono/SceneModeWindows/LightingWindow.cs @@ -553,7 +553,7 @@ void DrawBottomBarGUI(Mode selectedMode) { // Bake button if we are not currently baking - bool showBakeButton = Lightmapping.isInteractive || !Lightmapping.isRunning; + bool showBakeButton = Lightmapping.shouldBakeInteractively || !Lightmapping.isRunning; if (showBakeButton) { bool anythingCompiling = ShaderUtil.anythingCompiling; diff --git a/Editor/Mono/SceneModeWindows/LightingWindowLightmapPreviewTab.cs b/Editor/Mono/SceneModeWindows/LightingWindowLightmapPreviewTab.cs index c43e9cf357..35ab499d37 100644 --- a/Editor/Mono/SceneModeWindows/LightingWindowLightmapPreviewTab.cs +++ b/Editor/Mono/SceneModeWindows/LightingWindowLightmapPreviewTab.cs @@ -136,7 +136,7 @@ public void OnGUI() else { var interactiveLightmaps = InteractiveLightBaking.GetLightmapData(); - if (interactiveLightmaps.numLightmaps > 0 && Lightmapping.isInteractive) // Hide preview lightmaps when not in preview mode + if (interactiveLightmaps.numLightmaps > 0 && Lightmapping.shouldBakeInteractively) // Hide preview lightmaps when not in preview mode { m_ShowPreviewLightmaps.value = EditorGUILayout.Foldout(m_ShowPreviewLightmaps.value, Styles.PreviewLightmapsFoldoutTitle); diff --git a/Editor/Mono/SceneView/SceneOrientationGizmo.cs b/Editor/Mono/SceneView/SceneOrientationGizmo.cs index 56d1a591ca..7d08cf7ce4 100644 --- a/Editor/Mono/SceneView/SceneOrientationGizmo.cs +++ b/Editor/Mono/SceneView/SceneOrientationGizmo.cs @@ -566,7 +566,6 @@ public override void OnGUI() // Usually this does nothing, but when dragging the window between monitors that have different scaling // it can resize the RT as necessary. SetupRenderTexture(); - var prevCamera = Camera.current; SetupCamera(view); //Handle transparency when rotation is locked @@ -601,8 +600,6 @@ public override void OnGUI() labelRect.y += gizmoRect.height; DrawLabels(view, labelRect); } - - Camera.SetupCurrent(prevCamera); } void DoOrientationHandles(SceneView view, Camera camera, bool isMouseHovering) diff --git a/Editor/Mono/SceneView/SceneView.cs b/Editor/Mono/SceneView/SceneView.cs index b1fdd5c203..5a1db3bcce 100644 --- a/Editor/Mono/SceneView/SceneView.cs +++ b/Editor/Mono/SceneView/SceneView.cs @@ -94,7 +94,7 @@ internal static DrawCameraMode[] GetInteractiveDrawCameraModeValues() return drawCameraModes.ToArray(); } - internal static bool IsInteractiveBakingEnabled() + internal static bool NeedsInteractiveBaking() { foreach (SceneView sceneView in SceneView.sceneViews) { @@ -2495,7 +2495,6 @@ void DoOnGUI() EditorGUIUtility.labelWidth = 100; - var prevCamera = Camera.current; SetupCamera(); RenderingPath oldRenderingPath = m_Camera.renderingPath; @@ -2667,8 +2666,6 @@ void DoOnGUI() onGUIEnded?.Invoke(this); if (m_StageHandling != null) m_StageHandling.EndOnGUI(); - - Camera.SetupCurrent(prevCamera); } // This will eventually be modified to use the mouse right-click. diff --git a/Editor/Mono/ScriptAttributeGUI/DecoratorDrawer.cs b/Editor/Mono/ScriptAttributeGUI/DecoratorDrawer.cs index e639d6b154..88c1be6474 100644 --- a/Editor/Mono/ScriptAttributeGUI/DecoratorDrawer.cs +++ b/Editor/Mono/ScriptAttributeGUI/DecoratorDrawer.cs @@ -2,6 +2,7 @@ // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License +using System; using UnityEngine; using UnityEngine.UIElements; @@ -34,6 +35,7 @@ public virtual float GetHeight() return EditorGUI.kSingleLineHeight; } + [Obsolete("CanCacheInspectorGUI has been deprecated and is no longer used.", false)] public virtual bool CanCacheInspectorGUI() { return true; diff --git a/Editor/Mono/ScriptAttributeGUI/PropertyDrawer.cs b/Editor/Mono/ScriptAttributeGUI/PropertyDrawer.cs index 5dc8cc3d84..6740a3c378 100644 --- a/Editor/Mono/ScriptAttributeGUI/PropertyDrawer.cs +++ b/Editor/Mono/ScriptAttributeGUI/PropertyDrawer.cs @@ -2,6 +2,7 @@ // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License +using System; using System.Reflection; using UnityEngine; using UnityEngine.UIElements; @@ -59,14 +60,7 @@ public virtual float GetPropertyHeight(SerializedProperty property, GUIContent l return EditorGUI.kSingleLineHeight; } - internal bool CanCacheInspectorGUISafe(SerializedProperty property) - { - ScriptAttributeUtility.s_DrawerStack.Push(this); - bool canCache = CanCacheInspectorGUI(property); - ScriptAttributeUtility.s_DrawerStack.TryPop(out _); - return canCache; - } - + [Obsolete("CanCacheInspectorGUI has been deprecated and is no longer used.", false)] public virtual bool CanCacheInspectorGUI(SerializedProperty property) { return true; diff --git a/Editor/Mono/ScriptAttributeGUI/PropertyHandler.cs b/Editor/Mono/ScriptAttributeGUI/PropertyHandler.cs index 39e08689a1..dd56d8058e 100644 --- a/Editor/Mono/ScriptAttributeGUI/PropertyHandler.cs +++ b/Editor/Mono/ScriptAttributeGUI/PropertyHandler.cs @@ -373,45 +373,6 @@ public float GetHeight(SerializedProperty property, GUIContent label, bool inclu return height; } - public bool CanCacheInspectorGUI(SerializedProperty property) - { - if (m_DecoratorDrawers != null && - !isCurrentlyNested && - m_DecoratorDrawers.Any(decorator => !decorator.CanCacheInspectorGUI())) - return false; - - if (propertyDrawer != null) - { - // Retrieve drawer BEFORE increasing nesting. - PropertyDrawer drawer = propertyDrawer; - using (var nestingContext = IncrementNestingContext()) - { - return drawer.CanCacheInspectorGUISafe(property.Copy()); - } - } - - property = property.Copy(); - - bool childrenAreExpanded = property.isExpanded && EditorGUI.HasVisibleChildFields(property); - - // Loop through all child properties - if (childrenAreExpanded) - { - PropertyHandler handler = null; - SerializedProperty endProperty = property.GetEndProperty(); - while (property.NextVisible(childrenAreExpanded) && !SerializedProperty.EqualContents(property, endProperty)) - { - if (handler == null) - handler = ScriptAttributeUtility.GetHandler(property); - if (!handler.CanCacheInspectorGUI(property)) - return false; - childrenAreExpanded = false; - } - } - - return true; - } - public void AddMenuItems(SerializedProperty property, GenericMenu menu) { if (contextMenuItems != null) diff --git a/Editor/Mono/ScriptAttributeGUI/PropertyTrait.cs b/Editor/Mono/ScriptAttributeGUI/PropertyTrait.cs index 5fa7049c2c..aec6b76330 100644 --- a/Editor/Mono/ScriptAttributeGUI/PropertyTrait.cs +++ b/Editor/Mono/ScriptAttributeGUI/PropertyTrait.cs @@ -28,10 +28,5 @@ public override float GetPropertyHeight(SerializedProperty property, GUIContent { return EditorGUI.GetPropertyHeight(property, true); } - - public override bool CanCacheInspectorGUI(SerializedProperty property) - { - return EditorGUI.CanCacheInspectorGUI(property); - } } } diff --git a/Editor/Mono/UIElements/Bindings/BindingStyleHelpers.cs b/Editor/Mono/UIElements/Bindings/BindingStyleHelpers.cs index 531ed1d4ba..b04821390e 100644 --- a/Editor/Mono/UIElements/Bindings/BindingStyleHelpers.cs +++ b/Editor/Mono/UIElements/Bindings/BindingStyleHelpers.cs @@ -34,10 +34,10 @@ private static void UpdateElementRecursively(VisualElement element, SerializedPr { VisualElement elementToUpdate = element; - if (element is Foldout) + if (element is Foldout foldout) { // We only want to apply override styles onto the Foldout header, not the entire contents. - elementToUpdate = element.Q(className: Foldout.toggleUssClassName); + elementToUpdate = foldout.toggle; } else if (element.ClassListContains(BaseCompositeField.ussClassName) || element is BoundsField || element is BoundsIntField) @@ -53,7 +53,10 @@ private static void UpdateElementRecursively(VisualElement element, SerializedPr var compositeField = element; // The element we style in the main pass is going to be just the label. - elementToUpdate = element.Q(className: BaseField.labelUssClassName); + if (element is IPrefixLabel prefixLabel) + elementToUpdate = prefixLabel.labelElement; + else + elementToUpdate = element.Q(className: BaseField.labelUssClassName); // Go through the inputs and find any that match the names of the child PropertyFields. var propCopy = prop.Copy(); diff --git a/Editor/Mono/UIElements/Controls/EnumFlagsField.cs b/Editor/Mono/UIElements/Controls/EnumFlagsField.cs index e259a87a6d..7d167644ec 100644 --- a/Editor/Mono/UIElements/Controls/EnumFlagsField.cs +++ b/Editor/Mono/UIElements/Controls/EnumFlagsField.cs @@ -259,9 +259,15 @@ internal void PopulateDataFromType(Type enumType) internal override string GetNothingName() { - if (m_EnumData.flagValues != null && m_EnumData.flagValues.Length > 0 && m_EnumData.flagValues[0] == 0) + if (m_EnumData.flagValues is not { Length: > 0 }) + return base.GetNothingName(); + + for (var i = 0; i < m_EnumData.flagValues.Length; i++) { - return m_EnumData.displayNames[0]; + if (m_EnumData.flagValues[i] == 0) + { + return m_EnumData.displayNames[i]; + } } return base.GetNothingName(); @@ -269,10 +275,15 @@ internal override string GetNothingName() internal override string GetEverythingName() { - if (m_EnumData.flagValues != null && m_EnumData.flagValues.Length > 0 - && m_EnumData.flagValues[^1] == ~0) + if (m_EnumData.flagValues is not { Length: > 0 }) + return base.GetEverythingName(); + + for (var i = 0; i < m_EnumData.flagValues.Length; i++) { - return m_EnumData.displayNames[^1]; + if (m_EnumData.flagValues[i] == ~0) + { + return m_EnumData.displayNames[i]; + } } return base.GetEverythingName(); diff --git a/Editor/Mono/UIElements/Controls/MaskField.cs b/Editor/Mono/UIElements/Controls/MaskField.cs index 3edb9bcf7a..20198f4cfb 100644 --- a/Editor/Mono/UIElements/Controls/MaskField.cs +++ b/Editor/Mono/UIElements/Controls/MaskField.cs @@ -257,7 +257,7 @@ internal string GetDisplayedValue(int itemIndex) // Find the actual index of the selected choice... foreach (int itemMask in m_UserChoicesMasks) { - if ((itemMask & itemIndex) == itemIndex) + if (itemMask != ~0 && ((itemMask & itemIndex) == itemIndex)) { indexOfValue = m_UserChoicesMasks.IndexOf(itemMask); break; diff --git a/Editor/Mono/UIElements/Controls/PropertyField.cs b/Editor/Mono/UIElements/Controls/PropertyField.cs index 83cfdf1f8f..defe570261 100644 --- a/Editor/Mono/UIElements/Controls/PropertyField.cs +++ b/Editor/Mono/UIElements/Controls/PropertyField.cs @@ -19,6 +19,7 @@ public class PropertyField : VisualElement, IBindable private static readonly Regex s_MatchPPtrTypeName = new Regex(@"PPtr\<(\w+)\>"); internal static readonly string foldoutTitleBoundLabelProperty = "unity-foldout-bound-title"; internal static readonly string decoratorDrawersContainerClassName = "unity-decorator-drawers-container"; + internal static readonly string listViewBoundFieldProperty = "unity-list-view-property-field-bound"; static readonly string listViewNamePrefix = "unity-list-"; [UnityEngine.Internal.ExcludeFromDocs, Serializable] @@ -712,6 +713,7 @@ VisualElement ConfigureListView(ListView listView, SerializedProperty property, listView.bindingPath = property.propertyPath; listView.viewDataKey = listViewName; listView.name = listViewName; + listView.SetProperty(listViewBoundFieldProperty, this); // Make list view foldout react even when disabled, like EditorGUILayout.Foldout. var toggle = listView.Q(className: Foldout.toggleUssClassName); diff --git a/Editor/Mono/UIElements/EditorMenuExtensions.cs b/Editor/Mono/UIElements/EditorMenuExtensions.cs index 9c8aaa8b04..766df003d2 100644 --- a/Editor/Mono/UIElements/EditorMenuExtensions.cs +++ b/Editor/Mono/UIElements/EditorMenuExtensions.cs @@ -32,8 +32,7 @@ public class DropdownMenuDescriptor public static class EditorMenuExtensions { const float k_MaxMenuWidth = 512.0f; - internal const string k_AutoExpandDelayKeyName = "ContextMenuAutoExpandDelay"; - internal const float k_SubmenuExpandDelay = 0.4f; + internal const float k_SubmenuExpandDelay = 0.35f; internal const string k_SearchShortcutId = "Main Menu/Edit/Find"; internal static readonly Rect k_InvalidRect = new(0, 0, -1, -1); @@ -433,14 +432,17 @@ void ResetHighlighting(GenericDropdownMenu.MenuItem root) } var menu = search.userData as GenericDropdownMenu; + var newValue = Regex.Replace(e.newValue, "[^\\w ]+", ""); + search.SetValueWithoutNotify(newValue); + ResetHighlighting(menu.root); if (string.IsNullOrWhiteSpace(menu.current.name)) menu.NavigateBack(false); // Allow whitespace so we can search for spaces too - if (!string.IsNullOrEmpty(e.newValue)) - menu.NavigateTo(BuildSearchMenu(e.newValue, menu.current)); + if (!string.IsNullOrEmpty(newValue)) + menu.NavigateTo(BuildSearchMenu(newValue, menu.current)); // Workaround for getting window content stretching artifacts // when resizing to fit search results on Mac. @@ -644,8 +646,7 @@ bool ValidateExpansion() menu.m_OnBeforePerformAction?.Invoke(item.isSubmenu, menu.autoClose); item.PerformAction(); } - }, EditorPrefs.GetFloat(EditorMenuExtensions.k_AutoExpandDelayKeyName, - EditorMenuExtensions.k_SubmenuExpandDelay)); + }, k_SubmenuExpandDelay); if (!item.isCustomContent) s_CachedRect = GUIUtility.GUIToScreenRect(item.element.worldBound); diff --git a/Modules/Accessibility/AssemblyInfo.cs b/Modules/Accessibility/AssemblyInfo.cs index 3ed396d7e0..2a71def2ea 100644 --- a/Modules/Accessibility/AssemblyInfo.cs +++ b/Modules/Accessibility/AssemblyInfo.cs @@ -5,6 +5,7 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("UnityEngine.AccessibilityModule.EditModeTests")] +[assembly: InternalsVisibleTo("UnityEditor.AccessibilityModule")] // TOLERATED: modules or core packages evolving in lockstep with this module // Reducing this list means to improve the API design of this module. diff --git a/Modules/Accessibility/Bindings/AccessibilityManager.bindings.cs b/Modules/Accessibility/Bindings/AccessibilityManager.bindings.cs index 44ef3cead2..8ce97bfc9d 100644 --- a/Modules/Accessibility/Bindings/AccessibilityManager.bindings.cs +++ b/Modules/Accessibility/Bindings/AccessibilityManager.bindings.cs @@ -15,6 +15,7 @@ namespace UnityEngine.Accessibility /// platform. /// [NativeHeader("Modules/Accessibility/Native/AccessibilityManager.h")] + [VisibleToOtherModules("UnityEditor.AccessibilityModule")] internal static class AccessibilityManager { public struct NotificationContext @@ -83,7 +84,8 @@ public NotificationContext(ref AccessibilityNotificationContext nativeNotificati internal static extern void SetApplicationAccessibilityLanguage(SystemLanguage languageCode); [RequiredByNativeCode] - static void Internal_Initialize() + [VisibleToOtherModules("UnityEditor.AccessibilityModule")] + internal static void Internal_Initialize() { AssistiveSupport.Initialize(); } diff --git a/Modules/Accessibility/Managed/AssistiveSupport.cs b/Modules/Accessibility/Managed/AssistiveSupport.cs index 5a87f4519a..6be0e2030c 100644 --- a/Modules/Accessibility/Managed/AssistiveSupport.cs +++ b/Modules/Accessibility/Managed/AssistiveSupport.cs @@ -3,6 +3,7 @@ // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; +using UnityEngine.Bindings; namespace UnityEngine.Accessibility { @@ -159,11 +160,25 @@ public static AccessibilityHierarchy activeHierarchy if (hierarchyService != null) { hierarchyService.hierarchy = value; + s_ActiveHierarchyChanged?.Invoke(value); } } get => GetService()?.hierarchy; } + private static event Action s_ActiveHierarchyChanged; + + /// + /// Event sent when the active hierarchy is changed. + /// + internal static event Action activeHierarchyChanged + { + [VisibleToOtherModules("UnityEditor.AccessibilityModule")] + add { s_ActiveHierarchyChanged += value; } + [VisibleToOtherModules("UnityEditor.AccessibilityModule")] + remove { s_ActiveHierarchyChanged -= value; } + } + internal static void OnHierarchyNodeFramesRefreshed(AccessibilityHierarchy hierarchy) { if (activeHierarchy == hierarchy) diff --git a/Modules/Accessibility/Managed/Hierarchy/AccessibilityHierarchy.cs b/Modules/Accessibility/Managed/Hierarchy/AccessibilityHierarchy.cs index 6c7ef1af9f..9c1dc21858 100644 --- a/Modules/Accessibility/Managed/Hierarchy/AccessibilityHierarchy.cs +++ b/Modules/Accessibility/Managed/Hierarchy/AccessibilityHierarchy.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using UnityEngine.Bindings; namespace UnityEngine.Accessibility { @@ -42,6 +43,19 @@ public class AccessibilityHierarchy /// readonly IDictionary m_Nodes; + event Action m_Changed; + + /// + /// Event sent when the hierarchy changes. + /// + internal event Action changed + { + [VisibleToOtherModules("UnityEditor.AccessibilityModule")] + add => m_Changed += value; + [VisibleToOtherModules("UnityEditor.AccessibilityModule")] + remove => m_Changed -= value; + } + /// /// Initializes and returns an instance of an AccessibilityHierarchy. /// @@ -54,6 +68,11 @@ public AccessibilityHierarchy() m_RootNodes = new List(); } + internal void NotifyHierarchyChanged() + { + m_Changed?.Invoke(this); + } + /// /// Resets the hierarchy to an empty state, removing all the nodes and removing focus. /// @@ -126,6 +145,7 @@ public AccessibilityNode InsertNode(int childIndex, string label, AccessibilityN SetParent(node, parent, null, parent == null ? m_RootNodes : parent.childList, childIndex); } + NotifyHierarchyChanged(); return node; } @@ -184,6 +204,7 @@ public bool MoveNode(AccessibilityNode node, AccessibilityNode newParent, int ne // Update node relationships (SetParent checks for loops) CheckForLoopsAndSetParent(node, newParent, newChildIndex); + NotifyHierarchyChanged(); return true; } @@ -228,6 +249,7 @@ void removeFromNodes(AccessibilityNode child) } node.Destroy(removeChildren); + NotifyHierarchyChanged(); } /// diff --git a/Modules/Accessibility/Managed/Hierarchy/AccessibilityNode.cs b/Modules/Accessibility/Managed/Hierarchy/AccessibilityNode.cs index 129d16cc4c..1777e13a39 100644 --- a/Modules/Accessibility/Managed/Hierarchy/AccessibilityNode.cs +++ b/Modules/Accessibility/Managed/Hierarchy/AccessibilityNode.cs @@ -5,6 +5,7 @@ using System; using System.Collections; using System.Collections.Generic; +using UnityEngine.Bindings; namespace UnityEngine.Accessibility { @@ -14,12 +15,19 @@ namespace UnityEngine.Accessibility /// public class AccessibilityNode { - private class ObservableList : IList, IReadOnlyList + private class ObservableList : IList, IReadOnlyList, IList { readonly List m_Items; public int Count => m_Items.Count; - public bool IsReadOnly => false; + public bool IsSynchronized => (m_Items as IList)?.IsSynchronized ?? false; + public object SyncRoot => (m_Items as IList)?.SyncRoot ?? false; + public bool IsReadOnly => (m_Items as IList)?.IsReadOnly ?? false; + object IList.this[int index] + { + get => m_Items[index]; + set => throw new NotImplementedException(); + } public ObservableList() { @@ -31,6 +39,8 @@ public ObservableList(IEnumerable enumerable) m_Items = new List(enumerable); } + public void CopyTo(Array array, int index) => (m_Items as IList)?.CopyTo(array, index); + public void Add(T item) { m_Items.Add(item); @@ -61,18 +71,45 @@ bool ICollection.Remove(T item) return removed; } + public void Remove(object value) + { + throw new NotImplementedException(); + } + public void RemoveAt(int index) { m_Items.RemoveAt(index); listChanged?.Invoke(); } + public bool IsFixedSize { get; } + + public int Add(object value) + { + throw new NotImplementedException(); + } + public void Clear() { m_Items.Clear(); listChanged?.Invoke(); } + public bool Contains(object value) + { + throw new NotImplementedException(); + } + + public int IndexOf(object value) + { + throw new NotImplementedException(); + } + + public void Insert(int index, object value) + { + throw new NotImplementedException(); + } + public T this[int index] { get => m_Items[index]; @@ -382,19 +419,21 @@ public Rect frame return m_Frame; } - set + set => SetFrame(value); + } + + void SetFrame(Rect frame) + { + if (m_Frame == frame) { - if (m_Frame == value) - { - return; - } + return; + } - m_Frame = value; + m_Frame = frame; - if (IsInActiveHierarchy()) - { - AccessibilityNodeManager.SetFrame(id, value); - } + if (IsInActiveHierarchy()) + { + AccessibilityNodeManager.SetFrame(id, frame); } } @@ -409,7 +448,7 @@ public Rect frame internal void CalculateFrame() { - m_Frame = frameGetter?.Invoke() ?? Rect.zero; + SetFrame(frameGetter?.Invoke() ?? Rect.zero); } /// diff --git a/Modules/Accessibility/Managed/Services/ServiceManager.cs b/Modules/Accessibility/Managed/Services/ServiceManager.cs index fad1830c71..ecfd932909 100644 --- a/Modules/Accessibility/Managed/Services/ServiceManager.cs +++ b/Modules/Accessibility/Managed/Services/ServiceManager.cs @@ -18,7 +18,10 @@ public ServiceManager() var isScreenReaderEnabled = AccessibilityManager.IsScreenReaderEnabled(); screenReaderStatusChanged?.Invoke(isScreenReaderEnabled); - UpdateServices(isScreenReaderEnabled); + // In the Editor context, we always initialize services in order to allow users to build, + // activate and debug (using the Accessibility Hierarchy Viewer for instance) their Accessibility hierarchies in play mode + // even if Screen Reader is off and the Accessibility backend is not supported. + UpdateServices(true); } public T GetService() where T : IService diff --git a/Modules/AccessibilityEditor/Managed/AccessibilityEditor.cs b/Modules/AccessibilityEditor/Managed/AccessibilityEditor.cs new file mode 100644 index 0000000000..6896e498df --- /dev/null +++ b/Modules/AccessibilityEditor/Managed/AccessibilityEditor.cs @@ -0,0 +1,17 @@ +// Unity C# reference source +// Copyright (c) Unity Technologies. For terms of use, see +// https://unity3d.com/legal/licenses/Unity_Reference_Only_License + +using UnityEngine.Accessibility; + +namespace UnityEditor.Accessibility +{ + internal static class AccessibilityEditor + { + [InitializeOnLoadMethod] + public static void Initialize() + { + AccessibilityManager.Internal_Initialize(); + } + } +} diff --git a/Modules/AccessibilityEditor/Managed/AccessibilityHierarchyTreeView.cs b/Modules/AccessibilityEditor/Managed/AccessibilityHierarchyTreeView.cs new file mode 100644 index 0000000000..29b6678623 --- /dev/null +++ b/Modules/AccessibilityEditor/Managed/AccessibilityHierarchyTreeView.cs @@ -0,0 +1,246 @@ +// 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 Unity.Properties; +using UnityEngine; +using UnityEngine.Accessibility; +using UnityEngine.UIElements; +using TreeView = UnityEngine.UIElements.TreeView; + +namespace UnityEditor.Accessibility +{ + class AccessibilityHierarchyTreeViewItem : VisualElement + { + public static readonly string s_UssClassName = AccessibilityHierarchyTreeView.s_UssClassName + "__item"; + public static readonly string s_RootUssClassName = s_UssClassName + "--root"; + public static readonly string s_InactiveUssClassName = s_UssClassName + "--inactive"; + public static readonly string s_IdTextUssClassName = s_UssClassName + "__id"; + public static readonly string s_LabelTextUssClassName = s_UssClassName + "__label"; + public static readonly string s_RoleTextUssClassName = s_UssClassName + "__role"; + + [Serializable] + public new class UxmlSerializedData : VisualElement.UxmlSerializedData + { +#pragma warning disable 649 + [SerializeField] private bool isRoot; + [SerializeField] private int id; + [SerializeField] private string label; + [SerializeField] private AccessibilityRole role; + [SerializeField] private bool isActive; +#pragma warning restore 649 + public override object CreateInstance() => new AccessibilityHierarchyTreeViewItem(); + public override void Deserialize(object obj) + { + base.Deserialize(obj); + + var item = (AccessibilityHierarchyTreeViewItem) obj; + item.isRoot = isRoot; + item.isActive = isActive; + item.id = id; + item.label = label; + item.role = role; + } + } + + private readonly SearchableLabel m_IdText; + private readonly SearchableLabel m_LabelText; + private readonly SearchableLabel m_RoleText; + private int m_Id; + private bool m_IsRoot; + private bool m_IsActive; + private string m_Label; + private AccessibilityRole m_Role; + + [CreateProperty] + public bool isRoot + { + get => m_IsRoot; + set + { + if (m_IsRoot == value) + return; + m_IsRoot = value; + UpdateTexts(); + } + } + + [CreateProperty] + public int id + { + get => m_Id; + set + { + if (m_Id == value) + return; + m_Id = value; + UpdateTexts(); + } + } + + [CreateProperty] + public string label + { + get => m_Label; + set + { + if (m_Label == value) + return; + m_Label = value; + UpdateTexts(); + } + } + + [CreateProperty] + public AccessibilityRole role + { + get => m_Role; + set + { + if (m_Role == value) + return; + m_Role = value; + UpdateTexts(); + } + } + + [CreateProperty] + public bool isActive + { + get => m_IsActive; + set + { + if (m_IsActive == value) + return; + m_IsActive = value; + UpdateTexts(); + } + } + + public AccessibilityHierarchyTreeViewItem() + { + AddToClassList(s_UssClassName); + m_IdText = new SearchableLabel(); + m_IdText.AddToClassList(s_IdTextUssClassName); + m_LabelText = new SearchableLabel(); + m_LabelText.AddToClassList(s_LabelTextUssClassName); + m_RoleText = new SearchableLabel(); + m_RoleText.AddToClassList(s_RoleTextUssClassName); + Add(m_IdText); + Add(m_LabelText); + Add(m_RoleText); + UpdateTexts(); + } + + public static string GetDisplayRoleText(AccessibilityRole role) + { + return role.ToString() + (role == AccessibilityRole.None ? L10n.Tr(" (Role)") : null); + } + + public static string GetDisplayLabelText(bool active, string label) + { + return "\"" + ReplaceNewLines(label) + "\"" + (!active ? L10n.Tr(" (Inactive)") : null); + } + + private void UpdateTexts() + { + m_IdText.text = isRoot ? null : id.ToString(); + m_LabelText.text = isRoot ? label : GetDisplayLabelText(isActive, label); + m_RoleText.text = isRoot ? null : GetDisplayRoleText(role); + EnableInClassList(s_InactiveUssClassName, !isActive); + } + + /// + /// Helper method used to replace all '\n' characters (new line) by "\n" string in the specified multiline text in order to display it in a single-line label. + /// + /// The source text to transform + /// Returns the source text with all '\n' characters replaced by "\n". + private static string ReplaceNewLines(string text) + { + if (string.IsNullOrEmpty(text)) + return text; + return text.Replace("\n", "\\n"); + } + } + + /// + /// Tree view that displays an accessibility hierarchy. + /// + class AccessibilityHierarchyTreeView : VisualElement + { + [Serializable] + public new class UxmlSerializedData : VisualElement.UxmlSerializedData + { + public override object CreateInstance() => new AccessibilityHierarchyTreeView(); + } + + internal static readonly string s_UssClassName = "hierarchy-tree-view"; + + private TreeView m_TreeView; + private TreeViewSearchBar m_SearchBar; + public TreeView treeView => m_TreeView; + + /// + /// The selected node. + /// + public AccessibilityViewModelNode selectedNode => (m_TreeView.selectedItem != null) ? (AccessibilityViewModelNode) m_TreeView.selectedItem : default; + + /// + /// Constructor. + /// + public AccessibilityHierarchyTreeView() + { + var asset = EditorGUIUtility.Load("Accessibility/HierarchyTreeView.uxml") as VisualTreeAsset; + + asset.CloneTree(this); + + AddToClassList(s_UssClassName); + + m_TreeView = this.Q("treeView"); + m_SearchBar = this.Q("searchBar"); + m_SearchBar.treeView = m_TreeView; + // Ensure that all items (including recycled items) have a non null data source by default to avoid binding errors. + m_TreeView.dataSource ??= default(AccessibilityViewModelNode); + m_TreeView.bindItem = (element, index) => + { + var id = m_TreeView.GetIdForIndex(index); + var node = m_TreeView.GetItemDataForId(id); + + // Cannot do element.dataSource = null as there seems to be an issue with Binding not updating the inherited data source properly. + element.Q().dataSource = node; + + if (node.isRoot) + { + element.parent.parent.AddToClassList(AccessibilityHierarchyTreeViewItem.s_RootUssClassName); + element.Query().ForEach((label) => label.ClearHighlight()); + } + else + { + element.parent.parent.RemoveFromClassList(AccessibilityHierarchyTreeViewItem.s_RootUssClassName); + + schedule.Execute(() => + { + // Apply the current search query to the labels. + element.Query().ForEach((label) => label.HighlightText(m_SearchBar.currentQuery)); + }).ExecuteLater(200); // Delay to give time for the label.text to update first + } + }; + } + + /// + /// Sets the root nodes + /// + /// + /// + public void SetRootItems(List> rootItems) + { + m_TreeView.ClearSelection(); + m_TreeView.SetRootItems(rootItems); + m_TreeView.Rebuild(); + m_TreeView.ExpandAll(); + m_SearchBar.PerformSearch(); + } + } +} diff --git a/Modules/AccessibilityEditor/Managed/AccessibilityHierarchyViewModel.cs b/Modules/AccessibilityEditor/Managed/AccessibilityHierarchyViewModel.cs new file mode 100644 index 0000000000..e2b9eda999 --- /dev/null +++ b/Modules/AccessibilityEditor/Managed/AccessibilityHierarchyViewModel.cs @@ -0,0 +1,266 @@ +// 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; +using Unity.Properties; +using UnityEngine; +using UnityEngine.Accessibility; + +namespace UnityEditor.Accessibility +{ + /// + /// A view model of an accessibility hierarchy + /// + internal class AccessibilityHierarchyViewModel + { + private AccessibilityHierarchy m_Hierarchy; + + /// + /// The underlying accessibility hierarchy. + /// + public AccessibilityHierarchy accessibilityHierarchy + { + get => m_Hierarchy; + set + { + if (m_Hierarchy == value) + return; + + if (m_Hierarchy != null) + { + m_Hierarchy.changed -= OnHierarchyChanged; + } + + m_Hierarchy = value; + + if (m_Hierarchy != null) + { + m_Hierarchy.changed += OnHierarchyChanged; + } + + Reset(); + } + } + + /// + /// The root node. + /// + public AccessibilityViewModelNode root { get; } + + /// + /// Sent when the model is reset, causing a rebuild of the attached views. + /// + public event Action modelReset; + + /// + /// Constructor. + /// + public AccessibilityHierarchyViewModel() + { + root = CreateRootNode(); + } + + private void OnHierarchyChanged(AccessibilityHierarchy hierarchy) + { + Reset(); + } + + /// + /// Resets the model. + /// + public void Reset() + { + modelReset?.Invoke(); + } + + /// + /// Creates a model node from an accessibility node + /// + /// The accessibility node + /// The created model node + public AccessibilityViewModelNode CreateNode(AccessibilityNode node) + { + return new AccessibilityViewModelNode(node, this); + } + + /// + /// Tries to get the model node with the specified node id. + /// + /// The node id + /// The node found + /// Returns true if a node is found with the specified id and false otherwise. + public bool TryGetNode(int nodeId, out AccessibilityViewModelNode modelNode) + { + modelNode = default; + + if (m_Hierarchy == null) + return false; + + if (m_Hierarchy.TryGetNode(nodeId, out var node)) + { + modelNode = CreateNode(node); + return true; + } + + return false; + } + + private AccessibilityViewModelNode CreateRootNode() + { + return new AccessibilityViewModelNode(null, this, true); + } + } + + /// + /// A view model node of an accessibility node. + /// + internal struct AccessibilityViewModelNode : IEquatable + { + const int k_RootNodeId = int.MaxValue; + + private AccessibilityHierarchyViewModel m_Model; + private AccessibilityNode m_Node; + internal bool m_Root; + + /// + /// Indicates whether the model node is the root node of the model. + /// + [CreateProperty] + public bool isRoot => m_Root; + + /// + /// Indicates whether the model node is null. + /// + public bool isNull => m_Node == null && !isRoot; + + /// + /// The id of the node. + /// + [CreateProperty] + public int id => isRoot ? k_RootNodeId : (m_Node?.id ?? 0); + + /// + /// The label of the node. + /// + [CreateProperty] + public string label + { + get + { + if (isRoot) + { + if (m_Model.accessibilityHierarchy != null) + return m_Model.accessibilityHierarchy == AssistiveSupport.activeHierarchy ? L10n.Tr("Active Hierarchy") : L10n.Tr("Inactive Hierarchy"); + return L10n.Tr("No Active Hierarchy"); + } + return m_Node?.label; + } + } + + /// + /// The value of the node. + /// + [CreateProperty] + public string value => m_Node?.value; + + /// + /// Indicates whether the node is active. + /// + [CreateProperty] + public bool isActive => m_Node?.isActive ?? false; + + /// + /// The frame of the node. + /// + [CreateProperty] + public Rect frame => m_Node?.frame ?? Rect.zero; + + /// + /// The role of the node. + /// + [CreateProperty] + public AccessibilityRole role => m_Node?.role ?? default; + + // Indicates whether the node allows direct interaction. + [CreateProperty] + public bool allowsDirectInteraction => m_Node?.allowsDirectInteraction ?? default; + + /// + /// The state of the node. + /// + [CreateProperty] + public AccessibilityState state => m_Node?.state ?? default; + + /// + /// The extra data of the node. + /// + [CreateProperty] + public IList extraData => m_Node?.extraData as IList; + + /// + /// The number of child model nodes. + /// + [CreateProperty] + public int childNodeCount => isRoot ? (m_Model.accessibilityHierarchy?.rootNodes.Count ?? 0) : m_Node?.children.Count ?? 0; + + /// + /// The accessibility node. + /// + public AccessibilityNode accessibilityNode => m_Node; + + /// + /// The child model node at the specified index. + /// + /// The index + /// The node at the index. + public AccessibilityViewModelNode GetChildNode(int index) + { + return m_Model?.CreateNode(isRoot ? m_Model.accessibilityHierarchy.rootNodes[index] : m_Node.children[index]) ?? default; + } + + /// + /// Constructs a model node from an accessibility node, a view model and a value that indicates whether is the root node of the model. + /// + /// The accessibility node. + /// The parent view model. + /// Indicates whether the node is the root of the view model. + internal AccessibilityViewModelNode(AccessibilityNode accessibilityNode, AccessibilityHierarchyViewModel model, bool isRootNode = false) + { + m_Model = model; + m_Node = accessibilityNode; + m_Root = isRootNode; + } + + public override string ToString() + { + return id.ToString(); + } + + public bool Equals(AccessibilityViewModelNode other) + { + return Equals(m_Root, other.m_Root) && Equals(m_Model, other.m_Model) && Equals(m_Node, other.m_Node); + } + + public override bool Equals(object obj) + { + return obj is AccessibilityViewModelNode other && Equals(other); + } + + public override int GetHashCode() + { + return HashCode.Combine(m_Model, m_Node, m_Root); + } + + public static bool operator ==(AccessibilityViewModelNode obj1, AccessibilityViewModelNode obj2) + { + return obj1.Equals(obj2); + } + + public static bool operator !=(AccessibilityViewModelNode obj1, AccessibilityViewModelNode obj2) + { + return !(obj1 == obj2); + } + } +} diff --git a/Modules/AccessibilityEditor/Managed/AccessibilityHierarchyViewer.cs b/Modules/AccessibilityEditor/Managed/AccessibilityHierarchyViewer.cs new file mode 100644 index 0000000000..604e923ea1 --- /dev/null +++ b/Modules/AccessibilityEditor/Managed/AccessibilityHierarchyViewer.cs @@ -0,0 +1,160 @@ +// 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 UnityEngine.Accessibility; +using UnityEngine.UIElements; + +namespace UnityEditor.Accessibility +{ + /// + /// View that displays an accessibility hierarchy and properties of its nodes. + /// + internal class AccessibilityHierarchyViewer : VisualElement + { + private static readonly string s_UssClassName = "hierarchy-viewer"; + private static readonly string s_NoSelectionUssClassName = s_UssClassName + "--no-selection"; + + /// Controls + + // Hierarchy view + private AccessibilityHierarchyTreeView m_HierarchyView; + + // Inspector + private readonly VisualElement m_InspectorContainer; + private readonly MultiColumnListView m_InspectorField_ExtraData; + + private AccessibilityHierarchyViewModel m_HierarchyModel; + + private IVisualElementScheduledItem m_RebuildScheduledItem; + + /// + /// The accessibility hierarchy view model + /// + public AccessibilityHierarchyViewModel hierarchyModel + { + get => m_HierarchyModel; + set + { + if (m_HierarchyModel == value) + return; + + if (m_HierarchyModel != null) + { + m_HierarchyModel.modelReset -= RebuildHierarchy; + } + + m_HierarchyModel = value; + + if (m_HierarchyModel != null) + { + m_HierarchyModel.modelReset += RebuildHierarchy; + } + + RebuildHierarchy(); + } + } + + /// + /// Constructor. + /// + public AccessibilityHierarchyViewer() + { + var asset = EditorGUIUtility.Load("Accessibility/HierarchyViewer.uxml") as VisualTreeAsset; + var themeUssFilePath = $"Accessibility/HierarchyViewer{(EditorGUIUtility.isProSkin ? "Dark" : "Light")}.uss"; + var themeUss = EditorGUIUtility.Load(themeUssFilePath) as StyleSheet; + + asset.CloneTree(this); + styleSheets.Add(themeUss); + + AddToClassList(s_UssClassName); + + m_HierarchyView = this.Q("hierarchyView"); + + // Inspector + + m_InspectorContainer = this.Q("inspectorContainer"); + + this.Q("isActiveField").SetEnabled(false); + this.Q("allowsDirectInteractionField").SetEnabled(false); + // Make all input fields of the frame field readonly + this.Q("frameField").Query().ForEach(frameSubField => frameSubField.isReadOnly = true); + + m_InspectorField_ExtraData = this.Q("extraDataView"); + m_InspectorField_ExtraData.virtualizationMethod = CollectionVirtualizationMethod.DynamicHeight; + m_InspectorField_ExtraData.columns[0].bindCell = (element, i) => + { + var extraData = (AccessibilityExtraData) m_InspectorField_ExtraData.itemsSource[i]; + (element as Label).text = extraData.label; + }; + m_InspectorField_ExtraData.columns[1].bindCell = (element, i) => + { + var extraData = (AccessibilityExtraData) m_InspectorField_ExtraData.itemsSource[i]; + (element as Label).text = extraData.description; + }; + + m_HierarchyView.treeView.selectionChanged += (_) => OnSelectionChanged(); + OnSelectionChanged(); + } + + private void RebuildHierarchy() + { + if (m_HierarchyView == null) + return; + + if (m_RebuildScheduledItem == null) + { + m_RebuildScheduledItem = schedule.Execute(HandleRebuildHierarchy); + } + else + { + m_RebuildScheduledItem.Resume(); + } + } + + private void HandleRebuildHierarchy() + { + m_HierarchyView.SetRootItems(hierarchyModel != null ? new List>() { RebuildHierarchy(hierarchyModel.root)} : null); + } + + private static TreeViewItemData RebuildHierarchy(AccessibilityViewModelNode modelNode) + { + var childList = new List>(modelNode.childNodeCount); + + for (var i = 0; i < modelNode.childNodeCount; ++i) + { + childList.Add(RebuildHierarchy(modelNode.GetChildNode(i))); + } + return new TreeViewItemData(modelNode.id, modelNode, childList); + } + + private void RefreshInspector(AccessibilityViewModelNode node) + { + m_InspectorContainer.dataSource = node; + schedule.Execute(() => m_InspectorField_ExtraData.RefreshItems()).ExecuteLater(250); + } + + void OnSelectionChanged() + { + bool hasSelection = false; + + if (m_HierarchyView.treeView.selectedItem != null) + { + var node = (AccessibilityViewModelNode) m_HierarchyView.treeView.selectedItem; + + if (!node.isRoot) + { + RefreshInspector(node); + hasSelection = true; + } + } + else + { + RefreshInspector(default); + } + + EnableInClassList(s_NoSelectionUssClassName, !hasSelection); + } + } +} diff --git a/Modules/AccessibilityEditor/Managed/AccessibilityHierarchyViewerWindow.cs b/Modules/AccessibilityEditor/Managed/AccessibilityHierarchyViewerWindow.cs new file mode 100644 index 0000000000..2c06d1ad27 --- /dev/null +++ b/Modules/AccessibilityEditor/Managed/AccessibilityHierarchyViewerWindow.cs @@ -0,0 +1,60 @@ +// Unity C# reference source +// Copyright (c) Unity Technologies. For terms of use, see +// https://unity3d.com/legal/licenses/Unity_Reference_Only_License + +using UnityEngine; +using UnityEngine.Accessibility; +using UnityEngine.UIElements; + +namespace UnityEditor.Accessibility +{ + /// + /// A window that displays the active accessibility hierarchy. + /// + internal class AccessibilityHierarchyViewerWindow : EditorWindow + { + private static string s_WindowTitle = "Accessibility Hierarchy Viewer"; + + private AccessibilityHierarchyViewModel m_ActiveHierarchyModel; + + [MenuItem("Window/Accessibility/Hierarchy Viewer", false, 3006)] + public static void ShowWindow() + { + GetWindow(); + } + + private void OnEnable() + { + minSize = new Vector2(200, 200); + titleContent = new GUIContent(L10n.Tr(s_WindowTitle)); + AssistiveSupport.activeHierarchyChanged += OnActiveHierarchyChanged; + } + + private void OnDisable() + { + AssistiveSupport.activeHierarchyChanged -= OnActiveHierarchyChanged; + } + + public void CreateGUI() + { + var root = rootVisualElement; + var viewer = new AccessibilityHierarchyViewer(); + + m_ActiveHierarchyModel = new AccessibilityHierarchyViewModel(); + viewer.hierarchyModel = m_ActiveHierarchyModel; + + root.Add(viewer); + viewer.StretchToParentSize(); + + OnActiveHierarchyChanged(AssistiveSupport.activeHierarchy); + } + + private void OnActiveHierarchyChanged(AccessibilityHierarchy hierarchy) + { + if (m_ActiveHierarchyModel == null) + return; + + m_ActiveHierarchyModel.accessibilityHierarchy = hierarchy; + } + } +} diff --git a/Modules/AccessibilityEditor/Managed/SearchableLabel.cs b/Modules/AccessibilityEditor/Managed/SearchableLabel.cs new file mode 100644 index 0000000000..421527417d --- /dev/null +++ b/Modules/AccessibilityEditor/Managed/SearchableLabel.cs @@ -0,0 +1,88 @@ +// 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 Unity.Properties; +using UnityEditor; +using UnityEditor.Accessibility; +using UnityEngine; +using UnityEngine.Accessibility; +using UnityEngine.UIElements; +using TreeView = UnityEngine.UIElements.TreeView; + +namespace UnityEditor.Accessibility +{ + class SearchableLabel : VisualElement + { + [Serializable] + public new class UxmlSerializedData : VisualElement.UxmlSerializedData + { +#pragma warning disable 649 + [SerializeField] private string text; +#pragma warning restore 649 + + public override object CreateInstance() => new SearchableLabel(); + + public override void Deserialize(object obj) + { + base.Deserialize(obj); + + var e = (SearchableLabel) obj; + e.text = text; + } + } + + private static readonly string s_UssClassName = "searchable-label"; + private static readonly string s_LabelTextUssClassName = s_UssClassName + "__text"; + private static readonly string s_HighlightUssClassName = s_UssClassName + "__highlight"; + + private readonly Label m_Label; + private readonly VisualElement m_Highlight; + + [CreateProperty] + public string text + { + get => m_Label.text; + set => m_Label.text = value; + } + + public SearchableLabel() + { + AddToClassList(s_UssClassName); + m_Label = new Label(); + m_Label.AddToClassList(s_LabelTextUssClassName); + m_Highlight = new VisualElement(); + m_Highlight.AddToClassList(s_HighlightUssClassName); + Add(m_Label); + Add(m_Highlight); + ClearHighlight(); + } + + public void ClearHighlight() + { + m_Highlight.style.display = DisplayStyle.None; + } + + public void HighlightText(string query) + { + ClearHighlight(); + + if (string.IsNullOrEmpty(text) || string.IsNullOrEmpty(query)) + return; + + var indexOf = text.IndexOf(query, StringComparison.OrdinalIgnoreCase); + + if (indexOf < 0) + return; + + var startPos = m_Label.MeasureTextSize(text[..indexOf], 0, MeasureMode.Undefined, 0, MeasureMode.Undefined); + var endPos = m_Label.MeasureTextSize(text[..(indexOf + query.Length)], 0, MeasureMode.Undefined, 0, MeasureMode.Undefined); + + m_Highlight.style.width = endPos.x - startPos.x; + m_Highlight.style.left = startPos.x; + m_Highlight.style.display = DisplayStyle.Flex; + } + } +} diff --git a/Modules/AccessibilityEditor/Managed/TreeViewSearchBar.cs b/Modules/AccessibilityEditor/Managed/TreeViewSearchBar.cs new file mode 100644 index 0000000000..cc9ad2eab6 --- /dev/null +++ b/Modules/AccessibilityEditor/Managed/TreeViewSearchBar.cs @@ -0,0 +1,239 @@ +// 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 UnityEngine; +using UnityEngine.UIElements; + +namespace UnityEditor.Accessibility +{ + /// + /// Search field used to search items by id, label or role in a accessibility hierarchy tree view. + /// + internal class TreeViewSearchBar : VisualElement + { + private static readonly string s_UssClassName = "hierarchy-tree-view__search-bar"; + private static readonly string s_HiddenElementUssClassName = "hierarchy-tree-view__search-bar__element-hidden"; + private static readonly string s_SearchLabelUssClassName = s_UssClassName + "__label"; + private static readonly string s_SearchLabelHelpUssClassName = s_UssClassName + "__label-help"; + private static readonly string s_SearchFieldUssClassName = s_UssClassName + "__field"; + private static readonly string s_SearchButtonUssClassName = s_UssClassName + "__button"; + private static readonly string s_SearchPrevButtonUssClassName = s_SearchButtonUssClassName + "-prev"; + private static readonly string s_SearchNextButtonUssClassName = s_UssClassName + "-next"; + + class SearchResultItem + { + public int itemId; + } + + [Serializable] + public new class UxmlSerializedData : VisualElement.UxmlSerializedData + { + public override object CreateInstance() => new TreeViewSearchBar(); + } + + private List m_FoundItems; + private int m_SelectedIndex; + private string m_CurrentQuery; + + private TextField m_SearchTextField; + + private TreeView m_TreeView; + private Label m_CountLabel; + private Label m_FieldHelpLabel; + + private List m_SearchResultsHightlights; + + public string currentQuery => m_CurrentQuery; + + /// + /// The tree view on which search is performed. + /// + public TreeView treeView + { + get => m_TreeView; + set => m_TreeView = value; + } + + /// + /// Constructor. + /// + public TreeViewSearchBar() + { + m_FoundItems = new List(); + m_SearchResultsHightlights = new List(); + + AddToClassList(s_UssClassName); + + m_FieldHelpLabel = new Label(L10n.Tr("Search by id, label, role")); + m_FieldHelpLabel.pickingMode = PickingMode.Ignore; + m_FieldHelpLabel.AddToClassList(s_SearchLabelUssClassName); + m_FieldHelpLabel.AddToClassList(s_SearchLabelHelpUssClassName); + Add(m_FieldHelpLabel); + + m_SearchTextField = new TextField(); + m_SearchTextField.AddToClassList(s_SearchFieldUssClassName); + m_SearchTextField.RegisterValueChangedCallback((e) => PerformSearch()); + m_SearchTextField.RegisterCallback((e) => + { + var targetField = m_SearchTextField; + if (e.keyCode == KeyCode.F3 || e.keyCode == KeyCode.Return || e.keyCode == KeyCode.KeypadEnter) + { + if (e.modifiers.HasFlag(EventModifiers.Shift)) + SelectPrev(); + else + SelectNext(); + } + else if (e.keyCode == KeyCode.Escape) + { + targetField.value = string.Empty; + ClearSearchResults(); + treeView.Focus(); + } + }, TrickleDown.TrickleDown); + Add(m_SearchTextField); + + m_CountLabel = new Label(); + m_CountLabel.AddToClassList(s_SearchLabelUssClassName); + m_CountLabel.AddToClassList(s_HiddenElementUssClassName); + Add(m_CountLabel); + + var prevButton = new Button(SelectPrev) {text = "<"}; + prevButton.AddToClassList(s_SearchButtonUssClassName); + prevButton.AddToClassList(s_SearchPrevButtonUssClassName); + Add(prevButton); + + var nextButton = new Button(SelectNext) {text = ">"}; + nextButton.AddToClassList(s_SearchButtonUssClassName); + nextButton.AddToClassList(s_SearchNextButtonUssClassName); + Add(nextButton); + } + + private IEnumerable> GetAllItems() + { + if (m_TreeView?.viewController == null) + { + yield break; + } + + var treeViewController = m_TreeView?.viewController as DefaultTreeViewController; + + foreach (var itemId in m_TreeView.viewController.GetAllItemIds()) + { + yield return treeViewController.GetTreeViewItemDataForId(itemId); + } + } + + /// + /// Clears the text of the search field. + /// + public void ClearSearch() + { + m_SearchTextField.value = string.Empty; + } + + private void SelectNext() + { + if (m_FoundItems.Count == 0) + return; + + m_SelectedIndex = (m_SelectedIndex + 1) % m_FoundItems.Count; + SelectElement(m_FoundItems[m_SelectedIndex].itemId, m_CurrentQuery); + m_CountLabel.text = $"{m_SelectedIndex + 1} of {m_FoundItems.Count}"; + } + + private void SelectPrev() + { + if (m_FoundItems.Count == 0) + return; + + var count = m_FoundItems.Count; + m_SelectedIndex--; + m_SelectedIndex = (m_SelectedIndex % count + count) % count; + + SelectElement(m_FoundItems[m_SelectedIndex].itemId, m_CurrentQuery); + m_CountLabel.text = $"{m_SelectedIndex + 1} of {m_FoundItems.Count}"; + } + + /// + /// Performs a search. + /// + public void PerformSearch() + { + m_FoundItems.Clear(); + m_SelectedIndex = 0; + + m_CountLabel.text = string.Empty; + m_CountLabel.AddToClassList(s_HiddenElementUssClassName); + + ClearSearchResults(); + + m_FieldHelpLabel.AddToClassList(s_HiddenElementUssClassName); + + m_CurrentQuery = m_SearchTextField.text; + + m_TreeView.RefreshItems(); + + if (string.IsNullOrEmpty(m_CurrentQuery)) + { + m_FieldHelpLabel.RemoveFromClassList(s_HiddenElementUssClassName); + return; + } + + var items = GetAllItems(); + if (items == null) + return; + + foreach (var treeItem in items) + { + var element = treeItem.data; + + if (element.isRoot) + continue; + + var idText = element.id.ToString(); + var labelText = AccessibilityHierarchyTreeViewItem.GetDisplayLabelText(element.isActive, element.label); + var roleText = AccessibilityHierarchyTreeViewItem.GetDisplayRoleText(element.role); + + if ((idText.IndexOf(m_CurrentQuery, StringComparison.OrdinalIgnoreCase) >= 0) + || (labelText?.IndexOf(m_CurrentQuery, StringComparison.OrdinalIgnoreCase) >= 0) + || (roleText.IndexOf(m_CurrentQuery, StringComparison.OrdinalIgnoreCase) >= 0)) + { + m_FoundItems.Add(new SearchResultItem() {itemId = treeItem.id}); + } + } + + if (m_FoundItems.Count == 0) + return; + + m_CountLabel.RemoveFromClassList(s_HiddenElementUssClassName); + m_CountLabel.text = $"{m_SelectedIndex + 1} of {m_FoundItems.Count}"; + + var firstItem = m_FoundItems[0]; + SelectElement(firstItem.itemId, m_CurrentQuery); + } + + private void ClearSearchResults() + { + foreach (var hl in m_SearchResultsHightlights) + hl.RemoveFromHierarchy(); + + m_SearchResultsHightlights.Clear(); + } + + private void SelectElement(int itemId, string query) + { + ClearSearchResults(); + + var node = m_TreeView.GetItemDataForId(itemId); + + if (node.isNull || node.isRoot) + return; + + m_TreeView.SetSelectionById(itemId); + m_TreeView.ScrollToItemById(itemId); + } + } +} diff --git a/Modules/AndroidJNI/AndroidApplicationExitInfo.cs b/Modules/AndroidJNI/AndroidApplicationExitInfo.cs new file mode 100644 index 0000000000..ba881c4057 --- /dev/null +++ b/Modules/AndroidJNI/AndroidApplicationExitInfo.cs @@ -0,0 +1,315 @@ +// 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 Unity.Collections; + +namespace UnityEngine.Android +{ + public enum ProcessImportance + { + /// + /// This process is running the foreground UI; that is, it is the thing currently at the top of the screen that the user is interacting with. + /// developer.android.com + /// + Foreground = 100, + + /// + /// This process is running a foreground service, for example to perform music playback even while the user is not immediately in the app. This generally indicates that the process is doing something the user actively cares about. + /// developer.android.com + /// + ForeGroundService = 125, + + /// + /// This process is running something that is actively visible to the user, though not in the immediate foreground. This may be running a window that is behind the current foreground (so paused and with its state saved, not interacting with the user, but visible to them to some degree); it may also be running other services under the system's control that it inconsiders important. + /// developer.android.com + /// + Visible = 200, + + /// + /// This process is not something the user is directly aware of, but is otherwise perceptible to them to some degree. + /// developer.android.com + /// + Perceptible = 230, + + /// + /// This process is running the foreground UI, but the device is asleep so it is not visible to the user. Though the system will try hard to keep its process from being killed, in all other ways we consider it a kind of cached process, with the limitations that go along with that state: network access, running background services, etc. + /// developer.android.com + /// + TopSleeping = 325, + + /// + /// This process is running an application that can not save its state, and thus can't be killed while in the background. This will be used with apps that have R.attr.cantSaveState set on their application tag. + /// developer.android.com + /// + CantSaveState = 350, + + /// + /// This process contains services that should remain running. These are background services apps have started, not something the user is aware of, so they may be killed by the system relatively freely (though it is generally desired that they stay running as long as they want to). + /// developer.android.com + /// + Service = 300, + + /// + /// This process process contains cached code that is expendable, not actively running any app components we care about. + /// developer.android.com + /// + Cached = 400, + + /// + /// This process does not exist. + /// developer.android.com + /// + Gone = 1000 + } + + public enum ExitReason + { + /// + /// Application process died due to unknown reason. + /// developer.android.com + /// + Unknown = 0, + + /// + /// Application process exit normally by itself, for example, via System.exit(int); getStatus() will specify the exit code. + /// developer.android.com + /// + ExitSelf = 1, + + /// + /// Application process died due to the result of an OS signal; for example, OsConstants.SIGKILL; getStatus() will specify the signal number. + /// developer.android.com + /// + Signaled = 2, + + /// + /// Application process was killed by the system low memory killer, meaning the system was under memory pressure at the time of kill. + /// developer.android.com + /// + LowMemory = 3, + + /// + /// Application process died because of an unhandled exception in Java code. + /// developer.android.com + /// + Crash = 4, + + /// + /// Application process died because of a native code crash. + /// developer.android.com + /// + CrashNative = 5, + + /// + /// Application process was killed due to being unresponsive (ANR). + /// developer.android.com + /// + ANR = 6, + + /// + /// Application process was killed because of initialization failure, for example, it took too long to attach to the system during the start, or there was an error during initialization. + /// developer.android.com + /// + InititalizationFailure = 7, + + /// + /// Application process was killed due to a runtime permission change. + /// developer.android.com + /// + PermissionChange = 8, + + /// + /// Application process was killed by the system due to excessive resource usage. + /// developer.android.com + /// + ExcessiveResourceUsage = 9, + + /// + /// Application process was killed because of the user request, for example, user clicked the "Force stop" button of the application in the Settings, or removed the application away from Recents. + /// developer.android.com + /// + UserRequested = 10, + + /// + /// Application process was killed, because the user it is running as on devices with mutlple users, was stopped. + /// developer.android.com + /// + UserStopped = 11, + + /// + /// Application process was killed because its dependency was going away, for example, a stable content provider connection's client will be killed if the provider is killed. + /// developer.android.com + /// + DependencyDied = 12, + + /// + /// Application process was killed by the system for various other reasons which are not by problems in apps and not actionable by apps, for example, the system just finished updates; getDescription() will specify the cause given by the system. + /// developer.android.com + /// + Other = 13, + + /// + /// Application process was killed by App Freezer, for example, because it receives sync binder transactions while being frozen. + /// developer.android.com + /// + Freezer = 14, + + /// + /// Application process was killed because the app was disabled, or any of its component states have changed without PackageManager.DONT_KILL_APP + /// developer.android.com + /// + PackageStateChange = 15, + + /// + /// Application process was killed because it was updated. + /// developer.android.com + /// + PackageUpdated = 16 + } + + public interface IApplicationExitInfo + { + /// + /// The human readable description of the process's death, given by the system; could be null. + /// developer.android.com + /// + /// string + string description { get; } + + /// + /// Describe the kinds of special objects contained in this Parcelable instance's marshaled representation. + /// developer.android.com + /// + /// a bitmask indicating the set of special object types marshaled by this Parcelable object instance. Value is either 0 or CONTENTS_FILE_DESCRIPTOR + int describeContents { get; } + + /// + /// Return the defining kernel user identifier, maybe different from getRealUid() and getPackageUid(), if an external service has the android:useAppZygote set to true and was bound with the flag Context.BIND_EXTERNAL_SERVICE - in this case, this field here will be the kernel user identifier of the external service provider. + /// developer.android.com + /// + /// int + int definingUid { get; } + + /// + /// The importance of the process that it used to have before the death. + /// developer.android.com + /// + /// ProcessImportance + ProcessImportance importance { get; } + + /// + /// Similar to getRealUid(), it's the kernel user identifier that is assigned at the package installation time. + /// developer.android.com + /// + /// int + int packageUid { get; } + + /// + /// The process id of the process that died. + /// developer.android.com + /// + /// int + int pid { get; } + + /// + /// The actual process name it was running with. + /// developer.android.com + /// + /// String + String processName { get; } + + /// + /// Return the state data set by calling ApplicationExitInfoProvider.setProcessStateSummary(byte[]) from the process before its death. + /// developer.android.com + /// + /// byte[] containing the process-customized data. This value may be null. + sbyte[] processStateSummary { get; } + + /// + /// Last proportional set size of the memory that the process had used in kB. + /// developer.android.com + /// + /// long + long pss { get; } + + /// + /// The kernel user identifier of the process, most of the time the system uses this to do access control checks. + /// developer.android.com + /// + /// int + int realUid { get; } + + /// + /// The reason code of the process's death. + /// developer.android.com + /// + /// ExitReason + ExitReason reason { get; } + + /// + /// Last resident set size of the memory that the process had used in kB. + /// developer.android.com + /// + /// long + long rss { get; } + + /// + /// The exit status argument of exit() if the application calls it, or the signal number if the application is signaled. + /// developer.android.com + /// + /// int + int status { get; } + + /// + /// The timestamp of the process's death, in milliseconds since the epoch, as returned by System.currentTimeMillis(). Value is a non-negative timestamp measured as the number of milliseconds since 1970-01-01T00:00:00Z. + /// developer.android.com + /// + /// long Value is a non-negative timestamp measured as the number of milliseconds since 1970-01-01T00:00:00Z. + long timestamp { get; } + + /// + /// Return the traces that was taken by the system prior to the death of the process; typically it'll be available when the reason is REASON_ANR, though if the process gets an ANR but recovers, and dies for another reason later, this trace will be included in the record of ApplicationExitInfo still. + /// developer.android.com + /// + /// byte[] + byte[] trace { get; } + + /// + /// Return the trace data in string format + /// + /// string + public String traceAsString { get; } + } + + + public static class ApplicationExitInfoProvider + { + /// + /// Return a list of ApplicationExitInfo records containing the reasons for the most recent app deaths. + /// developer.android.com + /// + /// Optional, a null value means match all packages belonging to the caller's UID. If this package belongs to another UID, you must hold android.Manifest.permission.DUMP in order to retrieve it. + /// A process ID that used to belong to this package but died later; a value of 0 means to ignore this parameter and return all matching records. Value is 0 or greater + /// The maximum number of results to be returned; a value of 0 means to ignore this parameter and return all matching records Value is 0 or greater. + /// IApplicationExitInfo[] a list of ApplicationExitInfo records matching the criteria, sorted in the order from most recent to least recent. This value cannot be null. + public static IApplicationExitInfo[] GetHistoricalProcessExitInfo(string packageName = null, int pid = 0, int maxNum = 0) + { + IApplicationExitInfo[] result = null; + if (result == null) + result = new IApplicationExitInfo[0]; + + return result; + } + + /// + ///Set custom state data for this process.It will be included in the record of ApplicationExitInfo on the death of the current calling process; the new process of the app can retrieve this state data by calling processStateSummary on the IApplicationExitInfo record returned by ApplicationExitInfoProvider.getHistoricalProcessExitReasons(String, int, int). + ///developer.android.com + /// + /// The state data.To be advised, DO NOT include sensitive information/data (PII, SPII, or other sensitive user data) here.Maximum length is 128 bytes. + public static void SetProcessStateSummary(SByte[] buffer) + { + } + } +} diff --git a/Modules/AssetDatabase/Editor/ScriptBindings/AssetDatabase.bindings.cs b/Modules/AssetDatabase/Editor/ScriptBindings/AssetDatabase.bindings.cs index 3095d79711..07d7a90650 100644 --- a/Modules/AssetDatabase/Editor/ScriptBindings/AssetDatabase.bindings.cs +++ b/Modules/AssetDatabase/Editor/ScriptBindings/AssetDatabase.bindings.cs @@ -367,7 +367,10 @@ public unsafe static void InstanceIDsToGUIDs(NativeArray instanceIDs, Nativ extern public static System.Type GetMainAssetTypeAtPath(string assetPath); + extern public static System.Type GetMainAssetTypeFromGUID(GUID guid); + extern public static System.Type GetTypeFromPathAndFileID(string assetPath, long localIdentifierInFile); + extern public static bool IsMainAssetAtPathLoaded(string assetPath); [PreventExecutionInState(AssetDatabasePreventExecution.kGatheringDependenciesFromSourceFile, PreventExecutionSeverity.PreventExecution_ManagedException, "Assets may not be loaded while dependencies are being gathered, as these assets may not have been imported yet.")] diff --git a/Modules/IMGUI/TextSelectingUtilities.cs b/Modules/IMGUI/TextSelectingUtilities.cs index 5bab1e609d..6ebfee6816 100644 --- a/Modules/IMGUI/TextSelectingUtilities.cs +++ b/Modules/IMGUI/TextSelectingUtilities.cs @@ -911,7 +911,7 @@ enum Direction CharacterType ClassifyChar(int index) { - char c = m_TextElementInfos[index].character; + char c = (char)m_TextElementInfos[index].character; if (c == kNewLineChar) return CharacterType.NewLine; if (char.IsWhiteSpace(c)) diff --git a/Modules/PackageManagerUI/Editor/Services/Packages/Actions/ImportActionBase.cs b/Modules/PackageManagerUI/Editor/Services/Packages/Actions/ImportActionBase.cs index 5fc1d8472c..800b9705a9 100644 --- a/Modules/PackageManagerUI/Editor/Services/Packages/Actions/ImportActionBase.cs +++ b/Modules/PackageManagerUI/Editor/Services/Packages/Actions/ImportActionBase.cs @@ -11,12 +11,17 @@ internal abstract class ImportActionBase : PackageAction protected readonly PackageOperationDispatcher m_OperationDispatcher; protected readonly AssetStoreDownloadManager m_AssetStoreDownloadManager; protected readonly ApplicationProxy m_Application; + protected readonly UnityConnectProxy m_UnityConnect; - protected ImportActionBase(PackageOperationDispatcher operationDispatcher, AssetStoreDownloadManager assetStoreDownloadManager, ApplicationProxy application) + protected ImportActionBase(PackageOperationDispatcher operationDispatcher, + AssetStoreDownloadManager assetStoreDownloadManager, + ApplicationProxy application, + UnityConnectProxy unityConnect) { m_OperationDispatcher = operationDispatcher; m_AssetStoreDownloadManager = assetStoreDownloadManager; m_Application = application; + m_UnityConnect = unityConnect; } protected abstract string analyticEventName { get; } @@ -30,10 +35,11 @@ protected override bool TriggerActionImplementation(IPackageVersion version) public override bool IsVisible(IPackageVersion version) { - return version?.HasTag(PackageTag.LegacyFormat) == true - && version.package.versions.importAvailable != null - && version.package.progress == PackageProgress.None - && m_AssetStoreDownloadManager.GetDownloadOperation(version.package.product?.id)?.isProgressVisible != true; + return m_UnityConnect.isUserLoggedIn + && version.HasTag(PackageTag.LegacyFormat) + && version.package.versions.importAvailable != null + && version.package.progress == PackageProgress.None + && m_AssetStoreDownloadManager.GetDownloadOperation(version.package.product?.id)?.isProgressVisible != true; } public override bool IsInProgress(IPackageVersion version) => false; diff --git a/Modules/PackageManagerUI/Editor/Services/Packages/Actions/ImportNewAction.cs b/Modules/PackageManagerUI/Editor/Services/Packages/Actions/ImportNewAction.cs index 024e596de5..508d971f7b 100644 --- a/Modules/PackageManagerUI/Editor/Services/Packages/Actions/ImportNewAction.cs +++ b/Modules/PackageManagerUI/Editor/Services/Packages/Actions/ImportNewAction.cs @@ -6,8 +6,8 @@ namespace UnityEditor.PackageManager.UI.Internal; internal class ImportNewAction : ImportActionBase { - public ImportNewAction(PackageOperationDispatcher operationDispatcher, AssetStoreDownloadManager assetStoreDownloadManager, ApplicationProxy application) - : base(operationDispatcher, assetStoreDownloadManager, application) + public ImportNewAction(PackageOperationDispatcher operationDispatcher, AssetStoreDownloadManager assetStoreDownloadManager, ApplicationProxy application, UnityConnectProxy unityConnect) + : base(operationDispatcher, assetStoreDownloadManager, application, unityConnect) { } diff --git a/Modules/PackageManagerUI/Editor/Services/Packages/Actions/ImportUpdateAction.cs b/Modules/PackageManagerUI/Editor/Services/Packages/Actions/ImportUpdateAction.cs index 6ecab135ee..bd958cd4df 100644 --- a/Modules/PackageManagerUI/Editor/Services/Packages/Actions/ImportUpdateAction.cs +++ b/Modules/PackageManagerUI/Editor/Services/Packages/Actions/ImportUpdateAction.cs @@ -6,8 +6,8 @@ namespace UnityEditor.PackageManager.UI.Internal; internal class ImportUpdateAction : ImportActionBase { - public ImportUpdateAction(PackageOperationDispatcher operationDispatcher, AssetStoreDownloadManager assetStoreDownloadManager, ApplicationProxy application) - : base(operationDispatcher, assetStoreDownloadManager, application) + public ImportUpdateAction(PackageOperationDispatcher operationDispatcher, AssetStoreDownloadManager assetStoreDownloadManager, ApplicationProxy application, UnityConnectProxy unityConnect) + : base(operationDispatcher, assetStoreDownloadManager, application, unityConnect) { } diff --git a/Modules/PackageManagerUI/Editor/Services/Packages/Actions/ReImportAction.cs b/Modules/PackageManagerUI/Editor/Services/Packages/Actions/ReImportAction.cs index 4f5f67f574..c999095b21 100644 --- a/Modules/PackageManagerUI/Editor/Services/Packages/Actions/ReImportAction.cs +++ b/Modules/PackageManagerUI/Editor/Services/Packages/Actions/ReImportAction.cs @@ -6,8 +6,8 @@ namespace UnityEditor.PackageManager.UI.Internal; internal class ReImportAction : ImportActionBase { - public ReImportAction(PackageOperationDispatcher operationDispatcher, AssetStoreDownloadManager assetStoreDownloadManager, ApplicationProxy application) - : base(operationDispatcher, assetStoreDownloadManager, application) + public ReImportAction(PackageOperationDispatcher operationDispatcher, AssetStoreDownloadManager assetStoreDownloadManager, ApplicationProxy application, UnityConnectProxy unityConnect) + : base(operationDispatcher, assetStoreDownloadManager, application, unityConnect) { } diff --git a/Modules/PackageManagerUI/Editor/UI/ToolBar/LegacyFormatDropdownButton.cs b/Modules/PackageManagerUI/Editor/UI/ToolBar/LegacyFormatDropdownButton.cs index c193a972b6..105cac7c19 100644 --- a/Modules/PackageManagerUI/Editor/UI/ToolBar/LegacyFormatDropdownButton.cs +++ b/Modules/PackageManagerUI/Editor/UI/ToolBar/LegacyFormatDropdownButton.cs @@ -43,9 +43,9 @@ public LegacyFormatDropdownButton(PackageOperationDispatcher operationDispatcher { new DownloadNewAction(operationDispatcher, assetStoreDownloadManager, unityConnect, application), new DownloadUpdateAction(operationDispatcher, assetStoreDownloadManager, unityConnect, application), - new ImportNewAction(operationDispatcher, assetStoreDownloadManager, application), - new ImportUpdateAction(operationDispatcher, assetStoreDownloadManager, application), - new ReImportAction(operationDispatcher, assetStoreDownloadManager, application), + new ImportNewAction(operationDispatcher, assetStoreDownloadManager, application, unityConnect), + new ImportUpdateAction(operationDispatcher, assetStoreDownloadManager, application, unityConnect), + new ReImportAction(operationDispatcher, assetStoreDownloadManager, application, unityConnect), new RemoveImportedAction(operationDispatcher, application), new ReDownloadAction(operationDispatcher, assetStoreDownloadManager, unityConnect, application), }; diff --git a/Modules/SceneView/CameraOverlay.cs b/Modules/SceneView/CameraOverlay.cs index 604fb42aa0..bac2a4e0e7 100644 --- a/Modules/SceneView/CameraOverlay.cs +++ b/Modules/SceneView/CameraOverlay.cs @@ -705,6 +705,7 @@ public override VisualElement CreatePanelContent() root.Add(m_UserData); root.name = "Cameras Preview Overlay"; + root.AddToClassList(k_CamerasOverlayUSSClass); return root; } diff --git a/Modules/ShortcutManager/ShortcutManagerWindowView.cs b/Modules/ShortcutManager/ShortcutManagerWindowView.cs index e546fca6af..417dd6e669 100644 --- a/Modules/ShortcutManager/ShortcutManagerWindowView.cs +++ b/Modules/ShortcutManager/ShortcutManagerWindowView.cs @@ -362,11 +362,25 @@ void BuildProfileManagementRow(VisualElement header) void OnCreateProfileClicked() { + const string initialValueForNewProfile = "New profile"; + int uniqueProfileNumber = 1; + var profiles = m_ViewController.GetAvailableProfiles(); + string newProfileName = initialValueForNewProfile; + if (profiles.Contains(initialValueForNewProfile)) + { + newProfileName = initialValueForNewProfile + string.Format(" ({0})", uniqueProfileNumber); + + while (profiles.Contains(newProfileName) && uniqueProfileNumber < 999999) + { + newProfileName = initialValueForNewProfile + string.Format(" ({0})", ++uniqueProfileNumber); + } + } + PromptWindow.Show(L10n.Tr("Create profile"), L10n.Tr("Create a shortcut profile"), L10n.Tr("Enter the name of the profile you want to create"), L10n.Tr("Profile Name:"), - L10n.Tr("New profile"), + L10n.Tr(newProfileName), L10n.Tr("Create"), m_ViewController.CanCreateProfile, m_ViewController.CreateProfile); diff --git a/Modules/ShortcutManagerEditor/ShortcutProfileManager.cs b/Modules/ShortcutManagerEditor/ShortcutProfileManager.cs index f6100e5a02..42fd236401 100644 --- a/Modules/ShortcutManagerEditor/ShortcutProfileManager.cs +++ b/Modules/ShortcutManagerEditor/ShortcutProfileManager.cs @@ -134,7 +134,7 @@ public void ReloadProfiles() public IEnumerable GetProfiles() { - return m_LoadedProfiles.Values; + return m_LoadedProfiles.Values.OrderBy(profile => profile.id); } public ShortcutProfile GetProfileById(string profileId) diff --git a/Modules/TextCoreTextEngine/Managed/LinkInfo.cs b/Modules/TextCoreTextEngine/Managed/LinkInfo.cs index 60231fa0a8..4554d8a1e9 100644 --- a/Modules/TextCoreTextEngine/Managed/LinkInfo.cs +++ b/Modules/TextCoreTextEngine/Managed/LinkInfo.cs @@ -46,7 +46,7 @@ public string GetLinkText(TextInfo textInfo) { if (string.IsNullOrEmpty(m_LinkTextString)) for (int i = linkTextfirstCharacterIndex; i < linkTextfirstCharacterIndex + linkTextLength; i++) - m_LinkTextString += textInfo.textElementInfo[i].character; + m_LinkTextString += (char)textInfo.textElementInfo[i].character; return m_LinkTextString; } diff --git a/Modules/TextCoreTextEngine/Managed/TextAssets/FontAsset.cs b/Modules/TextCoreTextEngine/Managed/TextAssets/FontAsset.cs index 4fd12fac51..fd7e69a51e 100644 --- a/Modules/TextCoreTextEngine/Managed/TextAssets/FontAsset.cs +++ b/Modules/TextCoreTextEngine/Managed/TextAssets/FontAsset.cs @@ -12,6 +12,8 @@ using UnityEngine.Serialization; using UnityEngine.TextCore.LowLevel; +using UnityEditor; + namespace UnityEngine.TextCore.Text { /// @@ -134,6 +136,18 @@ internal Font SourceFont_EditorRef } internal Font m_SourceFontFile_EditorRef; + + /// + /// The settings used in the Font Asset Creator when this font asset was created or edited. + /// + public FontAssetCreationEditorSettings fontAssetCreationEditorSettings + { + get { return m_fontAssetCreationEditorSettings; } + set { m_fontAssetCreationEditorSettings = value; } + } + [SerializeField] + internal FontAssetCreationEditorSettings m_fontAssetCreationEditorSettings; + /// /// Source font file when atlas population mode is set to dynamic. Null when the atlas population mode is set to static. /// @@ -483,17 +497,6 @@ public List fallbackFontAssetTable [SerializeField] internal List m_FallbackFontAssetTable; - /// - /// The settings used in the Font Asset Creator when this font asset was created or edited. - /// - public FontAssetCreationEditorSettings fontAssetCreationEditorSettings - { - get { return m_fontAssetCreationEditorSettings; } - set { m_fontAssetCreationEditorSettings = value; } - } - [SerializeField] - internal FontAssetCreationEditorSettings m_fontAssetCreationEditorSettings; - /// /// Array containing font assets to be used as alternative typefaces for the various potential font weights of this font asset. /// @@ -760,6 +763,7 @@ static FontAsset CreateFontAssetInstance(Font font, int atlasPadding, GlyphRende internal static Action SetAtlasTextureIsReadable; internal static Func GetSourceFontRef; internal static Func SetSourceFontGUID; + internal static Func EditorApplicationIsUpdating; // Profiler Marker declarations private static ProfilerMarker k_ReadFontAssetDefinitionMarker = new ProfilerMarker("FontAsset.ReadFontAssetDefinition"); @@ -793,6 +797,10 @@ private void OnValidate() if (Time.frameCount == 0) return; + // See TMPB-187 + if (EditorApplicationIsUpdating?.Invoke() ?? true) + return; + // Make sure our lookup dictionary have been initialized. if (m_CharacterLookupDictionary == null || m_GlyphLookupDictionary == null) ReadFontAssetDefinition(); @@ -1772,6 +1780,11 @@ internal static void UpdateFontAssetsInUpdateQueue() /// internal HashSet m_MissingUnicodesFromFontFile = new HashSet(); + /// + /// Dictionary used to track variant glyph indexes based on their base unicode and their next character unicode. + /// + internal Dictionary<(uint, uint), uint> m_VariantGlyphIndexes = new Dictionary<(uint, uint), uint>(); + /// /// Internal static array used to avoid allocations when using the GetGlyphPairAdjustmentTable(). /// @@ -2354,6 +2367,16 @@ internal bool AddGlyphInternal(uint glyphIndex) return TryAddGlyphInternal(glyphIndex, out glyph); } + internal bool TryAddGlyphVariantIndexInternal(uint unicode, uint nextCharacter, uint variantGlyphIndex) + { + return m_VariantGlyphIndexes.TryAdd((unicode, nextCharacter), variantGlyphIndex); + } + + internal bool TryGetGlyphVariantIndexInternal(uint unicode, uint nextCharacter, out uint variantGlyphIndex) + { + return m_VariantGlyphIndexes.TryGetValue((unicode, nextCharacter), out variantGlyphIndex); + } + /// /// /// diff --git a/Modules/TextCoreTextEngine/Managed/TextAssets/FontAssetUtilities.cs b/Modules/TextCoreTextEngine/Managed/TextAssets/FontAssetUtilities.cs index bcc418c100..254affcf2d 100644 --- a/Modules/TextCoreTextEngine/Managed/TextAssets/FontAssetUtilities.cs +++ b/Modules/TextCoreTextEngine/Managed/TextAssets/FontAssetUtilities.cs @@ -2,6 +2,7 @@ // 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 Unity.Jobs.LowLevel.Unsafe; diff --git a/Modules/TextCoreTextEngine/Managed/TextElementInfo.cs b/Modules/TextCoreTextEngine/Managed/TextElementInfo.cs index 5dfe2afc6f..5178b55706 100644 --- a/Modules/TextCoreTextEngine/Managed/TextElementInfo.cs +++ b/Modules/TextCoreTextEngine/Managed/TextElementInfo.cs @@ -21,7 +21,7 @@ struct TextVertex [VisibleToOtherModules("UnityEngine.IMGUIModule", "UnityEngine.UIElementsModule")] internal struct TextElementInfo { - public char character; // Should be changed to an int to handle UTF 32 + public uint character; public int index; // Index of the character in the input string. public TextElementType elementType; public int stringLength; diff --git a/Modules/TextCoreTextEngine/Managed/TextGenerator/TextGenerationSettings.cs b/Modules/TextCoreTextEngine/Managed/TextGenerator/TextGenerationSettings.cs index 2d105e08cd..3a1c6e641e 100644 --- a/Modules/TextCoreTextEngine/Managed/TextGenerator/TextGenerationSettings.cs +++ b/Modules/TextCoreTextEngine/Managed/TextGenerator/TextGenerationSettings.cs @@ -78,8 +78,66 @@ internal class TextGenerationSettings : IEquatable public float charWidthMaxAdj; internal TextInputSource inputSource = TextInputSource.TextString; + public TextGenerationSettings() { } - public bool Equals(TextGenerationSettings other) + // Used by automated tests + internal TextGenerationSettings(TextGenerationSettings tgs) + { + text = tgs.text; + screenRect = tgs.screenRect; + margins = tgs.margins; + scale = tgs.scale; + fontAsset = tgs.fontAsset; + material = tgs.material; + spriteAsset = tgs.spriteAsset; + styleSheet = tgs.styleSheet; + fontStyle = tgs.fontStyle; + textSettings = tgs.textSettings; + textAlignment = tgs.textAlignment; + overflowMode = tgs.overflowMode; + wordWrap = tgs.wordWrap; + wordWrappingRatio = tgs.wordWrappingRatio; + fontColorGradient = tgs.fontColorGradient; + fontColorGradientPreset = tgs.fontColorGradientPreset; + tintSprites = tgs.tintSprites; + overrideRichTextColors = tgs.overrideRichTextColors; + shouldConvertToLinearSpace = tgs.shouldConvertToLinearSpace; + fontSize = tgs.fontSize; + autoSize = tgs.autoSize; + fontSizeMin = tgs.fontSizeMin; + fontSizeMax = tgs.fontSizeMax; + emojiFallbackSupport = tgs.emojiFallbackSupport; + richText = tgs.richText; + isRightToLeft = tgs.isRightToLeft; + extraPadding = tgs.extraPadding; + parseControlCharacters = tgs.parseControlCharacters; + isOrthographic = tgs.isOrthographic; + isPlaceholder = tgs.isPlaceholder; + tagNoParsing = tgs.tagNoParsing; + characterSpacing = tgs.characterSpacing; + wordSpacing = tgs.wordSpacing; + lineSpacing = tgs.lineSpacing; + paragraphSpacing = tgs.paragraphSpacing; + lineSpacingMax = tgs.lineSpacingMax; + textWrappingMode = tgs.textWrappingMode; + maxVisibleCharacters = tgs.maxVisibleCharacters; + maxVisibleWords = tgs.maxVisibleWords; + maxVisibleLines = tgs.maxVisibleLines; + firstVisibleCharacter = tgs.firstVisibleCharacter; + useMaxVisibleDescender = tgs.useMaxVisibleDescender; + fontWeight = tgs.fontWeight; + pageToDisplay = tgs.pageToDisplay; + horizontalMapping = tgs.horizontalMapping; + verticalMapping = tgs.verticalMapping; + uvLineOffset = tgs.uvLineOffset; + geometrySortingOrder = tgs.geometrySortingOrder; + inverseYAxis = tgs.inverseYAxis; + isIMGUI = tgs.isIMGUI; + charWidthMaxAdj = tgs.charWidthMaxAdj; + inputSource = tgs.inputSource; + } + + public bool Equals(TextGenerationSettings other) { if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; diff --git a/Modules/TextCoreTextEngine/Managed/TextGenerator/TextGeneratorLayout.cs b/Modules/TextCoreTextEngine/Managed/TextGenerator/TextGeneratorLayout.cs index dd09ccc8dc..867c4deaa0 100644 --- a/Modules/TextCoreTextEngine/Managed/TextGenerator/TextGeneratorLayout.cs +++ b/Modules/TextCoreTextEngine/Managed/TextGenerator/TextGeneratorLayout.cs @@ -145,7 +145,7 @@ public void LayoutPhase(TextInfo textInfo, TextGenerationSettings generationSett { FontAsset currentFontAsset = textElementInfos[i].fontAsset; - char unicode = textElementInfos[i].character; + char unicode = (char)textElementInfos[i].character; bool isWhiteSpace = char.IsWhiteSpace(unicode); int currentLine = textElementInfos[i].lineNumber; @@ -216,7 +216,7 @@ public void LayoutPhase(TextInfo textInfo, TextGenerationSettings generationSett // Skip Zero Width Characters and spaces outside of the margins. if (i > lineInfo.lastVisibleCharacterIndex || unicode == 0x0A || unicode == k_SoftHyphen || unicode == k_ZeroWidthSpace || unicode == k_WordJoiner || unicode == k_EndOfText) break; - char lastCharOfCurrentLine = textElementInfos[lineInfo.lastCharacterIndex].character; + char lastCharOfCurrentLine = (char)textElementInfos[lineInfo.lastCharacterIndex].character; bool isFlush = ((HorizontalAlignment)lineAlignment & HorizontalAlignment.Flush) == HorizontalAlignment.Flush; @@ -239,7 +239,7 @@ public void LayoutPhase(TextInfo textInfo, TextGenerationSettings generationSett } else { - float gap = !generationSettings.isRightToLeft ? lineInfo.width - lineInfo.maxAdvance : lineInfo.width + lineInfo.maxAdvance; + float gap = generationSettings.isRightToLeft ? lineInfo.width + lineInfo.maxAdvance : lineInfo.width - lineInfo.maxAdvance; int visibleCount = lineInfo.visibleCharacterCount - 1 + lineInfo.controlCharacterCount; int spaces = lineInfo.spaceCount - lineInfo.controlCharacterCount; @@ -569,7 +569,7 @@ public void LayoutPhase(TextInfo textInfo, TextGenerationSettings generationSett } else if (isStartOfWord || i == 0 && (!char.IsPunctuation(unicode) || isWhiteSpace || unicode == k_ZeroWidthSpace || i == m_CharacterCount - 1)) { - if (i > 0 && i < textElementInfos.Length - 1 && i < m_CharacterCount && (unicode == k_SingleQuote || unicode == k_RightSingleQuote) && char.IsLetterOrDigit(textElementInfos[i - 1].character) && char.IsLetterOrDigit(textElementInfos[i + 1].character)) { } + if (i > 0 && i < textElementInfos.Length - 1 && i < m_CharacterCount && (unicode == k_SingleQuote || unicode == k_RightSingleQuote) && char.IsLetterOrDigit((char)textElementInfos[i - 1].character) && char.IsLetterOrDigit((char)textElementInfos[i + 1].character)) { } else { wordLastChar = i == m_CharacterCount - 1 && char.IsLetterOrDigit(unicode) ? i : i - 1; diff --git a/Modules/TextCoreTextEngine/Managed/TextGenerator/TextGeneratorParsing.cs b/Modules/TextCoreTextEngine/Managed/TextGenerator/TextGeneratorParsing.cs index 943afd4262..94845ad2ca 100644 --- a/Modules/TextCoreTextEngine/Managed/TextGenerator/TextGeneratorParsing.cs +++ b/Modules/TextCoreTextEngine/Managed/TextGenerator/TextGeneratorParsing.cs @@ -403,7 +403,7 @@ public void ParsingPhase(TextInfo textInfo, TextGenerationSettings generationSet #endregion // Store some of the text object's information - textInfo.textElementInfo[m_CharacterCount].character = (char)charCode; + textInfo.textElementInfo[m_CharacterCount].character = charCode; textInfo.textElementInfo[m_CharacterCount].pointSize = m_CurrentFontSize; textInfo.textElementInfo[m_CharacterCount].color = m_HtmlColor; textInfo.textElementInfo[m_CharacterCount].underlineColor = m_UnderlineColor; @@ -753,8 +753,6 @@ public void ParsingPhase(TextInfo textInfo, TextGenerationSettings generationSet // Set Characters to not visible by default. textInfo.textElementInfo[m_CharacterCount].isVisible = false; - bool isJustifiedOrFlush = ((HorizontalAlignment)m_LineJustification & HorizontalAlignment.Flush) == HorizontalAlignment.Flush || ((HorizontalAlignment)m_LineJustification & HorizontalAlignment.Justified) == HorizontalAlignment.Justified; - // Setup Mesh for visible text elements. ie. not a SPACE / LINEFEED / CARRIAGE RETURN. #region Handle Visible Characters @@ -962,7 +960,7 @@ public void ParsingPhase(TextInfo textInfo, TextGenerationSettings generationSet #region Current Line Horizontal Bounds Check - if (isBaseGlyph && textWidth > widthOfTextArea * (isJustifiedOrFlush ? 1.05f : 1.0f)) + if (isBaseGlyph && textWidth > widthOfTextArea) { // Handle Line Breaking (if still possible) if (wordWrap != TextWrappingMode.NoWrap && wordWrap != TextWrappingMode.PreserveWhitespaceNoWrap && m_CharacterCount != m_FirstCharacterOfLine) @@ -1026,7 +1024,7 @@ public void ParsingPhase(TextInfo textInfo, TextGenerationSettings generationSet if (m_CharWidthAdjDelta > 0) adjustedTextWidth /= 1f - m_CharWidthAdjDelta; - float adjustmentDelta = textWidth - (widthOfTextArea - 0.0001f) * (isJustifiedOrFlush ? 1.05f : 1.0f); + float adjustmentDelta = textWidth - (widthOfTextArea - 0.0001f); m_CharWidthAdjDelta += adjustmentDelta / adjustedTextWidth; m_CharWidthAdjDelta = Mathf.Min(m_CharWidthAdjDelta, generationSettings.charWidthMaxAdj / 100); @@ -1106,7 +1104,7 @@ public void ParsingPhase(TextInfo textInfo, TextGenerationSettings generationSet if (m_CharWidthAdjDelta > 0) adjustedTextWidth /= 1f - m_CharWidthAdjDelta; - float adjustmentDelta = textWidth - (widthOfTextArea - 0.0001f) * (isJustifiedOrFlush ? 1.05f : 1.0f); + float adjustmentDelta = textWidth - (widthOfTextArea - 0.0001f); m_CharWidthAdjDelta += adjustmentDelta / adjustedTextWidth; m_CharWidthAdjDelta = Mathf.Min(m_CharWidthAdjDelta, generationSettings.charWidthMaxAdj / 100); @@ -1243,7 +1241,7 @@ public void ParsingPhase(TextInfo textInfo, TextGenerationSettings generationSet if (m_CharWidthAdjDelta > 0) adjustedTextWidth /= 1f - m_CharWidthAdjDelta; - float adjustmentDelta = textWidth - (widthOfTextArea - 0.0001f) * (isJustifiedOrFlush ? 1.05f : 1.0f); + float adjustmentDelta = textWidth - (widthOfTextArea - 0.0001f); m_CharWidthAdjDelta += adjustmentDelta / adjustedTextWidth; m_CharWidthAdjDelta = Mathf.Min(m_CharWidthAdjDelta, generationSettings.charWidthMaxAdj / 100); @@ -1433,7 +1431,7 @@ public void ParsingPhase(TextInfo textInfo, TextGenerationSettings generationSet float textWidth = Mathf.Abs(m_XAdvance) + (!generationSettings.isRightToLeft ? m_Ellipsis.character.m_Glyph.metrics.horizontalAdvance : 0) * (1 - m_CharWidthAdjDelta) * scale; float widthOfTextAreaForEllipsis = m_Width != -1 ? Mathf.Min(marginWidth + 0.0001f - marginLeft - marginRight, m_Width) : marginWidth + 0.0001f - marginLeft - marginRight; - if (textWidth < widthOfTextAreaForEllipsis * (isJustifiedOrFlush ? 1.05f : 1.0f)) + if (textWidth < widthOfTextAreaForEllipsis) { SaveWordWrappingState(ref m_SavedEllipsisState, i, m_CharacterCount, textInfo); m_EllipsisInsertionCandidateStack.Push(m_SavedEllipsisState); @@ -1678,7 +1676,7 @@ public void ParsingPhase(TextInfo textInfo, TextGenerationSettings generationSet bool shouldSaveHardLineBreak = false; bool shouldSaveSoftLineBreak = false; - if ((isWhiteSpace || charCode == k_ZeroWidthSpace || (charCode == k_HyphenMinus && (m_CharacterCount <= 0 || char.IsWhiteSpace(textInfo.textElementInfo[m_CharacterCount - 1].character) == false)) || charCode == k_SoftHyphen) && (!m_IsNonBreakingSpace || ignoreNonBreakingSpace) && charCode != k_NoBreakSpace && charCode != k_FigureSpace && charCode != k_NonBreakingHyphen && charCode != k_NarrowNoBreakSpace && charCode != k_WordJoiner) + if ((isWhiteSpace || charCode == k_ZeroWidthSpace || (charCode == k_HyphenMinus && (m_CharacterCount <= 0 || char.IsWhiteSpace((char)textInfo.textElementInfo[m_CharacterCount - 1].character) == false)) || charCode == k_SoftHyphen) && (!m_IsNonBreakingSpace || ignoreNonBreakingSpace) && charCode != k_NoBreakSpace && charCode != k_FigureSpace && charCode != k_NonBreakingHyphen && charCode != k_NarrowNoBreakSpace && charCode != k_WordJoiner) { isFirstWordOfLine = false; shouldSaveHardLineBreak = true; diff --git a/Modules/TextCoreTextEngine/Managed/TextGenerator/TextGeneratorPreferredValues.cs b/Modules/TextCoreTextEngine/Managed/TextGenerator/TextGeneratorPreferredValues.cs index 8070576e30..177ca500f2 100644 --- a/Modules/TextCoreTextEngine/Managed/TextGenerator/TextGeneratorPreferredValues.cs +++ b/Modules/TextCoreTextEngine/Managed/TextGenerator/TextGeneratorPreferredValues.cs @@ -815,8 +815,7 @@ protected virtual Vector2 CalculatePreferredValues(ref float fontSize, Vector2 m // Compute Preferred Width & Height renderedWidth = Mathf.Max(renderedWidth, textWidth + m_MarginLeft + m_MarginRight); - renderedHeight = m_MaxAscender - m_MaxDescender; - + renderedHeight = Mathf.Max(renderedHeight, m_MaxAscender - m_MaxDescender); } #endregion Handle Visible Characters @@ -965,7 +964,7 @@ protected virtual Vector2 CalculatePreferredValues(ref float fontSize, Vector2 m if ((isWhiteSpace || charCode == k_ZeroWidthSpace || charCode == k_HyphenMinus || charCode == k_SoftHyphen) && (!m_IsNonBreakingSpace || ignoreNonBreakingSpace) && charCode != k_NoBreakSpace && charCode != k_FigureSpace && charCode != k_NonBreakingHyphen && charCode != k_NarrowNoBreakSpace && charCode != k_WordJoiner) { // Ignore Hyphen (0x2D) when preceded by a whitespace - if ((charCode == k_HyphenMinus && m_CharacterCount > 0 && char.IsWhiteSpace(textInfo.textElementInfo[m_CharacterCount - 1].character)) == false) + if ((charCode == k_HyphenMinus && m_CharacterCount > 0 && char.IsWhiteSpace((char)textInfo.textElementInfo[m_CharacterCount - 1].character)) == false) { isFirstWordOfLine = false; shouldSaveHardLineBreak = true; diff --git a/Modules/TextCoreTextEngine/Managed/TextGenerator/TextGeneratorPrepare.cs b/Modules/TextCoreTextEngine/Managed/TextGenerator/TextGeneratorPrepare.cs index 39b244f7fc..6285a3c65a 100644 --- a/Modules/TextCoreTextEngine/Managed/TextGenerator/TextGeneratorPrepare.cs +++ b/Modules/TextCoreTextEngine/Managed/TextGenerator/TextGeneratorPrepare.cs @@ -324,8 +324,13 @@ int SetArraySizes(TextProcessingElement[] textProcessingArray, TextGenerationSet uint nextCharacter = i + 1 < textProcessingArray.Length ? (uint)textProcessingArray[i + 1].unicode : 0; if (nextCharacter >= 0xFE00 && nextCharacter <= 0xFE0F) { + uint variantGlyphIndex; // Get potential variant glyph index - uint variantGlyphIndex = m_CurrentFontAsset.GetGlyphVariantIndex((uint)unicode, nextCharacter); + if (!m_CurrentFontAsset.TryGetGlyphVariantIndexInternal(unicode, nextCharacter, out variantGlyphIndex)) + { + variantGlyphIndex = m_CurrentFontAsset.GetGlyphVariantIndex(unicode, nextCharacter); + m_CurrentFontAsset.TryAddGlyphVariantIndexInternal(unicode, nextCharacter, variantGlyphIndex); + } if (variantGlyphIndex != 0) { @@ -1210,14 +1215,19 @@ bool PopulateFontAsset(TextGenerationSettings generationSettings, TextProcessing uint nextCharacter = i + 1 < textProcessingArray.Length ? (uint)textProcessingArray[i + 1].unicode : 0; if (nextCharacter >= 0xFE00 && nextCharacter <= 0xFE0F) { - // Get potential variant glyph index - uint variantGlyphIndex = m_CurrentFontAsset.GetGlyphVariantIndex((uint)unicode, nextCharacter); - if (variantGlyphIndex != 0) + uint variantGlyphIndex; + // Get potential variant glyph index + if (!m_CurrentFontAsset.TryGetGlyphVariantIndexInternal(unicode, nextCharacter, out variantGlyphIndex)) { if (!isMainThread) return false; + variantGlyphIndex = m_CurrentFontAsset.GetGlyphVariantIndex(unicode, nextCharacter); + m_CurrentFontAsset.TryAddGlyphVariantIndexInternal(unicode, nextCharacter, variantGlyphIndex); + } + if (variantGlyphIndex != 0) + { m_CurrentFontAsset.TryAddGlyphInternal(variantGlyphIndex, out Glyph glyph); } diff --git a/Modules/TextCoreTextEngine/Managed/TextHandle.cs b/Modules/TextCoreTextEngine/Managed/TextHandle.cs index f729fd516d..c4fe1dea29 100644 --- a/Modules/TextCoreTextEngine/Managed/TextHandle.cs +++ b/Modules/TextCoreTextEngine/Managed/TextHandle.cs @@ -758,7 +758,21 @@ public string Substring(int startIndex, int length) var result = new StringBuilder(length); for (int i = startIndex; i < startIndex + length; ++i) - result.Append(textInfo.textElementInfo[i].character); + { + var codePoint = textInfo.textElementInfo[i].character; + if (codePoint >= CodePoint.UNICODE_PLANE01_START && codePoint <= CodePoint.UNICODE_PLANE16_END) + { + uint highSurrogate = CodePoint.HIGH_SURROGATE_START + ((codePoint - CodePoint.UNICODE_PLANE01_START) >> 10); + uint lowSurrogate = CodePoint.LOW_SURROGATE_START + ((codePoint - CodePoint.UNICODE_PLANE01_START) & CodePoint.LOWEST_10BITS_MASK); + + result.Append((char)highSurrogate); + result.Append((char)lowSurrogate); + } + else + { + result.Append((char)codePoint); + } + } return result.ToString(); } diff --git a/Modules/TextCoreTextEngine/Managed/TextMarkupTagsCommon.cs b/Modules/TextCoreTextEngine/Managed/TextMarkupTagsCommon.cs index a77cdf9423..38c7ca8282 100644 --- a/Modules/TextCoreTextEngine/Managed/TextMarkupTagsCommon.cs +++ b/Modules/TextCoreTextEngine/Managed/TextMarkupTagsCommon.cs @@ -216,5 +216,7 @@ internal static class CodePoint public const uint LOW_SURROGATE_START = 0xDC00; public const uint LOW_SURROGATE_END = 0xDFFF; public const uint UNICODE_PLANE01_START = 0x10000; + public const uint UNICODE_PLANE16_END = 0x10FFFF; + public const uint LOWEST_10BITS_MASK = 0x3FF; } } diff --git a/Modules/TextCoreTextEngineEditor/Managed/TextCoreContextMenuItems.cs b/Modules/TextCoreTextEngineEditor/Managed/TextCoreContextMenuItems.cs index c62c3fc1e9..5402f4c9d4 100644 --- a/Modules/TextCoreTextEngineEditor/Managed/TextCoreContextMenuItems.cs +++ b/Modules/TextCoreTextEngineEditor/Managed/TextCoreContextMenuItems.cs @@ -307,7 +307,7 @@ static void ResetSettings(MenuCommand command) /// /// /// - [MenuItem("CONTEXT/Font Asset/Update Atlas Texture...", priority = 2000)] + [MenuItem("CONTEXT/FontAsset/Update Atlas Texture...", priority = 2000)] static void RegenerateFontAsset(MenuCommand command) { FontAsset fontAsset = command.context as FontAsset; @@ -322,13 +322,13 @@ static void RegenerateFontAsset(MenuCommand command) /// Clear Dynamic Font Asset data such as glyph, character and font features. /// /// - [MenuItem("CONTEXT/Font Asset/Reset", validate = true)] + [MenuItem("CONTEXT/FontAsset/Reset", validate = true)] static bool ClearFontAssetDataValidate(MenuCommand command) { return AssetDatabase.IsOpenForEdit(command.context); } - [MenuItem("CONTEXT/Font Asset/Reset", priority = 2100)] + [MenuItem("CONTEXT/FontAsset/Reset", priority = 2100)] static void ClearFontAssetData(MenuCommand command) { FontAsset fontAsset = command.context as FontAsset; @@ -339,13 +339,20 @@ static void ClearFontAssetData(MenuCommand command) if (Selection.activeObject != fontAsset) Selection.activeObject = fontAsset; + ClearFontAssetData(fontAsset); + } + + internal static void ClearFontAssetData(FontAsset fontAsset) + { + if (fontAsset == null) + return; + fontAsset.ClearFontAssetData(true); TextResourceManager.RebuildFontAssetCache(); TextEventManager.ON_FONT_PROPERTY_CHANGED(true, fontAsset); } - // Context Menus for TMPro Font Assets //This function is used for debugging and fixing potentially broken font atlas links. [MenuItem("CONTEXT/Font Asset/Extract Atlas", priority = 2101)] static void ExtractAtlas(MenuCommand command) @@ -375,17 +382,18 @@ static void ExtractAtlas(MenuCommand command) AssetDatabase.Refresh(); DestroyImmediate(tex); } + /// /// Clear Character and Glyph data (only). /// /// - [MenuItem("CONTEXT/Font Asset/Clear Dynamic Data", validate = true)] + [MenuItem("CONTEXT/FontAsset/Clear Dynamic Data", validate = true, priority = 2102)] static bool ClearFontCharacterDataValidate(MenuCommand command) { return AssetDatabase.IsOpenForEdit(command.context); } - [MenuItem("CONTEXT/Font Asset/Clear Dynamic Data", priority = 2102)] + [MenuItem("CONTEXT/FontAsset/Clear Dynamic Data", priority = 2102)] static void ClearFontCharacterData(MenuCommand command) { FontAsset fontAsset = command.context as FontAsset; @@ -396,6 +404,14 @@ static void ClearFontCharacterData(MenuCommand command) if (Selection.activeObject != fontAsset) Selection.activeObject = fontAsset; + ClearFontCharacterData(fontAsset); + } + + internal static void ClearFontCharacterData(FontAsset fontAsset) + { + if (fontAsset == null) + return; + fontAsset.ClearCharacterAndGlyphTablesInternal(); TextEventManager.ON_FONT_PROPERTY_CHANGED(true, fontAsset); @@ -405,13 +421,13 @@ static void ClearFontCharacterData(MenuCommand command) /// Import all font features /// /// - [MenuItem("CONTEXT/Font Asset/Import Font Features", validate = true)] + [MenuItem("CONTEXT/FontAsset/Import Font Features", validate = true, priority = 2110)] static bool ReimportFontFeaturesValidate(MenuCommand command) { return AssetDatabase.IsOpenForEdit(command.context); } - [MenuItem("CONTEXT/Font Asset/Import Font Features", priority = 2110)] + [MenuItem("CONTEXT/FontAsset/Import Font Features", priority = 2110)] static void ReimportFontFeatures(MenuCommand command) { FontAsset fontAsset = command.context as FontAsset; diff --git a/Modules/TextCoreTextEngineEditor/Managed/TextEditorResourceManager.cs b/Modules/TextCoreTextEngineEditor/Managed/TextEditorResourceManager.cs index ab22a16bc9..bc10180705 100644 --- a/Modules/TextCoreTextEngineEditor/Managed/TextEditorResourceManager.cs +++ b/Modules/TextCoreTextEngineEditor/Managed/TextEditorResourceManager.cs @@ -21,6 +21,7 @@ internal static void InitializeFontAssetResourceChangeCallBacks() FontAsset.SetAtlasTextureIsReadable += FontEngineEditorUtilities.SetAtlasTextureIsReadable; FontAsset.GetSourceFontRef += TextEditorResourceManager.GetSourceFontRef; FontAsset.SetSourceFontGUID += TextEditorResourceManager.SetSourceFontGUID; + FontAsset.EditorApplicationIsUpdating += () => EditorApplication.isUpdating; // Callback to handle clearing dynamic font asset data when closing the Editor EditorApplication.quitting += () => diff --git a/Modules/UIBuilder/Editor/Builder/Library/BuilderLibraryProjectScanner.cs b/Modules/UIBuilder/Editor/Builder/Library/BuilderLibraryProjectScanner.cs index 18c6db3dcc..ec8794e149 100644 --- a/Modules/UIBuilder/Editor/Builder/Library/BuilderLibraryProjectScanner.cs +++ b/Modules/UIBuilder/Editor/Builder/Library/BuilderLibraryProjectScanner.cs @@ -93,12 +93,12 @@ public void ImportUxmlSerializedDataFromSource(BuilderLibraryItem sourceCategory var shownTypes = new HashSet(); UxmlSerializedDataRegistry.Register(); - foreach (var kvp in UxmlSerializedDataRegistry.SerializedDataTypes) + + var sortedEntries = UxmlSerializedDataRegistry.SerializedDataTypes.Values.OrderBy(o => o.FullName); + foreach (var type in sortedEntries) { try { - var fullname = kvp.Key; - var type = kvp.Value; var elementType = type.DeclaringType; var hasNamespace = !string.IsNullOrEmpty(elementType.Namespace); @@ -122,7 +122,7 @@ public void ImportUxmlSerializedDataFromSource(BuilderLibraryItem sourceCategory if (shownTypes.Contains(type)) continue; - var description = UxmlSerializedDataRegistry.GetDescription(fullname); + var description = UxmlSerializedDataRegistry.GetDescription(elementType.FullName); Debug.AssertFormat(description != null, "Expected to find a description for {0}", elementType.FullName); shownTypes.Add(type); @@ -136,7 +136,7 @@ public void ImportUxmlSerializedDataFromSource(BuilderLibraryItem sourceCategory // Generate a unique id. // We prepend the name as its possible the same Type may be displayed for both UxmlSerializedData and UxmlTraits so we need to ensure the ids do not conflict. - var idCode = ("uxml-serialized-data" + kvp.Key + elementType.FullName).GetHashCode(); + var idCode = ("uxml-serialized-data" + elementType.FullName + elementType.FullName).GetHashCode(); var newItem = BuilderLibraryContent.CreateItem(name, "CustomCSharpElement", elementType, () => { diff --git a/Modules/UIElements/Core/Bindings/BindingActivationContext.cs b/Modules/UIElements/Core/Bindings/BindingActivationContext.cs index 970e4293ab..8715e925ee 100644 --- a/Modules/UIElements/Core/Bindings/BindingActivationContext.cs +++ b/Modules/UIElements/Core/Bindings/BindingActivationContext.cs @@ -22,7 +22,7 @@ public readonly struct BindingActivationContext /// public BindingId bindingId => m_BindingId; - internal BindingActivationContext(VisualElement element, BindingId property) + internal BindingActivationContext(VisualElement element, in BindingId property) { m_TargetElement = element; m_BindingId = property; diff --git a/Modules/UIElements/Core/Bindings/BindingContext.cs b/Modules/UIElements/Core/Bindings/BindingContext.cs index ae9daa6ab5..68ab640d54 100644 --- a/Modules/UIElements/Core/Bindings/BindingContext.cs +++ b/Modules/UIElements/Core/Bindings/BindingContext.cs @@ -44,8 +44,8 @@ public readonly struct BindingContext internal BindingContext( VisualElement targetElement, - BindingId bindingId, - PropertyPath resolvedDataSourcePath, + in BindingId bindingId, + in PropertyPath resolvedDataSourcePath, object resolvedDataSource) { m_TargetElement = targetElement; diff --git a/Modules/UIElements/Core/Bindings/BindingTarget.cs b/Modules/UIElements/Core/Bindings/BindingTarget.cs index 9c76697a46..03e688d2d4 100644 --- a/Modules/UIElements/Core/Bindings/BindingTarget.cs +++ b/Modules/UIElements/Core/Bindings/BindingTarget.cs @@ -9,7 +9,7 @@ internal readonly struct BindingTarget public readonly VisualElement element; public readonly BindingId bindingId; - public BindingTarget(VisualElement element, BindingId bindingId) + public BindingTarget(VisualElement element, in BindingId bindingId) { this.element = element; this.bindingId = bindingId; diff --git a/Modules/UIElements/Core/Bindings/BindingUpdater.cs b/Modules/UIElements/Core/Bindings/BindingUpdater.cs index f39054f71d..eb8f5bdd82 100644 --- a/Modules/UIElements/Core/Bindings/BindingUpdater.cs +++ b/Modules/UIElements/Core/Bindings/BindingUpdater.cs @@ -373,7 +373,7 @@ internal static string GetVisitationErrorString(VisitReturnCode returnCode, in B } // Internal for tests - internal static string GetExtractValueErrorString(VisitReturnCode returnCode, object target, PropertyPath path) + internal static string GetExtractValueErrorString(VisitReturnCode returnCode, object target, in PropertyPath path) { var prefix = $"[UI Toolkit] Could not retrieve the value at path '{path}' for source of type '{target?.GetType().Name}':"; switch (returnCode) @@ -395,7 +395,7 @@ internal static string GetExtractValueErrorString(VisitReturnCode returnCode, ob } // Internal for tests - internal static string GetSetValueErrorString(VisitReturnCode returnCode, object source, PropertyPath sourcePath, object target, PropertyPath targetPath, object extractedValueFromSource) + internal static string GetSetValueErrorString(VisitReturnCode returnCode, object source, in PropertyPath sourcePath, object target, in PropertyPath targetPath, object extractedValueFromSource) { var prefix = $"[UI Toolkit] Could not set value for target of type '{target.GetType().Name}' at path '{targetPath}':"; switch (returnCode) diff --git a/Modules/UIElements/Core/Bindings/ConverterGroup.cs b/Modules/UIElements/Core/Bindings/ConverterGroup.cs index 010c0d59ec..b84ebe5a49 100644 --- a/Modules/UIElements/Core/Bindings/ConverterGroup.cs +++ b/Modules/UIElements/Core/Bindings/ConverterGroup.cs @@ -100,7 +100,7 @@ public bool TryConvert(ref TSource source, /// The value type to set. /// if the value was set correctly, and otherwise. public bool TrySetValue(ref TContainer container, - PropertyPath path, TValue value, out VisitReturnCode returnCode) + in PropertyPath path, TValue value, out VisitReturnCode returnCode) { if (path.IsEmpty) { diff --git a/Modules/UIElements/Core/Bindings/ConverterGroups.cs b/Modules/UIElements/Core/Bindings/ConverterGroups.cs index 1a210e8390..6de0cad70c 100644 --- a/Modules/UIElements/Core/Bindings/ConverterGroups.cs +++ b/Modules/UIElements/Core/Bindings/ConverterGroups.cs @@ -223,7 +223,7 @@ public static bool TryConvert(ref TSource source, out TDe /// The value type to set. /// if the value was set correctly, and otherwise. public static bool TrySetValueGlobal(ref TContainer container, - PropertyPath path, TValue value, out VisitReturnCode returnCode) + in PropertyPath path, TValue value, out VisitReturnCode returnCode) { if (path.IsEmpty) { diff --git a/Modules/UIElements/Core/Bindings/DataBinding.cs b/Modules/UIElements/Core/Bindings/DataBinding.cs index 394a06e55c..397fe57c9b 100644 --- a/Modules/UIElements/Core/Bindings/DataBinding.cs +++ b/Modules/UIElements/Core/Bindings/DataBinding.cs @@ -265,7 +265,7 @@ protected internal virtual BindingResult UpdateSource(in BindingContext } // Internal for tests - internal static string GetSetValueErrorString(VisitReturnCode returnCode, object source, PropertyPath sourcePath, object target, BindingId targetPath, TValue extractedValueFromSource) + internal static string GetSetValueErrorString(VisitReturnCode returnCode, object source, in PropertyPath sourcePath, object target, in BindingId targetPath, TValue extractedValueFromSource) { var prefix = $"[UI Toolkit] Could not set value for target of type '{target.GetType().Name}' at path '{targetPath}':"; switch (returnCode) diff --git a/Modules/UIElements/Core/Bindings/DataBindingManager.cs b/Modules/UIElements/Core/Bindings/DataBindingManager.cs index 1ec8e60a9a..c4b4f9067b 100644 --- a/Modules/UIElements/Core/Bindings/DataBindingManager.cs +++ b/Modules/UIElements/Core/Bindings/DataBindingManager.cs @@ -82,7 +82,7 @@ readonly struct BindingRequest public readonly Binding binding; public readonly bool shouldProcess; - public BindingRequest(BindingId bindingId, Binding binding, bool shouldProcess = true) + public BindingRequest(in BindingId bindingId, Binding binding, bool shouldProcess = true) { this.bindingId = bindingId; this.binding = binding; @@ -108,7 +108,7 @@ public static BindingDataCollection Create() return collection; } - public void AddBindingData(BindingData bindingData) + public void AddBindingData(in BindingData bindingData) { if (m_BindingPerId.TryGetValue(bindingData.target.bindingId, out var toRemove)) { @@ -119,12 +119,12 @@ public void AddBindingData(BindingData bindingData) m_Bindings.Add(bindingData); } - public bool TryGetBindingData(BindingId bindingId, out BindingData data) + public bool TryGetBindingData(in BindingId bindingId, out BindingData data) { return m_BindingPerId.TryGetValue(bindingId, out data); } - public bool RemoveBindingData(BindingData bindingData) + public bool RemoveBindingData(in BindingData bindingData) { if (!m_BindingPerId.TryGetValue(bindingData.target.bindingId, out var toRemove)) return false; @@ -243,7 +243,7 @@ public bool TryGetBindingCollection(VisualElement element, out BindingDataCollec return m_BindingDataPerElement.TryGetValue(element, out collection); } - public void StartTrackingBinding(VisualElement element, BindingData binding) + public void StartTrackingBinding(VisualElement element, in BindingData binding) { BindingDataCollection collection; if (m_BoundElements.Add(element)) @@ -279,7 +279,7 @@ private void OnPropertyChanged(PropertyChangedEvent evt, Dictionary elements, HashSet< } } - public DataSourceContext GetResolvedDataSourceContext(VisualElement element, BindingData bindingData) + public DataSourceContext GetResolvedDataSourceContext(VisualElement element, in BindingData bindingData) { object localDataSource = null; PropertyPath localDataSourcePath = default; @@ -666,7 +666,7 @@ public DataSourceContext GetResolvedDataSourceContext(VisualElement element, Bin private void TrackPropertyChanges(object sender, BindablePropertyChangedEventArgs args) => TrackPropertyChanges(sender, args.propertyName); - private void TrackPropertyChanges(object sender, PropertyPath propertyPath) + private void TrackPropertyChanges(object sender, in PropertyPath propertyPath) { if (!m_DataSourceRefCount.ContainsKey(sender)) return; @@ -776,7 +776,7 @@ PropertyPath GetHierarchyDataSourcePath(VisualElement element) return path; } - void AddHierarchyDataSourcePathForElement(VisualElement element, PropertyPath dataSourcePath) + void AddHierarchyDataSourcePathForElement(VisualElement element, in PropertyPath dataSourcePath) { m_ResolvedHierarchicalDataSourcePathPerElement[element] = dataSourcePath; } @@ -888,27 +888,27 @@ internal void UpdateVersion(object source, long version) m_DataSourceTracker.UpdateVersion(source, version); } - internal void CacheUIBindingResult(BindingData bindingData, BindingResult result) + internal void CacheUIBindingResult(in BindingData bindingData, in BindingResult result) { m_LastUIBindingResultsCache[bindingData] = result; } - internal bool TryGetLastUIBindingResult(BindingData bindingData, out BindingResult result) + internal bool TryGetLastUIBindingResult(in BindingData bindingData, out BindingResult result) { return m_LastUIBindingResultsCache.TryGetValue(bindingData, out result); } - internal void CacheSourceBindingResult(BindingData bindingData, BindingResult result) + internal void CacheSourceBindingResult(in BindingData bindingData, in BindingResult result) { m_LastSourceBindingResultsCache[bindingData] = result; } - internal bool TryGetLastSourceBindingResult(BindingData bindingData, out BindingResult result) + internal bool TryGetLastSourceBindingResult(in BindingData bindingData, out BindingResult result) { return m_LastSourceBindingResultsCache.TryGetValue(bindingData, out result); } - internal DataSourceContext GetResolvedDataSourceContext(VisualElement element, BindingData bindingData) + internal DataSourceContext GetResolvedDataSourceContext(VisualElement element, in BindingData bindingData) { return element.panel == m_Panel ? m_DataSourceTracker.GetResolvedDataSourceContext(element, bindingData) @@ -980,7 +980,7 @@ internal List GetBindingData(VisualElement element) : s_Empty; } - internal bool TryGetBindingData(VisualElement element, BindingId bindingId, out BindingData bindingData) + internal bool TryGetBindingData(VisualElement element, in BindingId bindingId, out BindingData bindingData) { bindingData = default; if (element.panel == m_Panel && m_BindingsTracker.TryGetBindingCollection(element, out var collection)) @@ -992,7 +992,7 @@ internal bool TryGetBindingData(VisualElement element, BindingId bindingId, out return false; } - internal void RegisterBinding(VisualElement element, BindingId bindingId, Binding binding) + internal void RegisterBinding(VisualElement element, in BindingId bindingId, Binding binding) { Assert.IsNotNull(binding); Assert.IsFalse(((PropertyPath)bindingId).IsEmpty, $"[UI Toolkit] Could not register binding on element of type '{element.GetType().Name}': target property path is empty."); @@ -1016,7 +1016,7 @@ internal void RegisterBinding(VisualElement element, BindingId bindingId, Bindin binding.OnActivated(new BindingActivationContext(element, bindingId)); } - internal void UnregisterBinding(VisualElement element, BindingId bindingId) + internal void UnregisterBinding(VisualElement element, in BindingId bindingId) { if (!m_BindingsTracker.TryGetBindingCollection(element, out var collection)) return; @@ -1068,7 +1068,7 @@ public void Dispose() m_DetectedChangesFromUI.Clear(); } - public static void CreateBindingRequest(VisualElement target, BindingId bindingId, Binding binding) + public static void CreateBindingRequest(VisualElement target, in BindingId bindingId, Binding binding) { var requests = (List) target.GetProperty(k_RequestBindingPropertyName); if (requests == null) @@ -1180,7 +1180,7 @@ internal static bool AnyPendingBindingRequests(VisualElement element) } } - internal static bool TryGetBindingRequest(VisualElement element, BindingId bindingId, out Binding binding) + internal static bool TryGetBindingRequest(VisualElement element, in BindingId bindingId, out Binding binding) { var requests = (List) element.GetProperty(k_RequestBindingPropertyName); if (requests == null) diff --git a/Modules/UIElements/Core/Bindings/DataBindingUtility.cs b/Modules/UIElements/Core/Bindings/DataBindingUtility.cs index 0ddde09c62..29a144b7c4 100644 --- a/Modules/UIElements/Core/Bindings/DataBindingUtility.cs +++ b/Modules/UIElements/Core/Bindings/DataBindingUtility.cs @@ -33,7 +33,7 @@ internal readonly struct BindingTypeResult /// public readonly PropertyPath resolvedPath; - internal BindingTypeResult(Type type, PropertyPath resolvedPath) + internal BindingTypeResult(Type type, in PropertyPath resolvedPath) { this.type = type; this.resolvedPath = resolvedPath; @@ -41,7 +41,7 @@ internal BindingTypeResult(Type type, PropertyPath resolvedPath) errorIndex = -1; } - internal BindingTypeResult(VisitReturnCode returnCode, int errorIndex, PropertyPath resolvedPath) + internal BindingTypeResult(VisitReturnCode returnCode, int errorIndex, in PropertyPath resolvedPath) { type = null; this.resolvedPath = resolvedPath; @@ -68,19 +68,19 @@ public readonly struct BindingInfo /// public Binding binding { get; } - private BindingInfo(VisualElement targetElement, BindingId bindingId, Binding binding) + private BindingInfo(VisualElement targetElement, in BindingId bindingId, Binding binding) { this.targetElement = targetElement; this.bindingId = bindingId; this.binding = binding; } - internal static BindingInfo FromRequest(VisualElement target, PropertyPath targetPath, Binding binding) + internal static BindingInfo FromRequest(VisualElement target, in PropertyPath targetPath, Binding binding) { return new BindingInfo(target, targetPath, binding); } - internal static BindingInfo FromBindingData(DataBindingManager.BindingData bindingData) + internal static BindingInfo FromBindingData(in DataBindingManager.BindingData bindingData) { return new BindingInfo(bindingData.target.element, bindingData.target.bindingId, bindingData.binding); } @@ -100,7 +100,7 @@ internal readonly struct PropertyPathInfo /// public readonly Type type; - internal PropertyPathInfo(PropertyPath propertyPath, Type type) + internal PropertyPathInfo(in PropertyPath propertyPath, Type type) { this.propertyPath = propertyPath; this.type = type; @@ -164,7 +164,7 @@ public static void GetBindingsForElement(VisualElement element, ListThe id of the binding. /// The binding found on the element. /// Whether a binding was found or not. - public static bool TryGetBinding(VisualElement element, BindingId bindingId, out BindingInfo bindingInfo) + public static bool TryGetBinding(VisualElement element, in BindingId bindingId, out BindingInfo bindingInfo) { if (DataBindingManager.TryGetBindingRequest(element, bindingId, out var binding)) { @@ -319,7 +319,7 @@ internal static string ReplaceAllIndicesInPath(string path, string newText) /// The element it is applied on. /// The result of the last UI update for this binding, if found. /// Whether a cached result was found or not. - public static bool TryGetLastUIBindingResult(BindingId bindingId, VisualElement element, out BindingResult result) + public static bool TryGetLastUIBindingResult(in BindingId bindingId, VisualElement element, out BindingResult result) { result = default; var bindingData = GetBindingData(bindingId, element); @@ -338,7 +338,7 @@ public static bool TryGetLastUIBindingResult(BindingId bindingId, VisualElement /// The element it is applied on. /// The result of the last source update for this binding, if found. /// Whether a cached result was found or not. - public static bool TryGetLastSourceBindingResult(BindingId bindingId, VisualElement element, out BindingResult result) + public static bool TryGetLastSourceBindingResult(in BindingId bindingId, VisualElement element, out BindingResult result) { result = default; @@ -563,7 +563,7 @@ static void GetPropertyPaths(object dataSource, Type type, int depth, List public DataSourceContext newContext => m_NewContext; - internal DataSourceContextChanged(VisualElement element, BindingId bindingId, DataSourceContext previousContext, DataSourceContext newContext) + internal DataSourceContextChanged(VisualElement element, in BindingId bindingId, in DataSourceContext previousContext, in DataSourceContext newContext) { m_TargetElement = element; m_BindingId = bindingId; diff --git a/Modules/UIElements/Core/Bindings/INotifyBindablePropertyChanged.cs b/Modules/UIElements/Core/Bindings/INotifyBindablePropertyChanged.cs index 642e56b1ac..8fcb76c014 100644 --- a/Modules/UIElements/Core/Bindings/INotifyBindablePropertyChanged.cs +++ b/Modules/UIElements/Core/Bindings/INotifyBindablePropertyChanged.cs @@ -17,7 +17,7 @@ public readonly struct BindablePropertyChangedEventArgs /// Instantiates a new . /// /// The name of the property that has changed. - public BindablePropertyChangedEventArgs(BindingId propertyName) => m_PropertyName = propertyName; + public BindablePropertyChangedEventArgs(in BindingId propertyName) => m_PropertyName = propertyName; /// /// Returns the name of the property that has changed. diff --git a/Modules/UIElements/Core/Bindings/VisualTreeBindingsUpdater.cs b/Modules/UIElements/Core/Bindings/VisualTreeBindingsUpdater.cs index ee85d1c0c4..65d1f9881c 100644 --- a/Modules/UIElements/Core/Bindings/VisualTreeBindingsUpdater.cs +++ b/Modules/UIElements/Core/Bindings/VisualTreeBindingsUpdater.cs @@ -34,7 +34,7 @@ internal class VisualTreeBindingsUpdater : BaseVisualTreeHierarchyTrackerUpdater private readonly HashSet m_ElementsWithBindings = new HashSet(); private readonly HashSet m_ElementsToAdd = new HashSet(); private readonly HashSet m_ElementsToRemove = new HashSet(); - private const int k_MinUpdateDelayMs = 100; + internal const int k_MinUpdateDelayMs = 100; private const int k_MaxBindingTimeMs = 100; private long m_LastUpdateTime = 0; @@ -220,7 +220,7 @@ private static long CurrentTime() return Panel.TimeSinceStartupMs(); } - public static bool ShouldThrottle(long startTime) + public static bool ShouldProcessBindings(long startTime) { return disableBindingsThrottling || (CurrentTime() - startTime) < k_MaxBindingTimeMs; } @@ -256,7 +256,7 @@ public override void Update() using (s_ProfilerBindingRequestsMarker.Auto()) { long startTime = CurrentTime(); - while (m_ElementsToBind.Count > 0 && ShouldThrottle(startTime)) + while (m_ElementsToBind.Count > 0 && ShouldProcessBindings(startTime)) { var element = m_ElementsToBind.FirstOrDefault(); diff --git a/Modules/UIElements/Core/Bindings/VisualTreeDataBindingUpdater.cs b/Modules/UIElements/Core/Bindings/VisualTreeDataBindingUpdater.cs index f585a7afc1..e8a3e89f36 100644 --- a/Modules/UIElements/Core/Bindings/VisualTreeDataBindingUpdater.cs +++ b/Modules/UIElements/Core/Bindings/VisualTreeDataBindingUpdater.cs @@ -18,6 +18,8 @@ enum BindingUpdateStage class VisualTreeDataBindingsUpdater : BaseVisualTreeHierarchyTrackerUpdater { + public long frame { get; private set; } + readonly struct VersionInfo { public readonly object source; @@ -86,7 +88,7 @@ public override void OnVersionChanged(VisualElement ve, VersionChangeType versio m_DataSourceChangedRequests.Add(ve); } - void CacheAndLogBindingResult(bool appliedOnUiCache, DataBindingManager.BindingData bindingData, BindingResult result) + void CacheAndLogBindingResult(bool appliedOnUiCache, in DataBindingManager.BindingData bindingData, in BindingResult result) { var logLevel = bindingManager.logLevel; @@ -118,7 +120,7 @@ void CacheAndLogBindingResult(bool appliedOnUiCache, DataBindingManager.BindingD bindingManager.CacheSourceBindingResult(bindingData, result); } - void LogResult(BindingResult result) + void LogResult(in BindingResult result) { if (string.IsNullOrWhiteSpace(result.message)) return; @@ -135,6 +137,7 @@ void LogResult(BindingResult result) public override void Update() { + ++frame; base.Update(); ProcessAllBindingRequests(); @@ -267,7 +270,7 @@ public override void Update() return (true, 0L); } - private bool IsPrefix(PropertyPath prefix, PropertyPath path) + private bool IsPrefix(in PropertyPath prefix, in PropertyPath path) { if (path.Length < prefix.Length) return false; diff --git a/Modules/UIElements/Core/Collections/Controllers/CollectionViewController.cs b/Modules/UIElements/Core/Collections/Controllers/CollectionViewController.cs index d3ac419c62..e3a731b5ec 100644 --- a/Modules/UIElements/Core/Collections/Controllers/CollectionViewController.cs +++ b/Modules/UIElements/Core/Collections/Controllers/CollectionViewController.cs @@ -38,15 +38,12 @@ public virtual IList itemsSource { if (m_ItemsSource == value) return; - - var hadValidDataAndBindings = m_View.HasValidDataAndBindings(); m_ItemsSource = value; - // We need to make sure to RefreshItems if itemsSource was set last, but only when we're not using + // We need RefreshItems if itemsSource was changed, but only when we're not using // ListViewBindings, that does the refresh already when the SerializedProperty is ready. - if (!hadValidDataAndBindings && m_View.HasValidDataAndBindings() && - m_View.GetProperty(BaseVerticalCollectionView.internalBindingKey) == null) + if (m_View.GetProperty(BaseVerticalCollectionView.internalBindingKey) == null) { m_View.RefreshItems(); } diff --git a/Modules/UIElements/Core/Controls/BaseTreeView.cs b/Modules/UIElements/Core/Controls/BaseTreeView.cs index c48e124415..34feec8b3e 100644 --- a/Modules/UIElements/Core/Controls/BaseTreeView.cs +++ b/Modules/UIElements/Core/Controls/BaseTreeView.cs @@ -176,7 +176,6 @@ public override void SetViewController(CollectionViewController controller) } base.SetViewController(controller); - RefreshItems(); if (viewController != null) { diff --git a/Modules/UIElements/Core/Controls/BaseVerticalCollectionView.cs b/Modules/UIElements/Core/Controls/BaseVerticalCollectionView.cs index 07fbd98c4d..07b8cb9fb7 100644 --- a/Modules/UIElements/Core/Controls/BaseVerticalCollectionView.cs +++ b/Modules/UIElements/Core/Controls/BaseVerticalCollectionView.cs @@ -1006,6 +1006,7 @@ public void RefreshItem(int index) { if (recycledItem.index == index) { + viewController.InvokeUnbindItem(recycledItem, recycledItem.index); viewController.InvokeBindItem(recycledItem, recycledItem.index); break; } diff --git a/Modules/UIElements/Core/Controls/GenericDropdownMenu.cs b/Modules/UIElements/Core/Controls/GenericDropdownMenu.cs index 1442c8ebb4..c775f25a59 100644 --- a/Modules/UIElements/Core/Controls/GenericDropdownMenu.cs +++ b/Modules/UIElements/Core/Controls/GenericDropdownMenu.cs @@ -208,10 +208,11 @@ public MenuItem GetChild(string name) }, k_OptimizedMenus); static readonly ObjectPool s_ItemPool = new(() => { + // Since item element callback lists are cleared we cannot declare + // any callbacks in the constructor code as it will get deleted + // upon release and won't work upon reuse. var item = new VisualElement(); item.AddToClassList(itemUssClassName); - item.RegisterCallback(e => item.parent.parent.parent.EnableInClassList(clickUssClassName, true)); - item.RegisterCallback(e => item.parent.parent.parent.EnableInClassList(clickUssClassName, false)); var leftAppendix = new VisualElement(); leftAppendix.pickingMode = PickingMode.Ignore; @@ -374,6 +375,8 @@ void ReleasePooledItems(MenuItem root) } } + root.element.m_CallbackRegistry.m_BubbleUpCallbacks.GetCallbackListForWriting().Clear(); + root.element.m_CallbackRegistry.m_TrickleDownCallbacks.GetCallbackListForWriting().Clear(); s_ItemPool.Release(root.element); } @@ -917,6 +920,8 @@ MenuItem GetOrCreateParents(ref string path, Texture2D icon = null, bool canCrea VisualElement BuildItem(string name, bool isChecked, bool isEnabled, bool isSubmenu, object data, Texture2D icon, string tooltip) { var item = s_ItemPool.Get(); + item.RegisterCallback(e => item.parent.parent.parent.EnableInClassList(clickUssClassName, true)); + item.RegisterCallback(e => item.parent.parent.parent.EnableInClassList(clickUssClassName, false)); item.SetEnabled(isEnabled); item.userData = data; item.tooltip = tooltip; diff --git a/Modules/UIElements/Core/Controls/InputField/BaseField.cs b/Modules/UIElements/Core/Controls/InputField/BaseField.cs index 6d6eb3ad40..5c519f497e 100644 --- a/Modules/UIElements/Core/Controls/InputField/BaseField.cs +++ b/Modules/UIElements/Core/Controls/InputField/BaseField.cs @@ -12,6 +12,7 @@ namespace UnityEngine.UIElements internal interface IPrefixLabel { string label { get; } + Label labelElement { get; } } internal interface IDelayedField diff --git a/Modules/UIElements/Core/Controls/MultiColumn/MultiColumnCollectionHeader.cs b/Modules/UIElements/Core/Controls/MultiColumn/MultiColumnCollectionHeader.cs index bb4bb0cbc3..5b87d0037e 100644 --- a/Modules/UIElements/Core/Controls/MultiColumn/MultiColumnCollectionHeader.cs +++ b/Modules/UIElements/Core/Controls/MultiColumn/MultiColumnCollectionHeader.cs @@ -17,8 +17,9 @@ class MultiColumnCollectionHeader : VisualElement, IDisposable const int kMaxStableLayoutPassCount = 2; // Beyond this threshold, DoLayout must be performed in the next frame; otherwise, this may lead to Layout instabilities. This is caused by the dependencies between the geometries of the header, the viewport and the content. [Serializable] - class ViewState : ISerializationCallbackReceiver + class ViewState { + [SerializeField] bool m_HasPersistedData; /// @@ -117,16 +118,6 @@ internal void Apply(MultiColumnCollectionHeader header) header.sortDescriptions.Add(sortDesc); } } - - public void OnBeforeSerialize() - { - m_HasPersistedData = true; - } - - public void OnAfterDeserialize() - { - m_HasPersistedData = true; - } } internal class ColumnData @@ -175,6 +166,8 @@ public SortedColumnState(SortColumnDescription desc, SortDirection dir) ViewState m_ViewState; bool m_ApplyingViewState; + internal bool isApplyingViewState => m_ApplyingViewState; + bool m_DoLayoutScheduled; /// @@ -243,6 +236,11 @@ public bool sortingEnabled /// public event Action contextMenuPopulateEvent; + /// + /// Sent whenever a ContextMenuPopulate event sent allowing user code to add its own actions to the context menu. + /// + internal event Action viewDataRestored; + /// /// Default constructor. /// @@ -826,6 +824,8 @@ internal override void OnViewDataReady() m_ViewState = GetOrCreateViewData(m_ViewState, key); m_ViewState.Apply(this); + + viewDataRestored?.Invoke(); } finally { diff --git a/Modules/UIElements/Core/Controls/MultiColumn/MultiColumnController.cs b/Modules/UIElements/Core/Controls/MultiColumn/MultiColumnController.cs index c1f75cc3af..f60888e0fd 100644 --- a/Modules/UIElements/Core/Controls/MultiColumn/MultiColumnController.cs +++ b/Modules/UIElements/Core/Controls/MultiColumn/MultiColumnController.cs @@ -66,6 +66,7 @@ public MultiColumnController(Columns columns, SortColumnDescriptions sortDescrip m_MultiColumnHeader.columnSortingChanged += OnColumnSortingChanged; m_MultiColumnHeader.contextMenuPopulateEvent += OnContextMenuPopulateEvent; m_MultiColumnHeader.columnResized += OnColumnResized; + m_MultiColumnHeader.viewDataRestored += OnViewDataRestored; m_MultiColumnHeader.columns.columnAdded += OnColumnAdded; m_MultiColumnHeader.columns.columnRemoved += OnColumnRemoved; @@ -234,6 +235,7 @@ public void Dispose() m_MultiColumnHeader.columnSortingChanged -= OnColumnSortingChanged; m_MultiColumnHeader.contextMenuPopulateEvent -= OnContextMenuPopulateEvent; m_MultiColumnHeader.columnResized -= OnColumnResized; + m_MultiColumnHeader.viewDataRestored -= OnViewDataRestored; m_MultiColumnHeader.columns.columnAdded -= OnColumnAdded; m_MultiColumnHeader.columns.columnRemoved -= OnColumnRemoved; m_MultiColumnHeader.columns.columnReordered -= OnColumnReordered; @@ -297,17 +299,31 @@ void OnColumnRemoved(Column column) void OnColumnReordered(Column column, int from, int to) { + if (m_MultiColumnHeader.isApplyingViewState) + return; + m_View.Rebuild(); } void OnColumnsChanged(Column column, ColumnDataType type) { + if (m_MultiColumnHeader.isApplyingViewState) + return; + if (type == ColumnDataType.Visibility) m_View.Rebuild(); } void OnColumnChanged(ColumnsDataType type) { + if (m_MultiColumnHeader.isApplyingViewState) + return; + if (type == ColumnsDataType.PrimaryColumn) m_View.Rebuild(); } + + void OnViewDataRestored() + { + m_View.Rebuild(); + } } } diff --git a/Modules/UIElements/Core/Controls/MultiColumn/MultiColumnListView.cs b/Modules/UIElements/Core/Controls/MultiColumn/MultiColumnListView.cs index 20bee4786b..5e600d8c2e 100644 --- a/Modules/UIElements/Core/Controls/MultiColumn/MultiColumnListView.cs +++ b/Modules/UIElements/Core/Controls/MultiColumn/MultiColumnListView.cs @@ -30,19 +30,19 @@ public override void Deserialize(object obj) var e = (MultiColumnListView)obj; e.sortingEnabled = sortingEnabled; - if (columns != null) - { - var c = new Columns(); - columns.Deserialize(c); - e.columns = c; - } - if (sortColumnDescriptions != null) { var c = new SortColumnDescriptions(); sortColumnDescriptions.Deserialize(c); e.sortColumnDescriptions = c; } + + if (columns != null) + { + var c = new Columns(); + columns.Deserialize(c); + e.columns = c; + } } } @@ -180,6 +180,14 @@ public MultiColumnListView() /// Column definitions used to initialize the header. public MultiColumnListView(Columns columns) { + // Setting the view data key on the ScrollView to get view data persistence on the header which + // is inside the contentViewport. + // Disabling view data persistence on the vertical and horizontal scrollers to make sure we keep + // the previous behavior on the scrollOffset. + scrollView.viewDataKey = "unity-multi-column-scroll-view"; + scrollView.verticalScroller.viewDataKey = null; + scrollView.horizontalScroller.viewDataKey = null; + this.columns = columns ?? new Columns(); } diff --git a/Modules/UIElements/Core/Controls/MultiColumn/MultiColumnTreeView.cs b/Modules/UIElements/Core/Controls/MultiColumn/MultiColumnTreeView.cs index cd4f2ee17e..eca8d98cf2 100644 --- a/Modules/UIElements/Core/Controls/MultiColumn/MultiColumnTreeView.cs +++ b/Modules/UIElements/Core/Controls/MultiColumn/MultiColumnTreeView.cs @@ -30,19 +30,19 @@ public override void Deserialize(object obj) var e = (MultiColumnTreeView)obj; e.sortingEnabled = sortingEnabled; - if (columns != null) - { - var c = new Columns(); - columns.Deserialize(c); - e.columns = c; - } - if (sortColumnDescriptions != null) { var c = new SortColumnDescriptions(); sortColumnDescriptions.Deserialize(c); e.sortColumnDescriptions = c; } + + if (columns != null) + { + var c = new Columns(); + columns.Deserialize(c); + e.columns = c; + } } } @@ -179,6 +179,14 @@ public MultiColumnTreeView() /// Column definitions used to initialize the header. public MultiColumnTreeView(Columns columns) { + // Setting the view data key on the ScrollView to get view data persistence on the header which + // is inside the contentViewport. + // Disabling view data persistence on the vertical and horizontal scrollers to make sure we keep + // the previous behavior on the scrollOffset. + scrollView.viewDataKey = "unity-multi-column-scroll-view"; + scrollView.verticalScroller.viewDataKey = null; + scrollView.horizontalScroller.viewDataKey = null; + this.columns = columns ?? new Columns(); } diff --git a/Modules/UIElements/Core/Controls/ToggleButtonGroup/ToggleButtonGroupState.cs b/Modules/UIElements/Core/Controls/ToggleButtonGroup/ToggleButtonGroupState.cs index eda87c2416..a574201c6b 100644 --- a/Modules/UIElements/Core/Controls/ToggleButtonGroup/ToggleButtonGroupState.cs +++ b/Modules/UIElements/Core/Controls/ToggleButtonGroup/ToggleButtonGroupState.cs @@ -91,7 +91,7 @@ public bool this[int index] /// ]]> /// /// - public Span GetActiveOptions(in Span activeOptionsIndices) + public Span GetActiveOptions(Span activeOptionsIndices) { if (activeOptionsIndices.Length < m_Length) throw new ArgumentException($"indices' length ({activeOptionsIndices.Length}) should be equal to or greater than the ToggleButtonGroupState's length ({m_Length})."); @@ -128,7 +128,7 @@ public Span GetActiveOptions(in Span activeOptionsIndices) /// ]]> /// /// - public Span GetInactiveOptions(in Span inactiveOptionsIndices) + public Span GetInactiveOptions(Span inactiveOptionsIndices) { if (inactiveOptionsIndices.Length < m_Length) throw new ArgumentException($"indices' length ({inactiveOptionsIndices.Length}) should be equal to or greater than the ToggleButtonGroupState's length ({m_Length})."); diff --git a/Modules/UIElements/Core/Events/EventHandler.cs b/Modules/UIElements/Core/Events/EventHandler.cs index 7d228d02d7..9a6e708081 100644 --- a/Modules/UIElements/Core/Events/EventHandler.cs +++ b/Modules/UIElements/Core/Events/EventHandler.cs @@ -330,7 +330,7 @@ internal virtual void ExecuteDefaultActionDisabled(EventBase evt) {} /// Informs the data binding system that a property of a control has changed. /// /// The property that has changed. - protected void NotifyPropertyChanged(BindingId property) + protected void NotifyPropertyChanged(in BindingId property) { var element = this as VisualElement; if (null == element?.elementPanel) diff --git a/Modules/UIElements/Core/Renderer/UIRMeshGenerator.cs b/Modules/UIElements/Core/Renderer/UIRMeshGenerator.cs index 8cda868d0b..994d9a8c8e 100644 --- a/Modules/UIElements/Core/Renderer/UIRMeshGenerator.cs +++ b/Modules/UIElements/Core/Renderer/UIRMeshGenerator.cs @@ -681,6 +681,9 @@ public void DrawText(string text, Vector2 pos, float fontSize, Color color, Font void DrawTextInfo(List> vertices, List> indices, List materials, List renderModes) { + if (vertices == null) + return; + for (int i = 0, drawCount = vertices.Count; i < drawCount; i++) { if (vertices[i].Length == 0) diff --git a/Modules/UIElements/Core/Text/UITKTextHandle.cs b/Modules/UIElements/Core/Text/UITKTextHandle.cs index ccb6985cb3..639ec4aba0 100644 --- a/Modules/UIElements/Core/Text/UITKTextHandle.cs +++ b/Modules/UIElements/Core/Text/UITKTextHandle.cs @@ -29,7 +29,7 @@ public Vector2 ComputeTextSize(string textToMeasure, float width, float height) return preferredSize; } - public virtual MeshInfo[] Update() + public MeshInfo[] Update() { ConvertUssToTextGenerationSettings(); @@ -318,7 +318,7 @@ TextOverflowMode GetTextOverflowMode() return TextOverflowMode.Overflow; } - internal void ConvertUssToTextGenerationSettings() + internal virtual void ConvertUssToTextGenerationSettings() { var style = m_TextElement.computedStyle; var tgs = settings; diff --git a/Modules/UIElements/Core/Text/UITKTextJobSystem.cs b/Modules/UIElements/Core/Text/UITKTextJobSystem.cs index 167095cd40..8e48a69655 100644 --- a/Modules/UIElements/Core/Text/UITKTextJobSystem.cs +++ b/Modules/UIElements/Core/Text/UITKTextJobSystem.cs @@ -192,8 +192,7 @@ public void Execute(int index) ManagedJobData managedJobData = managedJobDatas[index]; var visualElement = managedJobData.visualElement; visualElement.uitkTextHandle.ConvertUssToTextGenerationSettings(); - var settings = TextHandle.settings; - if (visualElement.uitkTextHandle.m_PreviousGenerationSettingsHash == settings.GetHashCode()) + if (visualElement.uitkTextHandle.m_PreviousGenerationSettingsHash == TextHandle.settings.GetHashCode()) { visualElement.uitkTextHandle.AddTextInfoToCache(); } @@ -292,12 +291,6 @@ void AddDrawEntries(MeshGenerationContext mgc, object data) var managedJobDatas = (List)textData.managedJobDataHandle.Target; foreach (var managedJobData in managedJobDatas) { - if (managedJobData.vertices == null || managedJobData.vertices.Count == 0) - { - managedJobData.Release(); - continue; - } - mgc.Begin(managedJobData.node.GetParentEntry(), managedJobData.visualElement); managedJobData.visualElement.uitkTextHandle.HandleLinkAndATagCallbacks(); diff --git a/Modules/UIElements/Core/UXML/TemplateAsset.cs b/Modules/UIElements/Core/UXML/TemplateAsset.cs index 38b79a8ee5..a0b3b90aa1 100644 --- a/Modules/UIElements/Core/UXML/TemplateAsset.cs +++ b/Modules/UIElements/Core/UXML/TemplateAsset.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using UnityEngine.Assertions; -using UnityEngine.UIElements; namespace UnityEngine.UIElements { diff --git a/Modules/UIElements/Core/UXML/VisualElementAsset.cs b/Modules/UIElements/Core/UXML/VisualElementAsset.cs index 1ff066471d..55f57839b0 100644 --- a/Modules/UIElements/Core/UXML/VisualElementAsset.cs +++ b/Modules/UIElements/Core/UXML/VisualElementAsset.cs @@ -11,7 +11,7 @@ namespace UnityEngine.UIElements internal class VisualElementAsset : UxmlAsset, ISerializationCallbackReceiver { [SerializeField] - private string m_Name; + private string m_Name = string.Empty; [SerializeField] private int m_RuleIndex = -1; @@ -23,10 +23,10 @@ public int ruleIndex } [SerializeField] - private string m_Text; + private string m_Text = string.Empty; [SerializeField] - private PickingMode m_PickingMode; + private PickingMode m_PickingMode = PickingMode.Position; [SerializeField] private string[] m_Classes; @@ -53,8 +53,8 @@ public List stylesheetPaths public List stylesheets { - get { return m_Stylesheets ?? (m_Stylesheets = new List()); } - set { m_Stylesheets = value; } + get => m_Stylesheets ??= new List(); + set => m_Stylesheets = value; } public bool hasStylesheets => m_Stylesheets != null; @@ -71,9 +71,6 @@ public UxmlSerializedData serializedData public VisualElementAsset(string fullTypeName) : base(fullTypeName) { - m_Name = String.Empty; - m_Text = String.Empty; - m_PickingMode = PickingMode.Position; } public void OnBeforeSerialize() {} @@ -102,10 +99,9 @@ internal virtual VisualElement Instantiate(CreationContext cc) if (!cc.TryGetSerializedDataOverride(id, out var data)) data = serializedData; - var instance = data.CreateInstance(); - data.Deserialize(instance); + var ve = (VisualElement)data.CreateInstance(); + data.Deserialize(ve); - var ve = (VisualElement)instance; if (hasStylesheetPaths) { for (var i = 0; i < stylesheetPaths.Count; i++) diff --git a/Modules/UIElements/Core/UXML/VisualTreeAsset.cs b/Modules/UIElements/Core/UXML/VisualTreeAsset.cs index bfb39c9da1..82f3453ddb 100644 --- a/Modules/UIElements/Core/UXML/VisualTreeAsset.cs +++ b/Modules/UIElements/Core/UXML/VisualTreeAsset.cs @@ -645,10 +645,7 @@ public TemplateContainer CloneTree(string bindingPath) /// A VisualElement that will act as the root of the cloned tree. public void CloneTree(VisualElement target) { - int firstElementIndex; - int elementAddedCount; - - CloneTree(target, out firstElementIndex, out elementAddedCount); + CloneTree(target, out _, out _); } public void CloneTree(VisualElement target, out int firstElementIndex, out int elementAddedCount) @@ -687,7 +684,7 @@ internal void CloneTree(VisualElement target, CreationContext cc) if (!idToChildren.TryGetValue(asset.parentId, out children)) { children = new List(); - idToChildren.Add(asset.parentId, children); + idToChildren[asset.parentId] = children; } children.Add(asset); @@ -752,8 +749,8 @@ private VisualElement CloneSetupRecursively(VisualElementAsset root, // context.target is the created templateContainer if (root.id == context.visualTreeAsset.contentContainerId) { - if (context.target is TemplateContainer) - ((TemplateContainer)context.target).SetContentContainer(ve); + if (context.target is TemplateContainer tc) + tc.SetContentContainer(ve); else Debug.LogError( "Trying to clone a VisualTreeAsset with a custom content container into a element which is not a template container"); diff --git a/Modules/UIElements/Core/VisualElementDataBinding.cs b/Modules/UIElements/Core/VisualElementDataBinding.cs index 68d1bc9a25..84d5cedecd 100644 --- a/Modules/UIElements/Core/VisualElementDataBinding.cs +++ b/Modules/UIElements/Core/VisualElementDataBinding.cs @@ -40,7 +40,7 @@ public BindingId(string path) /// Instantiate a new binding property. /// /// The path of the property. - public BindingId(PropertyPath path) + public BindingId(in PropertyPath path) { m_PropertyPath = path; m_Path = path.ToString(); @@ -81,7 +81,7 @@ public static implicit operator BindingId(string name) /// /// The path to the property. /// The property. - public static implicit operator BindingId(PropertyPath path) + public static implicit operator BindingId(in PropertyPath path) { return new BindingId(path); } @@ -164,7 +164,7 @@ public PropertyChangedEvent() /// /// The property that has changed. /// A . - public static PropertyChangedEvent GetPooled(BindingId property) + public static PropertyChangedEvent GetPooled(in BindingId property) { var e = GetPooled(); e.property = property; diff --git a/Modules/UIElementsEditor/Bindings/BindingExtensions.cs b/Modules/UIElementsEditor/Bindings/BindingExtensions.cs index 08f7a5cfdc..9d72bc99bc 100644 --- a/Modules/UIElementsEditor/Bindings/BindingExtensions.cs +++ b/Modules/UIElementsEditor/Bindings/BindingExtensions.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using Unity.Properties; using Unity.Collections; using UnityEngine; using UnityEngine.UIElements; @@ -20,6 +21,7 @@ internal class SerializedObjectBindingContext private bool m_DelayBind = false; private long m_BindingOperationStartTimeMs; private const int k_MaxBindingTimeMs = 50; + private long m_LastFrame = long.MinValue; public SerializedObjectBindingContext(SerializedObject so) { @@ -542,18 +544,21 @@ internal bool IsValid() return serializedObject.isValid; } - internal void UpdateIfNecessary() + internal void UpdateIfNecessary(VisualElement element) { if (!wasUpdated) { if (IsValid()) { - serializedObject.UpdateIfRequiredOrScript(); + if (element.elementPanel?.GetUpdater(VisualTreeUpdatePhase.DataBinding) is VisualTreeDataBindingsUpdater updater && m_LastFrame != updater.frame) + { + serializedObject.UpdateIfRequiredOrScript(); - UpdateRevision(); + UpdateRevision(); + m_LastFrame = updater.frame; + wasUpdated = true; + } } - - wasUpdated = true; } } @@ -842,6 +847,8 @@ private void UnbindTree(VisualElement element) RemoveBindingRequest(element); (element.GetBinding(BindingExtensions.s_SerializedBindingId) as SerializedObjectBindingBase)?.OnRelease(); (element.GetBinding(BindingExtensions.s_SerializedBindingContextUpdaterId) as SerializedObjectBindingBase)?.OnRelease(); + element.ClearBinding(BindingExtensions.s_SerializedBindingId); + element.ClearBinding(BindingExtensions.s_SerializedBindingContextUpdaterId); if (context != null) { @@ -1162,10 +1169,34 @@ public void Release() } } - //TODO: Pool this - - internal abstract class SerializedObjectBindingBase : CustomBinding + internal abstract class SerializedObjectBindingBase : CustomBinding, IDataSourceProvider, IDataSourceViewHashProvider { + private long m_LastUpdateTime; + + private static long GetCurrentTime() + { + return Panel.TimeSinceStartupMs(); + } + + // This is to ensure that getting the resolved data source is as fast as possible, since we don't need to fetch it from the hierarchy. + public object dataSource => this; + public PropertyPath dataSourcePath => default; + + public long GetViewHashCode() + { + if (null == bindingContext) + return -1; + + if (null == bindingContext.serializedObject) + return -1; + + var element = boundElement as VisualElement; + if (null != element) + bindingContext.UpdateIfNecessary(element); + + return bindingContext.serializedObject.objectVersion; + } + protected abstract string bindingId { get; } public SerializedObjectBindingContext bindingContext @@ -1225,11 +1256,24 @@ public void Unbind() OnRelease(); } + protected internal override void OnActivated(in BindingActivationContext context) + { + // Resets the throttling and make sure it will get called on the next update. + m_LastUpdateTime = GetCurrentTime() - VisualTreeBindingsUpdater.k_MinUpdateDelayMs; + base.OnActivated(in context); + } + protected internal override BindingResult Update(in BindingContext context) { - bindingContext?.UpdateIfNecessary(); - var result = OnUpdate(in context); - return result; + var currentTimeMs = GetCurrentTime(); + if (VisualTreeBindingsUpdater.disableBindingsThrottling || (currentTimeMs - m_LastUpdateTime) >= VisualTreeBindingsUpdater.k_MinUpdateDelayMs) + { + m_LastUpdateTime = currentTimeMs; + bindingContext?.UpdateIfNecessary(context.targetElement); + var result = OnUpdate(in context); + return result; + } + return new BindingResult(BindingStatus.Pending); } public abstract BindingResult OnUpdate(in BindingContext context); @@ -1304,8 +1348,6 @@ protected IBindable boundElement } } - protected SerializedObjectBindingBase FieldBinding => (m_Field as VisualElement)?.GetBinding(bindingId) as SerializedObjectBindingBase; - protected bool isFieldAttached { get => m_IsFieldAttached; @@ -1600,6 +1642,7 @@ public override BindingResult OnUpdate(in BindingContext context) { return new BindingResult(BindingStatus.Pending); } + try { ResetUpdate(); @@ -1611,20 +1654,18 @@ public override BindingResult OnUpdate(in BindingContext context) isUpdating = true; - if (FieldBinding == this) - { - var veField = field as VisualElement; - // Value might not have changed but prefab state could have been reverted, so we need to - // at least update the prefab override visual if necessary. Happens when user reverts a - // field where the value is the same as the prefab registered value. Case 1276154. - BindingsStyleHelpers.UpdatePrefabStateStyle(veField, boundProperty); + var veField = field as VisualElement; - if (EditorApplication.isPlaying && SerializedObject.GetLivePropertyFeatureGlobalState() && boundProperty.isLiveModified) - BindingsStyleHelpers.UpdateLivePropertyStateStyle(veField, boundProperty); + // Value might not have changed but prefab state could have been reverted, so we need to + // at least update the prefab override visual if necessary. Happens when user reverts a + // field where the value is the same as the prefab registered value. Case 1276154. + BindingsStyleHelpers.UpdatePrefabStateStyle(veField, boundProperty); - return default; - } + if (EditorApplication.isPlaying && SerializedObject.GetLivePropertyFeatureGlobalState() && boundProperty.isLiveModified) + BindingsStyleHelpers.UpdateLivePropertyStateStyle(veField, boundProperty); + + return default; } catch (ArgumentNullException) { @@ -1685,15 +1726,13 @@ public override void OnRelease() if (isReleased) return; - if (FieldBinding == this) + if (field is BaseField bf) { - if (field is BaseField bf) - { - BindingsStyleHelpers.UnregisterRightClickMenu(bf); - }else if (field is Foldout foldout) - { - BindingsStyleHelpers.UnregisterRightClickMenu(foldout); - } + BindingsStyleHelpers.UnregisterRightClickMenu(bf); + } + else if (field is Foldout foldout) + { + BindingsStyleHelpers.UnregisterRightClickMenu(foldout); } ResetContext(); @@ -1705,6 +1744,7 @@ public override void OnRelease() isReleased = true; } } + class SerializedObjectBinding : SerializedObjectBindingPropertyToBaseField { public static ObjectPool> s_Pool = @@ -1982,15 +2022,12 @@ public override void OnRelease() if (isReleased) return; - if (FieldBinding == this) - { - // Make sure to nullify the field to unbind before reverting the enum value - var saveField = field; - BindingsStyleHelpers.UnregisterRightClickMenu(saveField); + // Make sure to nullify the field to unbind before reverting the enum value + var saveField = field; + BindingsStyleHelpers.UnregisterRightClickMenu(saveField); - field = null; - saveField.value = null; - } + field = null; + saveField.value = null; ResetContext(); @@ -2189,24 +2226,20 @@ public override void OnRelease() if (isReleased) return; - if (FieldBinding == this) + //we set the popup values to the original ones + try { - //we set the popup values to the original ones - try - { - var previousField = field; - BindingsStyleHelpers.UnregisterRightClickMenu(previousField); + var previousField = field; + BindingsStyleHelpers.UnregisterRightClickMenu(previousField); - field = null; - previousField.choices = originalChoices; - previousField.index = originalIndex; - } - catch (ArgumentException) - { - //we did our best - } + field = null; + previousField.choices = originalChoices; + previousField.index = originalIndex; + } + catch (ArgumentException) + { + //we did our best } - ResetContext(); field = null; diff --git a/Modules/UIElementsEditor/Bindings/ListViewBindings.cs b/Modules/UIElementsEditor/Bindings/ListViewBindings.cs index 678eb20bf2..d15e57060f 100644 --- a/Modules/UIElementsEditor/Bindings/ListViewBindings.cs +++ b/Modules/UIElementsEditor/Bindings/ListViewBindings.cs @@ -81,6 +81,7 @@ private void SetListView(ListView lv) listView = lv; listView.SetProperty(BaseVerticalCollectionView.internalBindingKey, this); + var parentField = listView.GetProperty(PropertyField.listViewBoundFieldProperty); if (listView.makeItem == null) { @@ -101,13 +102,21 @@ private void SetListView(ListView lv) listView.scrollView.contentContainer.RegisterCallback(m_SerializedObjectBindEventCallback); listView.scrollView.contentContainer.RegisterCallback(m_SerializedPropertyBindEventCallback); - var isReorderable = PropertyHandler.IsArrayReorderable(m_DataList.ArrayProperty); + // ListViews instantiated by users are driven by users. We only change the reordering options if the user + // has used a PropertyField to display the list. (Cases UUM-33402 and UUM-27687) + var isReorderable = listView.reorderable; + if (parentField != null) + { + isReorderable = PropertyHandler.IsArrayReorderable(m_DataList.ArrayProperty); + listView.reorderMode = isReorderable ? ListViewReorderMode.Animated : ListViewReorderMode.Simple; + } + listView.SetViewController(new EditorListViewController()); listView.SetDragAndDropController(new SerializedObjectListReorderableDragAndDropController(listView) { enableReordering = isReorderable, }); - listView.reorderMode = isReorderable ? ListViewReorderMode.Animated : ListViewReorderMode.Simple; + listView.itemsSource = m_DataList; var foldoutInput = listView.headerFoldout?.toggle?.visualInput; @@ -149,7 +158,6 @@ private void ClearListView() listView.scrollView.contentContainer.UnregisterCallback(m_SerializedPropertyBindEventCallback); listView.SetViewController(null); - listView.SetDragAndDropController(null); var foldoutInput = listView.headerFoldout?.toggle?.visualInput; if (foldoutInput != null) diff --git a/Modules/UIElementsEditor/Debugger/DebuggerSearchBar.cs b/Modules/UIElementsEditor/Debugger/DebuggerSearchBar.cs index 303b6c7b2a..e27b26acb1 100644 --- a/Modules/UIElementsEditor/Debugger/DebuggerSearchBar.cs +++ b/Modules/UIElementsEditor/Debugger/DebuggerSearchBar.cs @@ -75,7 +75,7 @@ public DebuggerSearchBar(DebuggerTreeView parent) var targetField = m_SearchTextField; if (e.keyCode == KeyCode.F3 || e.keyCode == KeyCode.Return || e.keyCode == KeyCode.KeypadEnter) { - if (e.modifiers == EventModifiers.Shift) + if (e.modifiers.HasFlag(EventModifiers.Shift)) SelectPrev(); else SelectNext(); diff --git a/Modules/UIElementsEditor/GameObjects/Inspector/PanelSettingsInspector.cs b/Modules/UIElementsEditor/GameObjects/Inspector/PanelSettingsInspector.cs index a3048b13eb..1214443ac1 100644 --- a/Modules/UIElementsEditor/GameObjects/Inspector/PanelSettingsInspector.cs +++ b/Modules/UIElementsEditor/GameObjects/Inspector/PanelSettingsInspector.cs @@ -210,9 +210,10 @@ void UpdateRenderMode(PanelRenderMode newRenderMode) PanelScaleMode scaleMode = PanelScaleMode.ConstantPixelSize; if (m_ScaleModeField.value != null) + { scaleMode = (PanelScaleMode)m_ScaleModeField.value; - - UpdateScaleModeValues(scaleMode); + UpdateScaleModeValues(scaleMode); + } m_ScaleModeWorldSpace.style.display = DisplayStyle.None; } diff --git a/Modules/UIElementsEditor/UIElementsViewImporter.cs b/Modules/UIElementsEditor/UIElementsViewImporter.cs index 5175aab525..d36e2d6d15 100644 --- a/Modules/UIElementsEditor/UIElementsViewImporter.cs +++ b/Modules/UIElementsEditor/UIElementsViewImporter.cs @@ -1059,10 +1059,26 @@ void ParseAttributes(XElement elt, UxmlAsset res, VisualTreeAsset vta, UxmlAsset if (response.result == URIValidationResult.OK && !vta.AssetEntryExists(xattr.Value, assetType)) { - if (asset) + if (asset && !assetType.IsAssignableFrom(asset.GetType())) { - // Force loading using correct attribute type to support cases like Texture2D vs Sprite, - asset = AssetDatabase.LoadAssetAtPath(response.resolvedProjectRelativePath, assetType); + if (string.IsNullOrEmpty(response.resolvedSubAssetPath)) + { + // Force loading using correct attribute type to support cases like Texture2D vs Sprite, + asset = AssetDatabase.LoadAssetAtPath(response.resolvedProjectRelativePath, assetType); + } + else + { + // Force load the sub assets and find the asset by name and type + var subAssets = AssetDatabase.LoadAllAssetsAtPath(response.resolvedProjectRelativePath); + foreach (var subAsset in subAssets) + { + if (subAsset.name == response.resolvedSubAssetPath && assetType.IsAssignableFrom(subAsset.GetType())) + { + asset = subAsset; + break; + } + } + } } vta.RegisterAssetEntry(xattr.Value, assetType, asset); } diff --git a/Modules/UIElementsEditor/UXML/UxmlAttributeConverter.cs b/Modules/UIElementsEditor/UXML/UxmlAttributeConverter.cs index 0d5512e2cd..827f5b9cba 100644 --- a/Modules/UIElementsEditor/UXML/UxmlAttributeConverter.cs +++ b/Modules/UIElementsEditor/UXML/UxmlAttributeConverter.cs @@ -315,7 +315,7 @@ internal class EnumAttributeConverter : UxmlAttributeConverter public Type type; public override Enum FromString(string value) { - if (Enum.TryParse(type, value, out var result)) + if (Enum.TryParse(type, value, true, out var result)) return (Enum)result; return default; } diff --git a/Modules/UIElementsEditor/UXML/UxmlSerializedDataDescription.cs b/Modules/UIElementsEditor/UXML/UxmlSerializedDataDescription.cs index e1b4069ac7..3dba961ee0 100644 --- a/Modules/UIElementsEditor/UXML/UxmlSerializedDataDescription.cs +++ b/Modules/UIElementsEditor/UXML/UxmlSerializedDataDescription.cs @@ -17,13 +17,15 @@ namespace UnityEditor.UIElements { internal class UxmlSerializedDataDescription { - private List m_SerializedAttributes = new(); + private readonly List m_SerializedAttributes = new(); - private Dictionary m_UxmlNameToIndex = new(); - private Dictionary m_PropertyNameToIndex = new(); - private HashSet m_UxmlObjectFields = new(); + private readonly Dictionary m_UxmlNameToIndex = new(); + private readonly Dictionary m_PropertyNameToIndex = new(); + private readonly HashSet m_UxmlObjectFields = new(); private Type m_SerializedDataType; private UxmlObjectAttribute m_UxmlObjectAttribute; + private string m_UxmlName; + private string m_UxmlFullName; public Type serializedDataType => m_SerializedDataType; public bool isUxmlObject => m_UxmlObjectAttribute != null; @@ -32,14 +34,22 @@ public string uxmlName { get { - // TemplateContainer must use the class name - if (serializedDataType.DeclaringType == typeof(TemplateContainer)) - return nameof(TemplateContainer); - - var elementAttribute = serializedDataType.DeclaringType.GetCustomAttribute(); - if (elementAttribute != null && !string.IsNullOrEmpty(elementAttribute.name)) - return elementAttribute.name; - return serializedDataType.DeclaringType.Name; + if (m_UxmlName == null) + { + // TemplateContainer must use the class name + if (serializedDataType.DeclaringType == typeof(TemplateContainer)) + m_UxmlName = nameof(TemplateContainer); + else + { + var elementAttribute = serializedDataType.DeclaringType.GetCustomAttribute(); + if (elementAttribute != null && !string.IsNullOrEmpty(elementAttribute.name)) + m_UxmlName = elementAttribute.name; + else + m_UxmlName = serializedDataType.DeclaringType.Name; + } + } + + return m_UxmlName; } } @@ -47,9 +57,15 @@ public string uxmlFullName { get { - if (string.IsNullOrEmpty(serializedDataType.DeclaringType.Namespace)) - return uxmlName; - return $"{serializedDataType.DeclaringType.Namespace}.{uxmlName}"; + if (m_UxmlFullName == null) + { + if (string.IsNullOrEmpty(serializedDataType.DeclaringType.Namespace)) + m_UxmlFullName = uxmlName; + else + m_UxmlFullName = $"{serializedDataType.DeclaringType.Namespace}.{uxmlName}"; + } + + return m_UxmlFullName; } } diff --git a/Modules/UIElementsEditor/UXML/UxmlSerializedDataRegistry.cs b/Modules/UIElementsEditor/UXML/UxmlSerializedDataRegistry.cs index 04f0b983be..a9511b2288 100644 --- a/Modules/UIElementsEditor/UXML/UxmlSerializedDataRegistry.cs +++ b/Modules/UIElementsEditor/UXML/UxmlSerializedDataRegistry.cs @@ -2,10 +2,10 @@ // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License +using JetBrains.Annotations; using System; using System.Collections.Generic; using System.Reflection; -using JetBrains.Annotations; using UnityEngine; using UnityEngine.UIElements; @@ -18,7 +18,7 @@ internal static class UxmlSerializedDataRegistry private static bool s_Registered; // We use a sorted dictionary so that the UI Builder Library (ImportUxmlSerializedDataFromSource) can process the namespaces together. - public static SortedDictionary SerializedDataTypes { get; } = new SortedDictionary(); + public static Dictionary SerializedDataTypes { get; } = new Dictionary(); private static Dictionary s_DescriptionsCache = new Dictionary(); [UsedImplicitly, InitializeOnLoadMethod] @@ -38,6 +38,21 @@ private static void RegisterDependencies() } } + // Used for testing + public static void ClearCache() + { + SerializedDataTypes.Clear(); + s_Registered = false; + + ClearDescriptionCache(); + } + + // Used for testing + public static void ClearDescriptionCache() + { + s_DescriptionsCache.Clear(); + } + public static UxmlSerializedDataDescription GetDescription(string typeName) { if (!s_Registered) @@ -93,7 +108,7 @@ static void RegisterType(string typeName, Type type) return; } - SerializedDataTypes.Add(typeName, type); + SerializedDataTypes[typeName] = type; } } } diff --git a/Modules/UIElementsEditor/VisualTreeAssetChangeTrackerUpdater.cs b/Modules/UIElementsEditor/VisualTreeAssetChangeTrackerUpdater.cs index 40395140fe..11ee44c1db 100644 --- a/Modules/UIElementsEditor/VisualTreeAssetChangeTrackerUpdater.cs +++ b/Modules/UIElementsEditor/VisualTreeAssetChangeTrackerUpdater.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using Unity.Profiling; using UnityEngine; @@ -163,7 +164,9 @@ public void StartTracking(List elements) currentAsset = ve.visualTreeAssetSource; tracker = FindTracker(ve); } - StartVisualTreeAssetTracking(tracker, ve.visualTreeAssetSource); + + if(tracker != null) + StartVisualTreeAssetTracking(tracker, ve.visualTreeAssetSource); } if (ve.styleSheetList?.Count > 0) @@ -221,7 +224,7 @@ public void OnStyleSheetAssetsImported(HashSet changedAssets, HashSe m_LiveReloadStyleSheetAssetTracker.OnAssetsImported(changedAssets, deletedAssets); } - private void StartVisualTreeAssetTracking(ILiveReloadAssetTracker tracker, VisualTreeAsset asset) + private void StartVisualTreeAssetTracking([NotNull] ILiveReloadAssetTracker tracker, VisualTreeAsset asset) { int dirtyCount = tracker.StartTrackingAsset(asset); diff --git a/Projects/CSharp/UnityEditor.csproj b/Projects/CSharp/UnityEditor.csproj index 7d75d45a27..fc7f02c1cb 100644 --- a/Projects/CSharp/UnityEditor.csproj +++ b/Projects/CSharp/UnityEditor.csproj @@ -6,7 +6,7 @@ false true false - $(DefineConstants);COMBINED_ASSEMBLIES;DEBUGGER_LISTENS_FIXED_PORT;DEBUGMODE=1;DISABLE_SKETCHUP_IMPORTER=1;ENABLE_ACCELERATOR_CLIENT_DEBUGGING;ENABLE_ALTERNATIVE_LISTEN_PORT;ENABLE_AR;ENABLE_AUDIO;ENABLE_AUDIO_MANAGER_BASED_SCHEDULING;ENABLE_CACHING;ENABLE_CLOTH;ENABLE_CLOUD_LICENSE;ENABLE_CLOUD_SERVICES;ENABLE_CLOUD_SERVICES_ADS;ENABLE_CLOUD_SERVICES_ANALYTICS;ENABLE_CLOUD_SERVICES_BUILD;ENABLE_CLOUD_SERVICES_COLLAB_TESTING;ENABLE_CLOUD_SERVICES_CRASH_REPORTING;ENABLE_CLOUD_SERVICES_PURCHASING;ENABLE_CLOUD_SERVICES_USE_WEBREQUEST;ENABLE_CLUSTERINPUT;ENABLE_CLUSTER_SYNC;ENABLE_CRUNCH_TEXTURE_COMPRESSION;ENABLE_DIRECTOR_AUDIO;ENABLE_DIRECTOR_TEXTURE;ENABLE_EDITOR_GAME_SERVICES;ENABLE_EDITOR_HUB_LICENSE;ENABLE_ETC_COMPRESSION;ENABLE_EVENT_QUEUE;ENABLE_GAMECENTER_API;ENABLE_GENERATE_NATIVE_PLUGINS_FOR_ASSEMBLIES_API;ENABLE_HOLOLENS_MODULE_API;ENABLE_LZMA;ENABLE_MANAGED_ANIMATION_JOBS;ENABLE_MANAGED_AUDIO_JOBS;ENABLE_MANAGED_JOBS;ENABLE_MANAGED_TRANSFORM_JOBS;ENABLE_MANAGED_UNITYTLS;ENABLE_MARSHALLING_TESTS;ENABLE_MICROPHONE;ENABLE_MOVIES;ENABLE_MULTIPLE_DISPLAYS;ENABLE_NAVIGATION_OFFMESHLINK_TO_NAVMESHLINK;ENABLE_NETWORK;ENABLE_NULLDEPTHBUFFER;ENABLE_NVIDIA;ENABLE_PHYSICS;ENABLE_RUNTIME_GI;ENABLE_SCRIPTING_GC_WBARRIERS;ENABLE_TEXTURE_STREAMING;ENABLE_UNITYEVENTS;ENABLE_UNITYWEBREQUEST;ENABLE_UNITY_COLLECTIONS_CHECKS;ENABLE_UNITY_GAME_SERVICES_ANALYTICS_SUPPORT;ENABLE_VERSION_CONTROL_INTEGRATION;ENABLE_VIDEO;ENABLE_VIRTUALTEXTURING;ENABLE_VR;ENABLE_WEBCAM;ENABLE_WEBSOCKET_CLIENT;ENABLE_WEBSOCKET_HOST;ENABLE_WWW;INCLUDE_DYNAMIC_GI;PLATFORM_SUPPORTS_GAMEPAD_AUDIO;PLATFORM_SUPPORTS_MONO;PLAYERCONNECTION_LISTENS_FIXED_PORT;RENDER_SOFTWARE_CURSOR;TEXTCORE_1_0_OR_NEWER;TRACE;UNITY_ANDROID_API;UNITY_ASSEMBLIES_API;UNITY_ASSERTIONS;UNITY_CAN_SHOW_SPLASH_SCREEN;UNITY_EDITOR;UNITY_EDITOR_API;UNITY_EDITOR_LINUX;UNITY_GAMECORE_API;UNITY_GAMECORE_SCARLETT_API;UNITY_GAMECORE_XBOXONE_API;UNITY_IPHONE_API;UNITY_METRO_API;UNITY_PS4_API;UNITY_PS5_API;UNITY_STANDALONE_LINUX_API;UNITY_STANDALONE_OSX_API;UNITY_STANDALONE_WIN_API;UNITY_TINYPROFILING_INTERNAL;UNITY_TVOS_API;UNITY_UNITYADS_API;UNITY_WEBGL_API;USE_ITEM_LIBRARY_MODULE;USE_PROPERTY_DATABASE;USE_QUERY_BUILDER;USE_SEARCH_ENGINE_API;USE_SEARCH_MODULE + $(DefineConstants);COMBINED_ASSEMBLIES;DEBUGGER_LISTENS_FIXED_PORT;DEBUGMODE=1;DISABLE_SKETCHUP_IMPORTER=1;ENABLE_ACCELERATOR_CLIENT_DEBUGGING;ENABLE_ALTERNATIVE_LISTEN_PORT;ENABLE_AR;ENABLE_AUDIO;ENABLE_AUDIO_MANAGER_BASED_SCHEDULING;ENABLE_CACHING;ENABLE_CLOTH;ENABLE_CLOUD_LICENSE;ENABLE_CLOUD_SERVICES;ENABLE_CLOUD_SERVICES_ADS;ENABLE_CLOUD_SERVICES_ANALYTICS;ENABLE_CLOUD_SERVICES_BUILD;ENABLE_CLOUD_SERVICES_COLLAB_TESTING;ENABLE_CLOUD_SERVICES_CRASH_REPORTING;ENABLE_CLOUD_SERVICES_PURCHASING;ENABLE_CLOUD_SERVICES_USE_WEBREQUEST;ENABLE_CLUSTERINPUT;ENABLE_CLUSTER_SYNC;ENABLE_CRUNCH_TEXTURE_COMPRESSION;ENABLE_DIRECTOR_AUDIO;ENABLE_DIRECTOR_TEXTURE;ENABLE_EDITOR_GAME_SERVICES;ENABLE_EDITOR_HUB_LICENSE;ENABLE_ETC_COMPRESSION;ENABLE_EVENT_QUEUE;ENABLE_GAMECENTER_API;ENABLE_GENERATE_NATIVE_PLUGINS_FOR_ASSEMBLIES_API;ENABLE_HOLOLENS_MODULE_API;ENABLE_LZMA;ENABLE_MANAGED_ANIMATION_JOBS;ENABLE_MANAGED_AUDIO_JOBS;ENABLE_MANAGED_JOBS;ENABLE_MANAGED_TRANSFORM_JOBS;ENABLE_MANAGED_UNITYTLS;ENABLE_MARSHALLING_TESTS;ENABLE_MICROPHONE;ENABLE_MOVIES;ENABLE_MULTIPLE_DISPLAYS;ENABLE_NAVIGATION_OFFMESHLINK_TO_NAVMESHLINK;ENABLE_NETWORK;ENABLE_NULLDEPTHBUFFER;ENABLE_NVIDIA;ENABLE_PHYSICS;ENABLE_RUNTIME_GI;ENABLE_SCRIPTING_GC_WBARRIERS;ENABLE_TEXTURE_STREAMING;ENABLE_UNITYEVENTS;ENABLE_UNITYWEBREQUEST;ENABLE_UNITY_COLLECTIONS_CHECKS;ENABLE_UNITY_GAME_SERVICES_ANALYTICS_SUPPORT;ENABLE_VERSION_CONTROL_INTEGRATION;ENABLE_VIDEO;ENABLE_VIRTUALTEXTURING;ENABLE_VR;ENABLE_WEBCAM;ENABLE_WEBSOCKET_CLIENT;ENABLE_WEBSOCKET_HOST;ENABLE_WWW;INCLUDE_DYNAMIC_GI;PLATFORM_SUPPORTS_GAMEPAD_AUDIO;PLATFORM_SUPPORTS_MONO;PLAYERCONNECTION_LISTENS_FIXED_PORT;RENDER_SOFTWARE_CURSOR;TEXTCORE_1_0_OR_NEWER;TRACE;UNITY_ANDROID_API;UNITY_ASSEMBLIES_API;UNITY_ASSERTIONS;UNITY_CAN_SHOW_SPLASH_SCREEN;UNITY_EDITOR;UNITY_EDITOR_API;UNITY_EDITOR_LINUX;UNITY_IPHONE_API;UNITY_METRO_API;UNITY_PS4_API;UNITY_PS5_API;UNITY_STANDALONE_LINUX_API;UNITY_STANDALONE_OSX_API;UNITY_STANDALONE_WIN_API;UNITY_TINYPROFILING_INTERNAL;UNITY_TVOS_API;UNITY_UNITYADS_API;UNITY_WEBGL_API;USE_ITEM_LIBRARY_MODULE;USE_PROPERTY_DATABASE;USE_QUERY_BUILDER;USE_SEARCH_ENGINE_API;USE_SEARCH_MODULE latest 1701;0169;0649;0626 @@ -2942,9 +2942,6 @@ Editor\Mono\RenderPipelineGlobalSettingsEditor.cs - - Editor\Mono\RenderPipelineGraphicsSettingsContainerPropertyDrawer.cs - Editor\Mono\RuntimeInitializeOnLoadManager.bindings.cs @@ -3998,6 +3995,27 @@ Modules\AIEditor\Visualization\NavMeshVisualizationSettings.bindings.cs + + Modules\AccessibilityEditor\Managed\AccessibilityEditor.cs + + + Modules\AccessibilityEditor\Managed\AccessibilityHierarchyTreeView.cs + + + Modules\AccessibilityEditor\Managed\AccessibilityHierarchyViewModel.cs + + + Modules\AccessibilityEditor\Managed\AccessibilityHierarchyViewer.cs + + + Modules\AccessibilityEditor\Managed\AccessibilityHierarchyViewerWindow.cs + + + Modules\AccessibilityEditor\Managed\SearchableLabel.cs + + + Modules\AccessibilityEditor\Managed\TreeViewSearchBar.cs + Modules\AdaptivePerformance\Editor\AdaptivePerformanceInstaller.cs diff --git a/Projects/CSharp/UnityEngine.csproj b/Projects/CSharp/UnityEngine.csproj index b9fdcec823..814cb76f6e 100644 --- a/Projects/CSharp/UnityEngine.csproj +++ b/Projects/CSharp/UnityEngine.csproj @@ -6,7 +6,7 @@ false true false - $(DefineConstants);COMBINED_ASSEMBLIES;DEBUGGER_LISTENS_FIXED_PORT;ENABLE_ACCELERATOR_CLIENT_DEBUGGING;ENABLE_ALTERNATIVE_LISTEN_PORT;ENABLE_AR;ENABLE_AUDIO;ENABLE_AUDIO_MANAGER_BASED_SCHEDULING;ENABLE_CACHING;ENABLE_CLOTH;ENABLE_CLOUD_LICENSE;ENABLE_CLOUD_SERVICES;ENABLE_CLOUD_SERVICES_ADS;ENABLE_CLOUD_SERVICES_ANALYTICS;ENABLE_CLOUD_SERVICES_BUILD;ENABLE_CLOUD_SERVICES_COLLAB_TESTING;ENABLE_CLOUD_SERVICES_CRASH_REPORTING;ENABLE_CLOUD_SERVICES_PURCHASING;ENABLE_CLOUD_SERVICES_USE_WEBREQUEST;ENABLE_CRUNCH_TEXTURE_COMPRESSION;ENABLE_DIRECTOR_AUDIO;ENABLE_DIRECTOR_TEXTURE;ENABLE_DUMMYTLS=1;ENABLE_EDITOR_GAME_SERVICES;ENABLE_EDITOR_HUB_LICENSE;ENABLE_GENERATE_NATIVE_PLUGINS_FOR_ASSEMBLIES_API;ENABLE_HOLOLENS_MODULE_API;ENABLE_JPEG=1;ENABLE_LZMA;ENABLE_MANAGED_ANIMATION_JOBS;ENABLE_MANAGED_AUDIO_JOBS;ENABLE_MANAGED_JOBS;ENABLE_MANAGED_TRANSFORM_JOBS;ENABLE_MANAGED_UNITYTLS;ENABLE_MARSHALLING_TESTS;ENABLE_MBEDTLS=1;ENABLE_MICROPHONE;ENABLE_MOVIES;ENABLE_MULTIPLE_DISPLAYS;ENABLE_NAVIGATION_OFFMESHLINK_TO_NAVMESHLINK;ENABLE_NETWORK;ENABLE_NULLDEPTHBUFFER;ENABLE_PHYSICS;ENABLE_PNG=1;ENABLE_SCRIPTING_GC_WBARRIERS;ENABLE_TEXTURE_STREAMING;ENABLE_UNITYEVENTS;ENABLE_UNITYWEBREQUEST;ENABLE_UNITY_COLLECTIONS_CHECKS;ENABLE_UNITY_COLLECTIONS_CHECKS;ENABLE_UNITY_GAME_SERVICES_ANALYTICS_SUPPORT;ENABLE_VERSION_CONTROL_INTEGRATION;ENABLE_VIDEO;ENABLE_VR;ENABLE_WEBCAM;ENABLE_WEBSOCKET_CLIENT;ENABLE_WWW;GRAPHINE_BUILD_STATIC=1;GRAPHINE_BUILD_UNITY=1;INCLUDE_DYNAMIC_GI;MBEDTLS_PSK_MAX_LEN=64;MBEDTLS_USER_CONFIG=1;NV_CLOTH_IMPORT=;PLATFORM_SUPPORTS_MONO;PLAYERCONNECTION_LISTENS_FIXED_PORT;RENDER_SOFTWARE_CURSOR;TEXTCORE_1_0_OR_NEWER;TRACE;UMBRA_IS_TARGET=0;UMBRA_OS=UMBRA_LINUX;UNITY_ANDROID_API;UNITY_ASSEMBLIES_API;UNITY_ASSERTIONS;UNITY_CURL_BUILT_IN;UNITY_EDITOR;UNITY_EDITOR_API;UNITY_GAMECORE_API;UNITY_GAMECORE_SCARLETT_API;UNITY_GAMECORE_XBOXONE_API;UNITY_IPHONE_API;UNITY_METRO_API;UNITY_PS4_API;UNITY_PS5_API;UNITY_STANDALONE_LINUX_API;UNITY_STANDALONE_OSX_API;UNITY_STANDALONE_WIN_API;UNITY_TLS_INTERFACE_UNITY=1;UNITY_TVOS_API;UNITY_WEBGL_API;USE_PROPERTIES_MODULE;VPX_MULTITHREAD;VPX_X86;VPX_X86_64;_HAS_EXCEPTIONS=0 + $(DefineConstants);COMBINED_ASSEMBLIES;DEBUGGER_LISTENS_FIXED_PORT;ENABLE_ACCELERATOR_CLIENT_DEBUGGING;ENABLE_ALTERNATIVE_LISTEN_PORT;ENABLE_AR;ENABLE_AUDIO;ENABLE_AUDIO_MANAGER_BASED_SCHEDULING;ENABLE_CACHING;ENABLE_CLOTH;ENABLE_CLOUD_LICENSE;ENABLE_CLOUD_SERVICES;ENABLE_CLOUD_SERVICES_ADS;ENABLE_CLOUD_SERVICES_ANALYTICS;ENABLE_CLOUD_SERVICES_BUILD;ENABLE_CLOUD_SERVICES_COLLAB_TESTING;ENABLE_CLOUD_SERVICES_CRASH_REPORTING;ENABLE_CLOUD_SERVICES_PURCHASING;ENABLE_CLOUD_SERVICES_USE_WEBREQUEST;ENABLE_CRUNCH_TEXTURE_COMPRESSION;ENABLE_DIRECTOR_AUDIO;ENABLE_DIRECTOR_TEXTURE;ENABLE_DUMMYTLS=1;ENABLE_EDITOR_GAME_SERVICES;ENABLE_EDITOR_HUB_LICENSE;ENABLE_GENERATE_NATIVE_PLUGINS_FOR_ASSEMBLIES_API;ENABLE_HOLOLENS_MODULE_API;ENABLE_JPEG=1;ENABLE_LZMA;ENABLE_MANAGED_ANIMATION_JOBS;ENABLE_MANAGED_AUDIO_JOBS;ENABLE_MANAGED_JOBS;ENABLE_MANAGED_TRANSFORM_JOBS;ENABLE_MANAGED_UNITYTLS;ENABLE_MARSHALLING_TESTS;ENABLE_MBEDTLS=1;ENABLE_MICROPHONE;ENABLE_MOVIES;ENABLE_MULTIPLE_DISPLAYS;ENABLE_NAVIGATION_OFFMESHLINK_TO_NAVMESHLINK;ENABLE_NETWORK;ENABLE_NULLDEPTHBUFFER;ENABLE_PHYSICS;ENABLE_PNG=1;ENABLE_SCRIPTING_GC_WBARRIERS;ENABLE_TEXTURE_STREAMING;ENABLE_UNITYEVENTS;ENABLE_UNITYWEBREQUEST;ENABLE_UNITY_COLLECTIONS_CHECKS;ENABLE_UNITY_COLLECTIONS_CHECKS;ENABLE_UNITY_GAME_SERVICES_ANALYTICS_SUPPORT;ENABLE_VERSION_CONTROL_INTEGRATION;ENABLE_VIDEO;ENABLE_VR;ENABLE_WEBCAM;ENABLE_WEBSOCKET_CLIENT;ENABLE_WWW;GRAPHINE_BUILD_STATIC=1;GRAPHINE_BUILD_UNITY=1;INCLUDE_DYNAMIC_GI;MBEDTLS_PSK_MAX_LEN=64;MBEDTLS_USER_CONFIG=1;NV_CLOTH_IMPORT=;PLATFORM_SUPPORTS_MONO;PLAYERCONNECTION_LISTENS_FIXED_PORT;RENDER_SOFTWARE_CURSOR;TEXTCORE_1_0_OR_NEWER;TRACE;UMBRA_IS_TARGET=0;UMBRA_OS=UMBRA_LINUX;UNITY_ANDROID_API;UNITY_ASSEMBLIES_API;UNITY_ASSERTIONS;UNITY_CURL_BUILT_IN;UNITY_EDITOR;UNITY_EDITOR_API;UNITY_IPHONE_API;UNITY_METRO_API;UNITY_PS4_API;UNITY_PS5_API;UNITY_STANDALONE_LINUX_API;UNITY_STANDALONE_OSX_API;UNITY_STANDALONE_WIN_API;UNITY_TLS_INTERFACE_UNITY=1;UNITY_TVOS_API;UNITY_WEBGL_API;USE_PROPERTIES_MODULE;VPX_MULTITHREAD;VPX_X86;VPX_X86_64;_HAS_EXCEPTIONS=0 latest 1701;0626 @@ -101,6 +101,9 @@ Modules\AndroidJNI\AndroidApp.bindings.cs + + Modules\AndroidJNI\AndroidApplicationExitInfo.cs + Modules\AndroidJNI\AndroidAssetPacks.bindings.cs @@ -3932,9 +3935,6 @@ Runtime\Export\RenderPipeline\RenderPipelineGlobalSettings.cs - - Runtime\Export\RenderPipeline\RenderPipelineGraphicsSettingsContainer.cs - Runtime\Export\RenderPipeline\RenderPipelineManager.cs diff --git a/README.md b/README.md index 0dbcdb2928..bebd648950 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## Unity 2023.2.0a22 C# reference source code +## Unity 2023.2.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/Export/Debug/Debug.bindings.cs b/Runtime/Export/Debug/Debug.bindings.cs index bbeac91cd9..a04c526315 100644 --- a/Runtime/Export/Debug/Debug.bindings.cs +++ b/Runtime/Export/Debug/Debug.bindings.cs @@ -245,9 +245,6 @@ public static void LogWarningFormat(UnityEngine.Object context, string format, p // In the Build Settings dialog there is a check box called "Development Build". public static extern bool isDebugBuild { get; } - [FreeFunction("DeveloperConsole_OpenConsoleFile")] - internal static extern void OpenConsoleFile(); - [NativeThrows] internal static extern DiagnosticSwitch[] diagnosticSwitches { get; } diff --git a/Runtime/Export/Graphics/GraphicsEnums.cs b/Runtime/Export/Graphics/GraphicsEnums.cs index 76d69b08ed..7d94371bc6 100644 --- a/Runtime/Export/Graphics/GraphicsEnums.cs +++ b/Runtime/Export/Graphics/GraphicsEnums.cs @@ -550,6 +550,7 @@ public enum RenderTextureCreationFlags NoResolvedColorSurface = 1 << 8, DynamicallyScalable = 1 << 10, BindMS = 1 << 11, + DynamicallyScalableExplicit = 1 << 16, } public enum RenderTextureReadWrite diff --git a/Runtime/Export/Graphics/IRenderPipelineGraphicsSettings.cs b/Runtime/Export/Graphics/IRenderPipelineGraphicsSettings.cs index bd74d4def2..08aabe3003 100644 --- a/Runtime/Export/Graphics/IRenderPipelineGraphicsSettings.cs +++ b/Runtime/Export/Graphics/IRenderPipelineGraphicsSettings.cs @@ -7,5 +7,6 @@ namespace UnityEngine.Rendering public interface IRenderPipelineGraphicsSettings { int version { get; } + bool isAvailableInPlayerBuild => false; } } diff --git a/Runtime/Export/Graphics/RenderingCommandBuffer.bindings.cs b/Runtime/Export/Graphics/RenderingCommandBuffer.bindings.cs index 67b9d526a8..bd6c8e04b2 100644 --- a/Runtime/Export/Graphics/RenderingCommandBuffer.bindings.cs +++ b/Runtime/Export/Graphics/RenderingCommandBuffer.bindings.cs @@ -988,14 +988,23 @@ public void ClearRenderTarget(RTClearFlags clearFlags, Color[] backgroundColors, [FreeFunction("RenderingCommandBuffer_Bindings::ConfigureFoveatedRendering", HasExplicitThis = true)] extern public void ConfigureFoveatedRendering(IntPtr platformData); + static public bool ThrowOnSetRenderTarget = false; + + static void CheckThrowOnSetRenderTarget() + { + if (ThrowOnSetRenderTarget) throw new Exception("Setrendertarget is not allowed in this context"); + } + public void SetRenderTarget(RenderTargetIdentifier rt) { + CheckThrowOnSetRenderTarget(); ValidateAgainstExecutionFlags(CommandBufferExecutionFlags.None, CommandBufferExecutionFlags.AsyncCompute); SetRenderTargetSingle_Internal(rt, RenderBufferLoadAction.Load, RenderBufferStoreAction.Store, RenderBufferLoadAction.Load, RenderBufferStoreAction.Store); } public void SetRenderTarget(RenderTargetIdentifier rt, RenderBufferLoadAction loadAction, RenderBufferStoreAction storeAction) { + CheckThrowOnSetRenderTarget(); ValidateAgainstExecutionFlags(CommandBufferExecutionFlags.None, CommandBufferExecutionFlags.AsyncCompute); if (loadAction == RenderBufferLoadAction.Clear) throw new ArgumentException("RenderBufferLoadAction.Clear is not supported"); @@ -1006,6 +1015,7 @@ public void SetRenderTarget(RenderTargetIdentifier rt, RenderBufferLoadAction colorLoadAction, RenderBufferStoreAction colorStoreAction, RenderBufferLoadAction depthLoadAction, RenderBufferStoreAction depthStoreAction) { + CheckThrowOnSetRenderTarget(); ValidateAgainstExecutionFlags(CommandBufferExecutionFlags.None, CommandBufferExecutionFlags.AsyncCompute); if (colorLoadAction == RenderBufferLoadAction.Clear || depthLoadAction == RenderBufferLoadAction.Clear) throw new ArgumentException("RenderBufferLoadAction.Clear is not supported"); @@ -1023,6 +1033,7 @@ public void SetRenderTarget(RenderTargetIdentifier rt, int mipLevel) public void SetRenderTarget(RenderTargetIdentifier rt, int mipLevel, CubemapFace cubemapFace) { + CheckThrowOnSetRenderTarget(); ValidateAgainstExecutionFlags(CommandBufferExecutionFlags.None, CommandBufferExecutionFlags.AsyncCompute); if (mipLevel < 0) throw new ArgumentException(String.Format("Invalid value for mipLevel ({0})", mipLevel)); @@ -1032,6 +1043,7 @@ public void SetRenderTarget(RenderTargetIdentifier rt, int mipLevel, CubemapFace public void SetRenderTarget(RenderTargetIdentifier rt, int mipLevel, CubemapFace cubemapFace, int depthSlice) { + CheckThrowOnSetRenderTarget(); ValidateAgainstExecutionFlags(CommandBufferExecutionFlags.None, CommandBufferExecutionFlags.AsyncCompute); if (depthSlice < -1) throw new ArgumentException(String.Format("Invalid value for depthSlice ({0})", depthSlice)); @@ -1043,12 +1055,14 @@ public void SetRenderTarget(RenderTargetIdentifier rt, int mipLevel, CubemapFace public void SetRenderTarget(RenderTargetIdentifier color, RenderTargetIdentifier depth) { + CheckThrowOnSetRenderTarget(); ValidateAgainstExecutionFlags(CommandBufferExecutionFlags.None, CommandBufferExecutionFlags.AsyncCompute); SetRenderTargetColorDepth_Internal(color, depth, RenderBufferLoadAction.Load, RenderBufferStoreAction.Store, RenderBufferLoadAction.Load, RenderBufferStoreAction.Store, RenderTargetFlags.None); } public void SetRenderTarget(RenderTargetIdentifier color, RenderTargetIdentifier depth, int mipLevel) { + CheckThrowOnSetRenderTarget(); ValidateAgainstExecutionFlags(CommandBufferExecutionFlags.None, CommandBufferExecutionFlags.AsyncCompute); if (mipLevel < 0) throw new ArgumentException(String.Format("Invalid value for mipLevel ({0})", mipLevel)); @@ -1059,6 +1073,7 @@ public void SetRenderTarget(RenderTargetIdentifier color, RenderTargetIdentifier public void SetRenderTarget(RenderTargetIdentifier color, RenderTargetIdentifier depth, int mipLevel, CubemapFace cubemapFace) { + CheckThrowOnSetRenderTarget(); ValidateAgainstExecutionFlags(CommandBufferExecutionFlags.None, CommandBufferExecutionFlags.AsyncCompute); if (mipLevel < 0) throw new ArgumentException(String.Format("Invalid value for mipLevel ({0})", mipLevel)); @@ -1068,6 +1083,7 @@ public void SetRenderTarget(RenderTargetIdentifier color, RenderTargetIdentifier public void SetRenderTarget(RenderTargetIdentifier color, RenderTargetIdentifier depth, int mipLevel, CubemapFace cubemapFace, int depthSlice) { + CheckThrowOnSetRenderTarget(); ValidateAgainstExecutionFlags(CommandBufferExecutionFlags.None, CommandBufferExecutionFlags.AsyncCompute); if (depthSlice < -1) throw new ArgumentException(String.Format("Invalid value for depthSlice ({0})", depthSlice)); @@ -1080,6 +1096,7 @@ public void SetRenderTarget(RenderTargetIdentifier color, RenderTargetIdentifier public void SetRenderTarget(RenderTargetIdentifier color, RenderBufferLoadAction colorLoadAction, RenderBufferStoreAction colorStoreAction, RenderTargetIdentifier depth, RenderBufferLoadAction depthLoadAction, RenderBufferStoreAction depthStoreAction) { + CheckThrowOnSetRenderTarget(); ValidateAgainstExecutionFlags(CommandBufferExecutionFlags.None, CommandBufferExecutionFlags.AsyncCompute); if (colorLoadAction == RenderBufferLoadAction.Clear || depthLoadAction == RenderBufferLoadAction.Clear) throw new ArgumentException("RenderBufferLoadAction.Clear is not supported"); @@ -1088,6 +1105,7 @@ public void SetRenderTarget(RenderTargetIdentifier color, RenderBufferLoadAction public void SetRenderTarget(RenderTargetIdentifier[] colors, Rendering.RenderTargetIdentifier depth) { + CheckThrowOnSetRenderTarget(); ValidateAgainstExecutionFlags(CommandBufferExecutionFlags.None, CommandBufferExecutionFlags.AsyncCompute); if (colors.Length < 1) throw new ArgumentException(string.Format("colors.Length must be at least 1, but was {0}", colors.Length)); @@ -1099,6 +1117,7 @@ public void SetRenderTarget(RenderTargetIdentifier[] colors, Rendering.RenderTar public void SetRenderTarget(RenderTargetIdentifier[] colors, Rendering.RenderTargetIdentifier depth, int mipLevel, CubemapFace cubemapFace, int depthSlice) { + CheckThrowOnSetRenderTarget(); ValidateAgainstExecutionFlags(CommandBufferExecutionFlags.None, CommandBufferExecutionFlags.AsyncCompute); if (colors.Length < 1) throw new ArgumentException(string.Format("colors.Length must be at least 1, but was {0}", colors.Length)); @@ -1109,6 +1128,7 @@ public void SetRenderTarget(RenderTargetIdentifier[] colors, Rendering.RenderTar public void SetRenderTarget(RenderTargetBinding binding, int mipLevel, CubemapFace cubemapFace, int depthSlice) { + CheckThrowOnSetRenderTarget(); ValidateAgainstExecutionFlags(CommandBufferExecutionFlags.None, CommandBufferExecutionFlags.AsyncCompute); if (binding.colorRenderTargets.Length < 1) throw new ArgumentException(string.Format("The number of color render targets must be at least 1, but was {0}", binding.colorRenderTargets.Length)); @@ -1128,6 +1148,7 @@ public void SetRenderTarget(RenderTargetBinding binding, int mipLevel, CubemapFa public void SetRenderTarget(RenderTargetBinding binding) { + CheckThrowOnSetRenderTarget(); ValidateAgainstExecutionFlags(CommandBufferExecutionFlags.None, CommandBufferExecutionFlags.AsyncCompute); if (binding.colorRenderTargets.Length < 1) throw new ArgumentException(string.Format("The number of color render targets must be at least 1, but was {0}", binding.colorRenderTargets.Length)); @@ -1401,6 +1422,18 @@ public void BeginRenderPass(int width, int height, int volumeDepth, int samples, BeginRenderPass_Internal(width, height, volumeDepth, samples, attachments, depthAttachmentIndex, subPasses, new ReadOnlySpan()); } + public void BeginRenderPass(int width, int height, int samples, NativeArray attachments, int depthAttachmentIndex, NativeArray subPasses, ReadOnlySpan debugNameUtf8) + { + ValidateAgainstExecutionFlags(CommandBufferExecutionFlags.None, CommandBufferExecutionFlags.AsyncCompute); + BeginRenderPass_Internal(width, height, 1, samples, attachments, depthAttachmentIndex, subPasses, debugNameUtf8); + } + + public void BeginRenderPass(int width, int height, int volumeDepth, int samples, NativeArray attachments, int depthAttachmentIndex, NativeArray subPasses, ReadOnlySpan debugNameUtf8) + { + ValidateAgainstExecutionFlags(CommandBufferExecutionFlags.None, CommandBufferExecutionFlags.AsyncCompute); + BeginRenderPass_Internal(width, height, volumeDepth, samples, attachments, depthAttachmentIndex, subPasses, debugNameUtf8); + } + [FreeFunction("RenderingCommandBuffer_Bindings::NextSubPass", HasExplicitThis = true)] extern void NextSubPass_Internal(); public void NextSubPass() diff --git a/Runtime/Export/Graphics/Texture.bindings.cs b/Runtime/Export/Graphics/Texture.bindings.cs index 0dd007b035..319c690f10 100644 --- a/Runtime/Export/Graphics/Texture.bindings.cs +++ b/Runtime/Export/Graphics/Texture.bindings.cs @@ -792,7 +792,9 @@ public RenderTextureFormat format extern public bool bindTextureMS { get; set; } extern public bool enableRandomWrite { get; set; } extern public bool useDynamicScale { get; set; } + extern public bool useDynamicScaleExplicit { get; set; } + extern public void ApplyDynamicScale(); // for some reason we are providing isPowerOfTwo setter which is empty (i dont know what the intent is/was) extern private bool GetIsPowerOfTwo(); diff --git a/Runtime/Export/Graphics/Texture.cs b/Runtime/Export/Graphics/Texture.cs index abef6db223..00087e50d6 100644 --- a/Runtime/Export/Graphics/Texture.cs +++ b/Runtime/Export/Graphics/Texture.cs @@ -219,6 +219,12 @@ public bool useDynamicScale get { return (_flags & RenderTextureCreationFlags.DynamicallyScalable) != 0; } set { SetOrClearRenderTextureCreationFlag(value, RenderTextureCreationFlags.DynamicallyScalable); } } + + public bool useDynamicScaleExplicit + { + get { return (_flags & RenderTextureCreationFlags.DynamicallyScalableExplicit) != 0; } + set { SetOrClearRenderTextureCreationFlag(value, RenderTextureCreationFlags.DynamicallyScalableExplicit); } + } } public partial class RenderTexture : Texture diff --git a/Runtime/Export/Misc/ObjectDispatcher.bindings.cs b/Runtime/Export/Misc/ObjectDispatcher.bindings.cs index 7774831356..31e2920886 100644 --- a/Runtime/Export/Misc/ObjectDispatcher.bindings.cs +++ b/Runtime/Export/Misc/ObjectDispatcher.bindings.cs @@ -84,6 +84,15 @@ public enum TypeTrackingFlags private IntPtr m_Ptr = IntPtr.Zero; + // These are used to avoid dynamically created delegate objects (or GC.HeapAlloc for every dispatch call). + private Allocator m_DispatchAllocator; + private TypeDispatchData m_TypeDispatchData; + private TransformDispatchData m_TransformDispatchData; + private Component[] m_TransformedComponents; + private Action m_TypeDataCallback; + private Action m_TransformDataCallback; + private Action m_TransformComponentCallback; + public bool valid { get { return m_Ptr != IntPtr.Zero; } } public int maxDispatchHistoryFramesCount @@ -105,6 +114,10 @@ public int maxDispatchHistoryFramesCount public ObjectDispatcher() { m_Ptr = CreateDispatchSystemHandle(); + + m_TypeDataCallback = DispatchCallback; + m_TransformDataCallback = DispatchCallback; + m_TransformComponentCallback = DispatchCallback; } ~ObjectDispatcher() @@ -214,6 +227,30 @@ private void ValidateComponentTypeAndThrow(Type type) } }; + private void DispatchCallback(TypeDispatchData data) + { + m_TypeDispatchData = new TypeDispatchData(); + m_TypeDispatchData.changed = data.changed; + m_TypeDispatchData.changedID = new NativeArray(data.changedID, m_DispatchAllocator); + m_TypeDispatchData.destroyedID = new NativeArray(data.destroyedID, m_DispatchAllocator); + } + + private void DispatchCallback(TransformDispatchData data) + { + m_TransformDispatchData = new TransformDispatchData(); + m_TransformDispatchData.transformedID = new NativeArray(data.transformedID, m_DispatchAllocator); + m_TransformDispatchData.parentID = new NativeArray(data.parentID, m_DispatchAllocator); + m_TransformDispatchData.localToWorldMatrices = new NativeArray(data.localToWorldMatrices, m_DispatchAllocator); + m_TransformDispatchData.positions = new NativeArray(data.positions, m_DispatchAllocator); + m_TransformDispatchData.rotations = new NativeArray(data.rotations, m_DispatchAllocator); + m_TransformDispatchData.scales = new NativeArray(data.scales, m_DispatchAllocator); + } + + private void DispatchCallback(Component[] components) + { + m_TransformedComponents = components; + } + public void DispatchTypeChangesAndClear(Type type, Action callback, bool sortByInstanceID = false, bool noScriptingArray = false) { ValidateSystemHandleAndThrow(); @@ -244,60 +281,31 @@ public void ClearTypeChanges(Type type) public TypeDispatchData GetTypeChangesAndClear(Type type, Allocator allocator, bool sortByInstanceID = false, bool noScriptingArray = false) { - var dispatchData = new TypeDispatchData(); - - DispatchTypeChangesAndClear(type, (TypeDispatchData data) => - { - dispatchData.changed = data.changed; - dispatchData.changedID = new NativeArray(data.changedID, allocator); - dispatchData.destroyedID = new NativeArray(data.destroyedID, allocator); - }, sortByInstanceID, noScriptingArray); - - return dispatchData; + m_DispatchAllocator = allocator; + DispatchTypeChangesAndClear(type, m_TypeDataCallback, sortByInstanceID, noScriptingArray); + return m_TypeDispatchData; } public void GetTypeChangesAndClear(Type type, List changed, out NativeArray changedID, out NativeArray destroyedID, Allocator allocator, bool sortByInstanceID = false) { - var dispatchData = new TypeDispatchData(); - - DispatchTypeChangesAndClear(type, (TypeDispatchData data) => - { - dispatchData.changedID = new NativeArray(data.changedID, allocator); - dispatchData.destroyedID = new NativeArray(data.destroyedID, allocator); - }, sortByInstanceID, true); - - changedID = dispatchData.changedID; - destroyedID = dispatchData.destroyedID; - Resources.InstanceIDToObjectList(dispatchData.changedID, changed); + m_DispatchAllocator = allocator; + DispatchTypeChangesAndClear(type, m_TypeDataCallback, sortByInstanceID, true); + changedID = m_TypeDispatchData.changedID; + destroyedID = m_TypeDispatchData.destroyedID; + Resources.InstanceIDToObjectList(m_TypeDispatchData.changedID, changed); } public Component[] GetTransformChangesAndClear(Type type, TransformTrackingType trackingType, bool sortByInstanceID = false) { - Component[] dispatchData = null; - - DispatchTransformChangesAndClear(type, trackingType, (Component[] instances) => - { - dispatchData = instances; - }, sortByInstanceID); - - return dispatchData; + DispatchTransformChangesAndClear(type, trackingType, m_TransformComponentCallback, sortByInstanceID); + return m_TransformedComponents; } public TransformDispatchData GetTransformChangesAndClear(Type type, TransformTrackingType trackingType, Allocator allocator) { - var dispatchData = new TransformDispatchData(); - - DispatchTransformChangesAndClear(type, trackingType, (TransformDispatchData data) => - { - dispatchData.transformedID = new NativeArray(data.transformedID, allocator); - dispatchData.parentID = new NativeArray(data.parentID, allocator); - dispatchData.localToWorldMatrices = new NativeArray(data.localToWorldMatrices, allocator); - dispatchData.positions = new NativeArray(data.positions, allocator); - dispatchData.rotations = new NativeArray(data.rotations, allocator); - dispatchData.scales = new NativeArray(data.scales, allocator); - }); - - return dispatchData; + m_DispatchAllocator = allocator; + DispatchTransformChangesAndClear(type, trackingType, m_TransformDataCallback); + return m_TransformDispatchData; } public void EnableTypeTracking(TypeTrackingFlags typeTrackingMask, params Type[] types) diff --git a/Runtime/Export/RenderPipeline/RenderPipelineGlobalSettings.cs b/Runtime/Export/RenderPipeline/RenderPipelineGlobalSettings.cs index 73cf264d3f..3740a8f9b1 100644 --- a/Runtime/Export/RenderPipeline/RenderPipelineGlobalSettings.cs +++ b/Runtime/Export/RenderPipeline/RenderPipelineGlobalSettings.cs @@ -9,32 +9,90 @@ namespace UnityEngine.Rendering { public abstract class RenderPipelineGlobalSettings : ScriptableObject { - [SerializeField] RenderPipelineGraphicsSettingsContainer m_Settings = new(); + protected virtual List settingsList + { + get + { + Debug.LogWarning($"To be able to use {nameof(IRenderPipelineGraphicsSettings)} in your {GetType()} you must override {nameof(settingsList)}"); + Debug.LogWarning($"Create your own '[{nameof(SerializeReference)}] List<{nameof(IRenderPipelineGraphicsSettings)}> m_Settings = new();' in your {GetType()} and override {nameof(settingsList)} returning m_Settings;"); + return null; + } + } + + private Dictionary m_SettingsMap = null; + + private Dictionary settingsMap + { + get + { + if ( m_SettingsMap == null) + RecreateSettingsMap(); + + return m_SettingsMap; + } + } + + private void RecreateSettingsMap() + { + if (m_SettingsMap == null) + m_SettingsMap = new(); + else + m_SettingsMap.Clear(); + + if (settingsList == null) + return; + + for (int i = 0; i < settingsList.Count; i++) + { + var element = settingsList[i]; + + if (element == null) + continue; //missing script can cause this, preserve data, just not access it + + m_SettingsMap.Add(element.GetType(), i); + } + } public virtual void Initialize(RenderPipelineGlobalSettings source = null) { } - protected internal bool Add(IRenderPipelineGraphicsSettings setting) + protected internal bool Add(IRenderPipelineGraphicsSettings settings) { - if (!m_Settings.Add(setting)) + if (settingsList == null || settingsMap.ContainsKey(settings.GetType())) return false; + settingsMap.Add(settings.GetType(), settingsList.Count); + settingsList.Add(settings); MarkDirty(); return true; - } - protected internal bool Remove(IRenderPipelineGraphicsSettings setting) + protected internal bool Remove(IRenderPipelineGraphicsSettings settings) { - if (!m_Settings.Remove(setting)) + if (settingsList == null || !settingsMap.TryGetValue(settings.GetType(), out var index)) return false; + settingsList.RemoveAt(index); + RecreateSettingsMap(); MarkDirty(); return true; } - protected internal bool TryGet(Type type, out IRenderPipelineGraphicsSettings setting) => m_Settings.TryGet(type, out setting); - protected internal bool Contains(Type type) => m_Settings.Contains(type); + protected internal bool TryGet(Type type, out IRenderPipelineGraphicsSettings settings) + { + settings = null; + + if (settingsList == null || !settingsMap.TryGetValue(type, out var index)) + return false; + + settings = settingsList[index]; + return settings != null; + } + + protected internal bool Contains(Type type) + { + return settingsList != null && settingsMap.ContainsKey(type); + } } } diff --git a/Runtime/Export/RenderPipeline/RenderPipelineGraphicsSettingsContainer.cs b/Runtime/Export/RenderPipeline/RenderPipelineGraphicsSettingsContainer.cs deleted file mode 100644 index 577e420cc9..0000000000 --- a/Runtime/Export/RenderPipeline/RenderPipelineGraphicsSettingsContainer.cs +++ /dev/null @@ -1,99 +0,0 @@ -// 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 UnityEngine.Rendering -{ - [Serializable] - struct RenderPipelineGraphicsSettingsContainer : ISerializationCallbackReceiver - { - [SerializeReference] List m_SettingsList; - - private Dictionary m_RandomQuickAccess; - - public RenderPipelineGraphicsSettingsContainer() - { - m_SettingsList = new(); - m_RandomQuickAccess = new(); - } - - internal bool Contains(Type type) - { - return m_RandomQuickAccess.ContainsKey(type); - } - - internal bool Add(IRenderPipelineGraphicsSettings element) - { - if (element == null) - throw new ArgumentNullException(nameof(element)); - - var type = element.GetType(); - if (Contains(type)) - { - Debug.LogWarning($"Element of type {type} is already added"); - return false; - } - - m_RandomQuickAccess.Add(type, m_SettingsList.Count); - m_SettingsList.Add(element); - return true; - } - - internal bool Remove(IRenderPipelineGraphicsSettings element) - { - if (element == null) - throw new ArgumentNullException(nameof(element)); - - var type = element.GetType(); - if (!Contains(type)) - return false; - - m_SettingsList.RemoveAt(m_RandomQuickAccess[type]); - RecreateQuickAccess(); - return true; - } - - internal bool TryGet(Type type, out IRenderPipelineGraphicsSettings element) - { - if (type == null) - throw new ArgumentNullException(nameof(type)); - - element = null; - if (!m_RandomQuickAccess.TryGetValue(type, out var index)) - return false; - - element = m_SettingsList[index]; - return element != null; - } - - void RecreateQuickAccess() - { - if (m_SettingsList == null) - throw new Exception("Settings list not initialized"); - - if (m_RandomQuickAccess == null) - m_RandomQuickAccess = new(); - else - m_RandomQuickAccess.Clear(); - - var length = m_SettingsList.Count; - for (int i = 0; i < length; ++i) - { - var element = m_SettingsList[i]; - - if (element == null) - continue; //missing script can cause this, preserve data, just not access it - - m_RandomQuickAccess.Add(element.GetType(), i); - } - } - - void ISerializationCallbackReceiver.OnAfterDeserialize() - => RecreateQuickAccess(); - - void ISerializationCallbackReceiver.OnBeforeSerialize() {} - } -}