From bca1cbb32328ec398fe9a3ea36888aa78041e3fb Mon Sep 17 00:00:00 2001 From: AsanoRema Date: Tue, 4 Aug 2026 13:47:07 +0300 Subject: [PATCH] Init --- .gitattributes | 67 + .gitignore | 3 + Assets/DefaultNetworkPrefabs.asset | 21 + Assets/DefaultNetworkPrefabs.asset.meta | 8 + Assets/InputSystem_Actions.inputactions | 1057 +++++++ Assets/InputSystem_Actions.inputactions.meta | 14 + Assets/OtherAssets.meta | 8 + Assets/OtherAssets/DEV.meta | 8 + Assets/OtherAssets/DEV/FREE.meta | 8 + .../OtherAssets/DEV/FREE/SceneSwitcher.meta | 8 + .../DEV/FREE/SceneSwitcher/Editor.meta | 8 + .../Editor/SceneSwitcherToolbar.cs | 269 ++ .../Editor/SceneSwitcherToolbar.cs.meta | 9 + Assets/OtherAssets/DEV/PAID.meta | 8 + Assets/OtherAssets/DEV/PAID/vFavorites.meta | 8 + .../DEV/PAID/vFavorites/Manual.pdf | Bin 0 -> 555474 bytes .../DEV/PAID/vFavorites/Manual.pdf.meta | 7 + .../DEV/PAID/vFavorites/VFavorites.asmdef | 16 + .../PAID/vFavorites/VFavorites.asmdef.meta | 7 + .../DEV/PAID/vFavorites/VFavorites.cs | 1976 ++++++++++++ .../DEV/PAID/vFavorites/VFavorites.cs.meta | 11 + .../DEV/PAID/vFavorites/VFavoritesData.cs | 225 ++ .../PAID/vFavorites/VFavoritesData.cs.meta | 11 + .../DEV/PAID/vFavorites/VFavoritesLibs.cs | 1816 +++++++++++ .../PAID/vFavorites/VFavoritesLibs.cs.meta | 11 + .../DEV/PAID/vFavorites/VFavoritesMenu.cs | 140 + .../PAID/vFavorites/VFavoritesMenu.cs.meta | 11 + .../PAID/vFavorites/VFavoritesMenuItems.cs | 6 + .../vFavorites/VFavoritesMenuItems.cs.meta | 11 + .../DEV/PAID/vFavorites/VFavoritesState.cs | 57 + .../PAID/vFavorites/VFavoritesState.cs.meta | 11 + .../DEV/PAID/vFavorites/vFavorites Data.asset | 22 + .../vFavorites/vFavorites Data.asset.meta | 8 + Assets/OtherAssets/DEV/PAID/vFolders.meta | 8 + .../OtherAssets/DEV/PAID/vFolders/Read me.pdf | Bin 0 -> 47718 bytes .../DEV/PAID/vFolders/Read me.pdf.meta | 7 + .../DEV/PAID/vFolders/VFolders.asmdef | 16 + .../DEV/PAID/vFolders/VFolders.asmdef.meta | 7 + .../OtherAssets/DEV/PAID/vFolders/VFolders.cs | 1677 +++++++++++ .../DEV/PAID/vFolders/VFolders.cs.meta | 11 + .../DEV/PAID/vFolders/VFoldersCache.cs | 102 + .../DEV/PAID/vFolders/VFoldersCache.cs.meta | 11 + .../DEV/PAID/vFolders/VFoldersController.cs | 521 ++++ .../PAID/vFolders/VFoldersController.cs.meta | 11 + .../DEV/PAID/vFolders/VFoldersData.cs | 145 + .../DEV/PAID/vFolders/VFoldersData.cs.meta | 11 + .../DEV/PAID/vFolders/VFoldersGUI.cs | 1325 ++++++++ .../DEV/PAID/vFolders/VFoldersGUI.cs.meta | 11 + .../DEV/PAID/vFolders/VFoldersHistory.cs | 403 +++ .../DEV/PAID/vFolders/VFoldersHistory.cs.meta | 11 + .../DEV/PAID/vFolders/VFoldersIconEditor.cs | 5 + .../PAID/vFolders/VFoldersIconEditor.cs.meta | 11 + .../DEV/PAID/vFolders/VFoldersLibs.cs | 2667 +++++++++++++++++ .../DEV/PAID/vFolders/VFoldersLibs.cs.meta | 11 + .../DEV/PAID/vFolders/VFoldersMenu.cs | 128 + .../DEV/PAID/vFolders/VFoldersMenu.cs.meta | 11 + .../DEV/PAID/vFolders/VFoldersMenuItems.cs | 5 + .../PAID/vFolders/VFoldersMenuItems.cs.meta | 11 + .../DEV/PAID/vFolders/VFoldersNavbar.cs | 1411 +++++++++ .../DEV/PAID/vFolders/VFoldersNavbar.cs.meta | 11 + .../DEV/PAID/vFolders/VFoldersPalette.cs | 268 ++ .../DEV/PAID/vFolders/VFoldersPalette.cs.meta | 11 + .../PAID/vFolders/VFoldersPaletteEditor.cs | 1375 +++++++++ .../vFolders/VFoldersPaletteEditor.cs.meta | 11 + .../PAID/vFolders/VFoldersPaletteWindow.cs | 783 +++++ .../vFolders/VFoldersPaletteWindow.cs.meta | 11 + Assets/OtherAssets/DEV/PAID/vHierarchy.meta | 8 + .../DEV/PAID/vHierarchy/Read me.pdf | Bin 0 -> 116397 bytes .../DEV/PAID/vHierarchy/Read me.pdf.meta | 7 + .../DEV/PAID/vHierarchy/VHierarchy.asmdef | 16 + .../PAID/vHierarchy/VHierarchy.asmdef.meta | 7 + .../DEV/PAID/vHierarchy/VHierarchy.cs | 1808 +++++++++++ .../DEV/PAID/vHierarchy/VHierarchy.cs.meta | 11 + .../DEV/PAID/vHierarchy/VHierarchyCache.cs | 61 + .../PAID/vHierarchy/VHierarchyCache.cs.meta | 11 + .../vHierarchy/VHierarchyComponentEditor.cs | 6 + .../VHierarchyComponentEditor.cs.meta | 11 + .../vHierarchy/VHierarchyComponentWindow.cs | 599 ++++ .../VHierarchyComponentWindow.cs.meta | 11 + .../PAID/vHierarchy/VHierarchyController.cs | 511 ++++ .../vHierarchy/VHierarchyController.cs.meta | 11 + .../DEV/PAID/vHierarchy/VHierarchyData.cs | 227 ++ .../PAID/vHierarchy/VHierarchyData.cs.meta | 11 + .../vHierarchy/VHierarchyDataComponent.cs | 142 + .../VHierarchyDataComponent.cs.meta | 11 + .../DEV/PAID/vHierarchy/VHierarchyGUI.cs | 1024 +++++++ .../DEV/PAID/vHierarchy/VHierarchyGUI.cs.meta | 11 + .../PAID/vHierarchy/VHierarchyIconEditor.cs | 6 + .../vHierarchy/VHierarchyIconEditor.cs.meta | 11 + .../DEV/PAID/vHierarchy/VHierarchyLibs.cs | 2563 ++++++++++++++++ .../PAID/vHierarchy/VHierarchyLibs.cs.meta | 11 + .../vHierarchy/VHierarchyLightingWindow.cs | 6 + .../VHierarchyLightingWindow.cs.meta | 11 + .../DEV/PAID/vHierarchy/VHierarchyMenu.cs | 159 + .../PAID/vHierarchy/VHierarchyMenu.cs.meta | 11 + .../PAID/vHierarchy/VHierarchyMenuItems.cs | 6 + .../vHierarchy/VHierarchyMenuItems.cs.meta | 11 + .../DEV/PAID/vHierarchy/VHierarchyNavbar.cs | 1081 +++++++ .../PAID/vHierarchy/VHierarchyNavbar.cs.meta | 11 + .../DEV/PAID/vHierarchy/VHierarchyPalette.cs | 221 ++ .../PAID/vHierarchy/VHierarchyPalette.cs.meta | 11 + .../vHierarchy/VHierarchyPaletteEditor.cs | 1374 +++++++++ .../VHierarchyPaletteEditor.cs.meta | 11 + .../vHierarchy/VHierarchyPaletteWindow.cs | 711 +++++ .../VHierarchyPaletteWindow.cs.meta | 11 + .../VHierarchySceneSelectorWindow.cs | 1189 ++++++++ .../VHierarchySceneSelectorWindow.cs.meta | 11 + .../DEV/PAID/vHierarchy/vHierarchy Data.asset | 53 + .../vHierarchy/vHierarchy Data.asset.meta | 8 + .../PAID/vHierarchy/vHierarchy Palette.asset | 81 + .../vHierarchy/vHierarchy Palette.asset.meta | 8 + Assets/OtherAssets/DEV/PAID/vInspector.meta | 8 + .../DEV/PAID/vInspector/Read me.pdf | Bin 0 -> 42072 bytes .../DEV/PAID/vInspector/Read me.pdf.meta | 7 + .../DEV/PAID/vInspector/VInspector.asmdef | 14 + .../PAID/vInspector/VInspector.asmdef.meta | 7 + .../DEV/PAID/vInspector/VInspector.cs | 1574 ++++++++++ .../DEV/PAID/vInspector/VInspector.cs.meta | 11 + .../PAID/vInspector/VInspectorAttributes.cs | 139 + .../vInspector/VInspectorAttributes.cs.meta | 11 + .../PAID/vInspector/VInspectorClipboard.cs | 231 ++ .../vInspector/VInspectorClipboard.cs.meta | 11 + .../vInspector/VInspectorComponentHeader.cs | 463 +++ .../VInspectorComponentHeader.cs.meta | 11 + .../vInspector/VInspectorComponentWindow.cs | 498 +++ .../VInspectorComponentWindow.cs.meta | 11 + .../DEV/PAID/vInspector/VInspectorData.cs | 156 + .../PAID/vInspector/VInspectorData.cs.meta | 11 + .../DEV/PAID/vInspector/VInspectorDrawers.cs | 429 +++ .../PAID/vInspector/VInspectorDrawers.cs.meta | 11 + .../DEV/PAID/vInspector/VInspectorEditor.cs | 2105 +++++++++++++ .../PAID/vInspector/VInspectorEditor.cs.meta | 11 + .../DEV/PAID/vInspector/VInspectorLibs.cs | 2537 ++++++++++++++++ .../PAID/vInspector/VInspectorLibs.cs.meta | 11 + .../DEV/PAID/vInspector/VInspectorMenu.cs | 162 + .../PAID/vInspector/VInspectorMenu.cs.meta | 11 + .../DEV/PAID/vInspector/VInspectorNavbar.cs | 990 ++++++ .../PAID/vInspector/VInspectorNavbar.cs.meta | 11 + .../VInspectorResettableVariables.cs | 147 + .../VInspectorResettableVariables.cs.meta | 11 + .../vInspector/VInspectorSelectionHistory.cs | 129 + .../VInspectorSelectionHistory.cs.meta | 11 + .../VInspectorSerializedDictionary.cs | 73 + .../VInspectorSerializedDictionary.cs.meta | 11 + .../DEV/PAID/vInspector/VInspectorState.cs | 66 + .../PAID/vInspector/VInspectorState.cs.meta | 11 + Assets/OtherAssets/DEV/PAID/vTabs.meta | 8 + Assets/OtherAssets/DEV/PAID/vTabs/Read me.pdf | Bin 0 -> 49974 bytes .../DEV/PAID/vTabs/Read me.pdf.meta | 7 + .../OtherAssets/DEV/PAID/vTabs/VTabs.asmdef | 16 + .../DEV/PAID/vTabs/VTabs.asmdef.meta | 7 + Assets/OtherAssets/DEV/PAID/vTabs/VTabs.cs | 1535 ++++++++++ .../OtherAssets/DEV/PAID/vTabs/VTabs.cs.meta | 11 + .../DEV/PAID/vTabs/VTabsAddTabWindow.cs | 1223 ++++++++ .../DEV/PAID/vTabs/VTabsAddTabWindow.cs.meta | 11 + .../OtherAssets/DEV/PAID/vTabs/VTabsCache.cs | 44 + .../DEV/PAID/vTabs/VTabsCache.cs.meta | 11 + Assets/OtherAssets/DEV/PAID/vTabs/VTabsGUI.cs | 972 ++++++ .../DEV/PAID/vTabs/VTabsGUI.cs.meta | 11 + .../OtherAssets/DEV/PAID/vTabs/VTabsLibs.cs | 1783 +++++++++++ .../DEV/PAID/vTabs/VTabsLibs.cs.meta | 11 + .../OtherAssets/DEV/PAID/vTabs/VTabsMenu.cs | 197 ++ .../DEV/PAID/vTabs/VTabsMenu.cs.meta | 11 + .../DEV/PAID/vTabs/VTabsMenuItems.cs | 6 + .../DEV/PAID/vTabs/VTabsMenuItems.cs.meta | 11 + Assets/Samples.meta | 8 + Assets/Samples/Netcode for GameObjects.meta | 8 + .../Netcode for GameObjects/2.13.1.meta | 8 + .../2.13.1/Bootstrap.meta | 8 + .../2.13.1/Bootstrap/Prefabs.meta | 8 + .../Bootstrap/Prefabs/BootstrapPlayer.prefab | 155 + .../Prefabs/BootstrapPlayer.prefab.meta | 7 + .../2.13.1/Bootstrap/Scenes.meta | 8 + .../2.13.1/Bootstrap/Scenes/Bootstrap.unity | 401 +++ .../Bootstrap/Scenes/Bootstrap.unity.meta | 7 + .../2.13.1/Bootstrap/Scripts.meta | 8 + .../2.13.1/Bootstrap/Scripts/Bootstrap.asmdef | 7 + .../Bootstrap/Scripts/Bootstrap.asmdef.meta | 7 + .../Bootstrap/Scripts/BootstrapManager.cs | 58 + .../Scripts/BootstrapManager.cs.meta | 11 + .../Bootstrap/Scripts/BootstrapPlayer.cs | 32 + .../Bootstrap/Scripts/BootstrapPlayer.cs.meta | 11 + Assets/Scenes.meta | 8 + Assets/Scenes/SampleScene.unity | 432 +++ Assets/Scenes/SampleScene.unity.meta | 7 + Assets/Settings.meta | 8 + Assets/Settings/DefaultVolumeProfile.asset | 982 ++++++ .../Settings/DefaultVolumeProfile.asset.meta | 8 + Assets/Settings/Mobile_RPAsset.asset | 135 + Assets/Settings/Mobile_RPAsset.asset.meta | 8 + Assets/Settings/Mobile_Renderer.asset | 52 + Assets/Settings/Mobile_Renderer.asset.meta | 8 + Assets/Settings/PC_RPAsset.asset | 135 + Assets/Settings/PC_RPAsset.asset.meta | 8 + Assets/Settings/PC_Renderer.asset | 95 + Assets/Settings/PC_Renderer.asset.meta | 8 + Assets/Settings/SampleSceneProfile.asset | 159 + Assets/Settings/SampleSceneProfile.asset.meta | 8 + ...niversalRenderPipelineGlobalSettings.asset | 355 +++ ...salRenderPipelineGlobalSettings.asset.meta | 8 + Assets/TutorialInfo.meta | 8 + Assets/TutorialInfo/Icons.meta | 9 + Assets/TutorialInfo/Icons/URP.png | 3 + Assets/TutorialInfo/Icons/URP.png.meta | 134 + Assets/TutorialInfo/Layout.wlt | 654 ++++ Assets/TutorialInfo/Layout.wlt.meta | 8 + Assets/TutorialInfo/Scripts.meta | 9 + Assets/TutorialInfo/Scripts/Editor.meta | 9 + .../Scripts/Editor/ReadmeEditor.cs | 242 ++ .../Scripts/Editor/ReadmeEditor.cs.meta | 12 + Assets/TutorialInfo/Scripts/Readme.cs | 16 + Assets/TutorialInfo/Scripts/Readme.cs.meta | 12 + Packages/manifest.json | 55 + Packages/packages-lock.json | 658 ++++ ProjectSettings/AudioManager.asset | 19 + ProjectSettings/ClusterInputManager.asset | 6 + ProjectSettings/DynamicsManager.asset | 36 + ProjectSettings/EditorBuildSettings.asset | 13 + ProjectSettings/EditorSettings.asset | 49 + ProjectSettings/GraphicsSettings.asset | 70 + ProjectSettings/InputManager.asset | 487 +++ ProjectSettings/MemorySettings.asset | 35 + ProjectSettings/MultiplayerManager.asset | 7 + ProjectSettings/NavMeshAreas.asset | 91 + ProjectSettings/PackageManagerSettings.asset | 43 + .../CustomColorSettings.asset | 17 + .../com.unity.probuilder/Settings.json | 31 + ProjectSettings/Physics2DSettings.asset | 56 + ProjectSettings/PresetManager.asset | 7 + ProjectSettings/ProjectSettings.asset | 936 ++++++ ProjectSettings/ProjectVersion.txt | 2 + ProjectSettings/QualitySettings.asset | 134 + ProjectSettings/ShaderGraphSettings.asset | 19 + ProjectSettings/TagManager.asset | 76 + ProjectSettings/TimeManager.asset | 9 + ProjectSettings/URPProjectSettings.asset | 15 + ProjectSettings/UnityConnectSettings.asset | 36 + ProjectSettings/VFXManager.asset | 12 + ProjectSettings/VersionControlSettings.asset | 8 + ProjectSettings/VirtualProjectsConfig.json | 4 + ProjectSettings/XRSettings.asset | 10 + 241 files changed, 51945 insertions(+) create mode 100644 .gitattributes create mode 100644 Assets/DefaultNetworkPrefabs.asset create mode 100644 Assets/DefaultNetworkPrefabs.asset.meta create mode 100644 Assets/InputSystem_Actions.inputactions create mode 100644 Assets/InputSystem_Actions.inputactions.meta create mode 100644 Assets/OtherAssets.meta create mode 100644 Assets/OtherAssets/DEV.meta create mode 100644 Assets/OtherAssets/DEV/FREE.meta create mode 100644 Assets/OtherAssets/DEV/FREE/SceneSwitcher.meta create mode 100644 Assets/OtherAssets/DEV/FREE/SceneSwitcher/Editor.meta create mode 100644 Assets/OtherAssets/DEV/FREE/SceneSwitcher/Editor/SceneSwitcherToolbar.cs create mode 100644 Assets/OtherAssets/DEV/FREE/SceneSwitcher/Editor/SceneSwitcherToolbar.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vFavorites.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vFavorites/Manual.pdf create mode 100644 Assets/OtherAssets/DEV/PAID/vFavorites/Manual.pdf.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vFavorites/VFavorites.asmdef create mode 100644 Assets/OtherAssets/DEV/PAID/vFavorites/VFavorites.asmdef.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vFavorites/VFavorites.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vFavorites/VFavorites.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vFavorites/VFavoritesData.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vFavorites/VFavoritesData.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vFavorites/VFavoritesLibs.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vFavorites/VFavoritesLibs.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vFavorites/VFavoritesMenu.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vFavorites/VFavoritesMenu.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vFavorites/VFavoritesMenuItems.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vFavorites/VFavoritesMenuItems.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vFavorites/VFavoritesState.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vFavorites/VFavoritesState.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vFavorites/vFavorites Data.asset create mode 100644 Assets/OtherAssets/DEV/PAID/vFavorites/vFavorites Data.asset.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vFolders.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vFolders/Read me.pdf create mode 100644 Assets/OtherAssets/DEV/PAID/vFolders/Read me.pdf.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vFolders/VFolders.asmdef create mode 100644 Assets/OtherAssets/DEV/PAID/vFolders/VFolders.asmdef.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vFolders/VFolders.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vFolders/VFolders.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vFolders/VFoldersCache.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vFolders/VFoldersCache.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vFolders/VFoldersController.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vFolders/VFoldersController.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vFolders/VFoldersData.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vFolders/VFoldersData.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vFolders/VFoldersGUI.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vFolders/VFoldersGUI.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vFolders/VFoldersHistory.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vFolders/VFoldersHistory.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vFolders/VFoldersIconEditor.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vFolders/VFoldersIconEditor.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vFolders/VFoldersLibs.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vFolders/VFoldersLibs.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vFolders/VFoldersMenu.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vFolders/VFoldersMenu.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vFolders/VFoldersMenuItems.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vFolders/VFoldersMenuItems.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vFolders/VFoldersNavbar.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vFolders/VFoldersNavbar.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vFolders/VFoldersPalette.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vFolders/VFoldersPalette.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vFolders/VFoldersPaletteEditor.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vFolders/VFoldersPaletteEditor.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vFolders/VFoldersPaletteWindow.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vFolders/VFoldersPaletteWindow.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/Read me.pdf create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/Read me.pdf.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchy.asmdef create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchy.asmdef.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchy.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchy.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyCache.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyCache.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyComponentEditor.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyComponentEditor.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyComponentWindow.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyComponentWindow.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyController.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyController.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyData.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyData.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyDataComponent.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyDataComponent.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyGUI.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyGUI.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyIconEditor.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyIconEditor.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyLibs.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyLibs.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyLightingWindow.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyLightingWindow.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyMenu.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyMenu.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyMenuItems.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyMenuItems.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyNavbar.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyNavbar.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyPalette.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyPalette.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyPaletteEditor.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyPaletteEditor.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyPaletteWindow.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyPaletteWindow.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchySceneSelectorWindow.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchySceneSelectorWindow.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/vHierarchy Data.asset create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/vHierarchy Data.asset.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/vHierarchy Palette.asset create mode 100644 Assets/OtherAssets/DEV/PAID/vHierarchy/vHierarchy Palette.asset.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vInspector.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vInspector/Read me.pdf create mode 100644 Assets/OtherAssets/DEV/PAID/vInspector/Read me.pdf.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vInspector/VInspector.asmdef create mode 100644 Assets/OtherAssets/DEV/PAID/vInspector/VInspector.asmdef.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vInspector/VInspector.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vInspector/VInspector.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vInspector/VInspectorAttributes.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vInspector/VInspectorAttributes.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vInspector/VInspectorClipboard.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vInspector/VInspectorClipboard.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vInspector/VInspectorComponentHeader.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vInspector/VInspectorComponentHeader.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vInspector/VInspectorComponentWindow.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vInspector/VInspectorComponentWindow.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vInspector/VInspectorData.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vInspector/VInspectorData.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vInspector/VInspectorDrawers.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vInspector/VInspectorDrawers.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vInspector/VInspectorEditor.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vInspector/VInspectorEditor.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vInspector/VInspectorLibs.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vInspector/VInspectorLibs.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vInspector/VInspectorMenu.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vInspector/VInspectorMenu.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vInspector/VInspectorNavbar.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vInspector/VInspectorNavbar.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vInspector/VInspectorResettableVariables.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vInspector/VInspectorResettableVariables.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vInspector/VInspectorSelectionHistory.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vInspector/VInspectorSelectionHistory.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vInspector/VInspectorSerializedDictionary.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vInspector/VInspectorSerializedDictionary.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vInspector/VInspectorState.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vInspector/VInspectorState.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vTabs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vTabs/Read me.pdf create mode 100644 Assets/OtherAssets/DEV/PAID/vTabs/Read me.pdf.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vTabs/VTabs.asmdef create mode 100644 Assets/OtherAssets/DEV/PAID/vTabs/VTabs.asmdef.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vTabs/VTabs.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vTabs/VTabs.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vTabs/VTabsAddTabWindow.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vTabs/VTabsAddTabWindow.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vTabs/VTabsCache.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vTabs/VTabsCache.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vTabs/VTabsGUI.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vTabs/VTabsGUI.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vTabs/VTabsLibs.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vTabs/VTabsLibs.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vTabs/VTabsMenu.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vTabs/VTabsMenu.cs.meta create mode 100644 Assets/OtherAssets/DEV/PAID/vTabs/VTabsMenuItems.cs create mode 100644 Assets/OtherAssets/DEV/PAID/vTabs/VTabsMenuItems.cs.meta create mode 100644 Assets/Samples.meta create mode 100644 Assets/Samples/Netcode for GameObjects.meta create mode 100644 Assets/Samples/Netcode for GameObjects/2.13.1.meta create mode 100644 Assets/Samples/Netcode for GameObjects/2.13.1/Bootstrap.meta create mode 100644 Assets/Samples/Netcode for GameObjects/2.13.1/Bootstrap/Prefabs.meta create mode 100644 Assets/Samples/Netcode for GameObjects/2.13.1/Bootstrap/Prefabs/BootstrapPlayer.prefab create mode 100644 Assets/Samples/Netcode for GameObjects/2.13.1/Bootstrap/Prefabs/BootstrapPlayer.prefab.meta create mode 100644 Assets/Samples/Netcode for GameObjects/2.13.1/Bootstrap/Scenes.meta create mode 100644 Assets/Samples/Netcode for GameObjects/2.13.1/Bootstrap/Scenes/Bootstrap.unity create mode 100644 Assets/Samples/Netcode for GameObjects/2.13.1/Bootstrap/Scenes/Bootstrap.unity.meta create mode 100644 Assets/Samples/Netcode for GameObjects/2.13.1/Bootstrap/Scripts.meta create mode 100644 Assets/Samples/Netcode for GameObjects/2.13.1/Bootstrap/Scripts/Bootstrap.asmdef create mode 100644 Assets/Samples/Netcode for GameObjects/2.13.1/Bootstrap/Scripts/Bootstrap.asmdef.meta create mode 100644 Assets/Samples/Netcode for GameObjects/2.13.1/Bootstrap/Scripts/BootstrapManager.cs create mode 100644 Assets/Samples/Netcode for GameObjects/2.13.1/Bootstrap/Scripts/BootstrapManager.cs.meta create mode 100644 Assets/Samples/Netcode for GameObjects/2.13.1/Bootstrap/Scripts/BootstrapPlayer.cs create mode 100644 Assets/Samples/Netcode for GameObjects/2.13.1/Bootstrap/Scripts/BootstrapPlayer.cs.meta create mode 100644 Assets/Scenes.meta create mode 100644 Assets/Scenes/SampleScene.unity create mode 100644 Assets/Scenes/SampleScene.unity.meta create mode 100644 Assets/Settings.meta create mode 100644 Assets/Settings/DefaultVolumeProfile.asset create mode 100644 Assets/Settings/DefaultVolumeProfile.asset.meta create mode 100644 Assets/Settings/Mobile_RPAsset.asset create mode 100644 Assets/Settings/Mobile_RPAsset.asset.meta create mode 100644 Assets/Settings/Mobile_Renderer.asset create mode 100644 Assets/Settings/Mobile_Renderer.asset.meta create mode 100644 Assets/Settings/PC_RPAsset.asset create mode 100644 Assets/Settings/PC_RPAsset.asset.meta create mode 100644 Assets/Settings/PC_Renderer.asset create mode 100644 Assets/Settings/PC_Renderer.asset.meta create mode 100644 Assets/Settings/SampleSceneProfile.asset create mode 100644 Assets/Settings/SampleSceneProfile.asset.meta create mode 100644 Assets/Settings/UniversalRenderPipelineGlobalSettings.asset create mode 100644 Assets/Settings/UniversalRenderPipelineGlobalSettings.asset.meta create mode 100644 Assets/TutorialInfo.meta create mode 100644 Assets/TutorialInfo/Icons.meta create mode 100644 Assets/TutorialInfo/Icons/URP.png create mode 100644 Assets/TutorialInfo/Icons/URP.png.meta create mode 100644 Assets/TutorialInfo/Layout.wlt create mode 100644 Assets/TutorialInfo/Layout.wlt.meta create mode 100644 Assets/TutorialInfo/Scripts.meta create mode 100644 Assets/TutorialInfo/Scripts/Editor.meta create mode 100644 Assets/TutorialInfo/Scripts/Editor/ReadmeEditor.cs create mode 100644 Assets/TutorialInfo/Scripts/Editor/ReadmeEditor.cs.meta create mode 100644 Assets/TutorialInfo/Scripts/Readme.cs create mode 100644 Assets/TutorialInfo/Scripts/Readme.cs.meta create mode 100644 Packages/manifest.json create mode 100644 Packages/packages-lock.json create mode 100644 ProjectSettings/AudioManager.asset create mode 100644 ProjectSettings/ClusterInputManager.asset create mode 100644 ProjectSettings/DynamicsManager.asset create mode 100644 ProjectSettings/EditorBuildSettings.asset create mode 100644 ProjectSettings/EditorSettings.asset create mode 100644 ProjectSettings/GraphicsSettings.asset create mode 100644 ProjectSettings/InputManager.asset create mode 100644 ProjectSettings/MemorySettings.asset create mode 100644 ProjectSettings/MultiplayerManager.asset create mode 100644 ProjectSettings/NavMeshAreas.asset create mode 100644 ProjectSettings/PackageManagerSettings.asset create mode 100644 ProjectSettings/Packages/com.unity.multiplayer.tools/CustomColorSettings.asset create mode 100644 ProjectSettings/Packages/com.unity.probuilder/Settings.json create mode 100644 ProjectSettings/Physics2DSettings.asset create mode 100644 ProjectSettings/PresetManager.asset create mode 100644 ProjectSettings/ProjectSettings.asset create mode 100644 ProjectSettings/ProjectVersion.txt create mode 100644 ProjectSettings/QualitySettings.asset create mode 100644 ProjectSettings/ShaderGraphSettings.asset create mode 100644 ProjectSettings/TagManager.asset create mode 100644 ProjectSettings/TimeManager.asset create mode 100644 ProjectSettings/URPProjectSettings.asset create mode 100644 ProjectSettings/UnityConnectSettings.asset create mode 100644 ProjectSettings/VFXManager.asset create mode 100644 ProjectSettings/VersionControlSettings.asset create mode 100644 ProjectSettings/VirtualProjectsConfig.json create mode 100644 ProjectSettings/XRSettings.asset diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..5a211a5 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,67 @@ +# Keep Unity's human-readable project files consistent across platforms. +* text=auto +.gitignore text eol=lf +.gitattributes text eol=lf +*.cs text eol=lf +*.shader text eol=lf +*.hlsl text eol=lf +*.cginc text eol=lf +*.compute text eol=lf +*.asmdef text eol=lf +*.asmref text eol=lf +*.json text eol=lf +*.xml text eol=lf +*.yaml text eol=lf +*.yml text eol=lf +*.uxml text eol=lf +*.uss text eol=lf +*.txt text eol=lf +*.md text eol=lf +*.meta text eol=lf + +# Unity SmartMerge for YAML-serialized assets. +*.unity text eol=lf merge=unityyamlmerge +*.prefab text eol=lf merge=unityyamlmerge +*.asset text eol=lf merge=unityyamlmerge +*.mat text eol=lf merge=unityyamlmerge +*.anim text eol=lf merge=unityyamlmerge +*.controller text eol=lf merge=unityyamlmerge +*.overrideController text eol=lf merge=unityyamlmerge +*.playable text eol=lf merge=unityyamlmerge +*.mask text eol=lf merge=unityyamlmerge +*.spriteatlas text eol=lf merge=unityyamlmerge +*.physicsMaterial2D text eol=lf merge=unityyamlmerge +*.physicMaterial text eol=lf merge=unityyamlmerge +*.terrainlayer text eol=lf merge=unityyamlmerge +*.renderTexture text eol=lf merge=unityyamlmerge +*.lighting text eol=lf merge=unityyamlmerge +*.preset text eol=lf merge=unityyamlmerge +*.guiskin text eol=lf merge=unityyamlmerge + +# Source-art and runtime media are binary and belong in Git LFS. +*.psd filter=lfs diff=lfs merge=lfs -text +*.psb filter=lfs diff=lfs merge=lfs -text +*.png filter=lfs diff=lfs merge=lfs -text +*.jpg filter=lfs diff=lfs merge=lfs -text +*.jpeg filter=lfs diff=lfs merge=lfs -text +*.tga filter=lfs diff=lfs merge=lfs -text +*.tif filter=lfs diff=lfs merge=lfs -text +*.tiff filter=lfs diff=lfs merge=lfs -text +*.exr filter=lfs diff=lfs merge=lfs -text +*.hdr filter=lfs diff=lfs merge=lfs -text +*.webp filter=lfs diff=lfs merge=lfs -text +*.fbx filter=lfs diff=lfs merge=lfs -text +*.blend filter=lfs diff=lfs merge=lfs -text +*.glb filter=lfs diff=lfs merge=lfs -text +*.wav filter=lfs diff=lfs merge=lfs -text +*.mp3 filter=lfs diff=lfs merge=lfs -text +*.ogg filter=lfs diff=lfs merge=lfs -text +*.aif filter=lfs diff=lfs merge=lfs -text +*.aiff filter=lfs diff=lfs merge=lfs -text +*.flac filter=lfs diff=lfs merge=lfs -text +*.mp4 filter=lfs diff=lfs merge=lfs -text +*.mov filter=lfs diff=lfs merge=lfs -text +*.avi filter=lfs diff=lfs merge=lfs -text +*.webm filter=lfs diff=lfs merge=lfs -text +*.ttf filter=lfs diff=lfs merge=lfs -text +*.otf filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore index 22e7db6..a0bbaeb 100644 --- a/.gitignore +++ b/.gitignore @@ -26,6 +26,7 @@ # Visual Studio cache directory .vs/ +.vscode/ # Gradle cache directory .gradle/ @@ -47,6 +48,8 @@ ExportedObj/ *.mdb *.opendb *.VC.db +*.VC.opendb +*.slnx # Unity3D generated meta files *.pidb.meta diff --git a/Assets/DefaultNetworkPrefabs.asset b/Assets/DefaultNetworkPrefabs.asset new file mode 100644 index 0000000..cfbcdb4 --- /dev/null +++ b/Assets/DefaultNetworkPrefabs.asset @@ -0,0 +1,21 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: e651dbb3fbac04af2b8f5abf007ddc23, type: 3} + m_Name: DefaultNetworkPrefabs + m_EditorClassIdentifier: + IsDefault: 1 + List: + - Override: 0 + Prefab: {fileID: 3439633038736912633, guid: 398aad09d8b2a47eba664a076763cdcc, type: 3} + SourcePrefabToOverride: {fileID: 0} + SourceHashToOverride: 0 + OverridingTargetPrefab: {fileID: 0} diff --git a/Assets/DefaultNetworkPrefabs.asset.meta b/Assets/DefaultNetworkPrefabs.asset.meta new file mode 100644 index 0000000..48df624 --- /dev/null +++ b/Assets/DefaultNetworkPrefabs.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ade67b675ab279949a8c1b7b0684ce60 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/InputSystem_Actions.inputactions b/Assets/InputSystem_Actions.inputactions new file mode 100644 index 0000000..1a12cb9 --- /dev/null +++ b/Assets/InputSystem_Actions.inputactions @@ -0,0 +1,1057 @@ +{ + "name": "InputSystem_Actions", + "maps": [ + { + "name": "Player", + "id": "df70fa95-8a34-4494-b137-73ab6b9c7d37", + "actions": [ + { + "name": "Move", + "type": "Value", + "id": "351f2ccd-1f9f-44bf-9bec-d62ac5c5f408", + "expectedControlType": "Vector2", + "processors": "", + "interactions": "", + "initialStateCheck": true + }, + { + "name": "Look", + "type": "Value", + "id": "6b444451-8a00-4d00-a97e-f47457f736a8", + "expectedControlType": "Vector2", + "processors": "", + "interactions": "", + "initialStateCheck": true + }, + { + "name": "Attack", + "type": "Button", + "id": "6c2ab1b8-8984-453a-af3d-a3c78ae1679a", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "Interact", + "type": "Button", + "id": "852140f2-7766-474d-8707-702459ba45f3", + "expectedControlType": "Button", + "processors": "", + "interactions": "Hold", + "initialStateCheck": false + }, + { + "name": "Crouch", + "type": "Button", + "id": "27c5f898-bc57-4ee1-8800-db469aca5fe3", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "Jump", + "type": "Button", + "id": "f1ba0d36-48eb-4cd5-b651-1c94a6531f70", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "Previous", + "type": "Button", + "id": "2776c80d-3c14-4091-8c56-d04ced07a2b0", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "Next", + "type": "Button", + "id": "b7230bb6-fc9b-4f52-8b25-f5e19cb2c2ba", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "Sprint", + "type": "Button", + "id": "641cd816-40e6-41b4-8c3d-04687c349290", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": false + } + ], + "bindings": [ + { + "name": "", + "id": "978bfe49-cc26-4a3d-ab7b-7d7a29327403", + "path": "/leftStick", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Move", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "WASD", + "id": "00ca640b-d935-4593-8157-c05846ea39b3", + "path": "Dpad", + "interactions": "", + "processors": "", + "groups": "", + "action": "Move", + "isComposite": true, + "isPartOfComposite": false + }, + { + "name": "up", + "id": "e2062cb9-1b15-46a2-838c-2f8d72a0bdd9", + "path": "/w", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Move", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "up", + "id": "8180e8bd-4097-4f4e-ab88-4523101a6ce9", + "path": "/upArrow", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Move", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "down", + "id": "320bffee-a40b-4347-ac70-c210eb8bc73a", + "path": "/s", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Move", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "down", + "id": "1c5327b5-f71c-4f60-99c7-4e737386f1d1", + "path": "/downArrow", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Move", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "left", + "id": "d2581a9b-1d11-4566-b27d-b92aff5fabbc", + "path": "/a", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Move", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "left", + "id": "2e46982e-44cc-431b-9f0b-c11910bf467a", + "path": "/leftArrow", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Move", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "right", + "id": "fcfe95b8-67b9-4526-84b5-5d0bc98d6400", + "path": "/d", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Move", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "right", + "id": "77bff152-3580-4b21-b6de-dcd0c7e41164", + "path": "/rightArrow", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Move", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "", + "id": "1635d3fe-58b6-4ba9-a4e2-f4b964f6b5c8", + "path": "/{Primary2DAxis}", + "interactions": "", + "processors": "", + "groups": "XR", + "action": "Move", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "3ea4d645-4504-4529-b061-ab81934c3752", + "path": "/stick", + "interactions": "", + "processors": "", + "groups": "Joystick", + "action": "Move", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "c1f7a91b-d0fd-4a62-997e-7fb9b69bf235", + "path": "/rightStick", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Look", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "8c8e490b-c610-4785-884f-f04217b23ca4", + "path": "/delta", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse;Touch", + "action": "Look", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "3e5f5442-8668-4b27-a940-df99bad7e831", + "path": "/{Hatswitch}", + "interactions": "", + "processors": "", + "groups": "Joystick", + "action": "Look", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "143bb1cd-cc10-4eca-a2f0-a3664166fe91", + "path": "/buttonWest", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Attack", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "05f6913d-c316-48b2-a6bb-e225f14c7960", + "path": "/leftButton", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Attack", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "886e731e-7071-4ae4-95c0-e61739dad6fd", + "path": "/primaryTouch/tap", + "interactions": "", + "processors": "", + "groups": ";Touch", + "action": "Attack", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "ee3d0cd2-254e-47a7-a8cb-bc94d9658c54", + "path": "/trigger", + "interactions": "", + "processors": "", + "groups": "Joystick", + "action": "Attack", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "8255d333-5683-4943-a58a-ccb207ff1dce", + "path": "/{PrimaryAction}", + "interactions": "", + "processors": "", + "groups": "XR", + "action": "Attack", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "b3c1c7f0-bd20-4ee7-a0f1-899b24bca6d7", + "path": "/enter", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Attack", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "cbac6039-9c09-46a1-b5f2-4e5124ccb5ed", + "path": "/2", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Next", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "e15ca19d-e649-4852-97d5-7fe8ccc44e94", + "path": "/dpad/right", + "interactions": "", + "processors": "", + "groups": "Gamepad", + "action": "Next", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "f2e9ba44-c423-42a7-ad56-f20975884794", + "path": "/leftShift", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Sprint", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "8cbb2f4b-a784-49cc-8d5e-c010b8c7f4e6", + "path": "/leftStickPress", + "interactions": "", + "processors": "", + "groups": "Gamepad", + "action": "Sprint", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "d8bf24bf-3f2f-4160-a97c-38ec1eb520ba", + "path": "/trigger", + "interactions": "", + "processors": "", + "groups": "XR", + "action": "Sprint", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "eb40bb66-4559-4dfa-9a2f-820438abb426", + "path": "/space", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Jump", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "daba33a1-ad0c-4742-a909-43ad1cdfbeb6", + "path": "/buttonSouth", + "interactions": "", + "processors": "", + "groups": "Gamepad", + "action": "Jump", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "603f3daf-40bd-4854-8724-93e8017f59e3", + "path": "/secondaryButton", + "interactions": "", + "processors": "", + "groups": "XR", + "action": "Jump", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "1534dc16-a6aa-499d-9c3a-22b47347b52a", + "path": "/1", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Previous", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "25060bbd-a3a6-476e-8fba-45ae484aad05", + "path": "/dpad/left", + "interactions": "", + "processors": "", + "groups": "Gamepad", + "action": "Previous", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "1c04ea5f-b012-41d1-a6f7-02e963b52893", + "path": "/e", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Interact", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "b3f66d0b-7751-423f-908b-a11c5bd95930", + "path": "/buttonNorth", + "interactions": "", + "processors": "", + "groups": "Gamepad", + "action": "Interact", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "4f4649ac-64a8-4a73-af11-b3faef356a4d", + "path": "/buttonEast", + "interactions": "", + "processors": "", + "groups": "Gamepad", + "action": "Crouch", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "36e52cba-0905-478e-a818-f4bfcb9f3b9a", + "path": "/c", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Crouch", + "isComposite": false, + "isPartOfComposite": false + } + ] + }, + { + "name": "UI", + "id": "272f6d14-89ba-496f-b7ff-215263d3219f", + "actions": [ + { + "name": "Navigate", + "type": "PassThrough", + "id": "c95b2375-e6d9-4b88-9c4c-c5e76515df4b", + "expectedControlType": "Vector2", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "Submit", + "type": "Button", + "id": "7607c7b6-cd76-4816-beef-bd0341cfe950", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "Cancel", + "type": "Button", + "id": "15cef263-9014-4fd5-94d9-4e4a6234a6ef", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "Point", + "type": "PassThrough", + "id": "32b35790-4ed0-4e9a-aa41-69ac6d629449", + "expectedControlType": "Vector2", + "processors": "", + "interactions": "", + "initialStateCheck": true + }, + { + "name": "Click", + "type": "PassThrough", + "id": "3c7022bf-7922-4f7c-a998-c437916075ad", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": true + }, + { + "name": "RightClick", + "type": "PassThrough", + "id": "44b200b1-1557-4083-816c-b22cbdf77ddf", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "MiddleClick", + "type": "PassThrough", + "id": "dad70c86-b58c-4b17-88ad-f5e53adf419e", + "expectedControlType": "Button", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "ScrollWheel", + "type": "PassThrough", + "id": "0489e84a-4833-4c40-bfae-cea84b696689", + "expectedControlType": "Vector2", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "TrackedDevicePosition", + "type": "PassThrough", + "id": "24908448-c609-4bc3-a128-ea258674378a", + "expectedControlType": "Vector3", + "processors": "", + "interactions": "", + "initialStateCheck": false + }, + { + "name": "TrackedDeviceOrientation", + "type": "PassThrough", + "id": "9caa3d8a-6b2f-4e8e-8bad-6ede561bd9be", + "expectedControlType": "Quaternion", + "processors": "", + "interactions": "", + "initialStateCheck": false + } + ], + "bindings": [ + { + "name": "Gamepad", + "id": "809f371f-c5e2-4e7a-83a1-d867598f40dd", + "path": "2DVector", + "interactions": "", + "processors": "", + "groups": "", + "action": "Navigate", + "isComposite": true, + "isPartOfComposite": false + }, + { + "name": "up", + "id": "14a5d6e8-4aaf-4119-a9ef-34b8c2c548bf", + "path": "/leftStick/up", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "up", + "id": "9144cbe6-05e1-4687-a6d7-24f99d23dd81", + "path": "/rightStick/up", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "down", + "id": "2db08d65-c5fb-421b-983f-c71163608d67", + "path": "/leftStick/down", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "down", + "id": "58748904-2ea9-4a80-8579-b500e6a76df8", + "path": "/rightStick/down", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "left", + "id": "8ba04515-75aa-45de-966d-393d9bbd1c14", + "path": "/leftStick/left", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "left", + "id": "712e721c-bdfb-4b23-a86c-a0d9fcfea921", + "path": "/rightStick/left", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "right", + "id": "fcd248ae-a788-4676-a12e-f4d81205600b", + "path": "/leftStick/right", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "right", + "id": "1f04d9bc-c50b-41a1-bfcc-afb75475ec20", + "path": "/rightStick/right", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "", + "id": "fb8277d4-c5cd-4663-9dc7-ee3f0b506d90", + "path": "/dpad", + "interactions": "", + "processors": "", + "groups": ";Gamepad", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "Joystick", + "id": "e25d9774-381c-4a61-b47c-7b6b299ad9f9", + "path": "2DVector", + "interactions": "", + "processors": "", + "groups": "", + "action": "Navigate", + "isComposite": true, + "isPartOfComposite": false + }, + { + "name": "up", + "id": "3db53b26-6601-41be-9887-63ac74e79d19", + "path": "/stick/up", + "interactions": "", + "processors": "", + "groups": "Joystick", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "down", + "id": "0cb3e13e-3d90-4178-8ae6-d9c5501d653f", + "path": "/stick/down", + "interactions": "", + "processors": "", + "groups": "Joystick", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "left", + "id": "0392d399-f6dd-4c82-8062-c1e9c0d34835", + "path": "/stick/left", + "interactions": "", + "processors": "", + "groups": "Joystick", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "right", + "id": "942a66d9-d42f-43d6-8d70-ecb4ba5363bc", + "path": "/stick/right", + "interactions": "", + "processors": "", + "groups": "Joystick", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "Keyboard", + "id": "ff527021-f211-4c02-933e-5976594c46ed", + "path": "2DVector", + "interactions": "", + "processors": "", + "groups": "", + "action": "Navigate", + "isComposite": true, + "isPartOfComposite": false + }, + { + "name": "up", + "id": "563fbfdd-0f09-408d-aa75-8642c4f08ef0", + "path": "/w", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "up", + "id": "eb480147-c587-4a33-85ed-eb0ab9942c43", + "path": "/upArrow", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "down", + "id": "2bf42165-60bc-42ca-8072-8c13ab40239b", + "path": "/s", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "down", + "id": "85d264ad-e0a0-4565-b7ff-1a37edde51ac", + "path": "/downArrow", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "left", + "id": "74214943-c580-44e4-98eb-ad7eebe17902", + "path": "/a", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "left", + "id": "cea9b045-a000-445b-95b8-0c171af70a3b", + "path": "/leftArrow", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "right", + "id": "8607c725-d935-4808-84b1-8354e29bab63", + "path": "/d", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "right", + "id": "4cda81dc-9edd-4e03-9d7c-a71a14345d0b", + "path": "/rightArrow", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Navigate", + "isComposite": false, + "isPartOfComposite": true + }, + { + "name": "", + "id": "9e92bb26-7e3b-4ec4-b06b-3c8f8e498ddc", + "path": "*/{Submit}", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse;Gamepad;Touch;Joystick;XR", + "action": "Submit", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "82627dcc-3b13-4ba9-841d-e4b746d6553e", + "path": "*/{Cancel}", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse;Gamepad;Touch;Joystick;XR", + "action": "Cancel", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "c52c8e0b-8179-41d3-b8a1-d149033bbe86", + "path": "/position", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Point", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "e1394cbc-336e-44ce-9ea8-6007ed6193f7", + "path": "/position", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "Point", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "5693e57a-238a-46ed-b5ae-e64e6e574302", + "path": "/touch*/position", + "interactions": "", + "processors": "", + "groups": "Touch", + "action": "Point", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "4faf7dc9-b979-4210-aa8c-e808e1ef89f5", + "path": "/leftButton", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Click", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "8d66d5ba-88d7-48e6-b1cd-198bbfef7ace", + "path": "/tip", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "Click", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "47c2a644-3ebc-4dae-a106-589b7ca75b59", + "path": "/touch*/press", + "interactions": "", + "processors": "", + "groups": "Touch", + "action": "Click", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "bb9e6b34-44bf-4381-ac63-5aa15d19f677", + "path": "/trigger", + "interactions": "", + "processors": "", + "groups": "XR", + "action": "Click", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "38c99815-14ea-4617-8627-164d27641299", + "path": "/scroll", + "interactions": "", + "processors": "", + "groups": ";Keyboard&Mouse", + "action": "ScrollWheel", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "4c191405-5738-4d4b-a523-c6a301dbf754", + "path": "/rightButton", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "RightClick", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "24066f69-da47-44f3-a07e-0015fb02eb2e", + "path": "/middleButton", + "interactions": "", + "processors": "", + "groups": "Keyboard&Mouse", + "action": "MiddleClick", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "7236c0d9-6ca3-47cf-a6ee-a97f5b59ea77", + "path": "/devicePosition", + "interactions": "", + "processors": "", + "groups": "XR", + "action": "TrackedDevicePosition", + "isComposite": false, + "isPartOfComposite": false + }, + { + "name": "", + "id": "23e01e3a-f935-4948-8d8b-9bcac77714fb", + "path": "/deviceRotation", + "interactions": "", + "processors": "", + "groups": "XR", + "action": "TrackedDeviceOrientation", + "isComposite": false, + "isPartOfComposite": false + } + ] + } + ], + "controlSchemes": [ + { + "name": "Keyboard&Mouse", + "bindingGroup": "Keyboard&Mouse", + "devices": [ + { + "devicePath": "", + "isOptional": false, + "isOR": false + }, + { + "devicePath": "", + "isOptional": false, + "isOR": false + } + ] + }, + { + "name": "Gamepad", + "bindingGroup": "Gamepad", + "devices": [ + { + "devicePath": "", + "isOptional": false, + "isOR": false + } + ] + }, + { + "name": "Touch", + "bindingGroup": "Touch", + "devices": [ + { + "devicePath": "", + "isOptional": false, + "isOR": false + } + ] + }, + { + "name": "Joystick", + "bindingGroup": "Joystick", + "devices": [ + { + "devicePath": "", + "isOptional": false, + "isOR": false + } + ] + }, + { + "name": "XR", + "bindingGroup": "XR", + "devices": [ + { + "devicePath": "", + "isOptional": false, + "isOR": false + } + ] + } + ] +} \ No newline at end of file diff --git a/Assets/InputSystem_Actions.inputactions.meta b/Assets/InputSystem_Actions.inputactions.meta new file mode 100644 index 0000000..6b38b04 --- /dev/null +++ b/Assets/InputSystem_Actions.inputactions.meta @@ -0,0 +1,14 @@ +fileFormatVersion: 2 +guid: 052faaac586de48259a63d0c4782560b +ScriptedImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 2 + userData: + assetBundleName: + assetBundleVariant: + script: {fileID: 11500000, guid: 8404be70184654265930450def6a9037, type: 3} + generateWrapperCode: 0 + wrapperCodePath: + wrapperClassName: + wrapperCodeNamespace: diff --git a/Assets/OtherAssets.meta b/Assets/OtherAssets.meta new file mode 100644 index 0000000..54b46c5 --- /dev/null +++ b/Assets/OtherAssets.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e09ca9c29032caf40bebc8c816cf7971 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV.meta b/Assets/OtherAssets/DEV.meta new file mode 100644 index 0000000..f662604 --- /dev/null +++ b/Assets/OtherAssets/DEV.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5b89e375a14f4e049b207cb0f3daaadf +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/FREE.meta b/Assets/OtherAssets/DEV/FREE.meta new file mode 100644 index 0000000..07e261a --- /dev/null +++ b/Assets/OtherAssets/DEV/FREE.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 240c6ee064ec9b34888740ebc2d7c2dc +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/FREE/SceneSwitcher.meta b/Assets/OtherAssets/DEV/FREE/SceneSwitcher.meta new file mode 100644 index 0000000..000d4f4 --- /dev/null +++ b/Assets/OtherAssets/DEV/FREE/SceneSwitcher.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1d47210b6a0af2247b2ddbb08cc85e7d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/FREE/SceneSwitcher/Editor.meta b/Assets/OtherAssets/DEV/FREE/SceneSwitcher/Editor.meta new file mode 100644 index 0000000..18beb7e --- /dev/null +++ b/Assets/OtherAssets/DEV/FREE/SceneSwitcher/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9d463107ae5974c4a8bffb4f6a04bf54 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/FREE/SceneSwitcher/Editor/SceneSwitcherToolbar.cs b/Assets/OtherAssets/DEV/FREE/SceneSwitcher/Editor/SceneSwitcherToolbar.cs new file mode 100644 index 0000000..052f603 --- /dev/null +++ b/Assets/OtherAssets/DEV/FREE/SceneSwitcher/Editor/SceneSwitcherToolbar.cs @@ -0,0 +1,269 @@ +#if UNITY_EDITOR +using UnityEditor; +using UnityEditor.SceneManagement; +using UnityEngine; +using UnityEngine.UIElements; +using System.Reflection; +using System.Linq; +using System.IO; +using UnityEngine.SceneManagement; + +[InitializeOnLoad] +public static class SceneSwitcherToolbar +{ + private static string[] sceneNames = new string[0]; + private static int selectedIndex = 0; + private static string lastActiveScene = ""; + private static VisualElement toolbarUI; + + private static float positionOffset = 180f; + private static float dropdownBoxHeight = 20f; + + private static bool fetchAllScenes + { + get => EditorPrefs.GetBool("SceneSwitcher_FetchAllScenes", false); + set => EditorPrefs.SetBool("SceneSwitcher_FetchAllScenes", value); + } + + static SceneSwitcherToolbar() + { + RefreshSceneList(); + SelectCurrentScene(); + + EditorSceneManager.activeSceneChangedInEditMode += (prev, current) => UpdateSceneSelection(); + EditorApplication.playModeStateChanged += OnPlayModeChanged; + + EditorApplication.delayCall += AddToolbarUI; + } + + static void AddToolbarUI() + { + var toolbarType = typeof(Editor).Assembly.GetType("UnityEditor.Toolbar"); + if (toolbarType == null) return; + + var toolbars = Resources.FindObjectsOfTypeAll(toolbarType); + if (toolbars.Length == 0) return; + + var toolbar = toolbars[0]; + var rootField = toolbarType.GetField("m_Root", BindingFlags.NonPublic | BindingFlags.Instance); + if (rootField == null) return; + + var root = rootField.GetValue(toolbar) as VisualElement; + if (root == null) return; + + var leftContainer = root.Q("ToolbarZoneLeftAlign"); + if (leftContainer == null) return; + + if (toolbarUI != null) + { + leftContainer.Remove(toolbarUI); + } + + toolbarUI = new IMGUIContainer(OnGUI); + toolbarUI.style.marginLeft = positionOffset; + + leftContainer.Add(toolbarUI); + } + + static void OnGUI() + { + CheckAndRefreshScenes(); + + if (selectedIndex >= sceneNames.Length) + selectedIndex = 0; + + bool isPlaying = EditorApplication.isPlaying; + + GUILayout.BeginHorizontal(); + + // Fetch all scenes toggle button + EditorGUI.BeginDisabledGroup(isPlaying); + bool newFetchAllScenes = GUILayout.Toggle(fetchAllScenes, "All Scenes", "Button", GUILayout.Height(dropdownBoxHeight)); + if (newFetchAllScenes != fetchAllScenes) + { + fetchAllScenes = newFetchAllScenes; + RefreshSceneList(); + SelectCurrentScene(); + } + EditorGUI.EndDisabledGroup(); + + // Add Scene button + EditorGUI.BeginDisabledGroup(isPlaying || fetchAllScenes); + if (GUILayout.Button("Add Scene", GUILayout.Height(dropdownBoxHeight))) + { + AddCurrentSceneToBuildSettings(); + } + EditorGUI.EndDisabledGroup(); + + // Scene dropdown + EditorGUI.BeginDisabledGroup(isPlaying); + GUIStyle popupStyle = new GUIStyle(EditorStyles.popup) + { + fixedHeight = dropdownBoxHeight + }; + + int newIndex = EditorGUILayout.Popup(selectedIndex, sceneNames, popupStyle, GUILayout.Width(150), GUILayout.Height(dropdownBoxHeight)); + + if (newIndex != selectedIndex) + { + selectedIndex = newIndex; + LoadScene(sceneNames[selectedIndex]); + } + EditorGUI.EndDisabledGroup(); + + GUILayout.EndHorizontal(); + } + + static void AddCurrentSceneToBuildSettings() + { + string currentScenePath = EditorSceneManager.GetActiveScene().path; + + if (string.IsNullOrEmpty(currentScenePath)) + { + EditorUtility.DisplayDialog("Scene Not Saved", + "Please save the scene before adding it to Build Settings.", "OK"); + return; + } + + // Check if scene is already in build settings + var buildScenes = EditorBuildSettings.scenes.ToList(); + bool sceneExists = buildScenes.Any(s => s.path == currentScenePath); + + if (sceneExists) + { + // Enable the scene if it was disabled + var existingScene = buildScenes.Find(s => s.path == currentScenePath); + if (!existingScene.enabled) + { + existingScene.enabled = true; + EditorBuildSettings.scenes = buildScenes.ToArray(); + Debug.Log($"Scene '{Path.GetFileNameWithoutExtension(currentScenePath)}' enabled in Build Settings."); + RefreshSceneList(); + SelectCurrentScene(); + } + else + { + Debug.Log($"Scene '{Path.GetFileNameWithoutExtension(currentScenePath)}' is already in Build Settings."); + } + } + else + { + // Add new scene to build settings + buildScenes.Add(new EditorBuildSettingsScene(currentScenePath, true)); + EditorBuildSettings.scenes = buildScenes.ToArray(); + + Debug.Log($"Scene '{Path.GetFileNameWithoutExtension(currentScenePath)}' added to Build Settings."); + + RefreshSceneList(); + SelectCurrentScene(); + } + } + + static void RefreshSceneList() + { + if (fetchAllScenes) + { + sceneNames = Directory.GetFiles("Assets", "*.unity", SearchOption.AllDirectories) + .Select(path => Path.GetFileNameWithoutExtension(path)) + .ToArray(); + } + else + { + sceneNames = EditorBuildSettings.scenes + .Where(scene => scene.enabled) + .Select(scene => Path.GetFileNameWithoutExtension(scene.path)) + .ToArray(); + } + } + + static void CheckAndRefreshScenes() + { + string[] currentScenes; + if (fetchAllScenes) + { + currentScenes = Directory.GetFiles("Assets", "*.unity", SearchOption.AllDirectories) + .Select(path => Path.GetFileNameWithoutExtension(path)) + .ToArray(); + } + else + { + currentScenes = EditorBuildSettings.scenes + .Where(scene => scene.enabled) + .Select(scene => Path.GetFileNameWithoutExtension(scene.path)) + .ToArray(); + } + + if (!currentScenes.SequenceEqual(sceneNames)) + { + sceneNames = currentScenes; + SelectCurrentScene(); + } + } + + static void SelectCurrentScene() + { + string currentScene = Path.GetFileNameWithoutExtension(EditorSceneManager.GetActiveScene().path); + int index = System.Array.IndexOf(sceneNames, currentScene); + + if (index != -1) + { + selectedIndex = index; + lastActiveScene = currentScene; + } + else + { + string notInBuildName = currentScene + " (not in build index)"; + sceneNames = new[] { notInBuildName }.Concat(sceneNames).ToArray(); + selectedIndex = 0; + lastActiveScene = currentScene; + } + } + + static void UpdateSceneSelection() + { + string currentScene = Path.GetFileNameWithoutExtension(EditorSceneManager.GetActiveScene().path); + if (currentScene != lastActiveScene) + { + lastActiveScene = currentScene; + sceneNames = sceneNames.Where(name => !name.EndsWith(" (not in build index)")).ToArray(); + SelectCurrentScene(); + } + } + + static void LoadScene(string sceneName) + { + string scenePath; + + if (fetchAllScenes) + { + scenePath = Directory.GetFiles("Assets", "*.unity", SearchOption.AllDirectories) + .FirstOrDefault(path => Path.GetFileNameWithoutExtension(path) == sceneName); + } + else + { + scenePath = EditorBuildSettings.scenes + .FirstOrDefault(scene => scene.enabled && scene.path.Contains(sceneName))?.path; + } + + if (!string.IsNullOrEmpty(scenePath)) + { + if (EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo()) + { + EditorSceneManager.OpenScene(scenePath); + } + } + else + { + Debug.LogError("Scene not found: " + sceneName); + } + } + + static void OnPlayModeChanged(PlayModeStateChange state) + { + if (state == PlayModeStateChange.EnteredPlayMode || state == PlayModeStateChange.ExitingPlayMode) + { + EditorApplication.delayCall += () => AddToolbarUI(); + } + } +} +#endif \ No newline at end of file diff --git a/Assets/OtherAssets/DEV/FREE/SceneSwitcher/Editor/SceneSwitcherToolbar.cs.meta b/Assets/OtherAssets/DEV/FREE/SceneSwitcher/Editor/SceneSwitcherToolbar.cs.meta new file mode 100644 index 0000000..a60a905 --- /dev/null +++ b/Assets/OtherAssets/DEV/FREE/SceneSwitcher/Editor/SceneSwitcherToolbar.cs.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 09ae5cf30ab3ca54f92f91841e5f2c04 +AssetOrigin: + serializedVersion: 1 + productId: 313355 + packageName: Scene Switcher Pro + packageVersion: 1.0.0 + assetPath: Assets/SceneSwitcherPro/Editor/SceneSwitcherToolbar.cs + uploadId: 758164 diff --git a/Assets/OtherAssets/DEV/PAID.meta b/Assets/OtherAssets/DEV/PAID.meta new file mode 100644 index 0000000..aa89b4b --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 47cf9d3c724f25f4c80adada943326ba +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vFavorites.meta b/Assets/OtherAssets/DEV/PAID/vFavorites.meta new file mode 100644 index 0000000..e16c640 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFavorites.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d454140e53231466b8d485f9ff487b2e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vFavorites/Manual.pdf b/Assets/OtherAssets/DEV/PAID/vFavorites/Manual.pdf new file mode 100644 index 0000000000000000000000000000000000000000..84c4ea1af2428175540e21b67d00fb5b9be0f5cf GIT binary patch literal 555474 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ΜI z@O5#`%}7t}m{GXrHQ6oQIl3qaAJ7xKT`oA8&P%W$YVP_)sGFMVS>Y!3G?^awaqblI zW%eM)mgHTt%0rL??h)GKO>T$25nd0--#l%GH;{-YiBm(+#nbaTKkfz5Q<@#9BQbyl z(pvKN;O|Dxa7f6>OS@ebl8NP8Y0=Vte|s)lz#-`4NL9n+jd(+^wK4V)3A6)s8FfTOB$~)Da)jG>;x%i&2-CQcbT^{*c?5x;;C;NwTA;}gI||1rG6=LJNA^#(FAR%LAwf4Sx!iTdH` z4dnCbHrM>BH`}GI>C(b@x!=RL?;M)=)7k#5&Z!kjtcgHC@+$M%d`1g=ZJ!Py1l^$a z8Yz{p$D&}IHfBN5GkgWmq8iLJqNAZS*FlHls*+3hqb1uYmVwCUq{6)W-q?u<3BP-A zXNPOl9dS8p{@D$96Ts6$APcv2{nT5T31Kzoj&pYt2}Mz6aaPJ(@$CLxNT0i$pxyPQwtez@@r2+Y~B5sGZ8qS?=CtC{A70E zHv4i_qvtnvEy>)*3cD={Oe+1vOa60<*u)&7PNzj>;o2pl7P`Xi7r2;CDOXrk^zB7g@2`2NYM*w9bO)~*T>_nm;Y@~Y)k z&=Zv-5H_!{#6Axp!r)Hs^#dCWxPdB}qF1*-JMZ08JL!2BwP3g+7Zs2MWDK|>cejLG zHKVcLtE+)n;lpBAQ_B3JN#$*($74 z2Ex(pZBY^NBHqaonO|qH+-G1d4SICmGY-RArJy~;{@7%5N5(4Bo=uA$mxhS%xa;!r zK%~ZS#q0E`2+m!yVq6@x_upTDE!L?tGHg&Jn(qn-FBB&|Pdf^O+(%fq(Cmc>O%+RG zMV>t)u@o#-EY2I_T{n#NloW7(&09P6ak{mI9bz%xSrdu#S8#rYU(L}8Xg%-?%?ee7 zx{b`Xi*6$t=PUglKw3483CZxie_BEx%X_YWDyi<)*Ov#|JjgozZy$i<+q4dG(*oDm z38M$*K70L0kp0w({ptYa%cz*})TLS9?e(ec7_yw8!dV^QNJsN4hnuxw;qca$d?PYXG45oN4oQ-0~M7t#4j!$H_8*%np%*)hp9!?P|Jbm>{ zw&j zymqbcLjKP>*iy39>gO)0T0ETlX*4&;npuA8*WW1~&Zi8E=lYKmCCk6A6xIMAI|{#T z+8?T@w8XeLwK}_&Y#~yTX%v1rS*;Ej<6y6qex=c;nfYkQGBsY42n$Owz%lyz`VLP5 zObyz4swn$MLzJ1ok`nBTB6a#z-mbS2IQOJ01` zeYJ$KGIQUB+p!>CHB(mD+gHtA|1x(c(R(llefJjzdmUfT3H%v=zj3xL&;l*KS&xc> zsVTnywtvbNFo5B~fVyYZftb@0A3SmT`^`ap#Y*~u$f9C|%5PS!5>)8wPpFhfj)s;{ z3lJ6IJvr@t6N1}10qXc*!(+wA4Oo_r)2Q!u$$h01qFg?W?uJ2X;|t13kd4e;hjJGm zEd=L_B@gB5#xDf1Vo-E7`@x>TkbwoKkE(!&JJH$NAj{F%ID6>mH z0vCs}G95YJ0&{5CtCh}m&Z1DXu1?4cS{_wG7+BTD*rD3!dnb5Vrv4Y4Cc~h~UElVC z)`Wv~@W=52Wgtpe{AV>UQ9QqRts0$u47S`v#>B{<;fT1|(K^ny>r)?X*2M}IKOfA- z0_q<%xtbujCgZJ?>}rZ2rc~D_3~l9fCYhHuIW%|@+0qLBB~mosqIy5Nv#B^G(dgN5 zlNAJFL;Au3f$B6re$77@44PY7&*vKg3t?WXZO9EnP6g85>kGlghQ)4&_dMOM%su9X zrAQkamEX6$y$YW?NY>WQ_(nwk!IKWx1cM4;1qB7|%$kC`F-4)<*5PnGxnXm#47$YoiKJP zL)9JLXaXsfV;chg_BW4tp%?Fe39)Q{{iG>(D)w!v+M+ZRtbMbWG4jEGp7aAra}xCP z2LYskt}yl~DSz472NQdr%mvo?&A*Ge;_J2nNY`Ew_UTzCbz>}PpRhc$6QHm4xxd;1 z(>(JNkHiwK7L$xGDb{JC){~F{vXcNQzNKdY38*Y|c|tG0qpu@hztB(b9kXUZtP|dX zMtg#|#V_8ktCNY67lyy0SBpp(h~wTAic9SKFXe}7JRrxLY22OlF$xg5&o??>QcLw4 zU%%=vDk>_>@+B*e-^5|n&rL(-has)o&`CZD*I8Yc&(+{ro85v17uSr^%#FmHKRzv0 zqsPamg)c>6Rmty!FI7|{31b23G^6LC55iG|BQ5I z0(h-M9nq*9RxEDrfFL%X5pLi9iOj|C&CfT}c>VNFuf_wKp#NT>E3#KhumOQo!Wxq( z?jDr2*jV^hCl0eCs&Y~v%mTfit7Qf~L&xfM$dY0{uWdr!)H^ogR&TJTlgjV*A3mev z0%Zak`o)5C%rz0r0lN4E7hU~s)%UW4dWm(}U0RmrL&nB7?^&F~1BONCHSXu8ll3Y5 z9~EiV8AhN8UI?(_%nc!G4stbH60Y}Yx}baq6v-(mFBA7lHeI!x3_(xZMX7K^nL^1X@t{eT_J!Bym3?>=N z2lJH&Q-A*J=mogWV7qIAUR%gd---xj-S_76BgQBb1!-%!GG?~$ai)1I(ITZ@W@OdU z7QFFm@8U$pci4Dlyy7(Yd3y3J?ql&AjVBJ;DcJCPHh1 zTE2){6329p;{qnA>~4Sbw~*o)+_MgYILUEwv^5h%{G}Qt2~hOOhe=NhG1-_`K{Rdg z0<`KyV|8-hHjQ^Cj|Dp`3oAc<7d8umU%I|$EkCxqiWXl81TBAGIkTyIsg{pTQ$=Y3 z!7#OjdSQM5vP=Q23mqV4*X!L{T0QNPlESoN=}*7iRrynVLnQpEqr8yqnYIJ6Mpr|a z`!}J?^^``L{}he_)#+uyo3mu1Ijr=?$x16-rTxQiIv)QKtRoPxBbD}*`nao7L~42? z^PMJ>StN94SDNZ7F^E~&nZ@>*gJZRDNN#?4Pgp*_m7+GaP<3X}tygyId_A8}^4qXoc#Oi!TD?-t#GG~WKny6X)!3A>CoYCK&sis?CPN~X^EG}@GI9gtBl4vjtSU* zkN-Y1c=F%LuyfSj!4Kv@lrg9twaDb-{0~`w9TnC4MvcP;C?X&rA&u0~NH<6`#L!50 zD%~NVlynWv5Yj0U(jm;N4h2*1@h5k@{Th{w-hkA~m5RM7Ddn%ZYNB#<+Yx|Y5UUP06F9_`6cxF*5OlV3|K8R5oxa_v_oBMeFEJGM z;l~j05k0n_QT%!iljSKbF5>gS5JtH&`yKzFY0Hxc3#5m#&$X`(~e>Kb8_J zga6sGsQYG+di6$y`NO&^os@dCO=P~HT5Ad42d)oSxSR_ow*M*>4gEqptuozGvr{fg zUm1+O>He^am7eCs?1;LYTLASs0+2UE1Fr7uVTK^)>E^>&sCzJWLX>kgJ5y|+bIqbi zmfY}RsuDc^Wd&M4^bp*sd-brbnf%3lzCXFv{l+bq?$F`f!{UdOK4uUP7=gE}s%MF2 zBx6RAr<1pAYE1?|3)8p*3&zuMn>{;-PWNgpQQ($K38sPIfu-OCmcs2j0-ai^FIENl zOP{tR0bFsVNof(pt?OKbXCt`7iu zh(3jVh1>65owQh5%B)>Rr7mu9PnQFl*PrImtF@einA~;b9t5}sfy!FGOPv@OmpwE0 zgZX$M=Qe{dw`zoEcw*6Yl*KkZ3s0o!z<)0H{+rk`u)5X0FVM`tl#mtGudXUde>cRXy^k(=HS4-|v{n|DwNr7Y zHk}Fw>+d^8%tlxH>>r43Qon@4awwNTWzMDp&$>hAo5HdoADROhv|mEz)W%*` z1m{F~@m$+7TnRb*G&SaTC`bKE&3zV37mbB{6&_qvE|Yu4=4`@%)up--w`I8VmE2Ca zKi=Q^G4~-~9-AL(e2eWjz0r64I9(dGeF<0$jCu3&M6V#!RNGw>qVD@Qz|B&pATQ0x zsvo({_8S4T@Lwo=JyyCgcQwD!;xh{in*w5}Q`B*prZ{zZb@;19G%0`k62RjOJ@cxH zbW|~cQ96mR*8rG0fp9+T5bNjt3bZ8MRK4>;N7euOVhWLu$!7Id3oW-%VD}*N`46kZ zINQpKmKh-#1keIFWM>d_*i8NfSkob!-$PGvV40LD?9Ix_2{)uZ_Fi6Iri%DQO2}p) zPGuHh;}QN~!6J^J*TdZCTT)t^)3%Y;3NS(Gk!lf@9NP??BX;$KJUh4yXu}T>nUBz7Jkk3%P z079uxtG}(s&=A6wgPlF1|5Yz?HbS zOX6F|0RACR0J#1xR2jzA`_gRfjVlgSa@Q;PQ{*?=C$MJXoc{84P#~@h=85D?Q9Qe+ zp!ltExPq=KL$VMZp(*cnJX>QaQXp>#b3M&+?it2ut#r#}bd7E?48R`(d7`&)2aGOg znd*F_dnQmprq$She%SV3t`i_WzNu6Z{InVVt|Iq-8{i4gR()m)lqiINx*6VA^M0Ih z>E31eu=@Qt@h%$Y!+Zmlws!$f*m!Iv={4jP`50y*X(0uRnQx3uBn@EMi%|4UIB-sW z_ea=uJ6^4lu2DCy2FU!kOQz$WPQ#-daAoCIb7Z2kU#z6?+P63oz9i= zJ-V93jY6y2yvTr+>9i$_Ei87i!fRiScpm{gZx@kp2!PrV@xLQx(}msddG}|b(3%|1 zcId!BlGAzGPuYXfA0#v*OHkod5g!5aA8I^Y1h922(rIv=jnjE_hUgm1_i~g!MI9E60*#}c`JpdG&NoM> zcNJ&g*m9}H?lUmcnr*n;(_Q`E zN!+(}b$qvqUq6(+hAyBCq$b)on{#8h!?LAeX*^`7&zfwes~H3Zy;p$(|0T685XWX# zn2m986p8^j0|&(zxeO-50QH(Y`6P4*5MnYFr&egnxZ(*>?To9c_V=dgwDDV~l>#|( z2qX6^kA1+}B`txyohmb3zNsopw$M^O28pb~SYRf==Y2-%PI#8T2SuY*)Ey!%D3v#N zFFDkkxs0)+$CynSUh*xoDtoF_6dpEYJ#An?*VM_EgG8qy%+?v>U6V{1q7xL@JSn@3 z9xTyHUDsfAT0Yv#MA1GbWUlktBPN}!b5xVe>;1%64-OFU-kwvuc`(Dt#3YX$7z=zX zegI41NOIwI;9IVK3kPl(ZI8_!4BlLdgu4R4v)7OK-%nN*%N7yUiiso;IL#>Fd%WJ8 zg`xqCG7$SGyWlN?T1Spc1ndDAnf?GD60OO!8p!K-u719#YB3dGvv1+HIap-?DfcV& zm_zm?+!at1j-9?I8-cMdpwSW9sxX{Nz{eJ>7^AkcPb^8FwgvYN=y_iJU8;M+*6`6v zE$=-ZIwWHBG4S~IxLfW5I-Y>%L`pdLICj9^pQ`7TGC@BVT|O+3ot~QRO+nUl%KBx@ zw3PgNwS0v&WvWfYY?Wns3}z<-teux)53?2US(Smn%8&%Nupn3=Q-tgmZe^y?{Q)ei zEq?btPqW}ofYC?ah1 z$uICnPU@=nP{iBIu7BcENQ_(iX(jzer-C7gcZR0)!Y_m#&iIg*iBF{oU<=g?cLMMa zkdlOXQ6b7-M~9X&vh=IjD_^eMn8%#5=}Qgpin6|~QZ%I9w0tPN5%n}>p15Iz%$E0W z*%bseLFy;+`#THZgG2-EZ(PW}Zpup5GpE~hM2JgehgY$NZpCM2BjdbB*3u|$wI`ia z4cy+N8dWqto|GA&22+R?fXILU(Qvp*?3^wvv#kgErlsucvH;vJM$wuOEG~IXSn;!s z29Yhxf4O(UQJ{4Oi`(~DN4~Pa)LaAc%J1!QV}(U(!q+Y}XqEaOgm>%F=~0_k71`KI zL@xbn^mU9KICg*h0p>i?H@_kHjK^zlB|5fVKXjzzBgFm~_9Wqsn%#<9^(Oztt5fKD zP$#$=9dI=!VA{Ka*zVb>Js%fjoa4vFMw!)b)2K`qe{0@Wh1bb&xeXtgmJzsz5T60e z*i`s1ceAS#NnGEEF5pvJSXGg+ASi+o+2?=UgTzNk2#RMCg*$OHk5h?nFDRcX$!Q#d zZv9+ylzC4M+R9e{`Iw%;Vidj1~$oV1} z-t80*(CARKY4uQC+TV0OR}Js;i`KnLB9KZYE-J|Ol3adsX1(K}()zF;R&)7kHrEi_ zjTCPLGsd&M;0S;MdSwa_`2BCh{IPr16Y6*|!KTcI;`1$Y>#4CAVcTvWr4#;&vfhml z3j~;SKlZP{R`v^0K4pMQekY8Dux3xRDNpB=jg(1n)EUT~*H8>=zs}7rdybV5l+Lb} z%6bdW*LPYy-PpK+YOpJNfCe?wM;H`E?k4e50W}u)Tj&k&Sw!PEi`B4(%fbV|YTW;OSpE>r5a5&}F`-OoD(Y3OD^W5G&n?SQ%l{VJErGtY9( zBKJ7HS8&LMx8-Kso)Cc!v1*vRD>ZQ=&!hFiff2k9(ps zvx1YiBLdmVUtOzzc z1MNjBuRW3RZbz@w68~pV1!)J{gPxnGLt*iJmrilm+7S4zB4TsqaXYw}AFp#N{;hmr z(lm3L1KvRx#9YbZy@P}6qbog1Ar6z=P94FK*^zK&TF#FbXhQxjJ7-ZiPt^H?#-0I! zsi`(cQmfNVpW9lmlA_u=eg4&-J0xY+S1Mhkn%Un9S1)(-ioRsCx579+=y?O2yr)XV zr~b42);cEaoX%?rOi92z#-xms4#x8yZ)gepz_z^5PVychQSUS6&y`b}QGU9t)B8qf z@n|0pnmyGDV1n+0wat%GVqFJ;?+yBbPOTk_BY3>}@oBM=oHxww5!_FNCqRM60&$P# zM3&uO;9|WVp|8`#)>y%z1g8_z^Rw&^lLWE_uyRsSU&JG#H%1TEd3a?>`K%k8O(pl~ z;+i=3Z8?mAwz$x52=R?n9y^4}Z?hf(@l95TUzAwK%`W?|Xtty!n&s zRB1ZOU6)&ewtAxD3)Wa*m&NW}<51)-$=d;>KhMPLA?Xj|RpJja|wHE*eorssf|X+}``6(YWj7JFWC zJ1he)FGOVPL^uk$AN8Y@eIFumG~UzRoBi=vvo!Hl+)pc8CEhXRD1WoJ2vR5E{Q>dGN zvw6KPivUhLeTMWl*-smsamCSl;?)qC4wuw9F4B#`|c8^+JH=YOjt950Tpv2ZvN#y;TQfsF0mn#vmPPg1| zhvG2~2u#`IJUdZOcq79EbOd zECKL`;qC$3X3|My#t86OrM-?d$A0=tjp5z>*J=z{0>oX&<<*Wo_9alk>P4lEd*T~o z#Lw0_KHiNc=1%2%_f0P}{KMiP^bKrr#=bQmXfQ||e5FUP+W~r3Syjc4>Lckm>gL^p z77g!`aDjzAVz3=Hk2Od-Bgb{YTb)%_8JWO$pz?Zcu0%BVKFohwZXjDJzy95mjLW_R zpp{KXv0>71KG)?665O+pt$g!P*a58KfI^N7lU}V|%ihBZo+YCB%#U~^1Uhj%<3h9d z6w>vxpg7WEb(ik)Fg>~W`8gn7rj=m(0Z+>SD!yr}Ve1`Cm#G+&=yYcrfk#U)Ab^jC zqfGW}1OI{eVQ;f;t$RdoW2hN$0=TSAR?=u@k^041U>-VWOV)GfSL`W^wz#o%iuuk% z-Nnf_yVCa?Q`4^0hU*XK+I-CReXIm6cg{c_dRI`O0$+njptctRzcD009i}FAdqd+h z77r^4;iphu52ZL#LQAi4cS(IjJbV_FftQk7Ftba&K6K9aHC@))X|apg#3{)A^kX`H zg7HAc-VM~JxZa@>*iT9QgzCnI3M_v!{P%A!{W=LxDzV2;_oRTo(b8}B*7|(c-g|H` z{)=65RU@Z*`gZLcsvH$SiFKwr8W8G$SqmV z-I>1DCS}dkP9Z8z7Du$&JKxd5{o1#s*LU-V(-6?y9#Z!zEqn%DzgnEQuh^Bj*xWek ziR$giDp13R_2KF~>|v@DX0XDO$%S7g6*k#tP#-e(q%)|BakCMpM~D9JLJWiUSIX8* zUrF6(x|LL4@HxkI9kJlJhliR=Zrbi1oayI0R;7yn=oIT(7Z@(b7BujlE;S!N-bmPK~Kf6F{bEE7>XD_W`biw_`IT+IbKjT z>YUtlE>9-^{Wqg#H;fq-fgy(?yBnC zAUnI2Dz(g6mUFElC4conncN#AG9!o+Snafm(X5#)f9EjS-sL*#Ppe)#=EH1FE4uxK zoR7q26}g#q287_wbZriX6p#}g!}kuUMvI23{M6!kH~O#mq0Yj{^u_zT-ePW(nX5yW zzdfqk;s57kQPJ%%i^GF|<&bghnfTW2zT|qFQ3azau+ir<@wzs1Na6@+S?X>BjUtl|c` zM59nD|KX#3tQ9m77Z$hm2AUYgQ}uP!Tcv7R_sk!zZP{i=ZaHWYR6^eG9qyD=I~qXE zquNEGB`+^OU1-IX#evoliBP|4+>*yz8MwT)BVY=#*@M?Oit($}rK2T96hc|Z6SAKi zOpORbX;0Sgz#lMAO+zBt+#AnbY9q8HzaMRW%6ph#Fx&O%@kmw97iGqSl{XOWQf%5G z%WnfE`E~J5WdqQc-P`nNLF!^uW1Z=`2T!lw=AGB2{dci>KC1gY>^!h_#3}ZU4Bd0a zA5k;RX!y27!K!bPZaNstM9Wq0R0BB~oK?>tGt<-e(Ix9N;cH6Wb-M0!YbpNXTkBX! zVKr)j$r`z+tj`km?`Xa{fvgrz;99b;lAphx!ujt3H*YPTK0OaQFxYc! z^A9>b#SHyJt!XZwIi!a&$IZ=sgyt-Ka%QLE+`ZP+@*<+XI*;caqoGWaX?l7(CmH+} zN#Cus{BUb8z13mqiQ{{Nj#&I3W`4NRKcd&4;|}JIN^a#5K*XhV zp+ftK!H>LUXwP;Nnb*&tZ-_pna^$(*Trd_YCr9`-Hp={(IqRifIgIr!Wgn@jYBYmM z`>;-E5fsmW=D72t7}X}vw`6&e1R-wT!LKRHcy8}2HrsmUvRu(Jc!qFD5D zzl3B17h@llP5xYRs_8`mckDh6Xo}j&1=iWUH{IhQ|hEe)XT%YcZ#Feq66{ zT)$i1?^JXrREv^Hk7Q-98|5pyfUA^K)5Xw3&^>S=T?KzRmkdlEqewJxem{2aWPckm ziRv&i`1e7A_$ZdE>uGNa{hv|QPkteoh*&-b3*De_KKpsomLX8va;uf5CvZ4}=eO9S z9bJl*w@^dpRf?|RBaxgkcMo8uD>+V7ju=Ylj<~E8d1I}>-h-wO>8~cUTU=I_litQ# z=hwG|t=2)^CG*x!M&_yi2amXrO`Kh(TfRw5LDP==i^#d@nhSLvb>A_m^x-o2D0NGJ zxv;w_c3=OxeGv`|2jrqH=&{U{)J7rkfI#>pK1ul|ti^sYB$>x~onlc-we>|H1nY~p z@jG2z!R(96W5Q1Z4$KvzMrcMB)pvGn&;{`0M2zwmG0zJltNCC91a`IZHiaiorAu@dwbPF0P2jz=7ZOG0{TTs(ZWsgx+_Fn!Nka?LG z>1~~rJu>?o{^ZRzot#NTBZPm4X#Dr@)nm_T$oi%KU-~6a=uMkNg?RjYv;-SV*f~LX z6esVaH$RMiE3z60w9%9Jpb8IO^HE6+ETTN7S<6b>;Iypp4+nMg_+%90l{75OcO#zj zdm+vX;8>KV^M)AH-S?a62}|#w8O91Io)!7zwk?z+lF$Sg^NVycZL!kQDXZgGV}2@O zey|1EckhxaT6MGHZlMHBbzaOnA)#l`u&q=w2~5m93JC0&T&@5!fqaIwp|LUHZohCr z*Q}yD)eHu{=UNIRK~t`NFdsYlVb3;AxEw`f!$}^Rdfy-oIDXOPvneb`dld*TKhB%q zta61Na9o3YOIz2CoMpxHVx@&s2%_x9pZ(!gjoMI8*yB(9|;(kWAc#v z8TwUTKtNrZ9pfz*43|a!7Rh&iOeYkO+cry`U-~=cpF7(85W&Nv;n0LIhe+{?$Q?qu zctJD1JH0Zs$ZC6ken;he5S zP#B(q#L?5U!K9bKY`5B^%ehA4=rVO_$=K(qkQ@m6@;vNHTL0w@mf){@X*>=czJUx) zU`RFM;g)L6a_H5=$aepvAmv#bdgk^E)fEb4-nSK|jYz-NVCGrl*6 zJ;1gA#V_|hbK^QH)O}#BDwFT93*gQ#%iT+l;$-|(vqojc_hSt_prWr_KUiBsu_L76 ztUTmONfMX_nVqEB7`QsguG%AJY(H)@&yzMA+ewtVl1E#{_h z(G&{y+{XzbH^_#DtF|_=VI|SNbod1@Rc!|BpUhiK`jK`6j-thMav_h+iLx09iQ2P+ z1tml$*5k(kO2Vfu-C|Q4nVAoM{T&$+{nunQLM70Z$WjjC_58qgu0g9Qppb^cg?+r9 zMqPny%?gq*vC!q+-bCy$=Mgyp=0rjUL|~|^^%@5AFUO+gLK-=+#mmyuuH=R!uz z#QN&80MI0I^S5Ikp>81#kms?lU%!G)&rNS!2q5$-twuP-fjK25%>VO+3qb$Vcx)e8 zKOC5z7D#~u%-GrO;=ydA=P{k5)6pu!q!w5~aC`e8%C_@bx7H37a>#}YB$ok_i#W!o zDV%;?XW(Qzsl2!7xhVYMx(x`GSbopk<=y85%r{#FnNOZP0YS3-?8a>28tTG{;O%3z zM~!QF{P!e_ZlffHop!AtO^Izf{26o=Mmi$G(49g^Mu=i|>u~qfD9~a;|7=&)X(mF9 z)lrq#0e6IJR+cjAOt0?mvs{ousStxsM3qP=xJXS683HDTZlC^yoq#51rAPFsYpAye z?6Y|~oL=bcay{*-F}JL2!JxzJFLY+z>s(tsW!A}^uxHtH@pnll@rI#wr^Ai8m;Rby zdi_aMXk(ul971Be#nyU5HQ}uxiMRo|xDWAzfPoJNY#ICG$x(m#aK_)MzXv4tO$@T* z6Fl#~h#ObN0$UmDFMtN6_UVh)j8WV7<9FXj3?d;J9{wS>z=(ax>AT}ndk}_m9KKe~ z8T9V~BL5ZKi;ZY2tZ@-d(O@Axpz z@A-F84E=J6T%uFKa_>L|fj7t)zNT_@&Bv3Z`YnNf?3yq1>35vZBOckCPOM(fy3e4d zv-+bKP@zZQ|7>>M;fZlt?tGRML^6Br2qGU*WYM<$ePv0BiQijUsmreMrP%*0X>wc% zmVpf-F28*Js(a7&J1kLA7S$>(7sp2Z)yq}>z3``2OynA*5?kJ&gGCTcqTNoDdRgnt z_-n_ANO=9op6Oi(9)YFFk-Gq(lIM(8!YxY1R3 zl#hWyYkw%#ES3fN-}j?{k6pU{tg|tcSrBBx@Bl14;oD}4_z?3`23@&mmU*>+GoM60 zQ7FyPdmM(!0b}mi2;4g-S?q}J_6w0Wy|i`La1O?o2sW$b&IMu_QFImEx^oOe z&ovE?0zh^TY<&0$bvWpF;eO4O#!&kh0*;SoKw3pe;nr|6y`S>j}40P;AUb**=bH?`>(Bt(z zk7(eIXsUX-p<%{d^+QQBSQKeOOMe6@3j2D7yvvOi6Ei3kt6KD=GdZIDbasf~Dv31V zu2Is=z5hGfBZ%$6N-tSa%Uo}^Iy0eqgP| z0t=&jLk!A&KiS83?6}g+4$)@z6Q2{q6#q5agIeD?w{*51lZTS!l-1I+&6hcF5MsU#qV?|^CXqL7K^GGz&VG<%$2aC3-W2sJd`)I< z&W}PA>d1TQ-q^Z8hQmTaLXy)v;X!4Lvqm9R6K0-g{IA;Bo8rq1_zDYGzw9}L^IbcY z`{QHb0yO>46C!h1!qhe`BUmjj#pBPQ<~KAEU^2TTpnUesZ*+1&v3!X>CW7l^Fi0}_ zqRom~LhEwVCO3Mu8@aoA?x=9AlrOMP-Vd6tJ7l@4riR9`iKR8RnCepQ>;em~N}oOR zI+a~Ep4neV*=3{Ozucisdh%}7_2=%#Ug0=pwRP{uk9$c8vvWmHe~(?C?E!3jpohZm zW)-favJ8Cw9GeTZ`ff={NprQ?Rd7VFcdNj%_1XEkjEasXl*4JXxiwAa=gp%WGdYi0 z7F+NEJwNKIR2GuCyjm#NZtV_^Q9bd>Yw17iO%uj?+S;ZgWJ}aPWm59|dL(1%x0dkK zT#fG;ndsf648>kY_s!+=?;RjGU~A9SfQ&=?8SHFwV~e|`wva0vH9v%eHzL%$O}fiQ z3e%URfs-geRJ(15MYTvMF%UNrv3nrO&BY~I`r*R|3gU*-KdrtAv3%P4!k-f-l_k|) z0}neN8j1`bVd&OfZZex};C%O;UA_NxZm{Kt-^|;b(w6EM*lduJl-X!%WujKFGO@$r z=Hdb(vx&{6iWC$fI~|rwza~M zWAvPW$*Eb&2s3H;&!1Z74cKmz&vj!>-0$&m(;;A;Zad$EN>3NhfS|d{31n+N!9`;p z4TS@>%I?n0+vA3@&i22b!4W$>!XAJn$u1z+=;7MinRfyICu~x4PJqgiKwO;llVWQ2 z1)GfeG%MufgUUTL6p;#VNI3Nh&ocD!>4;}2tFmxKM-MJnLckwI75c4bC&m>5&#DWF)~&&;7L)DfYP$&W9i8u_vKxYb6j9D@t;OO zLV#}YVjQg_6pr^>BBW*Mfn_q>I$>XlaStvHTbKSQE9AjLSyyoF#wTwHU0uHpYi}lP zgHGjDU%;0lGE19I37#DQOSraBhi9N$$UE(gpcQ*vF)}kFzXiG*Wxmk&fx2S-hcP0z zCu+L^I(aAV-b*y)SSeX@N07CzVudRwcTOXN`=8qCGIW2E`s;?g!@F?;{V-ZHoS0BO zYKJ9G*Bk78f=&@cnm*pIcfdX5xOno#`SvPliHUdy0>JX<(f0Ac@bFf8o*z%*`=Iv^ zGtmqra>c*1kMB{m)H5RVr_0E(erShyb4JyHX;qFT`Y1oce9zZ%?%BFVsz183Ew}pZ z2SDpo(E(Oh_2m3b8NT(DKX8bQ)MI7ac!B`BC{4*j%OPJidb}9Wz6xB-T)?b&+n~E| z5j2(>4sq|kC}>LEBB!TysB*op%?1b_Gj_NJgl8}rVeRk1+cPCi29A{ySG>YyJ9G9b z9qOrOH@3H*eG^o8^yXA$?Q47@et^vi#JZC5?I@3uJ!|5-CR+lWy8li{+C8=fPt#47 zA<^;Qkpk${Lhq3M|)S{M;B0{xNb+WP%V1;@GD!+r3kOm@t4k0bmNly9PwpDqDR@OPVdV`b4+*1Ok ztgsrThVp25=`$}$t}01YI@Z4D$NxW&+9IQXQaPA0esB;rzO#1z);ITs%OL4m$}_t0 zfAHcoWX+cpG{&$av+UPB3))NUS*(KUUiGYOcexj;bdecDdK8v}dvNB&7oOVZZG48x zta9R4AFP?5!>;PMW-|8gawGae)}NbHbcg7zbGje&JgZIMC^bmN%jSrecjg}kV4XHa zfnK-Loi9dtZLeH`-h{6D1kTGAIjuo6dLu3i5)-?Ye(b9}Pf2sc9^Bf%9!F^(C=&mz zvH5Ks8~|b5+{8rD-4U=r{R`qI+KcU2>${o+Xi8}6qo2pkKx`dr-Hn3RxV-pPTv8el zT-)-T>xl7z2@#?O(Uj>^0+ly9k{G+qPHS!bi@-25H?&h8|IG)zckP*aPT`)evYODN z3RYX_lADkbR|aoJRp&?7Wl3#YypjqZj$l^`)w=oRezsPp1#O8h7W_HZL12%}x?gRG z;(PXuey>H^Q>3AtnC2HVDVFGi6-gX-nfExKnRt>Wn=wputr6-sNRJ5|`6aLLo@l=) zq1Xgju?JCci(k9%9;}1*?2`}>r|f_Z7%acy%s7}>4&FT81Fa!l>^~*Z+gv#2T7fkL z#izpQXErg^eQC!=3>HN0q`ZJeh{kn=DP ziz%?$3=n;GH_X%bajh?aw&T7+QIhdnA}(F6vz_?A6c@k8%V}x(T2^+Omt>G_J!zZA zlB!ELxgAsLu?kMxbU6R>*J`~42SL{F-^&?a&S9Dp%sh%)X38m#_C2yHHlPV^iP8cW zg>nd7=**d`5CyY3A<0R6o_9B%bwwus4qC2ZNQyujQO)GVT!Z_Y6_3*s!*;n{mXUZ(n=ZN%4Dvbxp}% zzt023@9&A+9SA~gQ2Xur=^v0Rf~*3rT7^&JueB?n?=umR#F83A{nvb*(#5$*|Lj5} zM(Kk$+tPo;7s8Xj6Xs)ZN7qw8icSeQu|)0X8ie1x`R=%qFBP8gP^W0^%}QIQUgKHU z^9H^%Cg~?jKqp~Pna`r&@QLxH{YBUbD&=z0s0c&PoQaCg z5yae5b=}N=lKDqP1u6g5A#v9^6?knzMTvs$>-2!?4OE@$t!hfayWfJe`>@VOZk_R{ zpjqxNwFs0uBq5x;fP#W5>q#NwXRng-R3hSEC_Hh=O|{@R1%37+RiM1dfGM~PIjzp{J>h|CA0e+``K$KzPz)hQ?2ki^W zSC!%*UEq($nWnLksm=!#bp!mu$q%R5UnZBzDt@Zugt@iB^^4*QumV(O13BME5qse1 zLVgq(x-Zb8Qxm%JNxhDtjkbjY36Ea~CFl%teP=JhJbkBJG$0Bse-Yn@1elRlX+@SQ z|JJ?|x;kCg7y5zc`=dXl=T;BIKXPADIxo{;WLARNC4uq$ys;=7-<6K(LE74QX>l^4 zpK56>Naf%Iw?HG#=08*=L2TV?#SGPfgV(>$$=Gy|+-vL)UdPoO^Teyzo>PJ~272Kd zrNr20Z)$34==2kD2YR8VeziRR?x90QvAg-6ksa%)lwbA=tt3Us`%uQOwVMaQj@RHG zOGwQ?=Kl0BupQ&7B~e41b8ev{32GZ!E;}pVwM-?Y{^X78%hhYHs~%`vS=ocpig;x| z7-z-vjaKP*vk(7oPCj7C@AE(Jihs`NJa|jkzr}c*vU7am^uJHIh0c5w<9`vjyR{XH zivNQ7#CJPBr_$Q2xu4FuqO0^w1nXf1M_ST8Iyz zSDhjNN-UU5O-=Ro-T?M$&l~a4QE7k_tOE;>UT>E{7|Y9YLgvKMY{&Cb9A_f&tu$O~ ziyV`-aN>OpOPLoOL)L{{fHM~KjpO70pb5E~7#~+dw~}F4qzO5{SZT&Knw*#jmNdd{ zRLl@UodGkStu;ed47g1f?%Tg>#Em&Z`sNf$tC8JEp#K?i81QisZ-av1{b3)+1i=io> zA}_RqLsH}dAmIt6z!68rZ@SugZ*Q;Lmy|-{7b<;rzNFClns_R0_*dC_lVc@mGI#2b zCyW)J=~4KHGK32NCE|#C_nL4#+CZ z%?M^{$shtDq4A!_y?$yfvPG^E89y8+02EY3B?g0rt z)2aB!*@x7SH5h4qt6;XH*zIk>&zSq!`>7j%6I(zheQ^##$A=Y}cOR+ND zjciwMd_N{=St-mDh=)1aQ5}4M(({A6A?`I>7M+=E3^5LxN$8R8YSMf6S1`*vPHvM- z-;b9Z?tQ)wM6PnmsPTfJJyS+S)o#681B&zmK&=3{_}eA&j%4&IUNrk%_8S66I;;NA{777m8YN=G z{d`BSme<--HtGW&DNA>6ds}=oy(u87u)z%Brw_m?ei@?9X^oNQ?Ch)sxQR_Zh~lxI z$B;GgKA4aG-jOXeWJGeYy|Z(DcYT~?rK_v!eO-MW_1X6=6OTh3NU8+d@5oG_Ga??K zHc#W(10ifPzy%`2CuZh!hc{*qEUCrT0kB7whVo$H9}>8Fwn1;0_E&qKiPxb-A7j;DT}zq*fF{3Yv#5iiY<{F8vboK%-6kulX z^leE=NqM<_GSEm&l)`o$fNUY61L?fcQlK#Z@t7r-mz#Tr&wAkg-l^VCyoiOgi5?{X zbJd&)5`KnH58fGY!t^OZA$AXB z%Wz;paKy$kfRtB8;EmNvF8kAU8u{K=?%tqn2>vu;P8@(x-i^96fSCJ*xQAQI?U^p0$0Q{z%o$LD7r;y@T8QHgaD{+D>!IdxTn^~L6s_UY>o)2p zgcQJRC8)4ap2Y5fe9xse3ueUb{=Oq(emvgya9PS>H1V->94)9|KsVLj^uB(?F5+*h z&2Wf6t~KX*O_1y@60zeI-kup@aa&b>e=7pMNlpt1njPa04ZDRqR;gcqUp#$E zagsrO2*PIl;b5kg`8Yj19I^*St;gRl2z^(;vkYE2TmDkrTCBA%Ul1O&=CrIUP4B^c zy{n|!PlKasoyjGh)6Tb5uFKf4smrc=ZI*kCI{t@uSiD-i6WDvhGOrKfZ(}ot{sgwb z&Ow)RC=`~^HWyU2GNSoJH!=S4hf*ZKK?+;);TgzFy>l@K zjzh~da4yxjc6Rpj9hHRhzX%v$%8`Jav(=?_{hFc zf6HtP^uGTZt-lQ~!1!^h1?Ct-(gJnB3qOkr!7h`kOdcZf%J|;|KD4TIBw)gvNC61! z6p|iP)ab?>iNI)%&LQb$3}f@Piq^Chpzg}D7}DI(IKB{p6%{Ik!Jzd~tORyYzzfjs z=QI5J4`t{ySkQc^06>;Eu+*rQUoEW@tWlCdK~65MAG%kpty0oc z8I^qBo5+TTB%plhGtDu{Ga&v+zkm&3IGY2OQLZKvR8oBa7~PG?T$^}jEVYc{Npmq^ zrzD5jf>#m9KT3*Q4_nJ7?*c@z*2CVcj7c$%RHDX&{$*rE) zbYw1{wu?{4qF`@(QAu&}nyE?#@4`o>W25wt;CBX|_*l8WzHoh;u~Kkhz(+hcr&u>z zE8481%=6(G-Spo5uEIV;J7X7e7unltt~*|chG*b|Na{Ana&_?Ykz*r|il4)t-Zji=%&rE{F*?`aVEV!PsCgk5U| z#16s1!9e{Q;EyBaxC=mHL=f2lNDsB>j?iY!YgZ`%rYjCh{#q)_JLRozbo4C|@)j`7 zWT0^a(jI_;)j!C{g$1*(2{a^syriAO{=cv~P^2RCDp$2r@kv=pANcP9l+BW~@3RF_ z|D%vf8-5HdtVf2`L*Z;Kfi5V6T;3#aegJsp+c(zO&_N`Cl>u1Bb;=qNrqqT6hq1EF zO(Q_8hk@!GPGp&AMC1Fn?Mr#{nE(vkh>BOYXwxZ*l>dNU>hBE>TnNa&YHNS#<3kejxJfO9;FjNE@d4TB)KpOV;o^*3 zkdV~B-=I9l&ZsE~EB-Tix^5N(1hkGsgz?WoL%htu4!NDfzHEHsq}miMVfX>Vk(X+W z)AEjSwDsmlp3ggF1o4}&G`6a8ZLuBd!()^V=Xih$(bYi@DMcP$18o8@bC4_vZL?PA zs7{b;A@%6x({w5AC@}-S2GD;V)n!rc!S{_%lh<#str0G)M3exezkD zRRCCGV%*$|@xs1&#$yJ#)U#eX0jbIov-^zvKY!!^5?w8t%P^*CHQReuA1`5Ecd{Fc zQMjhF!7p_@_=>4eeHy}`ik1Y&<9@6S{Nz6#uPdTkXZqc>IfViFhHkSL=HxQ-zXrPh zzRJn@d4Lg6c<5AF?19{eW``qi@)&q5q1Jiw0jos;*RaILFA&ppI)6s#~<$Z*B7tuU;Q0LU_%9MeD4Hw&&5N5^)b|kO}?} zl%iQ(%02P_=y0f)X$BSMCf<0M=j2u;>jxQNFmqr?_{fj#Azz||g^8&mnc2VgvV5-ed#-aF(3?5ds!DXUTS#Sd z7(rT+9yedW0qM%;`%&Yoveoj@{O67laCxkn;k1_gsqC7@<1 zC;@MO?u(GK%~NtS-?{NW zfBMx@Iqyf+(G@#&cJ*pLNG5aGqwXA&qb$lJ#x!Y#{ey}1%+}gC@rJCv*jX4(9|?L6|scQn%95=7TOto#&6#?*UOMny|(trLar? z&S6T=O((oNGpI3WpK=db&AF-7!xrjtfzBJ^4s2sXMJEr@&iM~HzMCiy^$InETLlL5 zqpLaA^L~=6cXmL0nSHl^xKaW%hRtMxZsG?`RX$**01$eY4CpTw ze6P#SXaCb1F5xp}94GJV1Eoy1IJ#oWD6z?03hzP1_4$GQbag1}$K4x&2(i-$K-rzF zOeX$+e7$!toNw4aibzBYK_c2JA&3?w>LQ}|8YNnyw-7CM1*;@FOZ4cSZ-gMx6WuD& zdvvQ4>>|4T-Ol@-Ip>cv#|&f4*nReS?)$p0@+sZAP9@05y)gQR5+^b>W@TfI?B@w9 z8ls}2k2Mo>J*gmQe-x-v{1F`!6^Jw$>41v^q|Bdn>DtiHTa3W$$qBhR?+uLm9v6HA zFnCJvUXnll0-S|e6O6b9qySN#5euY-AO9y)7-42;e{NQoCv+QPi_m+=-C5vt*BuLZ z=Dp$~?^lzI_Garz>3;r*phlbn*!mG;N6l|}Q^`_;Z1M~s84>)Ks>!lUkjY!|Dn;Cc zizX7{+{9XzsQ#N40IsPXzX=JB91&`0g(iLI)d^4_FbT|BD%|ip;W$(%M1WD z`NGFX+fIbjeFscRDlD7tWbXsg9e83M@GRf0&7h!G6!TsoqV~;<=#wXb#Z%p?Kk>Th z8ebS`cZ+k$0Qe^$`3c=Q12Py$X~fAAkr@lq{K5iaYm9pbc?kR!mYT$+u%OfNNG~+h z_3vzb!T^C0H8%+B)u3zZRfY-u*83bJ2;93xDef+p;!FQFVYMWSHpVhF!`TJT@Zus| znFKQv0)OxC?}M@WH}U~S2@e_u*~>#}jNhF#^MELm;=&RHxfxc?pHDMCN| z1&s=zJv+(Nx9)7Cb#*$3==yiQw>Q<`&7*bLFP8sD7Q=pRVTne{4v6R6XnY$Pjn=*b zuu5kazr+vXJ;s3`Z$)(_Xv;*%2g*3f8-r}hB?F*K=7n0quk$HuqC93}a*vuH(%s~f zPv87G1a2j-8!O!8l~K`P(A6p${%3OX`0{vN_tlPL6KjMKB3YUHZvu#}tE;dnTY^UbK5%q4uXf{Z&aN-~kkl5c79^?fL(z3tecJ?zf#d4sb($Tz_<@b)gEk7je zRs~fyII4w&!O+R&)vFd`Jm?YVJZ1Y~Tf%04z88y&BFKlfJiR$tLI*IOQhp~-%!@V( zKSSXp?n1(nySEf0TDbTGsar(m<2t^`_T?Y`Qyn&a{9l&P!t;G6$WU)o!cxu9<04Nz z2(X$lqV+y!C+50g!DV(Y1AGFyS&5wpl>?AGl9R{aje%M^X_`Y^RZxl$Z+U^w)Pg#L zol~ds+ATtH>u;vE*IeWAL%ak^LM^uBJ$gZL?=FH@+I3a8(-63G)mIPwj0#wmyRz62 z;@LE9AqfPVqFP&piVkkqcNmwrE!Ny~o5S=I#PJskyhpLHIei}4r&e)KS$F%V>6tFw zT}bsG9mLHf>_tLH&x4B|*~?9)0#N?)KXj+Epk8*@x$}NmpD-N^J|+Je>g!9{516GT zfDlt$cAwWc9Ff9r{#abK_(ty{|8v;mCF;lKthZ~I{$r92xJUuup~VmiZe6!*k^6X? z`(pm0)Z2}5YB%%2@o9%#J4tXJW zgDv;kUF}F$f=Y4q<>Z%IN?Xz-!XtpMcIV62ujGKJMlcycZP^4m4**^JcW)2X4C;e6 z>O{IFb`sd}u6dmekm(ycI})%C18Mtvps_jy1Ko=KGGIE(&f2Hl%r$gT(ZpVrA`bi1AEtHrY44kI8*wZ{50Wh<>H*g z`7DPAXf#dH>8|@13x8y}F&TSx6B1#-LVTzA5UA+cGHUOfU5V;>H!}bn9uoQQqvjaiGOBVrF_Iq@U828;~g5ZR=G?>j#9D{Zl3$Y+t zF4ycV;E1m}uf!vF2LZ1f)QK@-?4>WWJ*HXGXdb)kEf*|nKMvtMGHNnavU?RyP1?W_ z)vGX-S{*ij_>^wazkB^a_2)uN+v5;O5KW}~J=RB^`RkwVwHc#>3VRLl%=h_p1NPdP zbe}(GVcN&h$Gq}zgP0GRzlrp^BAHA`#<<~_CMd}yRAwf1Nah6<6 z_DQ10?SA!0nU@C$6boOq?RJG!D_#}pArkV`!q+v0A>R7St`ZxDMOz=G4~OJ4GtFm1 zTGXmXdag@G!1pSVbFh)Ak*%r##fRp2(W zuS&sJB}P|?gC|psfm2tXo}OOUS;t2w#7hOB(gjqJ$*68M|?dH5)uD&q3u$1Ds$c9a_ zGN>p?pHm3uoCI+?5NojtLtL#R-Vy944-AxNWld*|Q*XIu$gaET`1nu|=9A}djF$8v z|LAgQC}{?9M!XDZ$k#wD%}-}~JY+X~0_mwLsw6sm+C$z)!dWW6e*Jb=e;2O)8a1Dd z%Tq`nQhbsIkbRX_;HJaV*(H(e#XaKO#rgPBjNBXFoA9aPhZ!}_Scn%-vbyb1rsuvm zAl8#~<-2wXa5<+*sRYEtKPv2~VBa-%cHU0p)_TTBH8oDJNm%HxvJ@0h1=3+`aG1@|xM!;)ke)HX4tLi;Y(pX}sWQ?12 zTU8PM^D6bz_dP3>iQj7Owjaa|u6Ls4wEGC7xCfJ>xZg)2R=uQa57)Jy=KcC5lXuZ~ zdG+7`#pLYfR-B#0h4oRzxC*h}WKVk09_>6_1l{0|PR+_vfqxRp7cOl~Z2OWS`i9=G z8pFUJp zxlm^~vwn0nj@yzbkVVWIV{4BXKQL#lVL6i!^McYi(0)oxv=p57JYnuJgR1(&;LB?_ zk-b;($((PB8WU~I&CLPXoPgc7LJwl3T0%2=`bPs%-Z9GI){TgQEZgD=u&9b#|4Cdm ztZPdZe7jIxwG_7EKh<~ErthtgU5}(VZ5%e#>tN!PQ18t5dquOVTHN!d+or|Z$H&Ls zzIewtGUaPxz}w+w`6z{OA|=EJv#TFzs5!k)h`-_RLCc+tP%LMv?9#CH6{rb%k}U%U zY~AEeDEQ-O+8@QV_gPm5+u$D%e^J>K`6*a1Q<;^OwX=c$eEB>{qBQ4IR@~W{@AP;g z!s19F{qlIDn`6-xvtqfP+I-O&1o_Iihy2^paF^#P&N22Tx3Tf@Izsaio4`$Ea18%d zf%2=f8wOl1WERm?!fpA&CpA*`n-hZn0(rjEFgz<1N>m9*K3wnpR{)oVwq7vM)9au% zCs|dlx;f*)6(fFH;~(s8Y^eOZx}Criyz}hwp8NKjPtzk|YG-)n8!!VQB->1)@nHBW zJ9O>)9VS|XEyyD}r=OqLWVO_!im|0t>A_Z~Q-lYon-fyblA)=xtu;csNcF4biH}LL zM|_XoBW$Q$5>cuRWZXthmIDJC%we z*{4P9Nr({ZyQ>1brl<4FDHjY(QEWKCX6e!iNYOUeSk!e)0K9A^bxuf zs?CM`Wxa;R6dTapC6PUPb4e#GodQy-#D_kIY4VzYu0p zU%A?_`@$<~uUGD?X3OiHsP!9WFp$*^Zx`|*(yJ*em7bO+^Ql~0vpcP#>+y*7$)-h>XJU2NZK3@EBUqk2qHRjVAnU%Hvl=H zg++5AY;k%AoF@t?h!*VVocj8)`d*@93S#2wg6F)uk^6|WmOt|q=1plsh6*ONI(o4; z*44-zVVB6H@M45wOxV3=;h)>E*Q)-1bHUGZ*O{$OZAm)cAtKk@+O5@oSyr;@&<$S3 z`{=wg{7-N7uAcGoJ<)`u-cIec7R@%~}P7 zeS6E#LO(StA7nKzzX;80#ym^u)l=22>8Gl1Il*vg;F4FpRx7X18V9lO1#Cg2de(Q{w~uZxKkVb^_}x>m zzhQ+HexYsJB&%_blC$$k! zJ*UiZBp0cW`^@_?N3}r_*F4sl%-d(tGuQF2{A+Hg>oG_w-?|p_{?% z;Zh%9f(s$)oewXcLCsl+Lx3Uox#Xv$8H1=&pXcY0}h z&V|mm877TG?L9>mxjA^AtnY+4h(T7vW-_bmt*3F{)ws9{BvXC}d{@h>`aI=Qha_Gq z{*NJ+dV7lExiF2S$51MTZNa9({9?LpC!fLEwA3xqW7=_LG8Kt3sqM{r!;kE6_nHHy z<$@Qvdn1a1N|(Kw*;w}Pmd(F#P0A>B`qD?+o+rJn+O>N272Y6kpI!<{;(bbtfI>)b;zt;jI(VDK=u2jb#P%|JZun?^G&(cD$%%-Wqh* zubz_0N%@{$=kWMJdX^}9oS6bA6Ts*)9TS=EGdw@*Pt4*r!+^W-4r}E6|FMZh|DR24 z0Do)CB4Dg-*i|f>ct{X^8Xtx zM1{nK#s1&%V#hx$g=R8s4^esE~;4O_PZP&v22_sf!}V+ z(_>jfB-mMVANFuDy^i*y!BleeS-k&=H8tI~|LT(cW8vJ}-`vvQd++Pkt??F-*@aF0 z;)aGT!;SyW<`#UK_il;yfn0G9Ra~Ws(*yxKwGPmoUGgan>r%tf-vtad(fKJ^WWL@h$)};Pl^&n_yu@ zYB4uo>tQpK-$B1vFBzJF$b(oEbvTGS9&D1Sm2cKN7qK1k_*Hm{2a5=(ojG5a$B{rT z&T(#6`Ux{vbDgq;UTaAO8INp850CZ;pqSW^@oO%|d?f&=J|5=a)z zd(jk*>?_PG4^Eo*MgN>zx_yvHoK7yiZ|N9%l47@iI+}g;fjgk>u$l0L$M8fz z_G*P-+wbyd^1G0K3Da4GY0*b7gISJO$;D`!$P^=)jW+NamIs1OxTMR_o#)MltVVkk z;7`kLu0G*uiNxlwXAmdxG9i7XF8aH=9%9VG(h6QvhmYg-@NIi)39w&AtZ`AWv<_p_9e#z;_P)^5QgAbrpt|}``$!YW&s4HBcWUuaCoGS;(UMx{SF9&f4*(co`BJC|TBJF?oog6Y! zGpBIc1iz0CPbcmjl^t*L;=!f=URGo?F(=6!EFk+YEA|Yg;{(W!FRyS>Px$?L>aZ1` z3qMXJtC|^2IQG8%p40udGc@N=@9+yI=2l@fpX^26Vny#6t87mxmdAhNRpMMvt^N>I zVP+iim*v^n!Y)H7l68Y6hIEztK}Bj_NN{;{z_K4qPAZ9ZcB%?fF@{6Qp1&~dWx8n& zzl7msG-3!tuAD9ME(A7l54)0Zru!YtdzW1Lv|iS4DjpQd`a zUWARH{1_RfCi-C9$ny2sd!p+CJE%g0<6_sR84c$}NkS=xDw>im9-FSsVP1E~5;7NO zMoO%{(^7h44OVfSSX-xLVeU@x!TsBHt`aG843{0J znJn+eB47L+{M;JDEYF1a$FLoovtv-ZNXPeHPLqzpRKjkxCGpm(k!{zMM{o9Fg`Dz< z<)-;mPQG3Z`5*J47vvIAaW}Nqy`F1bT=AEU{PDR+*Q*(HAUnooGlkR?C~|*^_?1?X zR+g57|8}>z@dPxB)kzTtZq2RGqQldp3It#C2y|i{ZhAiPo$#O6}!*lZ0A28vE4jUpVnmNKL+59eBgQ$ z+N<59>`y&iH4+X5xJs_Sgyz(?Oinry?)5LamsHWkRS{Q%^c8`aV%f;%(B@uQ6%)%- zRSU*TUwWL$rQ`=Q`Dr^9=d)4_5Fgo=-(TEATrQeis8!h&@}AOPw`-1^iJCnyg;4^G z7ub~M!~>}9P=)cy>3TM91fQ>!=IhKmvBxdCqqLLs#OIlEod#@X?44;2Lbhr2EYOjZkO|A!tFKLs`)qiG^m-{P*UH;t4 z{6@kM_OIXE*1FpE#1t@MaqA7ha}d1uo@m83|?42OB;clKIi`0BRd9LJ^g%P zF>8)}&W*t5BR->M=(!Z&sL896n_&MEQ5NBu793e=ptk5Y&m;hw4mTA3XZwj&aNiD7{Sz z6$(lS!K=qasl6tK3GvX(C6CU+n4f>9{sx8FrMA|avf)@7WS}HnK*9SVLht@a---`C zOjCj*j#aB|st(I@yr0C;^^@ldSkSotDvEY=7g7Quf@3)ud%d5OvrMl1{9yJBesRXV z!GjyX*>B=pdX<7*Z`D_Me3wH}&J*l$tawHL{eY9|U6_OIA{quGNQgD2%c@M2g@0Q0 z7F?7@A(;Szjg#5`@UGppFa(m(ZzFgO9)Z1gVjTEK!MRHKnF6vrIPs!vx<}zzRp#G= zrIbsbe^IysIlat$Il@Np=Ctc$UL7KMugrmDs~{PLMk~Z_=cX3>Ie+ z9`PN_q-?kiwZ2~N+xGv}-XA~D9*km=Wk1Kw|47?6#v?s&oAn|o>z(d_7S=LQM5%j9 zzW*`oAU-n^(OS5hY;Eu5bP=#tB>R9dJ~frGByIel1pLnYyB;> z$`(C2>3?M?PcH{)6X0^pQ;(kMnd?aqd+PNw29?8p(P?gLU`8tcA~G&yU9{}A zYbwkICMrJMAoGTvq#{a>);I5>;|2V)CRQq%#(%Oo$iL(wT6Ew=8m(RSdM8dK9>v%f z?9A2u`lTas1 z^{NlpwQ!n#IfVEuvtdUnkwxT(J23t?$V4ikN!;;Asb2F>S~tBxpUCA}x|`Km7trh2 zTqGv~!=N)~FeYgraaRZleU?Ft+4qv`v4VYhR=^1<6=gm zp=7rGIS+LQw6cnXqDy#(2g9}1WM85MdBTbjg&ec=nVDu-6TFpxRpwSxZayx1RIJ9? zf{lv;t|G}hngMIXJ#-k7O9xj?A}#rT-|M%pv)pz!wHRbvz6lp`RB}_|alia{L0xMq zd!(u>wKJ|8za+W-KUfw|W$>4e4 z!yDNVhlq|^P405(abzGxO2Rx&AfNeTZ7 zec6_NF@`Vpe01L~X<`NQ*4e}J=hI7AG{3ZDqI}y?t{AeaA_rbiEAtrMJ8Lk+p;!K* z*ZPj+U0YB8Vvn1b9^ArbKVHOYW)%fYVMz*ZTpEs<3ST|+sP`!9U1N4Kl9veVdW##a zVXnhtz&`tTerM`TvqQ5j%{aaLvjlye-zip+NY?9ZYbzYz4;TY5+BuNCdir^p^AAZ3 zn12#~HTQmY5$hix$2oncG#1U84jq<^saJnlEWGUb{TMN%Dm`%A900xh3n%N*7{61i zx-wAoggydKg)|`nvMxn1(_88B4o14{t{5$hyiwM{{UGCVdIY4EVD9F;How`<+IGv%+h12^S zU1*Bw3XG1vMUTiNMZ*}`J2*+hZMAAS*&f;0qVFFNt8Z4NS&kXJ?6(ePtL9$~<;onM zXO~wWK6}5=DRrmilNeK6JfjFnJ-Ok{S}Ny|HxYgj@qqX99^lV`gmESm{;7zQkh4n+YLT`;N`^nw{LvKglqfY<0^H0`awk(c?@S^#`*hY zA&-Y1#b@~YYZqUd$mdByL}@7!UM{NVUA3V4$r~q4Mo|h*m2RhZiSEWZvqC=)f4q0Ffu) zuVRZN0wU9My=~8P0JZ^|pL07qFE4o@agFYyh$f35UrTXI=6#y)TwFP&$Bsc14+ViH zm1ckV0q3q3bvBH5&2M=Cl{OgFiN#vg*Te5P$cS-@1!ZK+?(8u7RaI40S5>Fm+X4nU zqw}(#x|$l~AKKwHCj8Z6OVIU%q@Fry^sU70l#$M%XZ2){Iky|A{2zH%sxf;z>K2dj zf9e=E4VbcbdyS#gi9|k&h?@B7_~&yGRVVR~NCang$GWP|=VxCGwQn8Yf0^4DsGVmz zRCTZ6<_XOsshfP61J9QAbvC?jk<{IY%MfhYBxaZ4?^_RXT^xNgrObXj^)m%=96VoH7%9;#VP+xrJh2^%#7(om#v+pN1fs(W8A&b z9pZ(^YZAi$T-&pQJngGN+{Iw3OsaAj{9sK^7Qj}Tb!GxD^fQsxpp{4qP`W6;ke;#vItUd3zyw@kzhV29*LO|KS#p{2{3Ft$NbR=Rv$?JPo8KzMx zMMx!NZP7d#xbIEppu}niuvBXB{>=!b^$Yxq=KDwnp=0*}OjEZ9p?~A|EVEty!?k`D zEUX5J%H)_<83!__mI*acVt9fFzfI z01m)s3)oYNygkg;GoCa|N*254x4M5saZiw0Wjyb!%m-lHqNcTE6d(N^V3OO>9X92_>^f4kaykT>WOjiHaOf*(aexo(#;t8nGnx94b z`})*Rn~fp04&ocoJNN({+Xn8&9R^WeSy>z_bUdB_dey_3{fyi50X>ZfM%>Yvo>NSNLq+QqI0ON!$EL&nP}i5ssiW0Bd9kNGV(Le^$_C;6f06g$JNEPMDyRYw}v%_fOoe{)*|GExPY4=Obl&H;Duao7Oh>PwL#!q7E$!cjsi? zFb>a`Fo1-&CPJPzlUot>*R;VW{+#HTpwwi5kJ{z+HR=H2pntm;X%wlp2tmn4@f&L~)XT?DUsk z!r7+~(SO`FCz^I(H}n3|7P2KYF7Bml}_HsIyS3&UyMr_`h^+smYb4u28R!D9?ghlZ-P3k2xzuPxad}vN9Q&#mHhC ztu7)sThO-<-dp8i@+s?c4c>t6M|c@Y=Lhz3v|!2*RF*&(7v z{1&LzrwoFj5}MD^GWj2#49!IrOKPejM;FCqJlI+cz%KXI! z4b6;aSQhE>#jJ~2CCQV4kW7;q9f@&wOi{vLez%x~?aL7h_ z6qHJz+3UZ5@w_b|XQ+~^;c8Hrm@(UeHkc&)#LN`xg?(y*@Qw zG1+eWC$}Yw&jU28Y6M@-rpM3zEq0goFiV})3~=>*@G@C;f>p}R)1@O2*pMb?kOl?Q0+_elfZEH@)CiW;O9;zj0A9uQlxnBC< zUYXXosKWdCkftJxLL&Vhv7(y%$HtN$v(uO7*Cf6;o6Py4(Y^}xjRc$S9wx^bOL$-BzB`U)WW3Gy&XQET zAf8}q^;krT(>5s{op{BO_M0ykm93Hho;E!VaI33Z3hAS?6$NP_IiwjhM3kBR6>{x$ zhSc5}YTcx6Lo@n>YUZFSC18xc8Q}Y(SoRY5^SZts&dJf^X0`3kQ}it7wmU1WcaC!9 zAJUjAIU>JTTxDl(;ZR zTBq^<_H`8inb-?bOkbpj96hPCz-nmsK0XppQ9cEVcO#SN`}fJgoj8rTfq`Wdkyzcy zpu6grbtHhpBebnT*QiIK_PO}=^%p(&giB-a>RpJ2sM^(t1+04JV)5aP5~Z)APiJ{~ zdGnrwHE|6>&%RjUr14d{sW1K^qqe*jkU_*L}f!4*!=myiO60%lZamRkt0ctG<<#e0y@r z&86bCDY&W~6&!q@FGJhoQN)E`mb)bD>8W6C@zT2opF|OCK3Hp%#_zVayjC3x3oYm! z=_`O}tTHz@R9~tS-dyQ27?87HsTlBOjLDH@ITDBY0bV{IFE2!WMT!?DpeMa>cYD|9 ztiy4Oc5jzcf>~YbK;>&61@XHq_d-eiw`Y2pM9b9et0ruKva@~*l2WLr5~%(-<(>CO@bP;rGY?O|!PJV@rRU&-SO;ph z(B6!J#Ev};?9Q}*S+`(5B6dvJCTEtt_YYsAAUoUUv<|tQNtbf0X$SY|-Pz;mRZ^Xw zRS^-WWsqnJ+K%GoWqbba-8;Yx?&Bl$J=!YVb~pK%xU;oWQQm=6hlIHlFQb2>p)y)M zvpFaL<2E@J)y*cj|Ai%HX=S{ZXY4hGE zW*_phVf4e#xyqlMWxD|h@AIgiXA?9p{Zzz162VTZQ4acqZlYC_eT2J%MlnaPo6q?m zOvaGO;c9zr^hsSCx(p?eo&LK*tH_(4xp-x(8F#s73mgeBdjO0a-_*{-!-JWRDk$B% zZPu>j!46WwD3sjnDt{6>R25s-wMHK?isHZ!A~{PkZf6orP#aieWMGfnA7m6kqg|IV z$*PZiln1Gq7d9mH;kk^G+Vte9sKPmmyPK3)it{-)%ti~g?#>1V$ob(wELn7)KzE>b z)*oGt!;Le(6zXKs(8+2AxE7#>|9}K~+ESVdf?{x4?M(t)U#}AlDYn zLCfVUza&76)CxDy=!RxVjQFajwRHK^xHlxMtjIV2_>kbbTmq=#mMrnBxbX&`y(=^` z?gThD4K}7edEF-cmy2Vcp9#1T>@T$o%7TJm=gz2u&+EG%G|=w%6qa9G%_gioM>ZB6 zO>ksAI6i<`t*PdDd|GME`pakjVnA&+*Q0fjSo<9_l2c_iX$2CI4$m-~sOCkQDulB@ zfFxIGsUy%;3qb${M!*$Rya^{Fdw`dwO6oqvBInTZ-iah8(B~VUKZkVrL0z2rQ*mF7>(0-a_Vx)z-4xtrRdoY+}zI&q$Rt82I+YvhTBO?<7^Vu2~ z4?n^y#okCpE5`oHeaDUl&0m~TK2bZ*r1?gzm;BbLm^Bf^7FdG8pG zm6H>|l|(X0{R<8O{g)-thf!x>4-CtSki|EC;E&nrKyM^#XA9E3D~DsOAk35I zb1!RLZaM~reVl4~f+;%G*#6XNDn5VAM~I}L7b!vD@WB-XF+M{PQ_xD)uGbmt?$?c@ z@5i%-qcHy=FrBm*rx(dS;2D8;?4@erSfToccfYEsvqIoeYcN^){w`eE2hf{Pzk zog_M*x0BHQgL7n1saHf^YaB>xQ<>0rcVMQ6Q>tyFk~xsGB)c3lFN8O!5O=m#28$-8 z@>hj+$xPiLsRF-Fo_A$_+!x2U54$mH`A4UMmfsA>Yl~ZSH`!Q z%L3JXwVEm1YY{;QqT1;n&XeDKexnhb&Vikm*B>}6?@MTT%1CBcb+qzRKVk?a z?-J)-irXspnN;ct5zIaz-irfrVQ*W5Z>se@^YCkGVuW53zl6T#m;OBexAgB3M46LP zcy$2Q(6EsW-DR{KKA`(ab#U;b-Y#sl$e z$3Vb}lp2+UQMv#pl-@2t9azd0l-mud+;Ax1FPo&T7$kgq-w=zdH^UMQXX z1S=l?fMO0<(zqu8an|f?cixVWzZtQm&c~8Gc%Nyo*7b70)H&Hgt-ZjkE z+SjqPTqFJ%S`8wY?^>EY=sykhR}K!Q%QE{s^%4}#mU5d19PFT_;TGT199Ac;+ z@xE+=%{QHn7D1k*pDAf%(db7l_02)8%ZDNpeBH+lmBr;OJ05{6Yp`y!EMcc^?j|D z(GLs|3Zad_dAwIIy5Zl518#a#*)G<%@C0`V{0oM-4TVrF!p%VBvY>%pFm<5E$H!qa zIhaIf>||$=X1Yo-TDs^!{6~IA=3S6Fi;wf!%8E%hHJEF>n8~t+8Aqz7<+_bS5|M!md1GBGJuYIRjI~w?nByWQ9vZ6*VG1}TKRezVRNAA=VInCWF;Yqg%H21( zn*oyxBxSdTUu7mjwG0VfhFcH88C}}0wtO$dr^h9-8M;63Y>kxgAAIR+C}i}x1u4dW z#|5?V%LwuugEJyB+-)Bs(b!+Ot83L&zv{Y57t@$RGTNqv_3A@m}~WM#bz`pavx>htH;ffE?_lqZl{ zdX7w4#1DJGDJwSXLAhwFMZ5@x5cxNsoFsI1`rd|rv7t&W_`+b}ld~3^Daqr@DE!m+ zco!rKYv+e>kW=w=BCS{F{m&Mk{MYdA?^wNO+aRC7t>s%G`9D65RDlNFCl$ohUO<#G zkt7lwbGG%R!lDU=3BJ+Po_4bDy8zMwPv(6HI85a2{` zp-)ySKyLmU-35KJQxV(FT}{V@*N3rpXJjm&U+qg7zMYI_X6L)#fu15U=Phdj2JluWo-F7B-mH;$zZHu9 z89BjF$XV(y#WS#YCs+`&?8=3a>8I&m3V+w6ks7#~i6dao>(6%BowNV+A7 zlQ35hR5&t`-Kn&$|Pb2pC z4S6(rXw28bkWRLIjPJ$8xCt`sq<*Oz18EmwGKuVKe1GiPgtv)ZehGBI$Qy1IJDN2Y z6ns#UPFy`f4gnGvrQxfIF2O~dUg)2`%$#N*i)O|1GWhb-@c=`?hB*{zUK%B3@o>7}x(oIgAXX75liz?O<1A!iEY*V(>#Ga{I$^1;;X zmV1;#?!gJ*fZOO0Lr6-XP+u5H4*Km3l35nQMk-)$5gZ`y#mLSMacbgt#sLV@T_MDu zyUrPBT>pG0jwI8-$NT^qWph}0M@)nbUex;79y>t z5kpIFuyS#FfH8z$cl7gtxNdHd?JV635z-N~kieJU>q=K!E9*)}1BZUDj-fHMQo*#7 zmP@u~Z(|2)U}ZT6;=W&j42ks?;ZpD$Zvjz}OX;4-_ zVSF+sIWMY}7xDpu%5QL;^Yxmnq~vp%zwq^+vbfUP*EVHbZ%36@zLu*vb!*dB#W|A*7ONAgII~zIogP$A!JfC*B(wYx5zBiQF zLLKvQgYUXrgIbuHs$b!Ww8LLL`vH0}-s<-t6jfigPeGepD0>F<9@0>6GHIepz$BTUqUJ@{E%FDrSZCxci((EF|cf%X{B6wJ){^H*JjT3GYcEtdN$EhPDLdBf> z?6kJqe||8$RQWSLC&?Y#;AS~p&NQJw)P7cPq7e1Iw@>Ys$aH=Yhmgoj9X;O)2V(_O zRsR0Hkua)>NX`_l##sz*y4Edw-Zaiz&qhXU0u4^H(^;BE$q4t(auDtvR3vFUq%cM*OPx!)TiU*a1&cS!>0l~y3k@Td8F0UH-#3Xw(|K3W*X)7 z0rT0hvW&s6sT$i_D=;iN#~WrbQ4l1ZmNIUAU}IsJHmJE`bj)FdL~*C3oAyElq&UTR z;)xzzcV0fgX0c!7=>xCjr{4ixz>T3?R{7A4ZI#A`%K5Z1*@4qpo7)n)66aq$FT^`R z3&YV5+FkDY+8xndcCod3H6*M*Jd)E;*mJR^Gpn=l#qg^Ko2}@`l*l-rH%}%R zK>Y9Z`-z{E%;zF!*`HKtMXD`mNqeKpcX=Xqlc7TB-bbPv8>-q=(cBU%gDD;b|)9ZLRbXTWde}mVgVXUB6(_q(- z6--jfw*LGut=&yZ`gI8W2pU_(RwVuw3?~*~UjEEwu8^+Lh=k?rB$Vvsc z1qr6obf0xp);@*KWjLHX1<) zIoevUdX4Z;Y7!O2(ZsxC7K<;_VSfxjgt3gBr(~;2Z*)bLWuLl`yPQQtU&CE0DAijA z2u`3>Kbqcr{BPV@7Mg>QDETy1F*n#nV*fR6bNobA&hey}WBbauC(6eiWXG-r+$QMQ zh?zDIvtMHx3Xe;kz$Q%|>>~1?Na`x{BsL*Dd$-{5Qn;OpZc~@sT#rs)p%dPzY9nw3 zk6oP%qbgLCG&kLaw|{;Ujw9HkcTc-=%5`64BQeIGPvL6yk?t*I!~Mf-N2KozO&iS0 z_}K{$)fiA^F+}IAV;4*O;>@1hR4Lw~;Gq6u)on{locy(Rce%07bbHb%^Uj~r zZ5;eC{r*g6(c0?o<2^sfLAHH>n@+LsyrZC?uxIUu2~H7C zkM9CDqQJ_ahe=6l3o`50Bm@3WXuAqg3RCowlaGpQHH&0tHOQoCOzzNEnc`VU-h@eYojR>?Gp=TXd!ylV5|ZB$D(Y%S0K!_p~H=auZp? z^|GsvE9Zx5M29@Tj6F+A7(xYw`G5ZxDfsbMMW(%XhYoJDH|$YRzpHqWnST5lCxzVZ zM-9IWh5MZO!~ypC7cP?w_UxKi5)O`ZxWgj083D**DEwr6ogS&)w3K<3|Vq z+e7_uc%7kVt-X|UBhOS=_oO(>KT-O4pl!)vBFR3n?yDRV1Z08eBsy}w(1QvJgn?2pRnUC*_;<@lq0ybeWP}h$&*gnMcFUr>uSC^W;e!d7oo?kIi;EWC@$|BLg0GVN zr3N9$QVz8gR^y#bfg)nUQR#Ml$TN0c7@9cu$L&7oyY}kp+g|VeWKV#pN*I})?8@vP za^DR0zrJ2v6_xv=3&Ff84Wki_41Uu+PxRTC)sTrQMYk~rWRl~91dHr8!G}&AGx!U| zO~g9=lnM~#i^LlS3MFJW_*yyK}AM;~v z);%y+)NV{=5nlsBgmwb914xOt2R~2@ZXYCP?X|r!^z7Y>__*E{cA=$C#Y0^tA1>WD zCQIOQZEjzk^GKLX53%oy*;&_y_?wx1)69pn=M0YofK|hcJjnp@4$TW%@)`Z#4J;nk zy6QYx?`i^@Lsk}B>~($pqrZBAI#ThR6Y+sL%RVpx5V)s|b1y9A%YS$L05f_(i_b!u zpfdBDZW+ISIjn31ITW^K%0`@)saZALzM~DDh7**fXJP_QhhA89$g8yBKL%PM3<`$H zGE*ICY&pJl`9jMA5*p|Acv9<6VXjryAlvFFa>-dH?+--F+4E}pHE}xoIJ=65o6uw$ z_H&WZD7Zkp%n`h{(G_n9Axs=XO^B!E;RGWx#QYlMluGN-wm;M7deDEuwR+?IF*6|8 z4x|h-5GKGS zBPk<8t=@g;Oc@Xq&O$fAnY0em#=U^82k9nJfyi1$?w;NM##h|CRuP0FZ)lW5f5FO} zP5ock4x`>;%yax?Fx6L?wri#9q29DLG4i{nZ_y>0)#f9FWTZrO#}!(R%j>vqR|EN2 zi@p?Uj#=NoFD2y3PvTFww}1vB66Qc-({ay)4O%r5yQ%#7T%?E!X~9m1!WBjpdcYPl z+bEHD_KgHG3L0RlsgfTGKz1FB8~m2eXCqhVCRXJ)qvh!Po(ot>7zWAyXmJp+CHkIo}n z3)c>YRY`JrJoP9$E0-f0^=y){DUGB^q8WJ6qHUCqZP1>oD<89+yPaQXY7kh8APH>G znH0DeFS(+quJsJUIH7gy^{|1#TyX=*wi(e{_lWZ}s`?Q|RhvwIax{i2^fb@3cus3m zN?gYinp5@D9Uwk{Yg^pd@Z;SsIDVIp{J>7a0=dUSsohi<=8BB=&VXs@aF#qMaJ%h= zNZaiO2daD}_|KB9Wd<0wNuI3AkG(fm_bYWTM|;nH1wrUDcyqvQ_Gns@X>v*oG-3f? zQs}nWC> z+j9EtYlpuN{Mpdxq4IVGhQm3QF<3b)!9=sLZ*|Xr&G!4VGf?5?vrA5W%iMPxYk0^w zp63ab@bLW)qV#B6GWkd-&U`8`20e7Zf1<;;=b&^~Z!_WXhA=Njds!%)TGJAeX=&_X z)aP<5Dc4LHJVr(_YfoC&GcC9HCxS3!$~9^X!7DLhw52fa7?T2Ny8d6pr14D>`~|_U z!$-Op#;+>QY<(})B{i~&2>qd_TpnV?#T~q!PS`e!O<#c9bnenR{87&Hpe$C*w<>`} z0W|Nf-G`ULXR3+9)X{_3TNx>m-ljCYXV@+0VehzyDX(^hQlWIz&bECu_khq*J1L#7N@=g`&olCuCvcIDttY--1H=bfSdMr?5dPz zrLr@pvZrGx!;CG@YUa-u7Osx}oY!-2nOG^WC%8EyndGW{!w`Ch;l%C0wCT(j8E%fH?n;_K&BiSwDM{QJL)WP z>CP16yKVBw(U7m>A9nF$Id?zl1g3fC6W~$5!e02w3Q+f7>=E?-{jrBmr?($XdDgr0 z0=bT`>H0|f4|FfZxl>zWeZUw1%TLp+-#=V)Ti?1u?t0JvW}kqCQA{WSXU@&7YJOlK zuQfaI`2H&;wNwO?bD8Rze1QDmXnn0%ah{zxPstf3?u5#8Z&&yqdc!h7YnwHo?WgEp zHEOWOUrBe0PD+xmX-#U}el~@W2^c%n zeq-=x%!|9DLe657kPfW`IEXBfWDsINk!wfF-op@d>=`k<3rqX4pohA@P$R>)gAv$O zA;Zm=v%`P-s@-8wvTAF~cyZq;*OddW||J6B)~Z0`sLj_ zQeaa(;ft83oJPeOLn zeO})w7ReRek0;bxC|?^j=t=CdhHO1*(-QiJ-tw8jIM$u}|1%%6HN8S{%z#aXxIr+3 zHWZ0J^sjBdA=#!=&ri{Lc$Q#%B12f${N|B|g|fWrmszTKm^}6(PZ^hJwt8*ki7Mo7 zC^trs%(vi?JIrvfHz@i;=jlMfVK?BVr!nCFJUDNFPLJcMU%hlaN28lEpPL>c<1NG||O2n)aZ2wWmfFE6yfn%tu z{@NBaU!ddcYW-QR8@bLP^?jYunQ5**2OH8nLV3=abBfjV<9eH5HA}wX2g_+lysnvK+k<<|~&)JU>n5;V07#Pn`n%aah@VZSj5|5GU#N zIN5CK=7T4qJ&lU{`Nj;ciF)JC@hiH#^zDFQoE3H*uOhy;{w#6?Xt4@`%%S)FV1{6s z%f>1b0LbYDg0Ytd#O!cZnUhbGR&HYlo&`N{NZR7vNs2M+J9N|_kpAiq<=S)V2L>Y@ zvlK*rutLL?lA@Xr^gtYiM*&Ir3*@eK3csLesp8Ej60XGyFMdNu=PN*y@?)081#_^> zqlKh2HL;|iM^Uz=vv2LIlGY{%JWNPz+5 zP<`C$>D{TPLKwP`y~&f~IJFrpCl)Z^Tk-wrd)<#VW@aL!szZ%%9AcVA%F6hY?|CU| zwpLC!*;-QEANtDIwyT^aV|mjgN~lPWY4^%L`EDuZIxTLEP6kipOHLGUt{5W*eo;#cHI>TrZx~RZV8?B?Irj0l!?Wp*OW1^6te+zU!(ooo(e;@VBf-o>ch?^q8&kyfeK#!L!dBf8tXH zbz_BQm2|+}mkQ+8_l8wqCQKkz|0D3m7_icvi@Vv)HMT7XX@$lIZ@ktz_HFVMT@NUq z$1A}MtvZ9J}O{Q)ryJ;r|flL zy80)F@6O;41BbWu!|P|}mtm1ffEi}2TC4_1S%Z_GDbQ50ayD9iSzIU%kK5NP#TkjOdAo@;dBB}B56P=Z!&YZz{@p#RDJ z34f8L9ZnCANu`m>WtjVqX27iRU^$OD zVGUPBv(=F&^hlvcf>zY=e*A@>B@f~iCHsZ5wf#9qt9DJ3iYIERnfOed&k=>$rCUs| zw$lTYi1U@VSJr6`Q022ehF3hChr;kVBFh*o85w0%V6V>==Zo}AMsblRT`yNWSoZ)Q zW&Nr2dT~1vH1rMPI27_6w(U^$oEwHRFWa;jc9%cz8zU`F6@xhjwUmzIQXaHJ>9c+c z77hBl-|Kwc0w!)lf{anY(>sTUVLS3?`_5yBUi3QwZ!F3kr|%h>w;p?#pz8i+fN&&U z;sml1Mm1B$xEGuFGh%p2QEx^_B-!Nc< zlOo=iUQwbm6S&@0+;1cTDo>haTgZNZlGU1~eoBS)*Wn8Qhp4X@m##TH-EN>V|B$z%DzOBdUSZqUTWn?aWN2YMkxV#d~?LiuW8mKk21 zY4pW&T@Aj!#}&ZT+U-{-pWSu4se9P-_|up{tH+lNfQQyRAi73up^9R~s!CTQC3`ydANFNcN+Mi86Bl9&Q+_$X|=+UDeHI>;3wfFUQ6HJ%-n&wQOt7 zD?9tIxtJxc>hW$@XYctDoBe<2<{ONEA3zO00LnToU{8JIQq3JU81us4*6RU?f@ad9 ze>7__HAv0&*ksA;MoqF0uaB!>JoWRr;Xfbf4X-l7sGk$)Q?pSq^c7UVdIyAA5CA}K zxJOUnhmmYKe@x|R)1(qXnY(QOZpqS;lRyYDFRgb;Np1(ekIS2HT0L0eqHD!tvoZXy zziy?P=+Aj zB49HGYpsQjjp@AM6V_3>D0iqdPaIBbkd28agi3r34l-3d|4C0Xm~fk z_hS*bZe8hm#)D8>nw5DF^1pP1^c$`$GY}{lg)qry?*D+cHv24?qz=70J1bsUr|{|& zZP$Rh%jDCy4XXuX`KZHtF6H9i1%*}HC`)f?r+kei*9b%=iRxC>tiuUC`1-DSH-DbC zR+PoRy!tO1{OD@1Fw0Zb_OyHidgCdTKS3mlj6vhBu&OW17O-T-_jez5I{A)&&uOd@pGuZ5RjBlaI<*>tE*lPX0fTP!x zRg&&TBuJ10BJd8{D{5geJXg< zv zs-IGm9R}7vGXWRDj=Y{PYCu21nldFYvWA6)L4SgzSxLlAlL~KT1~w*+J+!C0`#7!~ zQe1Oq9!$IEl`;Pi**LQ!wibEJG89$?DuKT5z3)bS_TJuh<~Ji7)jM?T4cs#mG;shl zI+-5$;P+X!D?{)`N>B=)6+UI9Ku7~=c2fmUm`-ysFU2vqN(_gc9E-b$Ofsn%qCp2_tA*x(bF|aVAPmKsN=>V6O6PSd$n2 zluG~h`AV7u*?r=x4U^t=a^o^c2T>_9dd9|z0D@4y5j-+#zxExwBIdTGUA7Pc(SkV& zpAyZ@%~hb5Ij+Ug4ea?WkmLE)fSPB-vjWu#7a1N<%mk6juI~y6@buyK9FL)#dh_DC zr|B&s$j)JG&u>#yy-j-%PydP%>ge}(6(ucsP>n$)Xz9a*ykOX^37i4&KmJNbI#+-3 zha?}WP3&048iP@y5`ou@*Ta?H`obE^c99x-L<53RsJCz3lyu5+NVbgD`(pLg`okE} ztv3|*1a}5rq0-e8GC+fOJAPo%ji_7V*-FoDeAqE$tW{)4142VU*cRhH8D$L3gxf*K zVfzjl2?iF04vj6oP+eC8;}^;FN0$Pq{lQF6FVk*8$EVEJlwhS7uYhjv?(VZg8L#0R zo;NuC0$$3s5tNUTi?(0N%ALm&2B{oUS1&xiLk^S#GYAV|VuQ zp~dfHqByjIU?4s;pz6LC9`whRtj^_S@Lv7c4iNn0LB2*ree<)n3k$KfH)Jl6%SX<7 zYSk->t3)$nY|WNlwcXy~+|qD$)P|{3c-+%6?lj8no&)@m3T;oS^6(Z>{=$zMTa}R< z-wFmNi6AnZKGf-fdIos=@3Wqe9&k6h_T}xge9qV#sLEt6OqXOUb=4|NUpaJiX|8e^ zT%(HOM#9t0Elqbl|NH4e^TmCiA4IRyJiJWn$Py&@x67C}>vX-DT^pO{pi!s)Z5BIf4Z zrf9N65j`jQym%o7aBzWHqE3nC35Z-`6^bJ^wV(J^`|UvPy?qKS(E;3=&{-2%fZ*@m zJTFIiZ;XeZGYF|vA~d28<|U1Qt_aGY76MiD)2rKEv`qN3YdZVh;Z)GcRXwYURHxAb~I8!>F75VEvBcr>meu#wfG)a||~Z&+wmM}ei0rl#gSwt_iv z5uColn9~^tE4fOVAfSx>L{ccZJzzn|dC@?oU5dW#aW|9uooTnGAA2e3%G0FU^yBV1 zlPZ{=q1J?9x|*j^{Fy>%jQqz|o~%%;O(dXZC)1h0xvVA;Szt|@S&mGmw=a974H1-+ ze?k%Lz7q!x>RKDXZ$lqcgxlE~6hu4=w(x6#6sZJ59iX&LZ2<}*RUkyAyuZw|FgF(u zK6AtSwf_5iw<^V939oGCAE>`(VB0+CCV^yLZZVJkj*;hp0bk{TouLv;a6k3ko1u@XfL*kruz(UcP*pZTCSJ z_`ZF)83S1wXg>{Ob3(K942()FSA~oppPDLk#nb7 z={%xZc{>&jP~?t>g@woGeD8|?fs^IA@dXIlcdDS@vcVDXc8!hJU(G5sGXv16{4Wy; zlEKLLDklK4JF*4#7GC7YPP5oLu^ThyKa6(fa|8}E062rf<_ytme2?YT+DDF|To7K= z!x{s;j@H}UkGMS^8y4|V-LRc6+#^m8de87t%(9s1l&Em0>+C4axGKaI#BOFkxfImn$E$m^Oa^sy45D z^)C1?T`^D*9GSJ<;t|`MQe#P`E*VJSJ8_ zN()j7c#2XNkY_c{ETTgcVEYjj9w9F~d#eoqohR*ZFG?N07r;m507wBJws%o2ap}mD zE5n2*WXkvXAXUKo;-DcsRu(Xjml=|TIMF;d8v9*YRQ{F$SyMl^HgQD6th>SDbMY*( z5ZoA{==VZXlEGZT+%k4|upId^P!kYDHQ1p`bTsXT4}XlR4+?oA-O#o{otx`jYD3U0 zm$CmM856YM&Kp*|8{QwT54}De4`}=I1NVmyH_&k(vv1bk3Ke=l?|~`d0mA95ppv^S zY*K3-6P_iZjsHWk72ba(Nj_XS*#ytCl4T2!(P4w|FP6SoAhM7pz>Svs2e2s}Cyh{p zm{eCtH6H7rSMJJF<@lZ8y+JtYjq|$dFZgEM*~Dl4PYB#7u~Rm;BM0wQBZW;WT zYwr})*g3#J9g)o1tZ*)Hy6yflWO-lus&qAV{x;3%(~19p9~%Ha=+=%FhYpJ9DSC?a z5Fz6a_RXCPzJFBS@G^FSFlXSX=f(u#eD|(xmuLVHKENrkRW?u0oZDidA`f}T(=?uo zu%=A=T9`%s{8Sc5#1o-bWS#@z~~53^59lD1u%=jU+^ys0o|?4A^qA5 zUjSG$j=@DwZ7o4GC(`#fNr4^4@deU7^tDvj~HeSi(6PMs3k+J&9BOMZE5= zOuO(|PA1u;d4@iEu*=%tOxd6?D+{cGpwt_q;bQo4D#=!d{Gx_ztrg{VR7PTwu)x+X zVOd)8&+lXeI^K2BTFEcUtkJ2q&v(f-mZjhYQ>p54%=b81k-E<%8CnpEOo9O~3ohxD z9HmmXMVhF}2iT!*7!ZsQq>g0kLp+OXwc^(;$YKGzm8*_e@}ZUD(`Nu|1zxOUP@bN= zFijR-+8rAIF5@i;cU4qg)lal#A{QQe@90yHu)K%Q+CcV_n;_iqp@C!V)B!Y`bwe{} z7I=7!ioP9C1-k^d`$e>HIAmge6MpQmIZDG@^ZLwgeII7}F896skB&*wCpbcN*D04r z%=6_cT!C-8)-AlWk~MY-(-!KDzxvN`j6OD(s`Mo4tQ^kcm>mG>ITkL2z$6GH|005& z7p~K@EIa$kwX&N!IyyIQ?2Xhg1?R;%=Th-)-!Qhfg~`0n;Ndjt*wY`xYlf}0Oe}@P zb;Xmy5)!($`L5Mc^7@UM4So1Ua@YRJxTxR>y)mXmr^IBklGrZhspgI|F4`Fgp1fTT z#c6lRCDDCNz(hmAl~WT9Q+FmHk3?j&Y=5sRYI)7izVr2OpsvN9=D_~}1e$AwnHq6*8X>s4oD%=eRhh zq2Z-neFECRWwsK|r_c95>Vx%gk;lV2xtg2a6W-|sKkI!DPzJ<)wX3@i$PPfMupW7) zcudYm1ZqZe?(w|oXGuGnN_--4r}E>k4~Z8Pj*dNOrK>1Q&sEl>hnrlHe;2>e^|2)5 zj5qBS%6qX<;i=+vz>Jm$hEbb7omUZnMUmGV8+v2@}8dc_emED0MxOsxTQ~tG6^oFE;jOrYD=6l(~14 z$BzKj@W0C`!oa~wD=Knt3 z_bwRnm9vb}tm1nDzk%ZfDw26rwvG>ylbPJ;(tJ#V9LGd^Qa-{0RwzQD#=nIT{es=p z$ja_p6_6-w2lRCY5A>~5EUTU@ay;h{*!d*)?b}Ucv<)tuk-vk1Ew>W5dlyb6>@yVO z!3D>HmiS8P228=&uO~SE@!nR5(4zG(L0lod^ab0_tc& zTaZTrovgYArvk7Qc&Bq=zCKm;hEK{l=doCUKrg_1;LJEnK7W;W&9kh$9CN{|ck|N~ zxJK_4JW!9;hb{9SpfE(sSd@dF305Yso7lsBt9?TK`N19<+zS8?S&zUHbKG<7DF8sv zo{_YQgs+l$wUnpbb@%WKFLk=^EG^L(Nw3-XZZvONN;(Ob=s?$QSDm?a5{_3S>Gw*p zji;y4?`4X*3+lS))`WS)xv!`SGLshk*N-1iPyv(w?4&#g43S9wd87_$uYe*Y25c9- zWhEuXbJztYs*t6f*z^1-hV6(P8NR8&p_^E{u}wNZ>Jf>X*=M6+I{^mxN_g=%J@`!? z0|#>N?p^3FMpsC&xep#hDa(L!x`kY`8i?cR-rml$B@WU|Y+>;|Yxl%1b)B1zal8Ws z_*Ry17w1Btvr*@72x`oS!3p4b!5qK{g1Ho2$7?6|?3y5mZJ^~$xhKCI^!il5(%|=gfR1``z$T5D_mnwdJ z1+dC~4BJB(xv*KvdInS~)5&!P>Jjn0O)JdeqAGe5|gjPO*L`%W+Kk;i?PFzVuIwS%7zlQFPgEx@$ric$Xhez}G;_O7$m^ z-28hE;A46548 zK@kYV#S8OAvWH6u`mWSMpoE>8vE7f`qaF`lwA|*xb*?ckT8Ywg#OoBA6>c9lIIiaz zM%~=J9FTs=)-_|Nr_S>=eb3{>5{t;vr%5vG$s^)$Srx?z&K3q6U5Q?Jqu>f?#sCfA zf9$~}%$|lZ*5KDwgromdK$rzHc8m%@L8S;PE=WVRcv)bD1<$L_UQq)$@~X;JzZIAC zKDWqW;s`@8ZFor*D-Bkxq;QkKreIF4-hElv+rm6yCv*QA0XvVHjW#W+i)+ z5cm)5!s#(0LXqP_PVNcR@!|jZ6^tDyIuRnHWCa-Z{w|y~08A(v;;P)DDE{a1oNTnK z&pIW~&OYhVL{xJ@>q(l%kY{dkJeg`%T2h}kT~hLW8T=~%P&B}ub~aDX$Fyy%4sC*o zb#Y}$g(jTuT^Sg_>BgvX4)K5sjBXpSkf8BN^F+x3$58W++(fsVgL#hJL?(8WQj-!!s#7{{35O%`zp6YR0v+R@_jQ zo%>Qx>Kz|wIReL?5FIRi~ z$H%NdHTHtsH<4{pz)w+&mnrfxxGt8$k`)BR;-n^KYe4}__d;8Be1YC>M^K{xWje1F zK~8!kyaoR$7-kPG8rYU9u0el*`P9|`epIdyoT2Wjhm_M{*2qqokK{Z-F1`qJ7 z%u}vE*Ozs-akxZB_9GcT5jJ<1-Gun@XrXP;#ZR%y*kOg2l&vcA(1e$pp-B zX+L2^6im^f^CcRbPopdzq57Q;DzCxQZUB#Xm0KW?^M)r5FJ?UD;?peLO(KmEENDZf`4XTpyQ9BWAx?$)c^4X0q70v#~K(t4028Vtk3hZ z_iei5o&DUq8lqWB6J}li?uYtIn4Se}q{0#HJ%fav63lJmJ%#y)61pA=4HNjzL8KQx zbR}u1Jhw+sR5^FK`1D5*qF+5nrP;A#^I7mgBpnurNBE=zz`Z$W2mcooH@PX z8XSWbQ?ppHLy{I#-%hfm`mTb?`oZy+%E?b@a+cvuns5muv`h<%!z`4y1IFZzVuB%7 zceS|o^zkv5Y^@0$`Q&(HaJ1#J{`43l-Ap-?x-X2@fgC1W_Y8l16*_qS-GutwRwgt8 zZ4^~({jj^sfwM~h2>_sdDTk4wR-UH9g&t+~ z33Wji>sXLh?g8)Zp~Dl{tTEkQkMgz#sn(S)Bq2^;Pskv=@-43QwW}~8ntK1U+<(T8 zix{Bj0Bzxtv_H$Wf-*-H+^yW|=K8!ENtua@i*u7sNekG({i$Y`0o=FEzZ|T+F!HGx zf#J@UdByuBZc3Vb3LI5(rEk5(@l{aK#qPX0dDr>pa~`3Y{YJOmCdXgUw9mwx!<_p? zAHYTnxWA|mQrEFM?$>lzyaqX;IrLp&fk34y?77nUw5bH0C4y?y?aS!Qi0Qxu?8xI# z62DiA71SexFe$Pq0+kNaie|C1*qfVl!`)c_!UWvUy=@41!q9Vi2dL20$yh4X6@K>l z2u(Y2vsXq70Ite8yPVWKO;#X&%G&il=&_Qm&j?^Af<(Wn$hK|cxc zzHepO5HEl;J0!>}Gz@p|pKN(DIUZ!{4wOak@ZzuVhD}96Osb$D#u4Ulg7;r28Cu1? z0U5#d-~nL$=@U;A%+qxFv%d1>#U43w5B#gE3$1?|BPH4=2Jm)^==~ZXwJY`}YbBVV zuDXDA62I=5cZCQ^5!e4qJeAAA80M`_yE|H=^m+IUkKoeXWR&T6xaMh^2~>Mtcvzrd zk*CprkEmtdZLvPYYuUdrN!~~H{S~=P_zDF=Hx_Ay#`A;PLvUzxJ<7_;63o7d0B0t; z@8jb})J(?`_aPld0i17;{RO8IC~)f?H&aqv9XLGuVfqS~XP_V=+yf(+;gi-!gbK3U z$yn+>3!C=X6BVQw*U#yCe0=UqkGuNQ9VK~s{_$3QIix!`M{>W!Un7sf1m|t!(o|dg zXF2eH{%bxNfVOrzfddmQJvp2Q^VAMiVE#uru)@-JqccHw)6`l2>39KK=8B8ol^woX zT4H6kryY4r``tMzJR~{Vt*LosetnnptNK2*c^tGAOL#^#H^uG3IN~Q^=Hez4tgoPb zV)K;;HfZ%lS6@E~C4mFzyi!7RKk=~7s+y*6G`857X3Q=ERb_!oMn z5*UOVMc)`iw9-I@IWS;}L)W6<8=Nd)bSUcPvvk*i>l1r;P!R2jJAUdo2KtJ}Loh#z z9A(Cig6Ylq8m_;{(c-<2$4Pl#k)FBYqvz`%IwWP2Mw^C5I}vYHdFu@V+g0@LUji`4 z_OlCw?NOUZ;4|Bh z8=0+vIAF&lNZ@GNk3uQ=Pn1GPz%ce1UlsshGa^1BA|mj>iJ@n_=5MpIOv*e?kks}J zV5I`zx~b35^AlnU5IxY+^n>DN;und5l`YR1IIf^>wjVq zyz_O)Hz&OlHoEM+5`NQl$ksc&!@ThY{jm$gdk$_)$P@K;j2x4q*OGsC_p5>u3?v4S z0VTkU!JYqE`QgR3CS=#ATsEU``#VdxNTUpFaZzoM2!@Ds4B^))x4dxiL#^wK|5M5a zmIT@1rqR~cudcHv6>Z`_ib)&REFl^x}y6(7h#a@SA!6k z3d9cMVzew^+c)^{P*w#76VDzi3pxI7Qw%JQ=7W|JfP44u-76k@q-q6d#g4OLPMfd^ zD1ul8;;|q0<5{7E_c-YrH*Tmoqf>dFd6vttTq(ae^lI6oxiIVU83SknDX1W6p9>TP zB@}};9-{3oRPZ~D7(_GBCV=d`VFNa@ytWV1g-(_@S`jT};s)>o=qd55kDb~Qm5Qut z{nZ0_z@ZbWi5F=3!iV#+S9W<*)r}G=Y=^G%xu6kRAXQ&4t$YV-A zXVABv9^dczozMu^o1VQ>l6-jqSR@;e)fBN9gBr?}mbl~vH)25I2w}hTjofb*$B!C} z5j4na^Tpqxs~oKoIqVJ40p({)2R$ucQoiSMIE8XQ8fvBV^NkSC|Jp=a?|AFp4IU$) zn{><+G;4sQ3_%n zx~tR$A9X*qRDHO5$~Xx!60e{}n^5B58g*wBbw7}0atYLV^@}%;3P?1};})H)&yC$; z#Hgs?b5*D3Jfc8NL%V8s0IKFzm49cl^N)=(+A<2zry*C{7l~8kQNy&g=>NXLtq@5~R8W z3$qSX==D#nUk0yR7&XI88Cj=Qd-B(80G5Y)+1;+=bRj)`{U8A)9`^hO;4uRP&b+XM`F(RNWoP)H zj=h*t@j}Ad#d+TLW}Cr;NrXZ`ejzDP6L+noWM#dJNE@d6F&GXLc^oIuPKbYR^7dyr zp@W5@;QH$^+H#GXBnKq@g0^|*IkxpZ@KJ9d6!btaja$;rGm-(U`*q8xKFkc3;3*LtI5E#s%%b)G@BE9S0`ZD7>6=r3e?B_S&D%n0o%3oi5+JkWq^W^YkiACVrT-JkX+_9_>sxpS$z#yEi zC4Hjjp?d)Sd0Y7fVN!4=G=^XaK+a{DCDu_pI6M--Ijiv^^~MAtAP}C?$z}R$t%EVI zZY%2%iNuwx=$$8Wy9Jj?mD=&)hgs?6? z-Q4{j>eYRfogE0ny4}qJ1IMUdnnoL$;P3tO&el_S$Cae9yrlDCI1omr=q;wI@`v2- z?3IEa9Kry#7+5X_fzKVL2Jz!s3AQkB_?u?v3hfdo$P7Zj-?sjE9}R-}%3mMq6`n&v z-*Pglq&9N}9B~ubd@_u4tvp^pXz}5R%fe?059NQUa$x1SLvYfb5^)=SD(j}re+@Sl zj&1d-BMu4P!KpUiE+>}D{hBdx&BFJMdA=(-)R7|1DBFXFZbW7MzMNcxzBSs>uLb)Zy>{mdtZS2FW3o$Md#zLjU(=X9T}bb82) zRt_Euwt)LH9TXGA5tF&)TCWyTiBjeQKDlYnPD)yu zL)S1QbGSLs3HleR*_K&D2;8RmR@)-=<@Jq?jm^!aA7mM2M2>hozdN7nJ~JIZOqfr8 z+tgS~+oDzVw5E9E!9$(7E)Q2nZ=A*y!3kzX8ZEzG1re{p$LCSx>fu?|N4%c~M=JMj z=|QwkI`-q7jPMQT)dotRI|TZ&w}J%nA@WDPm)5NpXNnxD2R%P6JH5UVI|q%^F=HV| z3i#!>1;@`LXPqqj`FIV+bY$<8DKe!-utlmH6W3}O{MmUZHykU)aq7)))!<)$Ai`EM zwz2bAE`b1XJ0|9g`?RUy=399{O3i@5A@%bXS>3sd~R* z#v>JRoa!{CyVPR^F>@v(00OpFO^9FLcI064F08(Rfjkh?v+NGFx?Wa>te%YU^dLeq zIq$%|_uimFaS@@5*zo(eZ%;MOa+X>*Hw3?DSJQRoZ=!GqLOA{ODM}$&V$+#QQo;_m zs%LiKfTjEx*hP4tYS(^PQSiXtTc@a*R@VG3pl`|Ue9uY_=f9PZPAq+Li(u*?=Qd?% zeBLn%M(Dv%um{kmmvNckA}N}9E}|E61PS-#vCvYwt$_ERA{aKlr+RwB>3+dfV$aHx z9;32s(2IG-UUv~ofef&dkaAhyl@Sy;0*7pfoRBn>28`LEB|$1vEsI6h%q+-&s#tx} z`;X?EMRffO2xSD#su}hQ$paH`9v=V!704&|R&v+I*AsES_2bS(A=gA|OQquy?X_L; z7o_Qxc1OFvV|rG}?rH0cT)nmyKjH(wAzI1Eg@pJz!gfOkJG)IyvzwurJSw|9u!X zn4R5?%G}CUn@Z4lEOQSGQ536%>Apj_jH-M`YinyAEa$KSJmwu37uV|z?hML+L9_|(0_`L>d-6RdEnPY3o;UM@s}b;-(xowjW1m1Cjf&zi$`5*a z8eqtyXTkCwzki&*d+ig@F)%jb%N+}-7NYVUzh*X(;C_UQUMq)*%C@ZuRk~i=KzZ_5 zo)x-Sm&dA6V-DO{-SwQD9G;O|1^hlbgFkYwg!1}(+Zrz#nLf>K>UEXz>RZUtEmOqo zuOLa+X2tWr7;LUT@^w(Y+_GxRVhBs{vXm%)uw3cp)0=MmsIB}3t6{|IceBhzQ6x8Y z@2DcI3Xkh&iv;^tU>_R@3~~sbe~hs6N?%c|uY12qV%KJV_0`R%29Hi{<40PzUZ|TG zY~9-AQNjQ4#T%?(F{>kw;|q=;VoI9jV~(xk$i6S0Lo5vfJtZTQ%(KC|$q}*@ic5mg ziv{wCM3;m9F__54yhu;CLBmJ?moid&q*dKm>qs=1Sd~m4@aEl<_&d z6fum>%+Dk!OcxQd9tkR025pX#fK^DJJ%8KfrxIXS9*_^C3NIjZw_HA9^IqQ{e^vQt_j~l1RE;ih#wt_8 zTsMfskGo9G&4VQ>`%2v#?n`fT1Hm*QIu=>x>^0ldd8p!W17ZE`i62l;%oYw|GHc~xH0U37}Ri1oJMCk9veTt85u z<@J1N=2tYvgR3{_r6(7^!*BYFS&v zVmdS?qoB>w-P2>u8}pmec?s*zJ|%bwTkfu??C@NEdoatY>{hVKzmPu{dwK`;(4)@E za_*vQjy%aMi`C^27o>8Ok0%Q}+W=kZ4t6xf_cZ|G^RjjSrISb*;4flueOMg-Dm{y19s`sL)mRxM&#ZTAA0#$hhWJ zX1M0X&9%v1zjOP1f8Reok9s`%a>x61Ugtd5@n>!&4Y@(?)+yYzvYDXf(OMvIAZ!%3 zF8^f3#l%EivbA~Siih8sHpl9sRh>l?s;#wvtfutTJR>VXZh^LkgR~N{!!i8+P4SD4 zfpYX_mI27f8{~64q1Bd0KGnHSV}ZpG$TXiK>aZZ^6RwV{cq`Sz}9O4g=$y_T`v#y5# zK)YiYpm~`7j$b6a@ah52v!asH!WHGE(E?g{U8Ebb42I^~+G_`ePP^2qOcT&yKvv_3ji3}m*S#A?+emM4nP8ivzhmu}eikw#-CHTqw# zCEYSMrfsEuYWaRt>mFam^2d5tYWSqtSvomQcQ2G6+Q=5^J0jy{)ayrE^<+JjE8TnOa;mM{-NWps_KY zwP=*QG6jvYcv9{;qKt}Ah1RVGjk*1F7YjAVl7sGhnj6hbHh5 zy4tG4Grx0x?_=I`iy*6l=6Ly3$D$`n=J6LVOE-nVj0?MnytqZ!ZQv}{65>9`07+YE z@ooBBxNa(NSY=r0f&G$|SyO~&zIuue_7|-h-96LfNLoN9L0@= z3NBu33Kt8Ho50Gg}L2w?FGPh$cSy?t^8R?ZsE6+j&vb zSw9tZ+6o-Y9pF3X%`kKP--v-(oa_#J`1SZj)W^0M%U+4;rjR&^mSOhl4i#yS{*e{V zwx?7^5%YWMd`ei0jK!K%K8WRrl4$wtkVX(n-J;oeYi3yWoej!CwCLTc%FqkHePB{e za5<=`=B`~`aiVX^;tjDMYw<;Bl!tjsGkgxrx0sA3Y%=_?)<{c>f)i1jlz+(iD$@5u z=;2o1K7PFGs~0d;KKHsA2sQ|~f!QP6JwO1WF`GP54iM9n1z1$_qe#c2XBy|UZW$UH z-ny0aqR@d$%eIhv$e7MZS82Em-;380sZBIlyvuL=58~+80(9GF7xwbrLDvv@ceDA(#mUC4{rw zQ#3(HN?e>@*>waAND#{|4e{FNS`n9T8yS5!s2GF^E+tgz&K|Fe4?V8=Tjr~L72EY2 z(^v4Sfq6zWP)n+`4YZtP`~o@INddsz)P-vD z>QFW(2EmzPp)2ts@57o(D^f=y#KV09=cW8zDmFG{5fdJ<+`s3(a)Qwct2lR{TqTV< z*_4qrU0X1oVL;fJJNAiixs$VLjbXm)TBn~!cSe?=44&h#NiKel7YV+@_dFV_utkFc zo6gG0&QI-*DUEe$pfo|=|5-&Pd3jKJa73}4ICj1A>NV7jWTUW;&tEH(%%6+zyX~p# z{iWy}d;=*~ed&#RFUKL_lr#&HJxg!G`Q%_NTkh0nR36rufyH^=J)pCS zQFWKREz2QZ`KOEUV)KP@1>)(+J0|-2enL__vq59tHg~Hs>2qIwARFk|+Ykld{eg=< zNdC&jrK5;6Y7G{Yprt%B{S?}bFRXePN;$f(b{(K@ON{4xS_h=11K(R0R__fMLhD+H z;ebK6sQ&^PG@0;JsPBJgB|xQbpt}q6joa&6U#G12YIH#2IFffFt5=6w^pwK&)@%a+dI~UZZK7`4971!n2mb)M~E9R&&-rC zKNmUQLk84e8I>lnHa@S

+9jJ9zv(9V-f3Ou?j!U*e0SLy}H z;JB;xMpgt0NhzsTNpV}rC?Rejo!AyPu;T3J1uz*v>k8n4Ih?)wEp*e<=*DET|7z66$(3ecLldtbW}Vn z4Mb=A61lF`9+KhCs1)WrJ$M0$=m;d=Ealv{pJW)f_-`GYq*`>dAiXTvDI{)_lBXj5 zD}he7RWHn30$BC6k8xI~nv-+t|eahjkR0r^X|B znsj{d508{(Q$|C!ss-088fk$nrvFj_v0EH%Q@Q8haQ&uE2ub+;q1iH)HLZ{1t~cS% zn9YZ@xPO;apC8i3T5FZ7G>2?wy$#05tw+kjh18OdEL=hp6s#+Jp#&=`DmtQ3D`|(x z=$TD`g`qxhXb*^e!$e8a3SEt>C-ZEBwBb z&icm#u3W+8o_MM@W=XZ)rVUKWiYr{%MQy;#-8baQkcD{&vVhdTfr*Oh zHO_RH?N;8rL~np~ikdwMYT>*c(%sV(}F$PY3&%yn+HO7vj9*y1{`D)tT*u zvIS1}?Q{(SHEI<19;Cm?9__5d)bMljm-y0k4x#?sYKAEm=eG}QI60?w&u3$_t|=r; zU8lL^q)X%!I*it{fixLdnS%t#uONjQ(&Mkin&N3>yv2jJf(hTp$O>Lh;L&asBuBgy z(}ib8xn#FX$cJv4+Dl6d3yA?uJaYtMtTF5oxu-RBYLQXT-Yp@K6c77jZzd|P#M4$R zDsq76Ujm2>FgomD#g1U##M{Tnb1NWW+Ff1=Ma6rzk|S>is=U%&kH35lO3GpKT-8e( zd<5z4WN+aNL4+IEo)hB6P31?cdI7Pk&}R8lYCx1&yHND_@W z0+5pHNp)q(@*08;cu@fi4$=p+8_Y~iD{KS#$+~;WUv;Ue3K+AfX{%uEfJEbvq&4^s z9C!9D8DRGPy#-umunZ+mIDT*m%1uRQt%OoWWwYWP-I7709pyjZlKW1uFWlgigsYO3 zec$XE3+`cKcZ&(vZmF@9f8uYHd-}CXTur0bdf4|}(KjL=OI>3~U4KpE9^^W)^GSGq zyi2tBY3fKV&Y>wRn-T`;DU|O?F zOaAoBG`R-OyMgM{t)(Z%*h^QN$rcOdMAo)3(=G-PaNJYXb9H-oT>=AVejoAhfioFV zyiMvXycJ|jUqzc(#SeEB?cdh|Q$xW(%xxZ9<;*nqnR)I=(k>N@d8aX5awJn&P^qnX zZ#xca6XL!wkrOE9$}l^gT$=k}&o%jdj|(QO;2w*U1+1IEPbO;O7xG9ZQ4x#^mn|ec zzW%&eB+aNhbd(fM*SCN5a;0NaMcOi{xbvhiC5&cw=-3;YPrpea3Pp(9hYd3CkGcKN zf0$>C`!(n!g*alxEaN9Cq^p(v&DT0cA-A1~>5myDF<&XgRgF<8!?;*W3UwbN7dZ%M z-YY#}*fN}8Xi-h{O0$22wLUY`BvV_mvPez;A7^mai!eq`ONjeJgwDSrHZJmTwX$XW zJmtQHQHFCG{M^Uzb3qIADaFq6VJ^!(szQ}=sdgIs5&9|Vn{BOwD$+7XN6(WxO64-M z1YU|a|N*et{V=41Y;_bzm0S)ozM|L|kcQn~5Z^d@)ORm!obU-&t+sBG8 zcHH~^$?`;oYfHS~g2bP};`G%V-J51_v0}cJe>|zLU^Y7ZeoSWoo7JRbu7rb&PcEppyiU`IC;gQOucCq@{_8@?@F< z)JD-!9PAOjg+J2pe1ma#*#Bm<$|rLpSDJzJv$2!FDnyjZrEZh#Lwi?&oMArqJXwcrnFR3>*RYcrlH)Lx95@vA1Q($b_Psh`F7& zv_fixSNaoo53iUR?4j&EV6?nI*+@x7y^Rw}5nKq&iQOfor)+rwDabG`T>ATm%v+Ti3yTF21;ER{P`}tGV71!|wzGSe*Cw*=@t;Ei6EYlmT`5rg zhvh35Aj^d&!!pS~iWAUdsR1*jC3Uui}N0xJAuxO&f*b&$OL zgq*Kj9k}A%WM% zn@Lf-B*GHy-yy_rTrV}Ry``mGkKZ%>d(Q@AQeR1Xp8EsJ=VdraLx0--9ohVJ{HLA` z9M&P2tW3$cFh_Ks7+dcslAO>iNQk=~X{(I7xun`QiT=XMD%prE%wA&&{p;hc+IH)qOBvCDbVmW-+0+a-Nn@i3zR>M*4Ea7;R!BQH5iWo9Sb{4E^0P; z^hiRxx@`?Uc5p{eLEKk+)gLzF5o7`^xJLFXY9^e7y+!;+w{D%A%|NR~{5s|?PF%-~ z-aK@yoKze%zw@VrFbMSvL84JZ{j3|<4EGBZD{Y54hB?QW)YH`Huux@54D3 z?+qo8Z%HDVjS==8Ym&Bq-_mGVa(n{q2h7F5&_IM=HAqFEIE50`f@viPnBW2Ipo3f` z#^nh}gWhYfC&D(>A`E?u62IPbKBDf7DI)b)VTCnTwpK2WjBdMg|IDX6w6LXxc*EI= zB9Bb$mgpYA_NN2_!?I3=Omw#iqpo#iA`J zu+nhf0z}H)8wD=-g3ED&eaF+;=Rz>9v40xcTMqGemz@P29UMF$6ZUGw#0eSRY0yp}pj?I_lIufNQ6O2RuE$+eJ&+rxH*xC!Hyi7$&czM@e^-4Sp& zlr{DbIwGPx>;VKT>4fW!PTro6D%T^JNz&shuj%wX-2k&%RRCAxlM4+zE%!BDgv`!6 zkW9yD<+}qBWY*`@jnZ7VB_y=cZ|0hmC0Tp#m#VmH&Y30LNcGgJE)v_M?47@AVf2MQ z=2FTv%Kevkp9H{#yTh1{REibahN5O$TI>*#dp$ljUcXd@9)E}|v%8yS9;MJVOg(${ zEPE37K6`$?gk1Q2M^vhhxVoxks0;qq=S{P3a6R**h@`?tvmcEqA)x8#SUf4o8gZ7V_irX=i$#t9T-v97)7;kSBpC$4#?E@ ziOnm%779qH(9-djBXAY!#)D!hH+4Cv*r+2Ks@|jf~NF^cE?DZWo~r zm+~pRVY%EdT7~4Y#3(dREzNFUC zN^j6v4aIjuG($Yk8NL5tzQUl zAA)4NLF8yit~yYOt}!&yt^IB*>aA%L8s}|Lwld5)yaENehm1BK0Eb%YuKK(v=Dzp^ zS?gj*=dd<4g_W6)T7QRdky^zpi=V8@?k6KSV8w=o=IyTcaOXlgmg@MiN$8Z2ATfm& z!FsXkEx92jMTBuVkj<$GbRkaJkXimwxwd+>VwUV+eNb~DRi9f~@b|3)Be=Nh-|lcZ zXx>=hw!?jXs6O3!wHUSK9ERQLF^(Y-y~1tUnUc>r*R&o&^) zJF^>a--bN4BFGOtmu^x;}q(4s@-HzkgoNtqyvQwER7q@cKca+YaZO z^tnrXLu1DFM?Z|`1jqfW(l$(;?-pz^q*0xKK!M=}6}TRj2 zU3A1(m9bA>4?cEL+~#>4f_{85yBWGxf3`^+#DoR!AtpLzo8_D7kC0R}BtLfE#?xA= z7C*l{VqV~x_l59$W}wf>8P4Q5OTj;3d(oq!&vtnCE&{c z29z3D-a+IM)HKqLAk$$-bv`^YtqlO(gaH)q1i3_iYSHd~mkDYfp>`w9X&7SDQDwZS zd3Z?(L1WsL8iau-Et)@GGW_`HRi7=~+1R)X?(qznmVCh@XcX}4A|B^XP$UNa5LH1x zBBQ2|`h;G`j%<9d%l9dN#Qzp%+$rNvtQSd6%ka%>%1?uo@x=snDrZ zG*>*h)0XBHqmG+>527LWcRtuif)H#5J>krf@|=cTgNH#xEBb;sg!qQ-A*01FHCLmL zxYBC5_I}KDOoZMz+W`%%ShHTB|CK4;8W!U>bMgn>^#yrPPM90Tn?<==bdr8+pxn+K zt)bo*h<=jUcoXq7=2B<85p(=D35VAV9O5GNjAdVwDxBmW+qh62>~z@oO%N8DciMWd zZJ@LBKYZAM5K?x%e@Br|nk!d5U#7pO=d5U_AqLS1{XSbh`Zu(B{Uz=P5J170<=o$6 z1PlQ5CsyU)$rnL5J35*|uMNmAuuR$N=_{$L@_#$Pb4Nzatpo}^_Era7<|M`3S#}ZF z>sCO%_dRR+GJ!^wcNFK+;?ycHj`EC5w687tc4djg>_lUs6P1C%>r3O84!=DFA-bND z=a&vp;vr~t8*}OHwh*`Z;wa&+D_=CcC}&4Jlj5Jwf$ThG3!R<6-6wzT7!!#fECiWF zqPo}Pj8A|li^M;600^Arz26vUjE|5jQS%iGJLNs*?;k{PUJlsDl;pu9$7D7jKPYwM z47`CLnqS*+th%IE;=9xFljy26OQ%a4^(l4TS%M31pbL30x#JU#SLR_(u2j0W*l~}Nh?)O0 ze+>ev2jU3b9dkhRi!Sc=d$y{}H=GV>g_Y?kqd+ED7P$&npE9>o({35QW)D(xzXzMX^>P>i0+mu8=%3wL)Q<`Zu5 zaTg{xM;2~)(eIkYMSYsLy$Gcf-~Z{l*DoKNig)4Mr6SW8b6AS|3>|*)jhlXqTU{VZ z!b})Mk8r1>XfQ4?u|@=)C)%C;%tNe6k?vlTVbB8A6m9mFZvqPqsQ2k3WYsWrRL9pq zLGrlFvEedhNgmBW?$qLOovw3p`_vc>8@Las(6HsD=^bp!h`5JF9qP z=4wZCaeDhxgctmckz-J*GPy}!(rKGrta4Sb4O&YA|FY`oa?gupvF?$g^AyV*4|}8TNGMR_#`a!_ z-HHp8GnVT4>J)EHXx51Ir6 z5_On*{P=N}lI&;B=|I)gmCgghnylbud)3`h3hxg|Ei_1A9CFc>eJn5%QL<~AB30je z!1JiM%q1pp;^;Wb?aY7}DA;aP>J)(I%5%TzmF#mm{=&~;(&ZU>Z%tN%I>X1BL;J<% zWit3I>n!R#T-EHZWRz%YvhJoJ=qB?|%H#Vm{5(rvzo_ra^56bW_yn z%*L}p#LIua)y4r0>NKG=bzx_M{Y2lsX#0&;h&|RY#UbhyKJL?@I&I4 z?tB-YUc^tLA!dGbh3>41!&JJZ=h4NPQLQh|> z#eZ{R)(tUu*G(X^dtPeeZ2+erLw7F|8s=N=08L2^QKN7bJ^fk{N(D4h{wCdNUTGI* zzAYy>r9MLgxg&#Rv?sOAjezF%{iD_)Bun4Gk)KCCKYH}2h8-Hd3S|4;qbK_F9FDvR z2-z83dkI!@!CoM6d~n9HRu0PUV}Mi+FdCsV9MkA3U;cfAf@b|inAEg12au;qw}R*6 z)ghPN`@BHjh~QP)13*Ac8M6UQGuZq^`BH~gOO_D~cCDP_&bN|1yKV4UIR5BbB?J9! z8yC!($JpNRu|E!cbpP!?s*R^#(Gvd$B5OOKQBPN@gtWZi*4()s|8oPh7C*<}O!ZF_S2~qet4>nIfEdAl`DP>K zbU~s86c5)5RzVD46CE~m;NCXisgOMTYYSkz5eKp?63-SKfDwcMp{yiTj^O7uX!5h% znrQ$$kh`YkTPf$Rn8K5rU42AYuq}vHjUjy3w6xCYG0%hNoFSfPW96zjBT!;K7DhSM zSD`kkO-s3`7d}aLP789)NQld5bd0co$vPAP#LFPb2r5iP-4@Nf^^GzGboREJjaFOF z4gtm7G9VZgpsX#^*xXY&2R9PL^s@=|03!tHCs)NQ=Rn=Cym-ZhJY;PSNzKgkooq7b zaAVx;GHtF@RVRRJ{e#j0{X*W^*dJODqXg+H_hS0$58}aPv<5$qju<*PQ@XG}pvD)H z1^{%v_VD2ZnDSNJ!@X`PO9mC`JMn*_nh$_5`hNYfd zS^?`k|MBD2-0;<@BKKdb5(HCIC1}V#A2pS|?}Ok6o>SjR`HU7wPrZLY&HMUvdkZ?1 zwpfskyKXDIT?0hygzH|s;ll#x(1x{;-CP5wQHN&*y;PqKZ9~pLjD1H8w*)?H=jG0r zKD#`#0FffM>{Nf;2i}pfsN2S$`Va=7t+~*`otzNY0-Y^Pp^_G5+pU^ROo9o=)xbsO z3{oXJ4jXvCZMft0csOK^GAsB*ao4e2?&l#et!O!`}ht=26XPnw++Hbit2Yfw>QTV z@H27DaFWP+5Z|G@HFWjb&tcppE~Dk*h~ky~b*c*`$w^-;JL=KprDiOZi~lZ?1|)=2 zx-hgz`}+x;?=`zT{i223HyP*2-&WB~wVM8ay}&Vwoi~;uM8At_&DXgP*c#4i=D(hgHt213_OLUumv|+1a0XEIg*w5OMz8Ly zEYv2$sGB+8;4z<6q{H2L#*CGeDVr3W&&B`(?TsxU+swckK_pN#M;)L;=^4SniJzSi z-!n&Cp6p(7T%J}pf2j6i48o|q!moVvN-Xo%)NK4di5`&dGEMM!$M{fB(^|Nrw|#p}P*vX3`R5E3yos_d?vzc0&7hf(9zbTW6(SgLNKK& zy|k7h-N25Z_xMp94_DGDVo(?OkdkIf_BP=>cNem*V+d{PyMwL&YldEk9!~B ze|dSgTnTT`60~!*S55Ajz6{?vWE?{DHrPAgv5MxSUt6UqQkwtP1mOj8K2m86XRWT3 zzy#OQoYQ!+bXI9Cax+bgt9rmf%rc68KC7JzJ)A5_yc;qh;&JF4`XJ%j=XAu0O~!FD zd@OHLp^Y>cBY*Rcj&w`up_$$;Ea*QYlOSXaZj-jP5HHYPYM`e6E2G?K!ql9`)?7$y z7Ha9ny9`a+Eu>}WDvg`YULyEOpAg4{EQz08NFNUnntEYr?!M$2Vx!L|-6W`9i9DW= z*!8`DaNb)b|7;iobQ_>eA-##c9nj#7U;h1*wb9TY05a`z6>U6JIF~OU?n{${Ud?AW zFlXl^H<80Y%rKUig<36ZfACb; z-3V>wH44&l3nP%sE>iF78x=IXrunXvtA;MnsG`CgG-}?)9AG99QBj5W5WZdj8)kV7 zduF5D))OBQ}bMSiR zBI$4r1#5C|hql2jJRpl|<;BnEB>}$g$OV0X7^pJmDHm*F+?Tq_w<2gzy3B)&599%K zoA4Z=G*RSNCpJ&sK?T589}|{jX!_<$9$TlaKRm!-Xa_@%j-zwzEoG+r4Q#GXzK!QG z2rDwUm7)1k2W{MRp)C0)D9pg7!)jnHh4}jQ>zs*?;vqdr7d5>mbWW^tO1w6%N>Hk@ zP`9&KS;MkPz(6fLO* za`qoCDAYcmno|rTOroP`sO)0qS!2Sdrrykw*B@gS(zwQmy#dN?r{`~=E({|_<3~-^ z3m>-|JEKtju+pdXYrf5uut)prd_dt;sIEW0_#c25vh@7~xJD3UN$Qdt$ZSY96}&ro zpTK;Q7aDyY<^~sp(t%qZM~zlNwwyL{PS9%B6r=ndjj`QL?%mn(x^ItyrGRtw=*ruZ z7v~r?aPu8PrND^R+CxK2ENXF?(2HCZ7PTIq1c|iUJ!-RRBLH@pV6rVTu{DN!lQa-B zX?7E_-Su?SIM5jGvE$^TP8AHI8fx11c;eQcPv|YCU-^@5NE`yFi8Hfr8W*88gqHG( z_zv6I<=eWumfZ)udHLJeStL=BE)uqQSP<$7ecGS1yWs;#-}NI)`TKE%fL)2xlYoaS z(4;fxqLI2o<^%~cCWf}BC95AW;1{eY&!dHpQmh1?D#|xMaw)y#*{=MZ>*+CQ^Z3-N zSCw$mMVX`mLhn6%hf z$ifB*zMT!U!)y2xc+3d=NM<&`0*ntrAu8m`yV6W)D$})gG}MTFt)p_d%hKyjBwBd; z_otB1?}Cs}amHQi`8d}@0R*`LzJ%q4D~}BSA>X+F&Z6AM8<6joLBrR2Yko~#FQpfc?dpJ3RW<}*YZb-7Gh;!_F|7iEb zpTp_cjqbd~@=wpC(F2F0Je{4eKkeN!a9QGxlmTnx@T3(@gW}w{JRc-wK%lfUcJGEC^sr$O_N9*M0=To^5 zU6}quoC{0;FBXJ+<2WZ#cC6T)Wb-TqnM@A{Ju?L**0iTM18 zoV>BdKs_ovoS82H>N}|3x~Capbkm)VhfJxo0f+60G(7!h*k*x>O6) zh`-YU_?7@7%p(Im@jrkKjBh@K2_oHk3wQ4x4UnmVmz+FbNAhp$hy@~~=JuwStSBmH z<_RqCwoHGK9(UKW(e@3^E{vOkaOb_>gKLuN6ZulfT}KhSR_a-(He~!XW|N`#lzNs3 zSBPyVMvg}PoWRf5he!uTc0CES?NVKSA8=SkSg0YWl9G-};2*M+imtI(jC-fV4i(?9 z{QO7gKmZ99L4;8)$;|Hzo8f!pFWb+6IF>jp&@$B%M2D$P7DhySrUMp;E+6|_9M{%B zpF-@M(dwa%7=siyRvoXpi^&d`1oDt?RL`ojv4Oy-{rip>`!xz-3SLr++oP8lHy(U+h*VCwx) zH-n5hFdjbCNe!L$P69YzOwb2dya-H&|AEh6Je$c^Cv^`BUyMW^y(P7u;l?AYks#J! z@qYas!+&dp^chm@TO*QkcUrb!O&0P&#m7pkDe49-+F!i_n9#Rq)Ix4{1(zmJUl!GW zOdl~-S^!^FV6ZW{PC44$4u*(#G~wDj+*kWIFN-?gn7GD|`wtC0@&l<7F!7)~GOhFU zV|P;;laOTOoUQ5TgOM%N{hfzBvxNjJr}hmm!f?k$OOU_Tqo2WO1awS}?%qjsMwur- zt>WV1a1)%bI%5A)Jq`3>m*2G90pSnOGXL0<1vJdIQlT(>GefLR27dgw5Uf*)b^__!W$`)vaxk4YH#Y-3Der{EDkw(F z+BSgcL;75SdCvWMP@=7n0M-I!E=Z~^NiH_f{>prZG+-Ows*TjP6ZonnayFDw(Wsq| zh2-q^B{l&O>N3_c|9tWoU=6RWUy1)jp$;7r7`>{}u&6wzWna5!XaDgO@dZ*J8mP8V z%3aTaR@BV_19w*{7d96(r~g5-fMT;=UZCZash|n-lr0TiZo0^NJfui4|9YxxW)kL+ zTe=bL@U=r(Z|~?3jyHWIO;78(v6tzh!hm_}Mk-%}>P+eL)c3Ox#0bSW-mM`IY?2sw z4>%QYzkvSE261o|r1BN+tq$k$Z51nn>p;YT_M#a6c}hwO@LFKRQW$sHfV9$z>|=Yy z4}A03t;FKH0_w?eEf*>X3<_FW-qHe3kv&fSkQTM%-{Cw?qn56sEukAU_rp=eGb#C< z@CXIFCWo5Et}#49X%_QN<#I&(lTcB{Hb_PsW3cL7`G<%!Hj`}RP(p)7hzX>dM zK>lO7Z6nV)-!IZH>M3mY7dH$qSrcpLKr=aaWeMg_;B{V??5tP@Sgzff&1zS)rgv>u zy2)kRUzO+!i8r&q+c`Tpd{}e6^Ze%pdzTGB?0zYYH$~9cS(NOK%>vGO%i9~|8OU$+ zrFZ9bVbI?bGFW;do>6wa&y1!W62v`!f=K<$e#H8@9C_4J-~5~2#UiJRnJ48JmE#O<~*s^mXA$R4dfJp(3DfbG=INWlYE$2NxFv^oMtH!JRWD;Y)M! zWSvG%gXiOGa2ywP-#_t7X@0`Ps(v49&^P&Yqvsf)=w|))@2|0dIg50(dGwIj)ZB?@ z4>PgqT74Egfk5*VvK8%iZ8NNUq^?9v2PG|cFU=f+iu$%7*6!bO5Nw0UsjI znSA`l#zz-X)|p7lRbDopx)0Za$qqqmM?UIGMry+;sau!&3~ucDObkJch#0o)y$cJT z+SF?27(2k>AqWGiYGW?&J1r0?k&>j2sx7KtW29Jtp!*-dtU6x=S zn)(mqYtTS08&A6Kk%t5paQw#Wu9Gz*xOXEYyHMVVWRo{~g?cHRPl(fQQvRjBhArIJ zi-2bd5bKaTs2HJz?e^jU0PzAlLewFmFQzd&?c6R+!H}? zSlWD_0W%#8D1aDKARjI4qk!@*Gk*R-2Y`-X{MW{V29UReb@|sAfa%a-*1LGAjUQVn z-x!rbx4a$Im9PgiR`mM<`Dj>a(v@0b(8gT$tYXbsxN93vDE(a2YWF*oiQV%jS`mz` zQySB?Mam)`=P}e=GSemccgjSYQ$j$;1g1+ED#w^eOsvpLbvSXrD-jG49Fm5YnWsdA zY{LetRvLU#p!iWQ5WwkqxzeHHO!*51m^`B)!>u4yVgB~M-e13vfUyPy1OT_HPf&Y2 z^d?MaVGf*f)-S-h6deZ~AWx&MKnNC<9y2uoI+*lNX8e}O` zPt_cl7!syC>pTo1kb)<)rwjO_(>+h0z_&YjwmWyxC`q;m~le$gz7pA3NH?{CWnLe(|K-^Ts3Br(8s+_(z~QgNojk?A-Fxn8D! z@t$X1<91>n`&!%7xT+UL#BNR)2#;ykGgaj3s(r7F-4`hHh%Ob(hHx)*f=q?p(Ffp- z2iOgcl<5{}1Q-#vwMnQjFz;i{+~BNY`9{!zKkrWz4s*-V1QQMY)7KA%h+j3y zr_gjh$x&1}_n92{!bJP zn2ky#XilyKnz%aq5bPT^^$JgAMaO;2Mu|Cq{VtJPxUQKK8}yoc{=F<}(&4}#qWhyD z;~55q3Lm>CJG|_xn`8(zMHsbcgU!SN?V3S*x0Y150SxW}GO1%qdsjn|%`q_Jv>;?Bhdwbr^h)%q@Bg_X(>*J&L z^q$lQX!D^$t z^I{fP)Rq4C&+0|h>U5B&z#~!y1 ztw1rWf6H9y4%8I{Z5jdp@UTnLDd4iTT>8`Vi5HTN>f^oo7kzQqM}gGKipKWZ3i?}D z`oS;l?~`48$pw_5_O$|!i3ncdzIktaiQ$M~^&@Dzm?KL#NYRjxqamGXONcYEvH*ht zqhe5Ju;uBNTsIA@U)?kf#kHi0>VR)lXaPYB|NTj%R19JUP#kEIotB{c8vwK!)D#fa z%T3SJI{+$sglpbgB+Uo7C zJ+qUh+#yWn9JXvPHr*>CO_3;%bGitZ)@XNGvQf(wxctAb#eao+zi3A%h<$Xh%vUM_ zf=m~1W2yS7C%CPJ0EF?StZWPVKj_byFOee(J}5{h?E%pf>{Samp6CaUuMj;j4Wg7% zGKaxE{yh?!=Ca3Av7OkOvwA?GouX!CUL&pgEbeP|sh^V$Q_DObc+OSks^qr`$7KU@ zv*n2~@=-kIbDUvI2GNqEb}Zh+_BOmc2!dKqst~ltKV(K6!59XfxEdxfP=Pz@`&Be3fFVbpcp9@2WdJLHIuz{{@IlnDkQOIR6X+ zuB@ubxx?RbJXxBLR;kI~QTIM{Y$|Q?PT;7bqc}o|PJXUD-Al9B)KeJ_lCUQ(| z4dL|w=l^8>bvkoy$W8C4M(3La(v3ro{yqa%C{(901XMg~z^uiudTTy{(rv_?#d87R z&Q7uI=flc#=8|nKjDUn1?hvEAS&JX#Cd2s+KR#@;Ne01|Q*oJ%x#2(j#L`lxEd%Iv zArKP;qa3;M0(>c9`=^A=ovUI5)VZIL>1nKI2xR@QcqSuaX- zAO(RIoPcz|oP=Ipt0$F+mNXS<0Q}e+2KIM>WYkH~odOSd)QZkO@?qw<4pv4*wxPO2 znOvZ;l>7-v&D=G0@;$qV#huQXYPDCb@2e{@!iITm7svjph?%7r&>^V5OJJ{p*1S1O zroo*U50eN7|!8EsOg=>a$a2xX;HQ|&?DhgGbzHGk7h&OXF zI_j#V<9Aa11G{Z>VM1K*)6$?|<3hy$V1sf`>vFm7r;tEc&+U3inMqXx#O_Cv*GPB{ z<@H~2PlVE1JX)sku)-Y+_b>ME+o%7q%%~WfL^YG|otkM9#rb8EIor>Ru>oauvlu#Y zsc-jzJv9OGRa(V)G1-|(nBGR$7?Z}J#h5e(wETW)@sye4;v_d{W-k@L<-BLkg%uCg z>N1ORtf!?pWa8T6{i7=ie}^45oFG0#g!q#oI&hA$YFVBu`ElU?6_9}&40xLNbQp7x z%QxmVGnr=F!If>=znn(kajiz9j*~HRmVIYi0nRW)c5J%IDctXSiF-G5uS<_2!?+rA z)YLd*{*nZy^n?hpxP~<@axY%o+hC(C*&^>R$NE=PUysfqZ*R~wj=+sfS*6|Ls3kBl zjiLd*=^eGXTadS<%Fh(D&ymhFm=1Q+JoyS) zyv&=0mbF}!JP0D>%~Ga{<8LPkE`Ip%*BI4k*{WtAdqfrmjcOOYI$_p>@&e8g5?4ev zOBx#+?LA_ln01z(t9pKy3@PYK@wpvx&{-yc8}qQiI|an=%AY6)eaW@>nyUiGH14QR zlZg+7JHPpdduysQogjFoUG=XlW|I<4%h@IcLq?RS>{c_1nvK|u79hpp{s$xIou0eDQv?Z{<5djD!DPhTy0jS>@6iL^UB zV%I}+Jt=aj52Tuqb23#C z%y`b%T{Sc0#jo7EJ3Bw`>Jjkku#^Km_zoeJ)cyt#htkOM{r8Oujf!z+M}jE_TvpwA-F3MfQCQnp7#H7bd)Hu)!nAyt~Er)OsRtWOnw zJuC!Py6Y?^^>c9dYz_04-zj-45ozT$l@B>ocQyCnAiQSy{P`O_oAw&(-GKdO`!1ML%aQt z52Z~jX}w;p>FCv)PD-fRc#B0@pPfSQK+92@-!WxhOrZe`FPbI`#JD9u!rol&tQU;( z)`amn7~_?Kc$xQ29`6h*7O3eN8Eo%$<5luO0A1>yzAH#nhp^jg6=(tAE*v4EF-K!% z@Hx2TrOBYv(b$;1ypOGyZ%pyWQ!rz~q^KJs%T^*~nzx$h+FD!|;31v{1?CX>k=sn@p2OkKSNiV;t+hhQ_!b=3_|CcFd0 zop8zWx4S0#a^Emo=Bl9aIrMhk--^KI2|5&mX-uKKEJI?diTf}Fu$C7s_h_rG#YZX1t zy&jt3cP#KzmX`P2prH)ZQa=BgGC-|5o`A^X0i?A6H-I7x+YVad7mN4@n(6>f3Vw5zjHNU$b$8nGmIPaT{HaQ7JM28wEUbcE8a&yHP(6pUc;EC z%4M&p`imb({#d&~OD>qtf0}eP4q+aPT#&>GhujO`E_Lg3+zV)xCb$n@!9svS zbMiUYU_9BH`fvx4dc)q=(;AzwZlvzmUxk$SL;g}rW|2;cdu88v-S{1QjBNg736=jB z`_5KRgsG!sa74(srEGOmf;iF(RDx@pTqR~rSNp#Q(4zx#^+teSWNd+UyDmI45IEol zWs*&9&KjUh6`@?9jERru;^G3hvXEyegYzeU8&-fx&5z9eoc|XHF%bdaS=g4X6e zu)y|%&(Nn~#sGjR_=O_W5pCQRK;Uq=+$?kfdcad)E&ZjLhk>%P!3_$zySI9)!zOoE zYlJY*;S0~x==2l;w<>To++G~a;9T4S!zV-mW1cytaRgj;cp^Z;65{Bp^@isVr2%<8 zvLHv3y?FI4Lrbu<4V#8Q?s{ZD;X3mOzz77}j|1dfI=MVz<%DO{Z-!qVsvq|Z;6p%& zCO}K-(CMQ2qg52jlqsmPly(c<{~O=3ioD#Lwgt{@Fm3Sz3JHk0C@}3nMc5VO#WCSa zARPb*u!EVuPrctFJ*@%pVGhn?h?~JfXrKlr0T63XO-^!faD)@H2L=Y@3j*jaf(O79 zJC}pV``3BteYMrB?`4xoFRrv|``^;R*R~Ca8PSGI5s@3n>JTIzIVDNTvd*WWWp(o7 zQvQW`e%uoEA)w~5M69_63MioXr@qyVerlQ?Q;gZycn85LNvprOLJzi89{pGgqPHaA zk}K#Jqyuj^{;01ad2~Da{>DS}BhzdsyZI4gMho2jRf)lorvD8cf+JGP;` z>Icw@*>idEGqsK{35si3SS*b$%a?++)9Y!DUt$B*xtVO=EdWh(kR8bPg(~YemX)0X z0Y?p8+d&^l?Es8yWYm137t}RizckkB#_?Wp`=Yjy413e%ho1-+=oBGj1=aZ}U1B;d67!tDeX2?Lx2RvlrQ7i(sAL7Q^qnRByV+@t`y5Pzn$50nv4=-SaTHB`Pa_`Uo&d=nePI(XSW@Zu zpmYaFZs!X%@@W6P?D*AWQQ@P$-`y}9?uLZQyxFDZqUelFv!!xrZJ*17 zQcFy^cZgrB89!OLb%ATof4N(L@{u38)i_l$afCl!5zsB@9Up4;g~+zmX{TK?1jx9B zmOvCyu$2o$(Ld?W87Jz~FqgIZ*-3>nNoCMIlIqVFj3_JoaP-dC>7yp`xhG?GV#Ro$;U9Ws0pB#qQR85g`N^1B?k0bpwlCR zuZ6O(o{srd%03*9|X1N$VdcKES)!j$PbBXSmW zVc#itC~_-FAgzrkKAmVuOM?1x7R`1in7sa zE(c?=0{=6F)y2l<|AsVt79nB59N=S+oBfu;!~BxXCJHy6Zv897F)vD6hohGxxElub z1lE<>XxP_=n@jt7GAzHV%cjf5h_3xJ>?JdzKKrRRVB^#5wY1oox#{%tQAYrNW;OpU zxqPkwC<6>YBJ9gS!thwW6V9u%4MPd6yIyi6UN;+Bf5z?a$1{Yj{$zTRiWO2ao_MIm zCAw(hagN}|-+jMp*mZ^e1QI3Y@F3pY4f%%*$*r!)#i@}IHWJWsCU z#HhV;sfxK$fNV`=xPweRDUv~T${0|UohOW;nZlfWdz-|tr919vA450(>eFuJ*RU%J zy?4RUO=qb`9gQY|!qE*`%^$?UYG1$O-UqIV+{s4@AJ}OF5GF7FP|%D4>n#-Kz!bw- z^CSk>!&B$&lbv^12{bISKR=QYEp75ln69|$1Qyo+-1S7O9rNWcWo8grSanwCIARRi zKbasoGNH6iM7Lc4%%T7Bc|nQusi=ocA`IiC)EQ7Nwo*N=H{yxT=x$RrH4`5@8+u)u zA2rrmqjC(s-~F-4HPlPU)E8iGo1SG=j~bd#Vx=4$0(K-WwgF(HI&|9nC$%$1b3`*u zHmn=bABp3={UzmF_VES!WQ$|}$?tcLO8_qj%p)Q;Z0BLJ(1z@DzV@bx>X_TeNBGG; zJ(OfC^w*9$%ceJ1d+Xs~h)jf~l#<1ta{8+a>PZ17*UTuMArYIq-`EW=4$ClQefFKE zx=#2TK`-iNa^5h^j{-;`fPC2Nw%Y4fXCw)ERRz+b7ww_+4h+o(KB{6+r{#TDsR?Z|aE+@OtN1X25sws4X!|Y zss*q>r1Ua6Yw0{>8XsUocULkzs`>(9u>90r=jIiPFVDT#N|>1hLSO*k#H}DM-U*B? zZ|s{d8;>j`Utkp9Mo`;7%FkaiYLtRntt_`3tkc|5Ukh~VrkJ{^->`cDiz@S8 zOTweHSf?DwF}Ncj*#ZySqttsq4;%;jc2TQ}>O5p4wxL_y;v5MSxp`?+(|_vnuVJ1u zuZdx7_WTBM{DgQSpx}P=$wS7Y88GiXc&cx*{UQ7T%A^A{!I7`InA^X6TYH4(?d*UQ zBsnRm)K*iII02Kov9`7rC|HqZ!5kdJL2&H=t0+4_$QE|wq2PG}X&`8uf&65;!p{i1 zFcGNF0!iSnyZuWF3S=x}qb(QLo#iqY9_xi(I?#o}{yydPl#j)a1*IVmB|BRKpo%xu z{kd5qW9J2J==jj?F=B$f6&E57pHPp#BVf*f z)-pgWQM{hwl7&)O0An$Tgv88+2}!!!Kq;`a)j*V9N5cN38A?;nMRSWiqDV@ z-GdqA&f5|a`HQ)vR(rrb9431gru8p1FL%Lnb~x z;0l4wC_!~LK{@F^r88AG1aG;Gb$Xq^9;OLxTYxl7f0tU{}mQCkQ#M@oIT3^_QlJqyOYx zA1FFB@xEh(Ez~eG5U6AAJAn#Yp>w%y{@`P33@$nUpC9!|zJ{@zvVGM}n#09<0X@5o zBP84&95V83W>2GexyfT5VZW>dCRsFxYQ=MygZUjK^2tsY_eWmjfxv^p`o6t^JsgIg zQuTH64-nJ>`e!frck83Fk4DGjIP{Rf^xQ!u(CcGP zj%m)&M>fqQM#li;tN@0Iie{@Rcz3S{xwRMMzRR zX^7ozUx-l1F`KBZLO4PRnS4{_w4thTrR8lN;%|9&R8Tf6r}bpqvf5rK0}ZD$V>NCY z>D&DiP;5{3bOeX;6u8Doei7S)tPdDWJsgvPw3N|56raw+KJnxMMHF7FK_jV7G!9ThnR0=tZ_BBUW1* zMOAqfW;yC6Zj977KQ;o5hS7Y|osuQlWVJ2V9(WNq0rq8g_-mBg$He98QyDimDlJJu zP_Y#6WGJ+rnc+d2?*=7R8*p6#Tu^!VQR(_0LE4J(08*azpD7^c3#6fq(m4Rd;pGN% z_x$riCZxoJF=waeL&7Xzzw+p5(F;+%t$BK){vp`OJc93?EsXo#e%~Q-ljc?ThO9tl zipM!-H@frK0!Od?B$;ixRGK4VS;km~h@SxmY<%zc_Y%4=NdL*gOYR{@1Er2ypqeu2 zXJ^;bE$@Z|d7nJ8CVqc`0AIx>C{&q5<*hjn>lF(yryQ?)I(te_)E-^&nEe|({VNw1%ozqu-3WojFU_5vO-NGw$Od_r(iTs@a zOZT`S1}Me2r1Qy2XGbP5zxm6_ZVh?M7ozBW$m>UL2Z*K^QPzAn1^dQ_vn`j-m9Vy@ zKGkN_MxJ83Eu``uWnN8QM={Bs`fsYxHrM%cb7-#`?d!eO;*u8ar;DHsql3QKIB~uU z_{g(lWpD=G^?3;QLoaRm9e_<_0mmLn;BBP+13>?&7Ce0#0X#7KG7y^;5CXO?`U3r4 z6{sOwX3~vS0>Q?&JO&aLWxqAq=mkho_3SEr_fyf8H+^_yeRw3u>c8w7dSrqbpgel} z!;$qQ>VFy)bEX=qu1^-eGs{^6Eh2lO!XZ-Iyh(5nF)R*N%VPJq_tHIY=pc8I05*~i zzcZ&Gdd30KnX-p9utWcVHqPxN-9$JyrZyS%4*|@G_A&T@tygG$0H|VNHr;4D5`xr* zfWD6-&ox0xPR{WHID_(uVE{_GWELx$^GUegJS^{hufR7;<@L_4yP9C*OVP!eYr3F9 zC;J1)X~7n6_ve^yqrb8@nPOVbe*1Ef-`}T|l3+!xb9}#_OhYAz8l%#%aUPPrH{rZ*rwYNInND8n4^=WzUD}EfWBr z6lR?T$x9LB42^?@3@D^&1@M#% zEX$}+JbpR2kOjs*vqCNI$!d%~5(cSQUv-=I@IvmE;rGjQ0V%mX z@PBziKCou^{ShSQ{`S&o#&P+NJ;?J*Sa(~9L~Qa&_tON1)N0!uhOt@3*@r-V4lw70 zIf$0?)380yqcXDDyIYwHn(9gNA}pE&4xuSZ!~ea$+pr@Qa+AoJ{r0x)+s9c_Es%s> zww}+NOdD_MOd^#XcvVd{6u62u5*Y{k^3D0hJ;4aZh{h{z$5%k{y3vq9Xv%?B@#F6=T?Ds*;awb5?V-3Ml=mg5iB~j&>=23(nA#P@wtbDuUMlDn2hO zd^K36e9aRfYjw%HF2^0L6);(jj*-AmR=~1X#e~5C*x=m2_7I7im2Pyc4QNe zff+`_@)egDe)DMMCpm3)7Ff=jdpueu3xfRC{pZcaWsFy9%^p}Hmu z4lwP74*WQO@?QK@mM z{%TBV_}y(-L80;m@u#=ZcR&F^>Ns1yYXX$0?V$xh+G}B_E6nS^94*GyuNX|Ck}sT{ z@YZsr^nP7|;yLg2Bpn}5#s^Cv5o$e205{DAsAT=g%?%tI%ziI-AYCbKGq+xYp=l0~ zr1^ck`;hNi-3ca9Ftt@!q|o8-{w7sg+n|E0hT$R@~uZ}mjB2ngGQq)6(4n=r< zc)R#6c1wwRD7guWa+u$OVAgPwX4e5rgSn|&My|aU4e`gl1e9tLJ7!}Us)_(|Gp1cvsi&5BukX}RxMQ)94As6H83st*;838D1$wv+^RK6Nm>jgP^eIz z>~7-M9;tNuPI)be)GpOcND6>PT*~kBn(=?KkRs#$h^9MjM_lkR`ubq76UT8+s-S#( z%0Bdjr`S;@(>ei`A}LqCJ`q8}g#qG$S-_a)-QTsH)C5;A$_#8rx(=tqEkunDw5p?o ziYL^a05^z(XjNUEd#SOW{9yA<23hs zHHmH#QZPw&BsmB`A4-=;1tmaV()#2dbtXCQ%}+|J3sL#GuELf~=Xmm5eCrmzK9TiT zysIrZ+uIe`+-5V174hqCAEu8fE1Wtkx8GDo4!KirzQ*7qj3jq<-RwnaqCSv)A}>l7 z?6d&Lst+nxw$HqT+yoBrq8KdL=c4pB^8Vo#txE847p+3pDc8MEGfsB|Rp$y$ZPVgi z1O)no*e~+!(I=0D+{#5|PnCrr0OzS*XS#XeE-C!A6hd8z0(VWV+>}^`#{hYDABF98BD4Kx?sKOs#3Gctk|?9ps`I# zisNf*1J#E(W?kvXLeoE`Gy-GHfTr=~n?Ld^`$v#Q3{Wx^U-~Q00ouD97Huu?GTz-+ zq*hRGg*S-U4&Dn-nAWzClcJ{Q)%tMyo+`d(puAD45Je)B9*Rf^(gN6s#IKaOci zBDQ&%aw@n*dn>`}^M^7Y1!gG>ydI2&ZL4e@Tt41pB==L}c_2bdUcTFmmKVIfZHC{1 z7z92C0=LMD4T&^o8lfEZ4QWqlmR^auG;)^@o)dB_KQl{pD;g`1Yt=K>SKEd@H&wQ% zTaZ{g9USySo(Sn(|M?aO{csR&>1X-+j(6J<&7JyGH~V2=@QC+V6w{s0he0_K+j*{8 z4gO>O`CP9h%e|JnvX9$F&oaEk^PGVZzPNl(N_c!FBT>T+zaI9IizN5FT1ANfvm5To z`RYK!x<0}NnE1im5`bdbdZY}KG~Qp#KX$1=nTSRnponX>cYL#eX`nx^0g-bPIm|ng zmEu$V;d7M7)(~K99D>Fdi0i#Wad*<~{L*VHc!@$lu*?g- zN{y6uQNjm`Db%8d^@mu^EjZ`)=v)<`M5lL-6c-nJRR0-qC2RW zdsU0>^?dVoqn_?SG&=nczs`!xDRor3{7iLjYREN*1$oScXXVe0xJ{q7CQP2(YsVfe z2{%ul>UtM(EVX)m4GwR-1AyifD1rngq3=D%ov-C`W3w8Qq)pEEnf*r@-(*Oz5! z+saRVRx!vv@o7qKenFP+N3HtI-+_a{7ZofrZm+7;XS{vPjXXgi^Q$L?&(YAG#(KKx ziuRA?`io0C{mCkLgDCvr`e231PeR9h-86btF7O$)R4#7~sw>+#>Yf3w2w~i*fCQ4h z9z`RAnB`1IF4Ku5*E1{MLAb+ zaU=wM!*ddbO_g~K4`^<_E}06ktlMHu?D<^c91@P zbq~LFXnaG={-OeJ9MXK^rE(8HNL92Y<-!br!@cH$_>Y?Y8Tv5am5SrFq@V?|jeQ96CG%beoy+&E)Y0bgqLm!!pJAa`BmxOw=f3N~i)?Nsb1dwri?=9wR1 zE;t{H8z@$(?FFMPTUk3PX~i@Yu-Tb^d8n6sU#}$8cT8;{E7Fb{Q9yEol~0B@YGwnjGr-Eb@AFt#p8i|f0^CYztM-qL z0Pj;zK_8`UG&cf2%V@34zjArwAq?b#I;D$TUa)}(KJN%Uxdqg3r23baS{|q2OLy@v z_NOX?nw8^^$)69smZVL-BVZm;~ zNPOR&NcfyL3+Z|H;id4KNCh|dpV!N1wF?vr)$YF|aA$SGN?pC6dJ_~#2yuS=VDy2S0I zBiLj{_;L{wSoT}?YwppfRkC_I@p8NYKo@hKPrIk3a3iMp<&t}N{#kR2oPv$w`;s-F zFCPENX&rp)#&U;LkWy2=!Hz}dd6(}_*d=7K8$U{tz`%KF@r}>nM5@#)oDMQ%!kuAFGsvB9%J!yHRDPPby|R8QQ?P1&UD7CaCfz-o;QWgE zP(%~MvY?lj#IZx0rXx!@K|q)2Qvt~d@!OYIhT^I-YOCntm9F)3k&^Seru%4n2bZ_1 z8ynvaHx>oWoXQmcmg3DpE76u3Ck1UhHde{gKeIJ)RmM(|r`@bML-g;e~DT#x7>AUes-)TkX?AqcwwW?Q_RLE!M&bx-&_dR zcp`|eNa`q-FWQh;u`@Q*^4It|ZLNP+)}6^b`%wQdkXBo1-n>MiMm>!HsHHEi3Y+@g z?0sZyVSxO$W|1^vWQ>rz7uTNpfwvs!U{VQ{F<}l58#ug*IaNc#EBTs8sUDcMw`X%@ z^yQDLMDz457lSvw+^zk7 zjk4v%uuOJr)}L}VEI9GL#(U@Ah4Sv!`sB`ui|i2XOrn{&{~?8Lh*QZ28$LU;C1gXW z6!v*nGY=E4O;wox-t70#?{gO3F|xe4nQQgq2J=+)`e(!pBy4yXLB$^l%AwntAKxiA zE?A)#X=&Snnun;s_!WP}eU+H+>TJ!AgZcY+g%ZbvT{~(e3lELE z7yIUYNiigmJ;rROv0hF-bTXY*ntb_@qLsPM(pRa$ELzX1vObWkrgT&&-bF*wf0-KS zsTKQ4fKGilWeydbe&pvDqvszhEz0Fx!0!dj<)#rH?4H23Q;uLUyg5JQDm-?pY? zbxOwe#)hzE)KsraPGwL*`eljREQ9(V1MugFvao^3~ z4jmYyUrzDFp7psq@WdPB>77>=D`~TxM_~@jYFSpVGRn3OlDx1vN-e+?n2Iq%47w-3 zAV05E{Q@<^Dkl>9laKPebOrEs{I)tKSW^o}-Ta=GwKar5u{4Jk)J?QL5hqJOzgd z_{x7sn-}%=S@)adtn9Hq%>DS>#B*CR<%v~-Zx|!G5FG%wG7`bPVM%kA1(Gwbz+ec+O8cK7QwuaVekRS~3}fu&I5vVX|){}gAx zkN6Uz)^$-&mMhGwF%8x|$29-qsl|70ZBV82m!)5W_&KST9@USWxuZHM;U82_(S5C6 zztGOtgjx3EURw}*CM1je`_eU|{==GsJ$hQ6$8Oo@UdYN7`!vDMne~&xR&`CzaJXIm zi&??7+vv+%@A$J_D=p6iB6zzYT$pbkem($~wPYZ%!O9Cjf6$`!%#HUFYicC>ZhsGI z)@kReKaMqH4P=BQE5A7UQ6yM6VW(ALO%^9QaKKhyDmq8C%;2G(@c!)(lu1h_+IK<%{LQodW38(y?SFexg2gFRh<-* zCYSEAP86=K`l;r`8`-;D_E{ipx?Mw1@o|3~H2dmF6v2#(dQat{^ z&(EUbVrNRhq9Jee)zsJljYZkf$l4H$`Jnw=i+|Lh*rv4UI+8)WZCW0|gt` z2Q(H@TPs_rvYnx^DTOwRl&I)OLwi#biudoq-&N8@WBF*|V6R{b6}7dtv$Zj`aiHKv zWBFuZVEJTa=wK>lYHVu){=BTIjXC)H?5vzT?1F+6|9ed5l>J5*2ZHI?Blw=) z;=OIj;q=aV6nAP39$$#{v-cs2AMu`lpzwL~hLTbkFDc;12of%C2BMVY$0xrKeGoJU zL`z${YX1IhJ#-FUtP+wms(F`3G%#E4Z?=FRw=D67sY}Fsu6&ybD z&9%?-8;47W^&d{-hcWIuHLlLm;bBKY_dD_$SJ9dBK&9yQu;umeWg81%Eswn5ZZ~Eg zV3M}vwTW0vTmLC-No)ulznQOMyaWx4K800xhCO6zmV%@ z*AGxet$T%jqAWMWYQ7_0u0W^L+F(@F{E@@6AXcmtXR282^k=_E)2vpF&_+;#rdcV& zMS>X@7lw3t6jDKf_|(6NlPax?1Z zzEk(+`k80JtSpu12Ac|wsg7Fz(-5BcOIEIl_Bzw<>L!D2qdrg%#UY;8UAbp)!}10z zVu_<~@7eW^OAPTkS#0WBQ_=oV;tONu*>i?8 zFJ3;&m)-kzc~GYK-0XMpGKVS;i>D?zZ*(_`uFOT#!-H2W%;-#WU+j23vWL>jdk6z+6sFk?5t+S2|S~q+`=<63``&Evv0{ z-&nliqdl^`^VsxAl>gDMqQ=v(iCL|RyAe249MXQ)hRDa)lkD`}mi~g7z&Q4vI^g#k zHqF6j2#qG!-%LH%5U;~C^YB)VpP{tai7UTMWX@eJfMM>Xl_Y5j*HHu1wG zEMc+)XvHRjsI~}hn?w5e8N&g|Mc_=VvK8(17Y5sJb>gH=rp^T92%dcy4z~TK8;e`V zgGO?9gTc;`PziwDHTZPawr1p_y>Z@MkkUvME0DCjvnje3Y9L5tKCM8wXLN+QyrYr< zO*%bN__lkz##gSiF0O2YTNhx1CJ}b{fm5MZHwv(=yu~3&bi-JZ>CyevwowvLlzfX z*TFeOq|7QO%s_$+N_mx#-ehXiJ%KDmU6L(vDnCp$3{0QwYOo>;&zw7oD z3%FeB;B{UyQN~32(4qXf13n_J;Sr#-F9&Y5ajyF}5Gtl)`ZM`NWt^>e^ah?SNL6hu zHeD?tGA|#l`NsJB)q7vk!$^?6Or$z#Lb_m%s zt%Bpvs*oNhXNx{tq4 z%QTkX(*=<<32jJDjR!sGngI6MWprFSZPDvAD%*$tv$Bm_`< zsSE_`tF!B?9B5JaU*BIL@hKT|8$DLYdTQLlKdj0FSy{17zno#>35?AGS$s>5J zA)Dc8*sMag?kE}6gO!`wN@hgV_|1e}>}M3rFyWX5!Ma4`>h07nMG>ZFvSDx%&X*{%DiW9>k1TKiM)2p6belrA%EpM zI8sp3bn}QKP4*hArV0uv9;b|9>|Y;fV8JzKkKbSWL9!VW&~LP3Kys){*p}QgFmxf~ zvR+-L#v?(KybvGL-q-_+p{0@kd^pr#8KlIF3^(no^j%^VD?zR6h0wyLln(XSP)*RM zUu1cb5&t$r<>Ie0nnM`1UA&I+Fc!O0_Dr6RvJ>LdDX^kpPf&KoGu1ZLFYbpxj?3K7 zcDNa5&oz5eYQp-8A)nXj5+Vmz5yMVfmRNP|&FAYLl66wzMNeAK=FP0uBgpSof;t{VB)#9>eY_;fEeRA2`a2lvkE-uF1b*=jpwnJ zzjU|+CC>~KoM`b#(_*goQ|=`&;iP9?Z=BvZ4ZiIFIDEMCVcqGxhkgLZFfDS z?=yY`%_SR*uaR9)$dJ3N|BQ;lC3*TKJu&gR>$UAl5Adq?mX@;Gn9T1{?)jJS zkfxJx+nO`Xy9L+7+O3`s_md@_ovCsJTkH0>5w(7|RcqD9r zzUMO(6kyH)y5PGXhV{SveS8oT>b+eQ6coCLBFf6x)-9~8#L(U)rKWm-!9<6Lhd|{V zeywS_YDmY(h=GCO5QhjSbyUv8q?c9vs@u6YHcPz|mQzCxTCYl6RE&}2gcX882|3O! zK$rmZ!>|7ILZ+E`v|68eTce^!fBhViWAGNsFG@hBw-rU^(FO@bVp}uV)ewDs@MieA z6k$@mN_R2e=rq0?x6~s%EZTaWy{@@%?l{&%m^bvj`1h}848Yg`ZBdG~w4otI7>)It zqod=uZ=b}(T9U+XfbKPtghxq1p%lzhFnN}~YSnai+Ak^f`Ew(f{;3zN!R>j+{qB1u zz!hAy2~F&}?*Hxf_45Os#BIi>OHSahrFVq8$zV)nFb4oh|LJ?3zeb&-I07dfcs29a z=6ZT$=71bg_hox0FMX(|$Bvz$LMK%7I|?>M=aZ(atgJ-`i3qJ4mzG~3TxlQ5Zpv+; zQ%!^pzhjrlhof?KSYx2urR>U_yfPZcN0%mj_16JRcvyMU6C(QLP)SM2z`YGgRpaJ| z;dWbg$Lj+?#-yO^xo$&e`C-iiHeF$-o-hzNaQPJg*OjG(1)|JP7k~@tu?J@Uii!^! zMJv{|nelqAs9uN8DOdM}09?P8<}lii$>^~Wt6q~#zKTyF-e+0Tm|Kcp{o-|szrq9R z0F?7o;rD6x!S9i}(1aX~xAhgx&C3B>Yjmqk0oGDBg5g71LyC{K@xL)atIE`Z=YdaZ zy!B^tYNADoPAuKp2&m(dSV;fIkn@trdUyyhwf|Akr~?=Qe}5#c;^N{iFA>HUSXdHh zaR81PxpkPIm)2I^FoKQ!BCbu1@{;@;iw0Ued3pdf)xg*A%;V~|{y@tW#-|Ht4V^t7 za#lI{8!+Yk+mz_0&vSrJZfJP;^8B3iCCL{L#NHdF8Ff|zJFJ!WDTk(dlmkqq_!PdW zy{fqbN?-f7Fcl|`#bR@SEqM6hObCuw{hoj0#CQiB8)!8?7IybBs-zb=l(^TqO+3wx-H=f zg_b4eNJ&W{aHzGObgM00*v<(rUk^Fm-QS-y^Yvrn<0HjISxCc>Ab4ZjD%t8PD%@^w z9Ot9OX*BzixNON%Mot>=jx-H+08#F#ZK~Fp=^0)*{uJGdc8QzEWpCsB2;Fcl|3}&W zApqS>u3HoZg&Yeffe;%TAr4He_l1I0Oh!c1f|q9!Xo=tqdffFD6)}I%k0aXI^z57w z@-@-CunX0=Pr+i8AiFEf&(Bl^Q{TRag(3MEu2of6123wZVR-lp|4{dfea(i`6w~LJ zm`&izd-PDf8bL{ka>AUfn&38#9;(xR7>Nl4jWGEA{K^Yai#6lanE~ z6{c(80b7d%W0YsuG8l+Lo*uW`+8OleQ!SUAjy)kNvAlP^lXT;y$y1Vb1J|5Pf zHc`w%Qy;@8=A+vKf1K4$HU&PSVy#jG5q*7f4Ky!)AtC)px3cP%v6p(zh;QE^r>?v+ zl&l5AW`VgVkKCi^?@POzB3qBWpAt>C*R=SflF_l=uY8W8B@>DeM1%SkuR? z5*Yg>$UPjQBmxRvyd+aN-#7dcUj0@sh3M5QwA*NnzFfN6HwK01^(r!!I>_YYp1d3z z?Q`p+^3wBoFDOjO)7`(8;?K8H`_XZ`?0+!ri?Ptsl0#A_9auN`LJ~vZ5O99S{hJFF_4z-iy-9#d}dr+Trkji{BOB&e;gCI>#p!jhkpP5Ju{=>X+XI< z{gv5%cOoBL!AZR~acyHjO9NdnX8SCno^2VYCCO>L17J2i-Rc^DDg^#U%6t5BMxeXC zTIy925^A2BngVL@;Na&7KXCJ28yf;N(8Aal6{HSOTB#)>22%OrJ%_-n8yFxUalQ;K znkv@i10zA+^GFsce%Igy)4;B-N`+d-)H|(MR_HZcH~I($mC$?57-P?K@R`1QxSP?5 zDQ4s!^vdjyS&ZklM#(lQgT*D=qY zu_ZM3;BZGS#`ueW4MI-fdGHy@4}{v={{&xeQc{u@IFOZc5(6X?Rq&Yx^GG=>grs< z5&y5evC#uW869JVg^UQqFWte{qw%aAVCucS`040S)v>1SffC=t!^7(4Yj{C$PN~dn zJ&Ihb(g93c&MdB2zqu(tA60HypcOlk^TBD_1<-`NgI*SLmbBR~&ddaxyMvew0%Cco zzjo89ROZss-(^>Cl#)1KGR7KS#t15ob)&e2h%;oV*nFc+}b%lKK`R#OXr;t-NRu|k7Pmy6B84-MWb>6z2Nfw!f>P8 z<=2vBfnVR;OJi>1zt&IuQqn;4D=jK2s;qPb_TeiYaPAHa$R-Hbah~AG-+&`{#mYCO zCuEBhi<(5L@W~q#6qHqdqv(c)hOsfF3}F#+HN+M!F!4ZWgZX-yXqhbm^jnT6Ylog*C0&+%2M}a?Zg?7Y0U6{~w?WikDI&0Nn zYy~GGBm`0Sq-Cw>QKJOV*wm!|o?e!s3ZwNAynLC=B-OS9exZ;g8uv?>==Xq9hWFS{ zMAIj1fUAmwgTtC;=zs__NkD`dAWyZi|GH7Ck};jfI}eE}-#n?IJ=EV7sgLn*_Fnt( zZpSQ+UTBc9)3hVjWi5pESb|*!0;YNxW&ZgiT|VJQmFKr7lV2i;MKSp;eEH@?n*qUdb)6VX;4;l;B9+4i3(P{X@-9Epr3 zE!47s#U^xj3iy~@>=YC>-C*_2P1H0Ne#r8ZVBvTl@mmD0{bZp2!dJK4g!;~8_R zXd}xX!&wJNj6;Lp3XYv}jLD7z^%!z7mU&lb*cS{I1m>rNK&1D5@UM4UeO(>zw{I)@ ze%4rVYu1u-C}FB|4Rv*M?g&~i%CFs94>=Pt^5;2o%$0F(9yghf$Z7oQX7nw_Q(P}D zVU!LZt*hLT*3z#(5b5k1?_c1^Y`tppL$&PI;Xr$4r0sl2Q7&2^)4_=B2wPiwerkX` zg7%t7NaR`X*u}-Sxnp2{S%|fHyDA;RgE3bps@a34( zhXzKi@qI5a{uPN!PG~+l;&j$Zp%V!O3NR-@cWFkRv zRJ~(iVHgR_6&NP1IzH_YM-vk&r61nBL6W0d@Jh%BhC{mw5q2U@=Q`K;}C-ZWuGT8HM$0$rWw4X;6J4Z;9Lh^*9C}wUlg;L)`M%; zrdxt+S6}-5CI2C8S@pQj@Owo?#l-Y9NM9nXSG8;iFDJJ|VC3?a8-Qsn6eN~Re3VaS7!UkwY)rk~$`AfvIYRbFV0 zfr%;3N0&q7F*VT+7VE3`#FgAC+QAVD%vTTM(?E!Z9)F;#Rc|=c};Z`chHyJEM#xYfA(jxg2LAyyO{~n3>f$ zQDh*t*?D=!E+C7q5de{9fN3^J+Jc^SBZk!2gV}68V*kg}cZXB`|L)2$Rq-@!Ho@|j3N5(NC>)0WFkMsU~uV4RkUFE`gy`Im< zxF7d@KeWom$H$?6m6DPYFaWzePY}fBJu9D^ooy63LqoH)x?1r0bCY`X`}cQ|t;Qpe zQV1~aIg3kzCgDDx{`)4;h_LZKQBjS!*zb#-+RUYq@D3#=R( z`vEa<-3z=TEFMa3yF%N#Aw^_BN8m&mX{T5NFByg_1Jv>K*)#XahGPk0Vq&K{{V_!4 zDa4u}axf-_E}oWT--dXXrWFuGjxZ2f8iUyV{R3HU>)h@`V?Fkn7R`i&gb`{@YSD+v z%0FVO&T@|e$p4qUi*PNRVjBixloGkYm|Y(SIT9;e2IySckZ()JpF$T=(x*4;l;QaY|kym35lSLM;8S7Qs$6d~f-;O6W0 z2=jzDSQp&XqmZ4eHflx)JmW!j#j~|~O2+%tT|H2E9UOR=G)z2lu)BeR2Fv0ybRXuJ zCO%#yvZd@>M0Pt1-NxCUp;$N;(rOy|Jo^t4jFaB$S#9m1GdQ}MAgo+H8+eTM41P^G z?4EKIwoqVdGzryE=0d-w)fnD-;y9-1ar&sn)xgbG)Yk~v(`Yg*f9SU!_XNlB>z z-4`!J*7GCf(wDVRpR_fCDC6f(pU7k)Nj>~GAIg$~f^(|ZXmli_2%bY2OmgPTlXUlG z+FMGPsy?Kw5V=Mc`Q84Xn!;h};b&2V?p0cNkRs=DrU^G~Rf|<)LciF(DdT#GIu+>q zu2dt1T$YZ(pG|>c2I$?0ArbqaW_{+2x2)H~-*+U~!2k#Ck`-7c7gyi7#l zBCtC8k424hE$`y*<+{l%ArcdsO;X%HLi4G*}5NbLIUtCgzh7k7o~K zy0w&SRex|PwFN`)yaQ31h^qCb$vHhcGvkwH!m$@xNY|-oX=gVt@EHA)=^Nd8k9?)V z`4Phj4gF@pnt9raqZ@<=f{MY|p6Nw?twIALw+ZWJ)~)Yo)~T|5o*wS&8wmyj93qa7l~WY#r|!s&4MNRa&D7&rG;_6Fi9K z98~-HN);sPu8phyvgX)-@ZCeT;FQ`CTIzLbsfi4RXG6+!b1b4uTDo_^rVPcar_XZ> zc3}GWkg<%kbV9@6xFT7oF@~j&`lr?5bmt}^m7D&n&^d&b5)rlhovQ;U-~xD+VeiYO zrB7eJ{8?H;^6+E`K33`h7q!j@)FpF;cdb%}uQ;`9_2E%zS@YaQ$u@0h4i!4Fht4^t z+1Oldgabvmxh_*xVj3ED^)&B!(%BpieS+w_t>AOxms#^dbWJDw;$^3 zrzw;CBCM{`nd2ZeeuGP_hP+S)? z?h#&@t!>3lx_tKfFdWay&*(IEvcdv-@#w$Y$uj*>>E?=wY ze#z#$PG+uMPEV=Xb^<7?GhQmlGIuH@WMob;zT3i4AVx*I^-ptU}i#fz9jYXT~kZw*Lv5o1& zH5?w=2rJyt=74Iq!xTy=Ctxly0Mhc6ni5(fly z@@-odrF7LAJqx*>9@YRL%mR9dm1Jdud#IK%LVB32$Spy)XwDlqU|kdeawBx5YaOeI zvUYGo&CJNSNK0!N5I%A5*F1VOm3!`&b)XBLR;$XenL?HJzV+AQo2XgItR=!t&n;7l zI;l3PhmB8yqVwOpb*qHSfnUoao#=S1(h>TyLq-M5gbFfXRv2IjDUtThOZ4;<7_=St zijfjhZqGtX$(Qc&S!U|n=Vdz+;PQUvkA><@m2Nl=0uFSm#VFfJf-LmyQ2Oletgepy zyjLP*>6-2me({Q|lC<=TCbcrc{*{eU{c2~}%no*hXKvfe%Trt&mp{jgs4o7I9DRL!v|N`jN{7tN-31e~x}^(Opg{Tc>+CO9G5ZhWee>Z@ioug%baYhk z&YOJ|p|W73xGFPC6O$RZU-mL2$qogIc+TjU%sE!cQ%r*?G~chmrE+9N7itiRIdyb9 zN&Q@l|8^$I6^{A?11Uzh;H@j&d8;%a;zXT$+1vZ*(WuhaaT178aYR2g?>j5j5g7Z#?{uHJagAz)*W+t%93PHqzTXl#5uB`z-L zA_WD-`?E&IQ+?^O0zn?~6w1fSNJ=^@tEMtAVLX;R?mVPP_fG+OE0M^pirkDhuTh|0FG9Wll#kn1iQc5h^eh+`El@JTyLm;z-os0@aV#BbaeE) zkPr{%M`Ch_v)XRn$LWzE{}RFtvW4k|JaO5t-(RV|%Jae)->^ovh8q{~w@vdtbnP2q zM@4RcloigLR-60z9`YBvIe{q&NlDN2^jI!j@J$0yt=V^KLK%g6llQLKz}gusQ0=AO zvvnF88h#TWj6XwJPKjp|yQd+<9hT;jN8ozFI?@X!G!;xl+H&fZghxrDdAgO3lJRp0 zIQ6>v9m~7@e7DM_h|C;i!TLPjRpGYN#zvO5w!Exo98XwM&=$UIkK&M~Kv6~P1_zPa z{6iMvP!~FDvfZ(}Z9h60sjW`}I?WKhvQQ-PX;HUKv61XCrNJo_-!H&0snv+_VLPT= zXaO12`TJ`^t;SK% zta(xxFc{xFSg3FXK$d1?TI$_#-&p|Xrk`iDJ&vJxN$Bz2IPP?1zv;D&&5~GT_dh7B zX7R3I!ztLUHeJOs!RonHe-&lu*EG{W8yOvag6UFuPy!a>x&5-?COsWpvW)jHa+Ig8 zPWp|7)rngryscZ<4rUI%(bTutleb5Fp{<0hftzEx{UVE6`9nWBig;vn>1|LwL&bGW zPpT9XC%2NA+vcaVG`14DzeV)7+c3P(OvLXRftxN^eBaf13|k{3pQ_EOCa`~Y!G(Xj z7%1`Pz}}<5ei0WS70%KniF`&%w&K|(Htv4ez0%TQ?om#6o{i#Q1!7M}$8?=)7uv-n zARxenFXo`|X3VebwbA#q^5l=m$xD3K-U{7Q>v;1(lCb-{&O=8mCxMw#~xfv2X2OB7PC{Y;Q<;NP1!xFWIrsZ5y@J(TW_I=GS`}6JJT_d1N9bk z!+CN(s8hk8MbQK^G(2p~`zyN(eK$U=eRtPo^1`$4Nv}UQn&8jfwTNf0adOZQP!Gn! z$Xia0o0d;z$nQ%I2U;cslJhkw}z-#oA-p4`( z_=y@-=1Lb@dqk|k06i^JB&ppnjr%IyYyMKH@5w!9Jw0Z!HD&gwlrNxi&N_Ymeu{j4 zlpJOFF?tQh!Oo#hP1&<*G1usmta(=i;?aTk>(Pa9h`J+c? zlzikuUiLjPL_7|aptX)0kgl56CTh^L@T7425Y(ei5){v9@ zK00%@Q-SCG;r2T`{lF|FWTBr?T?7G)gPYY1l0c>9Q>vW2?_aRKlTkK2u^pR^*T1*D`6au&PkgL!J$%NW4-M2uPlLGWRlHV)nQmo#Sx-N>zHX{>J)l@DduZ>%~su?pf`S zN2#Fh20iJy@;#W#PFc_bNTwAbAXdcN?fBa(72gY~2dhVY-!B@jP7#!~o54nG`fYxx zzsrr36x*~sJ?@uqDbQ6M5oomqc&koLtl%?u6J*3Cy?-Z3Pq}U;&;_pI9~#QC=?L^I z&|eG0gxMMUIt9+E=q5`ckd$e`?}+WJdzMIhYOnrvMQVD-1tx^Nnz-&qvA~+hf8odn zcxjt7MUTMVcl2#DZs9S)U*E}5{S<+2Vy}vQov2qx=Vwh*a!d@nh$y5x`LXEJ$mfz0 zCjjC_G#+)@!Y|*>R}1dRy$fBQ85T4P7IW^2%rSRfsZD8le)KO$tNDsgVM{ZeJ&i8M z+*;22Bl-2kv)77?npqX5!=~ZV*yzk4>F5m6$&KZ|H@W2L&hYXh66l@?9oXh3GS@az z{tTr0e)y{j?ibn$ajZFGE@+LEDH~ z_T>T#+t!ajzODI+vNlb5s_)o$nltKSv_T<813z9RbGs1jqZ^;&Yg^DZe)}@luQ#WV zeK-0UfyHx$Kr}d)^C#XNA8u!fk)Ic}o!D?0`_)V(Ct=2=iMw3tydp9Bb)NWbNuFc= zaJ62{eg%72@>I#0nHW}ZU&&GKsfO3s{K;JbRM_fo!^fdMFHd~G7{9mv+U&47j*U4( zGsYn27~lO*MOJU^WRbT%iLo<}Ad}BO7Z9wmzpr)ZU+5R9dbqYTeIPM#nv-U>#>sVe zOp8H>kD15$)66R^0cSzK=?mN8eS>63&kW_4VSG&D#A5g9&ymquI#+Q7Ctc4)eYas( zMVL0NZR7IXVFfz0CZ91s*5595qc1znx5v9{=qnj3@^<%kFaOJ{4Qd`5y1DR9sO*;a zszYvUaQ(s7Q5a%TD{5lM?ROJl{`q~SN5ctAwv?93KjK4j1Y^HSnrf5V-QxJ@7c9v8 z;(9@%QG=lyp3{VZ!t(3M1p(2<{vZ%$v(>Njl68~OHGht$Q8Q*_zCPwSett91B4KAF zL)Sx9D|vq;gP9P`TelD`u^!pkW?pmv|Ml3iZRfGCYcuzAn?4ufDX$=z>+vjxNH*T| zS1lQJw#653tad!%6j(xEk?la5R=a<_RWYxp9~a?W|7S9IvFuR%=4WvSH{IF9_3->dLPXkbUDv^{0xt=PB8t;aQC0>2 zmQJ+yFpo46-!^O}g$buyvvG$t}OUPP^koB8js!L2I`ljg;9=o#}H%ZZ4S&z-DXF1 zS3jczQ~6eMN=ZhWxpwR9@KLYdX5d=#T-+fcTR=UNdK6*a9^QPFYxjJz<`MqpYENi^ z^wIRQXH`86W{WIWQpITlyVBTZ+_3y2?j)1B5!q@oX zg`(Y^@~>1OuP z^472DUcv@p$Lmr;IU%^yj*ow(#y0rjs&t6w->swlFytO~{;%8cIo=o=2-RI5e ze#z8?K#>asvH7{agx)0+pfX>YOkr}Y&TF2F;Y!)X$JoHiB4bhs*t ztI+KPJBle#^~zeEU`x|hO6&QjfVH+PLtQo#X);W+6p<7}U3b(;{kLO{U~<3T&%{@= zc~&6ciT(B;w=}U0{JHr&ykFW0)w-`m8#hjcs+v3>%k}SGTOouW88$4xB@S$yhEh1K9#M&}RuY8l=g}ILeefvf zjBf2M?pMzgb;kWR_xid5TdkRzi^~;eL=@)A9?UMk<+BKTHj$ulSUVGv&yLX?H?thl zEZm$tk78=<$0cAdvEYd7IisFd8>s`OdgcZR!^@r->8p6mim+T58kyaXXx;my31T=VDGjmVk(>z7f0n_t$sSz_jQUBow&$@=UgpXJ-S+Df-F5@b95g+BAw*4mqMEPk7hB$qwh_iq)K zmi;Gp`Xj&IZHX7nOS4SJ)kOn74$7Mh@^;&)ASPlp$5X0py-EIn6g!#w8` zx6|yzcQwY+PfM_Qf81DW9alKY``0%RD-rQA=GDn;oAqNlL6vv>HRl%3>)0`U=^C7l zWYsTcn!8WC!9B$Ix%p;6vu zsblXcDSj=WWIPV=_5AH@R_>pd#4eRRNO|qiP7d+MwhABN$ku1iwOzbN1jRe z_+vZf=(qhJk8OXsN?Q6^<%-PO!?FS1e!KE5+Ud|$n-{)S1Sd*g;i=)?@SA}VY_VhU zHHPz5Z6~(ed&|z~j)?DeF{dXd6d za(E$=d-Q_yo&JXg6?SB=y|Jf_e&oE+`g)BG$?+b2g!TaL3VASW(uQ5wDXu1%ww%`; zUPx@+9`jg>O&pJ{s`EPW#M43NO_t@6*CzKyHH{V^!OJne8rzYq%iI?j@~Sh+M&8ta zUh}8R$xb;e?o35sCgGeliJh$=g>eg+6`|w;f0gNl|!<1!I|MU#@&+xAjHB z{L#O)0U5FFuA35+seE>xl05EXG2ad({v2~uKn`9oes%IQ6me4fOC|W2bD5ifJDSA7 zUf5&E6l9jeVmFkbEIn!&n=mE*VVKN~DtTG_onJ(#I^A&R?!SCr(uURGjdcxPQ6l;^{f;;bwvkI9!;5n#gxDb8ea=2iOD;q_x(17I?pqpcpULU zaqyx#?=dBkjbRa%aCNr-bd6bcAxkrSrvN?3PcS;Zn0x zD>u99_3B&}0#I=Ul1I}he%*gN1M_?@K3ks&e(U+N18IEIMuN+}aauiFB}K-YwT%W{-p0hXgVL&|wyNQoV&<}rKzf$bk#&z}AOtocy zN}|Qv)mohYTD|HmzX0IXu}V18eB`7YatCT}Lp#h|E0({}GcdfRY2bV@o_(hA8cD#} zn=I}GOJ#sQCP0r25IoPqvTQsVBk5_BQ_z+(3|%mG4;@0=ihL!i5G<||6Nlq(RgaE} zDu!KuFw^k&_rGO51e2zbVpEi|G7T$Vf-nyckC<4zH=usN9b4=`_+WnEm_r>6eX9?%$jUQr18}c<=xaw>8B#D-KDufaSxEyP<{# zvR}bCniOfzD~xwF@XcEC9qF<`{R0C;1@$vefa%DRx4l`bUS^WB1ivM@%<2`ckkQnd zXe{8=fX{<3xX2$qj%nYFb~ad+cSEJzc1(U_G#X~vva+(k^7DojgaK8?96)HBiA4S* zMtFx_$KChz{5JtEj{`D_53WV47NKUt@#jDNY~{P5reImn-(3i@0`Q$|UcUjHwlJo1 z$t`1SXAfcfqTz>BF^A(+#70!24^^f+3xs%T-Cw@%CKEIY!q_MZ# z0YJU3XL~ZQ#7J~#T~pKj3NP4F0_+aQX6+I)!%M8Pi2^Y*UYMvD zfn7eWkkf~y1)j@nhf{3Qd&>&DKG=#SGvi*vPaeHJGN5UC_nB<&^UEW4t)w;n-}1AQ zRRApzQT(<3?hNz`UPO^Kzpzd!;U}%+w)jIx`nEJ;A{%R`|Lw3pH>GU^;1Dz zHozDvhL#2fR||M~csjbf10w2S+zq^d-1}aSzgAbf!@82fr&GgDW~xOR9qB}Q$UR1k zrvg$azM7ev6S8G7caj=g-Z%>Kez49^5%>^dhiB08DNxhZRW{@LzV1ykwKu5}`0k{O z^!m5!fUaxE2E-xsXqm|>|JDWSeRfzp193g}Z>sL@uH-qyI=91?R4dvl&)|KNa%?YB zOUVWHwQK`==8z2J6N8-kdYNLwJag?un~z*(2wt*yJw3gX=!1iy^11-RF}SG}Yqz~O zZtqeuruT%RzkHK>7h7Ze#?KFMdYH2v!M4gKJ`RkR)hQX6aMfA=25P-x(6Mv3{k5nS z0+Z6d*4%>$u2CHLjs+F7q!HpAJ#&Ou*_?CC|DeE${IvSXtF7fi+c zqUZ?3)yrG~N25|(*t1_~86)%sZ@BuU0U=RUYKt!Ur}qKQJ7$LHAT5`3X|`|Y92J!~ zjH2P7SGH5lw5&g~tCf4B^yxyH+h*fMGMggy!v|uAYtrv4A!l{EJk5Vre)fp}Z3xqe zo_E>b9VgoJk=S_mLR2som?9;DSt1j(l8<|f>B66Rcu0ndaIkoJ-n(}!`f)K`@aA0y zGcqn-UfY6nAZ#Z7WJKl_6tHDWt4XU|G4)um z>5cTnms}cj$xoz-yxiNgynE>*;<+EtUtRufZEwS{imbq;a1*SDuNTo#NInn$z%4ZC z)gW_|&3so#>z$0}(c!;^g@x3Nj(>=xq90Q(9soAjIZwv4y?3;sEk@O5doRGM`L5u# z@iM!KUn3(Gpdn#vM8|&s%A@^e7X=sEtUK{lf&0<5E2c8n7Mf)3m$?||o*cMg>wZ+x zBOWfykX;}hMKaj@!I!IUhDhG)=~1b9GSykXxA-Lr%U`qB1dFl?(@S2c?NTvq!H^NA z5*-_?Oib@2vI`5}^F+R;_r?Zt0s;g5upmsLnc4(GmHh@R!KO@p zUiETexqJfCoF)S{$Gf{hXF>7UH$JYXsTp|rOMwC6c?uO36)<+mgs4y2#m=)ZVPayk zva%YAyE{4(rQDri43u}D-6dFEjXQ|8(%y)l<61MbEFprslLV*!LnXkxK00%zu;9>r zwbuL+W{*Ki?cNUxc@b-iUZTZcWC-|Zkbj_x_n_Cnhzn%ug@^ITuo}6SQbNREl2o#88w;77GZV3py zkB%m+uAUQ;FhMXg>SwMq^Bb92>gwt~eR}!%0Fc)2a!96tiY%)@uUb?}YP`4C4i+@P zT8#UDAu8SFJNprKcZBp``x%Q>O6FetqH3#JA!90W2){G<4|*1?6IZufH}AJMtDnko zA)Y;>CahL_;2{%ae@Cnvv{A=W{nd-_!*KS=5`sI<;!bH=whLJozEdQy6u+G9h-GJIpR|RguO)Q$AG^rRfSUz1@6?u0Z*6VuZbD)rX{am! z0uTDL7w&q(hPyJoQlQx*_IEY(sa3qduTbd#5bK)`Teayd-nl1XW84{!2BNi%^8Zz6u!u=_2|cQjqj=hu}mTA*dO>#k7?n zWWsc%^r6!o^)zl%n1)lg!GP5$XE+?kh9xl)Gs(-PQvEVAu8jz|$WIoCXPviUlH{M{ zExXceTyYcC%~v0>vAst;O*88-m1Pju#7(XO`%3+pF1|+SX3eL0v-TXqJ~ANZi*p5u z{H*tuNw$9R%5QQU_NuD6eoZOTxHH#O2}(jf2EP(j!5x*9JLGR4 z3Eya5AoQYnc!>4h_b^QXl&nScG+dab6x;ya`SU)eAzw~>Db3ENT&Xw9k%iMn#}$H; z0wQYiz)u*0^r}sFBLkc+)6&Wk{x#LRQYVcB`WfvGf`Akoqtg@LLKn!0%h8{(CsgrrVgG6 zZfe=hfXmMtj#ntX%BFkD_Vful<|GDv&g6yR?GifC8y;WZzkmNdlk7ItaA{QDGj;V$ zlfGA3e9(kUAA%&nRI;wQVsD1fxrbggM}!SUgolgnumn6kRX0Uv@bV$LYC79!dSeT!S$K z+DSKC>m%eKu>Ov1w1b1R8_U6RY zBqwOs-J4wwszbDujJJWyMdVu7$>6YI$X$lu>dmIu)!xqTq*g3~L;`Q3TUWZQ^~5Q% zK^E~ppiFj+?b^JRsEV@k_bc$S2FIM zBAfPpsKOBcP_xV&_;Bxyxknr-G3LfAVe(N}fdd2XX{zbcD9o-?BU;>FDL(ej;)H-%VMu zi97ncO;$%-)wvD{3Ne@Jx*}=iKK(a!2JZ?Cn*IGaAyi`#TsmxhbQXtyP-m;odkDHL zqFi?qfr16g6I;M%7M*J;+1jH;Etz4$AY-zksi|q?glqwV#djRTkWzrglP89k`Bcnx z=k3w#zrO^Mq4EAEADTqS zQ3@M$10Q;bW&s#$nEKBF6T3?)$X1nZtcAzf^6}&IyK^?SDk^7psrKLRy}O8l-@Naf zb$3#q?N#+Pjl@iiN_yvGLa4Eeh{DK~ocH@m)F{J%x;xi=K>w#j&E_4099*Y>Z!CE( z2Z}jW#;Z+Jc-L=s!H3w_mps~Fh#8*3GHI@=dae%CydX}IWF1#1l`h|{m9H`+b(^U#y+wa($rY>8DL-C|_Vlpw z0pPu8ufjnucNq_}!BE(;0Y+ zK;urctKWXVL@S5Hf|zvsWE*U$=;xU^>Zd$(Ubj*7{{%hKeTC53oF@ ziD#q=gg}HlZ$4+YKvOpr9NHgu#1-Pyd3y`=-E zUkIqL3c<2$(2zCpl~STSX>NYa&81;J!Ss50l)NR4ZW{<=BER%=Mo*A4;D6t%Ca}yVi8@>6e-thC(@rwZ)Ml->eEbc6GL zz5*l#@B0JAvK;p6)vAjQm65bul_JQIeGO%Seg&l!Rr`IVj{J5f;P2*v$Hdr z%rzz=vsenH-Oo4MgBAHr6*{;D;iFdDZP*!9ajgVmt&CTWwXaT(QBp&zrcsh^jZ0BZ zPEODH)-GI`;>q9KvUl&^ef|11)NR(*HyQrC$HM(9)F~3DZ>WM&9GaenvKh`BTInnj z5}z7Ke8F-!RafVsaCT4>^8ryx({zCFX=Ge%tZ#my%_bknMOUUvEf>l^DC zmM92q`0MXG%CbQq;`+Y>(vA+%??7V3Qy^`YpY-L1T(Xuf68F7!zMx+GN|XVhhzrK3&Pm=KzLwynEGhyABY!uo2MX00*QO2$73G zCj7zGvFMCAeg2I)(p=#C`uz)#iFk|^UwP)t0_(OzFsq}2%VmcemB++o6(}e;>k^YY zt0IU(EU`XG=vUp{CFKb{?GC}6Pu_@f0xb$rMit>6wT1oF+yCLi2Y54}aoybB262v> zf?sh4)L|m}tfiC2$7DbYPx=u&TUdO!>Qs7{b53&5kIpe7Adpv>n1IDoBrx6wZ}|1n z$g!{JY)(0dHIlN?*0C`$A5KbEf^voNOXlE}Fqaj{VpJurE}y?Nc?pLWXHE0)z_O(6 zKLBNqorODT2Rx0R!hPyqkt?*cP6xXVk2Ne6GZMJZvE0dO=}iNm2}%|akH7vlpReRN z>ShzJCn6$pgOhXb?_Y=7V?uwZlG>iZJZ_#nw&=qveNXEJF9U#wK%Si%6f>IY!iqjO ziiK{n{WZ^>JBjH*I4i(LL4VlV+KLwo4*+f7?tX)rhDHpMm!H4j>sLC?n^%{NyWYND zpLwW5?$}g%!yncdvoXsT8CU}85M;O!f|L_Q#<=a3@za@4hdaX4zTxKg(2g6G-mthS z!j2v23#*IVjo!(-tdP>~EN zVfuYCL6+;XB67FAtG}Y6VsFolt@)%B?6xj>`K!hiBHeXs`T+H0+C1X>Q2o!On?uU0 zZeR{zl+%2OC8>25oor|eFF$kjko2;!;4XFqU=oi|Y8&v3{V*acmZueXeooZV8+BoOh9skRYBi zH#M~#DfC?@FKz=Ct`;m@a}aYoVx%MI}8czQ}QI~_kgMD zm4gHNx|JXHG(GZh|7tLH?Pujl$vhCFC)|0p2yw4LOHc1i^d)3~gF85*K;^~l6PJn=wwC$a5$4i-2L&kSGtZ=83|+1NQ-r%X)-r3K{E>K;#x!- z0n%>2_@d6daSM3LCOG}#m!QHEvpLIf{0+SbhCOx-B6|WXJb05(_2c}O5=E%{^i^YwI#xzGOp9@ULtEt>hoaoJW^*R6HgVBd5iVv`b@f#0DhL)8PDeZ1bPhXai_NhzVnx)0&2lb5rv+iR zTeibi^}j(RkkyTHZjV6CTI0fyQN=kx5#_Me&Nu6-_)4OV#OY5YEOwFvGuXeWVsoiy zKo;=`HaLDO)xYYN6#NH;(4a#N$LXkmn9v4w+38M_@yd9G0|BVpB{&b&T%ezWW`1~h zxVcd9HC5j_E2uu_xeco|$_dXDVUqq1s&ZYR%f~u*=K^*MPxHw}Wa&M@N&oa`a6>F4rrs*^HTwVS5vAop$Co zzgw06u;M8(0Nzd%mhQAp31nstl%=HfaNI={ysd;F4aNwdrk$msfq?1rUg*;TC=cL| z!}B+9-Uw|7_ez%7O-QeNqu$s8ouKkMQ+~) zRD;R6AobmhlOkh8#8^bN96R{}1f*22EKsFG`3f83G)QzSs~7I*Irof;yLrTb>_6+QxMOiWCFjJuYSQ&N`82EIneWAP~co|NoADBc?Y z!s53dY)CnzaW4?Ar@$dD<3g1agn415Y6*0w(Ojxfau|uNyuyCPw zJli}C^zgCu+0I5QBZTw!iHU-fXZo&SO?3a=e45|q;_>rs0sIKA{UT@yjM>uWxE)67 zHHv>MO4r>-jf53*4s@a4+k?k~z%9dSl_b$HEowWKIvLxM(NSuQ(a+)G*Ki;2-o$$w zAg)-Ht)VO4lVz^ukOU^k$;Y2rA^OB8(O_*FQjF zX3~KRs!}Kd%`csl(;Q75vF7QMC%n290+0c)*Z|K1h?ev7^VeBqa`Nx~d5&%24^Rua ze5ZTor12Aytm>bZ)yfxD=bT*rR-m}gmvQk2+pf!YPyQqVxzwCI$x z1P^gTL1DiiY1u&_LYY8{aMFKgq5)gk8Z^iFr2(P~^EwXDo(;KpTs|%d>KVxAOjoXi zvr6fxs}JDuH7^&?a(&@K5s z$uuUqR>o9rRH)Kz4Gv4!k$$A3=K^ek@g~00{}%5qveJ$xyN*g4$RXsd27m!y5sF8K(p+V$&i0t2jEvn}elSYSy4IL19s)cZHdm#+bs+AL_Z*wl+*`EVxdA5;`GM4)}n^ARWhIvCv6E zlPN7f@Z{H9vMCTb0l*qU83dB}g^B@VzHr*sv(iAr#=BP4!R-eKB_D2oKcqWS=gd(? z^`B%K93T;caXMArmd?(i-vAF*(o0g*QbD2qtgQ63w?`&8d}I=}dHe2N^ZkbpsnDRy z9v>Pi8-48PDD)^pL3R}eR%37 z=IS))xM7?rAS_HP%Efg&7&=NY>%i%#BtxV9OQNR6=BE(b8!Q;q72myrpIKeiS67ct zO8SF|uHe6cd*9*gkvNpFAGav2_FomaybBD?Z{736T_Kg2!9S?RT0mg5qQu_8Ah|(G zA3`wr*FY4$f8S4Nh3ciPogG99qcuOQg!9tdTP?4S!xK~pX9lddp4Cl;6>Jmkfd*oY zSgL2m3k@N><04eI)BFVzKqk6IOu)b(7zFAt>J94c39oEI$ID-}h50U+9&6_7E*KZm zxt1|PNY1eFSp*XQT|Jhpmy$Pd1j6WhOX3>Z4 zqDM0oT&V*IMoIRbIoL6x0^67g6jurntXO*w5DmhVPdZ#HF*l9poTod+irZ$b1*Dx#A3jg8V28oug1WMieEkjTD{Kp-9}Dl$xfV)g+M1vCC7dMtE+ z91wfc^{DkzjX-++xl6XgY5r#eF5=z06L8K@?LId!J&?5!plQM~?4p71Ft{P@t_huh zmDltx_P^81fdXp<3()LhMz7OyxS`pOIu?Y+JHQc|9<$qOo1k&h2MzqHhmY4OZ+ zM+)vVXo05<>RrRh^``}(?@bjVgY)ejgzz# zLG;)vdW_xBsY6VyP_dm4bPhRpHi@>){ctKPw%=XmAG)wwtKHKkwIG;#^=;&bVChwW7cvlxR z#^&bp|4kiW!@Px-ZHrQ=MRL2@V9Rt_k+fvq3Z5di5fO##_wn&*Y`mw`lBoxlc#gA5i#KEA z;}t2EAw^l@X)nSc0X)tuEqIEdG=PSBiU|oOi{Cms;Sda5I-MCAO1^9#esMe;T8>zl z>muM|;$Dx}|DZsEZcgAWWwsYI4Nx+Z?cDtis3dqI;qkhr8gwOe;!r~GMbc{E3k`vb zaRPmJZf(sTd6fgA?VLj(fsvN>&1v$RFIVuDaC!N8c&MqUj-d=M8DF9y0P<3~oSVlj zzCzV?5_PQisr-Y%Y)~EPK*WSmn}>(^L^LSf35VDRsF4t|D<3F}8%tGHRkK}ss8O(R zPuqkvDfI$*h!QYPlK;WQZ>qt=_(+Uu-w6!&KvC$^dU^n4(7$2ZJn9VelK^vEKc{(t zu>n0&bTS02Z@j&|%Tp@k0J(6*)83L}PB%E!=%JTZp=W5?Lo@$azGk*NL+gTpKP2`@Chr zy>BB_8#kuvuam3tg=(?AX~u7!+ounGq@>ie4s{_V1qJstB`qy21A`Au=n*xocwzIi zp-)U$%V|q%J~FIduvMMpdi54mc}7XUwfg?knG;c_o`_cdS3}zm1b1xCpT6=T`a%*1 zDw2<|xc_z!Ek-C5^_4zoS!k}7dHMUiszouZU2~f6A?B(AfJzG2FwgnMc>er{!msJ+ z$C8@+!h(YthlNYSz;lMi=Du`Y*?$oS8j=A9v>D>S&F2RQGDb<-Q0B6)5$C={*(frO zo$Lfe$iBaQuI7u+qha>VY?vUf-LcN0eE;g?p>cv{r{SDP1z4r4)iop#o?cLXQ&R z#)C1;E>>hESZnW>`|3|K8boUM{}A#Y52+@ACIm5kXyBJ?tcgTjC@lw$RMroHZvDUS zmI&bm3JzUQrp-Zfa!Sho4~w&;R_vd<+gS=WSRgwO{ZjD1_rU=ev)`Pv$3}9GBR*Pc z&fRgl!OAtMi!}bmb198sN#B{enuHGDU`yM35E;??57mhFxVeS>_E5Ucb_vb98V6LI zdzlZ^Sfe6d+Q#YpFRXzv4fNK@+>@Y|jPlI*S#vx*K3qXsp)&dXHXF}&T*BjtMrC4(pB;hI2guO?)}nr z7>QO=4Qq<|uR!<@q7Nq-H9;m>Ab^O?J=DFw_`Mjt2suBt2MaaB{~G2hFtbYL-X@xQ z=4=y7@z>YEw+@gf4gJrg?lcxVw+F{4jGY6MIRA18Ip~<3714V}^?wft1#B@GOV2(e zqGEwYon7+ZUE;m{4o3h3CL#({LY=@>i51-GlCJy7H%R&=O<5y)eIH%L2mRkqhyrpu zsk(7hgby43rC=G&`#MCs!#Rg}C+mNmiw5|9IBb!o7`Uf?`geHGwAG>BWX z>Yjkq=>6v|&||a`skeudIV6_!O7CZ~XZ3lg(s6aLI>lIMt5B189;8-~5pr7HMJ}bi zT^I-N@DD;Ei$~=1pE-S+K!krv4gX>%2aw+y-t<2(!G_i;jT;Wg?RWDpKbCP}y>nJ* zO-Z$ku09VOprS`Gj{;yN0yn(=R*Y0wf+40GE7aLAEc=^U;hmG5=|7e_!+>7YhmZ9F zH|+QZ%9apv-g)lto?Cg``$aMP4;=J8t=u!j)c$iz`?#e489rfou=W*tdU<2T1dz|` z?*tcPYnjI^q1!I;hSN)NP{S>KE(n{WMSW#cG+6 zz=@yOQTVJ8V4YO496avvnST&j`W~SB1;PgODh?Uahr7nuf;!TeiuHm@<$oRvB&P!m z(i05G>448!2mS}1TQb1SyQ30=$-d#~u@-Hd^thZ`PakSjXzj-yERdZ4Bv-(P%J3%m zeqfU1{-G>Y%+DGsrElH(U-7l&;vs$xu@h{BS&Vnrc@Yif8DsyCwYLtcI_my^kq+tZ z?vjv@F6r);2BneiP6gor5=tHrkVZ%Cs@)23F4LFs?b{qrPVWnm(I7W6-pV7jzk`QNi*Iy(h;=!Y8Gv!j8I=r@(% zv;WJwfl}R;by(BTytF$ERH6^p-!-WMQQdXy8tDbdv4F@_T|&#K;9q|FsGQn(_n!&= ziAH9p^tVmsN{;x>eZ`G;|GklbZ9WU~B#_0jREUFmnK;0Jm}Wwaobl%Y*KSYxK9ADk|Kcz_5@f&jQ7-2p!+Nc$ z(%}vNa>~Cmi-|F)B>Onl>S$M3={xwl{}frI9_pv3tZI<2BI-l*YdnRHkiA(ZqSO(C zyKa2-BvT+>?B@c_Ua;H`@S%pKn?V_%YQR%irTm|&XaXo(xzDG7((K=_LZEl#{T1YM zn?a}GSs%qVX93{gzZBw2&lH&dnFc7)dkIp2pGh{o0~*~wJ^^4mT`TM_B!&eQd;bqlOO8-wwh^@o@P;AMK*fE*TtjwVV_lo&rxdh4siTOp3Jus+N5->(2{ z5zwzAOXLQH{d(rbkCmIa)uu}4Nm|;VTRQze%XRKbmg5odqk$=^Dk$It1oUuCxm|*j z5)=&pSm)#DKgh&?!UcG}L|Waoe@7HRhK=Dnx?ge%YzDmg@}D+KrJ z;F5$UW^kPo(gbS~gef57L(YjQU6(b~1SdQ%c1afqhYKm4TL1RB6TBKwaDF?Cbt|1e z{dc7RxQEp?u+sqtF)tt7i`HZ{9q|{Lm4?K$wpgAim)w5cSN=u2wPZyELE%mZ3?Q$P z3Ff%ofAq9BtQ@*z*M(JpDPwd*0A`21N4)@#j>@s`njg3Ao2dIZsO*0&h_F0jJx>aQ zzEA~v1>f@`unNF|L4l1i1!Nk zx8Mx{J}iYY!01E)re%j3*uB5`y!5EW4@A{bO*&AH6jlQDeA4*{kiB!*gB9T&2UZxk zHe<6U=z8~t34~An32CwOB`OACX8#H7a>e@f+Qdq*7Li%2Rb!xz>3$4bU-^=PX6UqH zN)7LjkkST)zkoxVc>NSan@6zl=^3}Eu4Cy?a{7{*N4PvuwR$IIu*ELG*ZEjx{@~+|9MnfEO}l5Vo^FnPQ0}`Y8q=#UI7DIXcNZ)+aj5uz<#7oOx+q64>t)`Z)m)z-n$WoC7MN7k2T^ z{64doN7!gp1FhKh&!pJQSP+Nj>bi(k3D~2ej8zxly0Wo;o)qx)ctDK9(;gD0j4gT+ zQvA%+ALHaUh%iX84LpYm4>~Tmy%zbHVMlMtL*)ck`hlSSx=|SJ#Jl3a!)W#>Li8N3g`d zi$oWOLHD+||0x~pSi)^?IAzQcduB0|Tt8WW8w4PY-wo=wTAEz~visD**3VTkwJQ35 zBq%Z&Kd?i{5=R5`tA;%wu8#P7=HP_oypC~Y+kS7G*6h2Gk^zO?Rl?r(63lMy?*nDv zUjSpWgCIBn7wM#oj3(#BSH%6Ed4RiP543xLSb&(By=mC!YT@No&$m(lXgl0o4(VEs z_-Ch-HK3qb`0p`mgIlWA5i9<7Q@J{1uETqFgMftOb9y>Tb~6pFK{Eigaj1NG^-@BD z`X&;Sl$x3vz@p<%!F&UNf`Z<(VVylL$0%42`=A~Ja_mL|6Zpu{((m8j1JG%_w4`G+ zO1R}n`K@zf0$8|-w7&o(iI3X+iW}f~_={`*oqDzzR!36KM`aRZX_ii}U-tr$vPr{1 zI21H|0h6Dm9Rq{tYmkl(0aa*rHU$nZC{!|Sf_n2m1y=#^#pX1uK_sSSXIG{zewP+B z_f17XMFk)-_^1*<4T>Y^8DSEX6|%3!eE$HW4a z3NQpZ`%vWc%Z(yMs5c{`qnQ~Q!DVYW^JQ6nei$l%aKtZmxB_tzl0Q&!F{@_2SLf)F zf-kIc82JIj^j36y$|Hf7U*@17038wEf~7=thp3suay_hRY0K(r-An9>(gl#w!*W0I z?r#``Za}r8iq^t63@W(;l)P0KHOe#J4a1+WaO$zr!<*C+h(@cDgA2X%@Vh3~9K*e+ zcS5V;Z&>>T+=P}>Ks|~BBq4PRG|^TH19VVdg@L|n9g)^r+Y4Y*T_(vWw3Xog@9Id* zU0n~Z(vI*5cr3t$yX`|HsJvU-*;&a;(y2@*BqV@le@{z<;l5?d6tB$h<~9KC==%EB zzw?^mXUv|ml$>)P3MXsOE;S~r$15<`P1&Y1L=Wf`9t|v(C0T(10;&#UDP2_ZIG(=l zuZ3${Eo0K$YG@9@D96gAHm&>NPNdTp!*aOqDEH}VIpTpurVQ(Q9w6H*hyZ~LpK)Iq z2D_?odKqv1L3|Jy*G0e=VccKSynXv5Brb6^l8o;(WZO9PTTCu)#NjjSln6(Bo5?Sz zxX8Azf5rHZ#|KxR1x`76*%reXCh7R6raW&-{cIl1DVu**i0NKLJ$z-MxrYW;tVde} zUBBP_TLl^@i$A@)xaDqP*PUps+l6m;m-|4~w9;7-5@?QI3D<4yloUzI^E@ zWQ_apFza^@an_rNR<*bWHB@XdUCu|5Mi7xfJ?7>si~TMbk+}Ygf4$ch4EySTx$mJ) z7}z;;3j2W-!`SZ4f%jK0RUEF+KBG|1qaJfFDj6xY z5g5G^6qM)0yjQ*z6^Hk$6rX74{*sd;&$S^Ic=vv4BJMJ(a$O?m;dAQ_fB?SWg-0v- z7IUnsEU(vG1mpOHMfP!7LV)DW1ORIbbjIh(*Jr0K0bmJ`nSw^n37^En)w5Skb#;)( ziAwV}X(QoYa}yI;r9$xkmm3+4Gv`7V^ppsZz6$2P``eagdt8Uff_`{L2I&n{qdzhd=^Nv+Qz!QJ?ytKTDN#t8vC3+?d;IRsjy-s%OI1L++X|m4 zjegbY2uDo_KVO{ou`VFG&iJPQmi387#Cz)@hh5-}ej{aPPhUsy3hhT>+8}pi_5}B( z{^z%>Fi=d;Rc4hiL0hs)oJ^7&UF^sk=B@ z{5jTjVu^pPoSvS3iLPz$U@({i21>}u%fLq?zXBR`VhGz^^E1>0dec9Ejd;O4kBp4`{{1mAQJ=Hiz@?u% zGHn7kGD1yVeO+h@u8Un33{unutI|Z3N51Q;@^g%L*acs6r*#R@=`9><9YF=F#4Vy7 zp?h;(T?8cHr?_n6Td*qHNvNq5l88FJ8Oa%6S<^J`(|~bvk9Yeij6>B;tp^zlfBj>0N|_4wsf(hcPe_?qTg5 zn*!QT%>VrR@s=-1(> z^fMvLCXHdY1;Rbz_$-Iu-vB=gl6nQ5@94&_m1zsrzlyF3eyx+EHc~2vq(IvfsU=8I zD_G_4lC)1qBPCNR&3^d-oCF}7nl`$wg3=)9AlyScW@l&Tkb$QK09sj)Jj-TVDHft5 z{x}@7&W-Tm__NgM_ZWHGi>hL5o*+ftA@{`VGn)iUYZaF1Ki`r&FS?Dz#j$m^W;!kBGczz)y(ZcA_g=S( z)QylY*;*Uh5gjWu9!Cg#43h(gRyO4(k^NXgrXrfDefyk$m^ho(guL8q&eMG~)}%Ag{_6xd1n&E0nMSeqs#}5(2T@r*K5?zyLFSwS9JlEiyAoUXeC~+Pm#t0 zzOXW@F%}o{2n`cHE3XI}_;#lnK7;m><)zuo%IC@W{FHK8x7p;-MKj5y9f+_;;U!u4_zy*P$VdujKx0)8JyfeH>)WC1$w95hwnMSD3% z+^GTz->VxKRPy&r5lOB;+?(2!zv(?{@=F#mcYi~pti!v;)nSXd1)-Go`-hgMi-&tG zZ9W&Bi$P{$@^Yl{vTd@S5Sei5}Cm^+aiM{$`FXLRARpZ`Y1+oN-;{$~!W zU~P>*JIqc%02bMRkdz@lz9Bsrs1f|~W+_BOIhfyd1~QuRc5QMKQYn`A&7Du8Z2crh zp6#|p>QfwGi>s~A$PSZp#O25gIH<_Y~G(}_85gLh=(2)AYPOh$z6N-Cz8GU08sLAbPmD(=z zBp!o2ug#2=9bgYinVvXI7b-h(_2SPrb)G5(VN>Lj{1dSUVGE%d8WQ$2!nbw^euWF@ za)}UWu-nIg*+VBj*44nqSl(fm62Y>N|;whK2nVUm_o<>x1F8#gvjN+%*Z zA0c!RbFpfj=ID3Q zi(w2+j+e1aul@LQi`HkN`68JxkV|S{fuu)+J#5a*#raDX1mk#Tz|xKgW8eqM`T*2Y zlh&phE$@5|Ek1nN`!CBFOW z&y*zVR0NKCdLxOs$}vyo+aZmNyd?pQ$IoR$z0ns`yC2RFoy1n=JUpa@og&}AN3wH| z>-}Do5^qxvC9vCwME8r69m*}N_^2j_nYr^_DMg0c^Yb?48sgJLQ2Kn_nOemItY+?J zRFQ#=*#S%ix*Iv132RX={!F{eY(>5&I~MuCSc<-r_ygwr20pe5drtI2luPr*C()_# zcc&zY7cgGzXazxWNPC%cdE8@(;#awJok&4}x>qCz*L5Q+uDivmScl1qt5{J99sLXB zPO*zq8{-4?uiX%32?7cqgli0uBUWhxa z>k25E?#U4vPkyq!{Pr;V_`>9=mYSCyB*oY>>jlyH*k;~c6dR6J{&#!J?H3RZr;Q1t*4zuAi6 zet}z&B&7_)7`Klw{(rC~7W!XX;{P|dU>-h!7yqYO@GnDSmvvc!pwY?`z6rJMg)KVW z)XX|lAA?wB1|L(-MRY_Ah-qxjsGwK25Xd>1=o~U9@pEo}KZpCuL z)8jG<{1MnES2}op-=KTGdTHZ%u2-7grf(yr`K{mhn^_K+30l5nkmZs{THRMp8V?Za zZ@kSco1f3}&T^r0eLQl0tTHTbD9d6lizqwnBczmURzQ<(kXs=C%{3YPXtL-xy7jB* zthubdt%9n7FT&t`c1$_<(0%}E2SvP+C~iLXlJPUhshUsEt+e7uMz);58+$tUMtSX5 z?@s!>v!+HG{Ij%Yw%Qp2b;Oyqkg}(b+8ttl)qURJVDi)uza#w|baVb#EXHxvIT4qi zayCZwN{^Y9SbKVwMJOkLpwh{}@vUpO8a!zWY_1mPbuu9@dp4c8AgPq7} zQ4KsC>Lq;4ko}%H(a^mwv~NAgCZ@@p24X>SP@cj$&Wa-qhx@4#svSbG)^8!1TTcF^ z+m}hzPeVyi1Us|zni6}j>vMfU#R!H*3iihuKeDi2{aCU~4qM5_2vNDcI8mycSvv_D zzsn;NUV2%!mB0 zsGu!1X^*TOTIG+AX^|oE-;ti)4;XMexZ5l7kePDEA|j$nBM-g#i+}V=1VbCspXFG*pchd7DC>?4itWy_YA4Z zG`FvU7NZym`U`e)J9NKXKMD(|aL%F1`8w;rF}G-{%W~xS&h8k8f}g;s{Jy ziOm{xX?*#WmUo{DK2lR6$y0Jn93_W#)zvERBw?$jCm$U#$rpN_R{P&gc~stxs=ss(XOYlGI`73ugKD~c8c>X%~Qsl~Iit{qyqf$d6SydwO#>J)OI9)Iah}EiWr4$pkwX zZj|a1-i~UlZ^!I2A=D)+{!26|Q&#j$zou56m|>MPDv~o4{%Sex)*J4mk+1(TiT}Ub z#q+=T-*fr@lU=+3-^>5Cix=km|J^SBC7&R_(Ek|$4t_;v5zM3tJgGnET@8i}{1L;A zB4y%z(T@0;bmGNEHm>mYb9lsK73_bthVT1)-08&A@fUJU1Dk~wRvyY_gMHkqV4IWYzz*qbOb7{&qGhA zsuTC}ru=4zJ2{#!#d=tc(ac$m9Ud6 z!jO$sSRrJ7trmL9Zh8W-B4hmHqnr0|zYyob0$b+#tM-In!9z8&FY9i@0;8mZ9X`J; z`v802@-!O!#85lx@F;(q|z2+K^y>qcAZeT&=tc z*Y>(@hVx7J-<4lK^tH0#Zlyh=I%^HUhphK|uu2S~$}vD{S)$LHMb`=a6d|H9L6uKy zLEU#qEDc7tp=N>|`(CFt5FB5NyoZL8hjU*-P0d1O!xEFGAi=-VPn)okC;W>Pw(AXU zXNOy57T8gu6EiH>O#;hi(|mDkW`Xwte-~R}iN|;UX$Dz*(B>&E%|UAUYg?+khI8YB zoLN-RD_^cnjk?!OaK02zN@gU-a?5Aj$;9-PAJpwyXzzZU$~UEtGVcE5gCq`qm~yG9 zsSaYi>kXhTeBN(N8!7K=aFIwuO*F8Svj|(fTc76EZTr<+$a8}l2e%Ji_u0DBOd|DQ zTg#1$=95E4YVnz5YSt)q;D&bF0A8?{L}2BIUmq*Kk8@v+W-<(6CSV^0zYP5(!e$(G zcI-)xKPX{d=pOt;M}v0Fy&U4T$#Yp;xezBY*Dh~#vrY}`r@qyHlW{fCkdC6+KHpF2 zi<#4DM{Yy9-MQ)eWqe!d0)KpT<7FMU9vkzN3nZxIF7>=;r$O^}D%gEl@hJl>_~U}U z8EsDg_Nz;bt!S<`OJN?f=R>vbe-fk_k3)jK^itbAWCTwzFy|56K0IhV`KsOzwGk}B z2>(8*UzOW_+4V^CN(zS6BAluDnM$(2?(`2?VKDZ7BT}{(w@O4$QQ1SqHz{NJ!F8uF zjvI~_ke4k#N4cKl72FXXWyR;$1<&p(+n!!!ocld;<P?cT#V@Wa(39kyh;7{>T_G-RmTMJEOksk>(=z1hdypJ%zUy z{7R=BjE8{cAj_WSawdAO&m-1hB|$Ykfs}L- z?6eANb^}#~pc#F9z88nHNT>?uy2)3XGmk2pe|or)cr+-J8(lB(B`XrN3e0BG$xALNxXiy^9ALDE^EY2JCId!yN&D-STmIBc)?k57N z!Rv{m8}&c!O=&3EnbI$^k5!ziZ8sYq~EuGxXhFGAsUU$yk#tqF4T!+JKZu zUb$>|qXb7?{gr5^jJxe2xi$Z=jW=}qyuqDsDIP0J^oBQ~I7R>WkT?e79p~T_S#Z3& z^`pjM@14_w(obyjCYP|&5HtN`oB_x0%lJVH50GHyToYj@4s-Vpt;6rCLe|NT3tJcp zLT;CXTXW8Y2Gar_&jbheHPwg*+uZ2_4VP!lpA?^0!Hsl{X4c_Ict8IB(>sWp+~mm3 zKo4txTMwu4#n@~U+cejgMQ_8$#ycdl`+$#Ekqw_F{??1;1WqPTf1wvv-Hrh~m63i0 zKh2LigoeI6qg)>+@iHC`Bkn-ZL{+t zUK0z-F;q*Q$l3rQA3|EMJZT#k{IZ(GT4~b8d6&r8kyIl!^q}@`BkYuUC##QNwYNKbx)LpnKW1E z1JulafPI`A-5k&SYrsr!j3|5?Y)i0he3Z*2eql?Hh=$px*vVe1b%_2?uRFrh_)R>? zS!p53NqH{PUzJ-J!WJPD?WX`Rw*KmAnP| zP#tH&YOYu^cFh|XO&1ZDJyw3zY&z&M&d2}>E^uUcFdf{zxF42gOgFSuAg-1@)qglT z7i#sq)!#@8{=~&FII>!>tA#rlEYjabKxJ#UWo{{XCc0eJsrK0;b!W59uMmUv*?{%Z z;0fW`REqA9=n(uvy!pf0wt)EEP64)40K-Whc}sE|uQOA_jNmk|EJ>PYXFfB$`YYFF zTbUa0c3tX1`y{Nz_EDbnIQ-OWcV@+NWWXeKhBWXiYiLDiTaUZ~?=oFTVWi%KJlv20 zJv64FFdWC;u*qjrkDZvNvfC%}?gKB?m=BjPbwc9k_S3%L@_t&7y`NUEho{r+_6G+( zJaQs7xYFdF*a-PjCSTG)`Nz_WcQ*=M(M&rNjFpCSrd0Avl24|0F4&vP+a}9t54GrY zxcTEq8EFRswBhMO>Fc8~duWfQQYP{I(-dXOu;WlleRsX=4XlMWPMj`oiV zb6r6WY?H9s$n%c#zi$WLYKY4#$3)x@u3J4zmpUGxND@eS2Pqh!T&`WY{4_mw)RMdW z%-8Ae^aI8n<%=U0!6J;4zh)Kb+q_R+cNxe2A+4RZ3~I-j-IBM20g)i8*8Vvz4@z%w zj;}Ejhi~N^?6~wN9Bd2PGfoQjrC9ph9o24xdQ5GQgfYmuS>_~iJL3{|W(-N$>l+8( z;Licr&-(YLERn;A^}YjB!>bk)z2Bp`?msv1c3i=Xo^5ZuE*ijfpG;y5dq-2QFmrn~ z@6vWTZR}#?(*@6YBHxsJatHk0^Hx1EdO2@bZtO)HgXqK~-IO1Vn=;%HVaxdxAA%xb ziK3qR%(KKbxV5m_ho2(CKw<_(_=P89NocZ9wZ?D%;o$)8~Qj^)w~SZh*r9gS>~VY0`Fcx=e=+F65bt?>b` z&rE}H?l>m|L1^IIfDzKVaHzQ$Ac$U(BsFBHnAUg*C&vk22baSaiksO8dnymiST7|r zXUr1Wb<4YsPx*oh~p$}P`)Wj zf~^mNqAxJ5UQW?xcW=GoiPBPkYP9Pi@j<^)FzP1E0N;42){Z-H!77wE1Z;+M;tj~AoLtyHkJ zxA0bpJNnVXz@?tHG4_r)9-s5zy|__lZG88DkxSr)lR{k;lv566XARqc#&FGBx>Nq( za`K|DSR!y;=0YO!tk7BMHsTC4ErUuFLH`?@$)!s2`33%i^Jr`6mm1aIY8amd$`iLkl z>c$FC%-l9MHj0Yv3VS|%wUsWt)S8Zoe82RmUPbZ4vtw4)>gyv`$+ynVR9t8?bK?!1 z5!4YHb89*j=`6+-CN-0_cApqEvC#c5`bW9@2L~;MPYJ1%ZQ*oxETvQT6m(R+DWo-f zC;qBJQbRn&V%+nB+xWmx`uHp(8coH`Z$(~-EG%#^Ryw$jO@N%)7527xIr%(8xjMAQ zf_vS5L-7fyRQ3Rt^NuQ7Ap|eflALLg#CV5vCh0qszma2>2T%q|1MHdO;daaXK}nEY z_uGL&jRc}mX|ba2S%D&QZAZq8MTprHdIVUkj=}X^&L)f|ql)hg9Py1%6JkFNHC1p|Jzl$~r|5 zsEWLgLDIpc!$q9&&t2KR(`plRp9X(yv6p4`Q0t?7Q#WeEPqoG*iZnW~LX_f2UFt2mqL>khIE^Nc zyzjiEI%%`(To)tuhgXdv$f$r$xT1dUpi+ zTA^{HR{j<6vvnAsBB=7**CQ$hq6@W5{*uMx_c2)^3=5#m4-j1+$zGzr%@aer2BP}_ zuor!p(kI+4D%k!snh8t6H9>^QQMV4kZa~Bf1~xkHJ0q=IcS6B%-kGgcZ-VdB?RGWZ zv38M)!*ZYde8;BlG=CwKd_-K*i?wgt71)RIEAXU&DHmg4gvZM4N?F$Z^20lk$2GkQ ztHi?il?j4fxC6Z%CKH8^=^BdjhzL#K8=trXoFg!ZS%3YZ)k%Zywc8eLHrcoDzqR2E zJ3GG!1o|=H{S!3cI~R%IwKJ;i4wC8S_d^}!RX)kGMkoj2(SurkZ6cyyLiRnk7)1O60=)?Y4WIUTB_onmYUz#Fo~-vxU%}1gB&19K_iM&nLQN8EV&vZl zC+gJsArMKx>?c@=bQu@D`l*Y7=(u8d^>0MH#ODSLq@*(Nz&AIy+3_2l&vcnQ_}1ZV ze?lmOYewo{tpZM1Eo!L6ztJ7;STZnK2P6ozo;Ef%hyIB&3;YbLK<7mHnu~FCVy7Yy z_4>z*Svc!I%`W?&MxFl7q0Y_xj|b|uok(ulrX@hy_)kM1E+Mf>;S9!@Sb7f;YMeaF zB9dKIJsQ;@?xFrz#O|p%IXPegZ-Tcb&q9OaB63#XPdOpq1_!zSXH5LYMauIuA^jh& zjCj9S!@hLs{HTu-kJ#6B9PDEJec9gy6r5@|ru<})iecdicbi&sQPrP6i2yI;oy*-LH6e9Zg|dUkIsucKq2PG->a-)c_`VKTMLGjTW+_q zUMOtxbM>ie=GIuQ4x*WyF>97D4j3Mhq{hc5yq zV1=eh6vM*v45AH={yJwnEWs^s=&3L=E&Mw^x3;`mf@%Mvg#qKe_O>bIXqKxg- z{yj7r?Otci`eo_Q2K&h45gu*JPalDiO%=6OIyN*kL0NrxYmOoG-hG@E_|LAa&U=5< zC6HXg#8@>lP-ocf<1kpYHRVfrF3)pd+3sOxftOQBuHo=v-*x`dSC9%~Uy{UsWs6AW zh_HZP5tC?xL&vXNXla>cC$1!Mci$nDne?-fF90b3*+?iDrKnAy#}9$k(t$6&09&7Va4CubAPtv z7fu6Ody{OVOWUKs$f}lY5{Ce^7)jGqo|IosmwUD|@OshPO%ms*AW__iDF06Z(EBZ+ ziBc6@)Gg^zhR^ydy7b8OPornBriJFuw!BccdrVlwW`ps(^ERqS zvdWYom(I!r2@BsphiB4nl)F*hrn!+)26jm=yrR+Elxhb@l4Rq77P5YrYl$FN69jhC zCqY&KiS@97@ZxiEm{6b-9i_7COLtbsIH}eJj&HA^`zJ+lFa*=Q zQ|PGgY;bgQ_JT}Vc}3~Aif`!>;d0ZidIdq6X@9+yZB%1r3w$V2(!oBWmaUOp>sGTB zw#d6l1KGr=wzW}27H!RFfx+ju^plZ#L5%asWflPNQdI_$| zYtzmP7Qp-TD&u0!GEphihN?}tw%PYqV0CME^_g=HnzBxUYm=h6LZg&?2-E<3%4omQ}Mcr>VDH4S|=jf5? z8*;1U#rrfqIS+dX&DbP%&k5-9rc;J^O)aRDO3Jwn$$SN{O( zvgWo%sSyd>jucmMDrl*=^P3>+m(32zofTyic+R zNjLCr#dy9`S4O3y9&UPthG_dW!>Y8HrD~8Aty8SF_XQ?cQejTI^nw75yJnEt)w5D= zJ^4)&AaXsJ_g?A#q)g|jWfX?`5z*!1XuR=*2Mr zRWPh}cK#c!`s`YQDVCu3)+}iZ8UU*1bS7WB{%s_S)z-5?cJLV8C6Vnr<~u!con>UY z(#nnhsmg82nxx!-`>(bK-)lF>fB_&(zU03|HhuMC`uBG-Bit;I8aCJ0WA$QOSj)*V z{>Bt58D0&GS5_X%wY5l{-Q!_bYy3pQ{D*?;_d@kGXM_ZYCR?r99-_00h<%(evFN{P z08Da0X-Z91J_95mR7XI@iU^TP%l)ypXSrD!>yg)SH#I4#@WQ98;t1n1rZ&XYLEf=) zc=ffrJB_Chu3jnEW*xu68~ascO7vA}X)J2-=hNf>w@@Bx2C!6nF}UVJ1{aBV8Tmyq7c=FdelR92y*QG==RJF}3KHWUk%LTD?V_+;V`F)#PekdEH)7pX zCc^J`s)8QhO1Ao>N5tC<{T}H_ZU6Skqj8r9Lon@|vMz>nx;u09xCPy|>n019WmP~= z+!n3O7GS8K!trsLw_4uB-~wRNTgSIy&EYf_8iM}6DI60bap{d{G}oC`LeLTdTKSS^GDXyvToQXrE8^@G^dNfDCiR^-~ z`X4A9?k;?4ZeoHr_~`+RkxR{1;VlF57%maw`@g^hy`yj_om&EMj0LiU+}3~!*Ow0| zcN}Di^xU-}LbhE zAqYDn{2`po&PtEAsd61Y_<)vrsP2WLN@(TA372AuwCyE-JFolO^J(%@9$) z3a}?VFbSY|^?nDan*n<5lpEWhNRN5g;Q2;wW_!;~=|Cs2^|nxzB$z2Tj{Zf&ZT4Ka z8`;dG!x}5xL+X$FBY22_j{Xveg@Jj5lrGSMjjn1LC8;~eV`b(GFMzojmWUxK?YzkO z0nH;X)+`PariG=XRdC3G|F2^`3V`CIWh~PH^=t`j`zohA?0amI7tRYeqzM_>iUaPIGuXJ%#si}zF<;yB$XB2Ey^t6K&?g1mGj9+yi8 zTIYI7&ENmQEj;sj>O(-D^KkI0?!2Gp$_f4>`8GBMe`CiFQlWLIhV~@H(wMa~<_nm09IY0$*IAkr=&*E=+vUWgDwnV+1#u?5dFtQqQD2g-zht zaJ!_nl9OhE8XwgzM||pySwQy=F8p+@ojOU&pW@MP2ERcCSN z291yR*tw()y+urM?y>0fGi){b=;&U>NIe`T2JAXT?LdJ%`QgEJ45CiX6!7=O)t@`f zoar9{Mj{t82xPFIMMu&(&(`0OOLsa^-?(x`sXH!(QKxentMNu{0{Bj9x_&!DC=bR} zHm?nv-Wu*Y@Z7GwyE+tfn_%cSwy>a;#r=+%LL`ma*|UNvA0lMIfnu5JjQ{Ri8g?FC zPAnlI4tAzeHOB%27z*wE5I!Le$OHtu6{VC%7BLL8Xn?IZu+pJ~QTLpTQX)vSHO!$3 zFo!f~-xCjO8RU*3fO2lo4p(Mc4}7-+AdYm8CrSQwTxkzQpRcd0lYCxP{k!Kti|NHG zz&X4H_T-|`Lr}^&=<`3RTdp6Vl#HvL6igVzKbf<;K-Kmz7N1!S4cLYwq%S@tvLe#w zMPh^+)>tbZ8%S$#*x+sLRjqcRV0|}j@fiTTkbmbX<67X>cf&XHK`&p<63-OqOEdOlu=qlO*JqwvK{$ps7RqlIv})aG|&Vgn*1Z%NRNye zRPX#y>{d?+bLjf4zHcN^PX4U(aVn(a_@X?psr2}qF!lT%6BVtYoVG+Son6G9E$Vcg zS(3VUUnLdN9@3-#0ulI@N`O&=!9lifBcyzG0EHFU9Yf@IG;1?FTaAesj_%*xOwKQe z{p`^qTcEMpZEU6E<+ST>LVm)Lj!)-j7z=L4yAUG8S89rcZ_Yxz zfHlSwC_o-AG~rKUPCAGz&EVK7bRtzsQO)jSTtz6xNB^)JrNY7dYWwcpeh`L+$t*H8 zHiZyeI5Bp^g3?~7VtnZGN=JA#=JX$1ycHW9!RR-v?WAsIPpE@SV9zzZOa_zwSl#UP&^)qQ3B24&~Op46OdXcAC7ViLeir`#YcQ)xn96jHi1a^E;lU zYQilf=q$=p5T*k z>UQbSSVYqib19$PoKwQ#3@~B_nb+o{eT9gIVV?9ZyApS3VZTSCpMyL#1~CUL42?t#i}F?7947kSv`gjMXrAo4v5!h) z4mjxksB1c_A}(}3LqHb`l<{W$`R-KMGt}Bedm^r8N}1M9b*qnE)lHM;<-26|W!sS5Qv^ui)45m4^9XWBv>__Ot z$?E*}n?5QrHS`BO>9_LoUoYiexvog=yj}NufDOLH;j>Yl2^mG?B5cKpV4tg>O^q-v zB;ulX%RjTJNvknk94Bpa!{eIaihoJ%jq8kj&3jVH?+G>>0P4zc`#!3m4i_q>cyOW#N&0(qH|3xS_-;%n&VV3YVuxUG-ShuX{AG%jX*5xoGkp+XqX454s4TbHvjnyCzkCRidjDQkBGNU_6vWj&YP*q4q}@o=T|rE9(pv}*7&rlE z3mZH5MGdIaX~qhRpmG5CYP%rkTN=j4cpe~>zU_*%{VHPL*-WE*1Or_T%1UQa>=9Z< z`TAo5dR2Il$q&fk-U1rxH5la8kh;GXZdgO72&qc5mx=e@pI1b8-TpOsMX zS*bw^05B?;Uq+px;czR`m2Z1LkfYtN!i)PhPKKB@@rg&gi%<&?>Wt@ZZ47$!yA!hR zPXePun$l7rI)dX3J|nY?<#gMd ztF@CE-qDzSp6Lq~&5DKs)-*sX%Ivkb&EoZSe^hClCAmNP`vW7|sR3ot+4Vj}u7QSK z2-H&?#BGSRikzjq9Qw@s4tb>-^TnK7U^*3he5}*i*FQt13vtU~D4{jSBp=XxF!jv#dWVVwZn4S{{y<29o(XJdD*K@- zKOmR6td_=$(1b-_teMZz{iK%4g{*?1#t5Q2yBp%W6)4a(6( z81%0bLSBHLD8;^1L6QY3CvpX#kcU0DLZCo|=@G1879d|opb}@Tu7kZOxi*$@NH0tY zU%4v~h&8jP=5n9H`P(MgUGq~}M&_Y95!YrwPp3_N>J_RtMx53d#NI5)*3k@(%UD}v zGPA-dwoUSf0bkYb3<5GNzv#(m`B|mjWu&iviT5$H|D|7*{L)o+9U+qD7gwG*1PRL~ zC=|(vpynBA_=9B0M^ZjyrWa9m;2s5_YmsLWoeVSRubtm$^^;U zjQ&JB5%#^&`<#O70{gXw4UzI1ZJ5{b&tq{G#1@J3VLp)RGPp+sjiC`+1?^DSFj}vG zAoizNJ^J%_QcyIKE6cF;H>T(JV7Lui*9$<@Sr}Q3K#4yR#}XkMgZFCIDp5-N$3Pbb z4ZLh4f3&iL6fd?uQR~>h%RczY!R!Q-{Yk=$S~)u-A7rjh65%?+-RUuirLi8P#@AES z#TC!(!8Y#o`zBIK3jZkzE?kiZkEMaBHP*E{gohyt^5QMcux#Vl##r?%FE!`Tm_w>tth-m0Fit`r=&I3PVLS|W2rMM%zp6vDXz>4bxy?}{!S85zU6 zX1FFi*EBC9&(;OMle59GJ;Jw9>svo(ww)9{bGXJ_bzk+JW;L?K0G{GyC%|kN>F%XYY`$oN>xb_U4SttRpfq&)Ksw&K`+F_Q;7u$T%}j zWMq@A&Q6iC6@G8u-|vs#pFJMm$K!kVIiL6Y^?FXFKw4yQutwD?@!IWMjW>PYn&jEM zlIqfMrW?%9b*$9dgZp2L6yt{)#NWGWcoJ>!d_e;5akXpqod!$8lpE{fFSEZ+lJ0X) z6)VevYu^6*1&<#-*z_vC9T1@3v-Gy*0kaz5y5znoFUNsXjy#4RPduq|{n4r=#8HZ% zBBb(N?>*4~v*nAx3{qxY^5dAi%g1uQ8zUYnI64J%&SQGF{2aUVWJOr&pqG|ZPK<|F zocD$)^XtIv%9lB)R(>dy&#;dMnZ@tU$L5WFf$B^QJYf%syTdjCk)U%RnTzzf^HTIZ zsq_QQfj<*3+&i?ci#d=E6~{7pkf>ktLoaZnxd~!BN|1;!V3776nE<=a&V_)$9xx3^ zy=m$xbC-C?!+H0S2|Ze5mZbWhz4801M{%ekMefgqTFxqoDm9l}J}J@u`2Fw-v|Cd! zp@be5cySPDDuBRMdKi-GW%fa_xW66C`2-LQ6fzP3byfm>!hKCy z_@vJw$GQ4ADnc`&Uwo6~iHY$!ciYKi+tfRz=&fh_<;NC|lJB>giu0-Wdu3BH+ite} zUHbe>&b~vvcmaqv~Y{`ZZcukeUk#zN1z99ji2e>(|#n!_B`^DGTjm;Z&hR7 zZV6+MOigIE0<6B2Hl*=0ofO4d8uz>y$Rltl1(**j*%kq$(_)%+|ndc(D5_Qibby<}thnFMf5}p7gee?cQB5 z4E=kpI&cHjsA-T(aCBc)@xeEUG`c1lh=@{%6`crhq%#*ffpe}MtUQ&L_2dK7B(roO z%H!MbmC_`wSu{9p6H(%PgthFuQyZ!cgL7IFzxh5%I(#f+6W?M^_rU;^4+Rx+6w|<2 zV*U(FUU~SABW&u~{`Ygsg6zzSlQT`nNyT)tb^}W$`#BrixqqrbGdacO31S}_B`W4H z@!nA=Zb1gtYvD!jZpTMfMUF+%sE}OX5CfNg6iF2w7OEHN6p?YuNOV(YaaYC;R$uD6 zs@c&vAdIns9>b@1K~pKWr^k7qEQ!W0lh$)9oW|T2KK%?e3k7nvze2_=NE-Yd9N-s&lj76REJ@a)*&_ql!eucXCjkTcA9u=x-fOt6 zKA2I8W%73zEgt0ZD|w-1{KV?M;07r>p%!4u{<_nxVZsPkrFSiuxmc8H9jzRq|2u$P zNh!Ze>AH8b)MwKIEb@&74yl6{K~&f&x3U`q=c0^_E#%+$;JD^P-dy|VroQ^Y^uehc zcu1LMXQ~$6yI8E^wzYQ=Zi+gA{66Cv$LE1X7NWVurrWzit3g?V`>q_wSWjjSQ&D)? z4zX2zkTm@jntFct&@^jXe?JBBM>6m9o7q>jGyhm~phM371-%jb=;n(ju86qaw@+LA ziiQhs`UjQ1M$CY9e=E@)1U?54eZfUTM>UjY|W~mbp273LPd5C}X*GlzsT>KIQZD2Xb>Qhdyl~igPV1n*81C2?;|?`++5o zJ8L3x+nTa|@ItFSIvaFqoAs&$sDHFBz6KDz2DCGgVjD)*JQ&*80IjF6Z5H+0385)wvVer@1knp`2|0$mQ*@*bLiQVEA zVwu0YwGFE!mA#t{Befo#P47b(qw6tqtP0%yJkEm8XTE&uO(Gv!)sA;h6?kOzI2CXI zhDTf>iLDW}C3x|L`rjJag7NAe$khiGXh3&nX<}VzxGME zQWz;S6D-}I508}t*OHW!Xv!%HRHP|0!G;NRgkVY0uuv)vIousGw1~{?H898P;vS6+ z#(DPb$3abv!|yr^&TU-s{XF*;cV(nJpmVb(B%YdZS@p-}1ICqkdcCh%X*H7B4Y1T& z;R?2<7xwNsep4l5OFhxiDX2N3GZ$qqFR@!h*Sn#t>G^4~VlixYh6r$c(xR*Vg>JQ`75d(aVepN)=~R%h=PMqG3WSKm>O9s zybsRq%E_fpU0nR(u*VAPIG@KmZ;AX*olI2*&ut80?8cik$mUL>4RxH-ct`kh@S!DF z;FsVho=!Gj$~NCrb0S+hH>5!QDLoSQc!7L9?gh$muq?33;$w1W!UjF=%`kd-ahdVP z)@?bcJ{ei&$ize)K>APr9*UY*U(!Iz&Uevh^O&PUKt1Pn45a7Zzs7<{5CB4;oAVA) z0Q2lEXF;`FYA$bU?TZVc#^&YE<(JSnExc*lvza$Y1{dY)#T5|KHm%=U5LO5LPWxo# zDmE}lP}IY{Y)TryF(L-AH$I0_`)djGNTf{_REq0+N?0r%kn7wgaI?2ymT8TF6a)PO zpe#HBV1V}?!Q^(OyC|R4V;CXQb|WUS`j9V4ttq#ur*v_5#-(m;k{hnE5*zZa3liGTz}okS;N8dtmS#_-=ajYtZw}ag7p2I2M`s9?^n&B!O>(antYS;G z@DlAuFFVrxVOF?Jc0iYrXL`r#Cm02+U!JQ#&^4mu2d&P!jl*73Y zK3HfG+it5w3B5t~autN3#@(vxY8CgR_^lTdEh)7F*QVw9=50H~#mh<>)0iCke@Pwd z9x)b_R1bKy#76~ViEHlGMCEM8laqpP!K;L>C?STPqRt(-xe4BwT8eCP>;BFNh3}=S zgywj?xLD}0fa8IntVWfxG^-$ENkX$y@tBEw&jpmW%gaq*g*;?=a9a0zp4w+JfX9?*+u{WU3{T1>LWA#a|OE~h)w+4PNj708@ zrBsb2SIHq%MH~mQ?y{H0$F0SYMLIHE)hoU4r!k~YGLiJJpS0c!|G|n^SD#EMgFb@$ zKnETbd1cnB;uNqqRId_u@*rG?HPcHmG6?eQ%M1!~=vDy}1Neo^FMc7Q57Tl{{-)b$tEuI^d7DjUhlQEDLfjz$Lu^}xQPJc_D z?*54`JATk#8>65~xJn{G$q9a)D)7-@F@y_--RsAH3v&p_b6?3}zI`FtP=^kz5xW!o z_g;~{V1ezMtBES(Ec1@h3LWeg$5K3hP?Gz$ zXy?)}(@L0L{C}ezta5xpy2%jWe134K~Okx%m++J6G9SrAPY8`g#TB2Biqxi?rhSYm52E?h6p+CUZGoBLa*6Z0n%aRapcT}Fk`U+ zA)(;!87Vg^%r_4U2dPk^GK&&eKtwXN9Qb41wSYCjLVqu zaF`2Z;fT6gVtEr8|7EAK+yjQ3Sm=RXI=LYUjXsf0r9Fy2bGi7y|q%-;-ZH|#IojJk1$ z|C49!^{V$=uWv0;a;C$L^>@K%iv(vi>@~3$0lfC$p8r+|UBC*9T>N;XxXD+WMnn5= zgY9~|7?q6;X_uA+LkZ)8TG@QR7el`n!wlf@+eR=b^m?F9Syk0`N66wTp}Xm}&oUFN zOD^8$qc#dT!6<=kST`nkLwzI}hkk;qvN3@RJXOeH7quc^X04G+eB#Up*FrI4a2|CD ztX}F|y}#|K1^F}dZRF>RGV$+gZ|?A0F0qOr9D4?lN(Hn5P}%fS1Z)*hi9$)pPm)y4 zp3bKUyw_S6XAk#ZOulP0$#Z{WbKAM&TQo_N1>oDSE$&oNazdu^V%$)|Y(5d*-fFw$ zvGUpkq!rgH{5#V}3nzVSc_7EOUJK^;tJ%FTQ8?nOFHn7WVtYaK4@ZLF0-$Xb&sIOx z1}R_sb&VDXlbO{)?%)32vx~GWtQD%Am=0Ua>Z$L7kK=Zt0x-2IxWCbop%TcB$IGq? zY{sWE8wv$UZ~nPE%sl1|J`Xm3mypCye{B++R#XPdiwojF2XsOa^}@rNEl38>sP&`UhBZ;*hEPp_*3hKk1I>LyyP;9Dvaa?p&W>eXB=Ljq$)wGG3mSjKj9%MtWn}P^E<6-? zT49pipK>!!J|-amW^7=~Gfx|NO@7Mqa3(OpmBSBB z%x=&jt-5$A-reffGqj`$N=sie&d;sTGMT7gj+O@Zp?7mpMlChr;QRPv&_ZNj0XW?( z#b*l60YDZ43Y{hZ8d#06v02QQmO|5B+(Am`o!2-~B91iMpsOvE)XWkGM3|CRxcRXB zzXZVm63cC{A&cU?K*$u(`bM>9Cr6C7Mv)iGA?D}|=XS48zG_L!o~!r?&)tK)emlzx zchQuDXOT7r8FV! z5!Q_wiZP50dK*R=rlRw;C3|=SYknz$lQu+pP#Xd`=xq&r6#A`s&1Lw{vtM5ogZG?o zi#{VBB>VGZ<%OowM^LvU1ckbMYGG4R2bv&Io9vV+hD(hob-;qx>g|Nl+8VJM4$Qk? zPJGsh22x~|63yn(iHQ;&Zx@$~zPxszUA0w_iIE~nAAWg9wWiRE=I7ZUr|(pl5Sf`k z9}6LPg}};N>ibO0X2#W1p^D1E$?E!2v6J4cHajsZp$b{`+)!k$!T#j#nupG>;pqJn zb)R8rlGXD%sr9C(7KabkB_t<=&{k$6T4 zK6L^GGN5{|j=k6HoQYNAO&+&uG^k>Q&ZU^lZ+cMC0+|$`mV{iC6}FD2v2WF&#v=?C zIB6Z6jd`_gxHp>U3W*#5KY{p1!F#0_Fn}c_<9D9^I|6EGKs=c!l4?|bJ(3Byp|vnQ zK$hJx zgYhEuU(!rAF1>%(!(V*(krPy0{P&2+l$?-%&(9SorT+ci1`sT;2vl*0gJSOE`vjo> z@Bkc9KOC_RbnQK|OUs}_=pu$r1_1!~L4|kvRPV|zEzL`0cTGm4g0++;1{>yg?!feU zD2C;eay$}@8;qe7jMCj+zsePuws(gW&*y!HmE(YLUE`eTJ`ExW+$}ckp%!qeD+ivS zE$e6*NUQDCHm(^O$=GUky={*Qc`6HTA+T~zy?bbu&HS$~fx9sGO6NWXTcdyuXQ z)@NV=dOL&~NMZmD-F1E$&2&O(NoC&)3X_3U|4|27Q{~&YA3t1?iq4J9e663?9D3|6 zCJ>9NAwlb(GZ<6?-T)*+I;pFxxAbb=QW2H*g-mT34f#zGi)BK;!Hu_jV6*`e-L_L~&1r#vaTw|q9%vbOiV zh<+Q$MD>uW>}hL1V^NxkEJS_b49Y89ir)r-VB?XFyOezo#g6FN^XeIpL{~$}=Q>?& zSDjo#!W#}a3IMUk+#A=%cf$bH)JassVXwmb{u;A_jIz2v7D{_3eSK~IahYkDGn1a?Oqh#{~UwW&-K zU(gWnTgr1G1cbB{OgPUVg)KXZmlr=uam`b*Nde~NH7GKGdrrPF1^odAZ>A}(b#~U! zg~)>>PaL&DHI4Ue$+Hi$s%rP>oe$=J3(hr<>fuNN9Z@(H1TNe~YOX1|j>{~zHdAM& z3vWGT!hCCHx`Ic_ldaK1r=x+B`unksJ<;d6XV+Pp%+rDz1U>Hm&T|_W^TS)eccMI^ z9RNnN1@P;GoLtgTyZ{v|W9n|l73xGbX)4w~z>Ai0M~nH3l00xikm<&Iu|_}p^qi9u z$jXHuP@ET`(gVYNNzfurYnSqDG!YJ;0Z?h^e+NARQNT#3?GxO_mg?I z=(RdP6I$fG31X7l^4$aYsQ62aOjed|rziWMc=pnkiq5xFO~n)1FG@Av=74I0Ulqqv z67Iw8XK*)#Tx+&Ff%*3yC_79b%mZSjDEqel6A_=*z`aq;J3Pd@ zDhrHO??sNjhX4M%l6GIE#ET|Y{szq^t?J<4?G|bi$X2kd6>i1QzZgi$b6#X-dWv~B zS>vXBtxVRgs!v#k((*s*m;bIINuKMv3$5yJbRC&kD< z+smo>c?w5}sWx82hT$i1P!~z{LnT~y#*M4$A>hmV7vx5$8vI}QGx@u)la*Dhik z*KglVCMz!_FhxIRnNk++nyJbG7gT03=4;UU4SA7RxNPvGQAU&~SObwtUp3GPU5~ED z64RMDt53f+55uVl>DS)2!(V;-7#}4D`rGJ9GOFRW4E7ISGWse}NEYeW-0^P$(s=-R zE~I}>;^Us=73%wUmyo@S@~bSX(DaA8QR=>*zE;L;lpKS*3usqyNzcqbYo%v*qOnu4 zi+8%dJevD)XRcvqU-|glFmpHUY-V_G@UC@waP{Hg1Lnn=atYI0^I&fSq(~%>a>QL> zTA0HXvB>o5XD+qrfF^6uma;uoc||u&M^G_SU5!ZQHq*d&cNW>-`RDd7X1>aR{bn84 zScE%dO0VUB9a+Vr)6ZUY+xvZ6F$QbUtt0D%TVAA}Gb5h=F200D82i4;@~_g8ngGWuzY8$^Vm#y?tt^;@j1@3hq_{n zL*Pe4`Fg+u#>JX4F|%8UUTWt(aECDX!qF|>tNlQZU?=a8NXpDBO->gU80ngmon0uX z{DqvbQ~}0GJ0LZshc(1}BYaEIj&)<+tw_0R7yii;%=fiB>d4wt)Ur5(%_O?xKQ?SrEDV5P6 zqDg1pOU*j6YqxSc2K&6$udc34iz8peR#d$zI`Sw5(38!<7w&n9q3o;Vbpor6ISC4` z+sC_W`J7URp&!aW)%RRxX;7>fK6UZfyLF}Z*}Uj3qM(l6al!xK#HxC$Isa?k=sPuT z1Sz9>D=@O=M_6&0d{fq9<|D_81DF*=bue0~wzq{Gs*LUYiu7Vta|UJb=Fdl2B?OtO zuQQ<}*okEEWu2X%7m+K3M(*B?q2wEf7SnAZ0nEJfkNg;3H)D3)@p{od`*QPI6E9;1 zzB$feb7@7HHYGwC;+#AHDwb8rAs{u)oU26Rk=GUHgKe+^;ZR(+$-q)zBY1@fYHPWbqjjm;PIhW8bgy zdCbKk37d}Oh|N1TE`pOjRLtm*hfj{(D1*$dtrAWrqvy}-$__ZMIXtZDJ2CY8+#pZY zAaXL7PW&u#s(ER9kkZeNkkAmxqhIBYup?x{W!GNj5LjIwmibrJoSS64?DCs5($}Gn z68((6IEw|;d`~m4O;a*0p4B1oyY9SA`u&}Pj4b!(S+zaH0 zNuyU&PMH8q=`1y9N!ei45nUa*mN;H=(HSPbwGJ=yM$ zknL!wfP4_I^z<@!c~eP9iy0{TRho;W>hX!Am(5sqXRDvHQ}gu_3)DxrDErH-f?5b- zP&s;$d=8F?*NN4Sq%SCGyDOn`{y$&Ztbe?AcNQ4905f1?W_IYYurSN)Zp^pa-NDxE{E1j|`$R%QD&e z2>-Nva?KRb2N?0ar#OWu(Xc!Fr_apuj!pb}qBaw6{VgfjN;}X}?;ov2hQ$9ud|j4x zm|OA-P)C~UG(8#7sjzm6H80;kW>|Zga_&R9{yWl`L?!%C|K$9 z6`!2}rr_84*(unh!R=)`9Uo7=!H@b4#^F-48p!C`1-6WZZZh#f9v-Pd=`nR&d8nj8 zNSTU`7^#(a9d)cH;6F@fMsEHWvKkpV z31@?kA7+MnKI_Z-HnS!Fp{4564c&SnySWGZ&UHC^z@1TQ@CrjrSPBo;)LmuC5f<|L zrLXR2nmeK7=ns{RbGlOk5@|*0q(lW85-nItsu>DLU^cBsN6XJ6{mWPMTNZ|C6u{@* zggIt0sil|x%VDM=7+rk$k6Xuhl09Xj3e73@D$64kJ;)fD*@LUzYp5o>l%{5T|I#_U z-f*vk2K3fAbz@D@Q$&FcB*f3kruV_tjf9YSnODMMZyRp@jG0qF{p-6&h_5R|GgawS z6(7h&8SnFK=66WqFzS6lZgLgC2?NU(PUG7_M4-U?PmUN~5oKMR2E$OqSh zXI{Dg0MQS0#9PO$#TZpz{gDVJ3md}qQ;d#yOp}cC z8#s{wY`;Xits~mz3%V|WA`WPLZkPUy`N4_(tZY1Ot^xg9LL9Y$MwM%tVQR<16wE4$ ze=dYAeo4|7tkPOa*&M~PsdFIZocqPJLk0C1t#U)$HpD2^OI-VI0MBXC|pr{cyt?oJeX_a2p*XgBq zL@=WaP|UVUdLxsIU@4L8@{UFYUVyAC;Sk3iRidCjfR9a|lv@WjL_BAth_K3~NMj&r z!2ZtZl}vaUvK#cNO*UO+`4qLaM!fJuP%ny<9L$pp*w2(3aeyHp7R8%36Z6mHi>j@o z86~>2l-&uTftP&lEn(}TarD1h+pc_CXWyf9bmHclR{|TGkjTZ4@|yT`hD53(W5wu!px$-JL-YoE zV|8T1L6J&i-!993zby9>_^R!jupkfab9yE|IB{ceVjez5o*m&RBFKp1Kx*MS!)&b%*^#-Zag6OH z(K)G@0o`(}36BlbsW~dAh>~Dr%~bVRWR*r)nTE{FMd?1wF}n+kBvxPoIlQ_vFkGKp1Y39LD8fxdnnLJJx*P<_~cK&%K=Hg+ell9_2hpY2bYF<^zQ`vKh; z1lEWF72jYCdGFgSkj4%)y~)=`ZY(X?5d=lNURA|H?ME;aUfSzRjV{HURa;lk!9O=b znDErdI6>3e8Zj|3Z$0ddwy}t%yw^+N{S9i41E05J72IhzjTqdv4H% zk|I;GWFcR4;f+bk8ddft6CLta%nOvxj$oX~x4!0E&=ne3_=_J5kR()%fzOp-`*UbL zQ5(NuKopt{bu}3UwhEJd{4M8xa~EV3OiCwA#GAN;S!JwdhkC@W|Zg(d)0kKgf_0Wj`w9Y7%1?R*8R%i!|YWx zKPx{3fA)&O0b(OC!#D$-1N(CqCz0|NGVvd+B^;xjMNJSS3XJRSlS~uh{mrx8nWJf$ zlJ1;QdMkT|juF@hX}R!|uP|U1MKi(7vhmw?mImK0Rhk3WJ^xFTdTheO^~=(8_bMaM zFWg`y?o`?E%&lYgkgF+ZD;pb|5SkNWudX6M-b;0AGi zP>?klvb?cLNUEyBT_K^^z_ESpCh!}gDa^u=GT}eCwMsuDxecSW20Zd^(9Q?d1CO;uTkfc3-pGAKq4n{_{Y&A>w? zYOhu)vHBo2e-LF|j;qxTU+j=bn0^ok9mL$hM)Ogb0R!)LY^ieQl*##U{vVb(e8Ym} z$?;n1ur$N7_U4Vof#A^QvTqABP3Kblk4Hz|{HNbgHeOWz#Q~=DM@0-2-^9Fm<8`uV z-$|rreb4XG`+`Ln8-wi#QC1FgC&HF>AOXY+2pXROWw-zKybJ*1z&KR5ZcS;Y2A=qL zCN&r3+A++wQ3g|$@K)Kx1(o?_a?<+$`!JFN4Y-$f$g;WwWeEuRoNz96cw8BmAq~Ja20Jm$2aYaNhW!`(RA8Vec%|`1ksc zkSFcU>z;GVpVWMpYC)~ zJ95Bit7^s=(jSzXAiD7v^=n%NF(4KMxGO+oMij744FU`SHWZM%=E0xnmOvq*T3F!RFoF9zi(XUVi2i<16)$Z7r3J%KUIiZd|gwM zMS#qb!CiGz({u#zOx^_;JgmHmUK1*-vTCN+&5hva3tL{4&t}FU`mgg)+<%X1BR!n1 zu?Hw0%RS$+DNl5VQdh@Y^VYzcKc=8i-k;rR`HKTtt$fg*6&RG`D=b1C2a+hjiQ+hd zd4Ve5iMm5=cF<+IoybfGw(Cpf@2BNL1G)7A5OV^4h$Co)mb76w(!3Fw=hZ+QL;2W_ z@85XD9?dGkkhOP!#Sp{yddW2|%>p=zV{j!xFh^Nucs=UjocZC*T?A$iT)3<`cTlvHJSD{v5ZwInrU|(kbx*^+=jkD^$%cmTEgmo&2()a@q+>t+MGJ39Qb7yDg zXYX%S?W}cbu^6;6gj(WD2wZXdek|mi@vb9az8ueC>kSn4} z^`9Fmg3tVeo_`gFwJM07&wkF$ELD3dCbf%?t^509yE#NDTptIjm~W|El>w9r`9D%Z z_*f3;4#up5Mi{S_k{uk85YVim13Du^qA|Q%}84+WH&>`gA=dr7*e1&t%S4Drc-U3iUMXt_&t3H| zp@0}c5f!JZ{1Ut~u~!Yaw$#gg@>aD&2lXwlM&DVjdz;;N-xZbVClVby)0LGA)?L+rvWj0*?2$^rITB{ zt>oyJ$}`|dxHs9feENDRN>q-F`J<(i-r5qkN=!#SG56AG*5mw6rHb-$vac#3eA>7i zkwi{KjuY}v=#*Uf5T-v@8nDEq98^?xk&7(=mgg2<;X^yyC0-ki2(vSox;ZS|3#Og3 zpN|!MkpV{E!~(GFo`a-^8$l}h>In%&r2HdDN*@y(>0ardTf{>^C4 zVHS9zqvsc18%SkFgUlYIo?10pyjSyU{YP-O?rgo+o20=xmj@ z?fpacv||&US=GoE&mX%BdXnV;{4`OjP2t>V1fFqv0=_UT5N!NEfS44#t(_Tsat00H zz|V0ba;3?>9uh8JL-d(u!$_EdRl*r~@<%>jqvnvHgP92VO15`DdlVwK=8ra`Lu&s5 zutFbJII?u2)9T~aeJS6MTEhlp(v#LLp9_Ek1q#9wu1-EWLa+BO7V(iWcZCRWx>JM$ z$32Z2u}VbE_4bH}V&Y5Q#CBKOBC`1jV|>K^osyw$)a4amTCT9daUsC;Tfqr?P#ZN6 zgae9)ISbs1baaPmy7B|SF}Apj-)y4%?ViPjU=iJ$Tz;;YT*Loe_a3W8BCwx0YP8D} zv~l__pKy;Y%$~>RV@!`6LK9jJqAYbR-e}IWNl5C&8A+Mrta{9zwb#Br-TXc0c&FHy=_lCCxuBL(#Gx zftB6WQ6H??muH5V*3Mm@Dp1S?IISV_m`;ba!4v)6TlZeoIQh%h`le0Uih*r&39czE zT(v07x>=3>u^NucVw$1Q%-NC$LB|nF{5#TF`6NZUkbEjiF0q1xIR=%uI-?~fC1%MnUBAoS&-cM6}xw8nmAFnL7%8(D&$T~Y>l(Z4-~T(o17KfJpKzy|kSX-)6CMNeoSkEv|dTfz8b<5H0me)5<2$|Q?NTD~hi0!eY_jQ&8{=lqC1AUMHWE4OOY*=A`nJd_ z(L?9l$?^LQx0BM1ARKqQdLaMRrdOCjnBy)*R2^#TdVFjEN7aDGQWe8yE$Rv6q1^9} z^q{6L-KF{-;gLIun}mtzjRe~Zf(U@(IyI;n;4TzRggH`WFCmp32>w!dkeGVAn-H@T zao^G)pI$FJQz&!*|M&6FEjq^`Kk)gJgE8U#{Utf_F*)Vx&`)W1eepLGaNGZ0&Oggf z*&kkhe>Qydwjgn^RpQ>2=&iIaV@{9Fr?`U<*^_@F?sMtbXYWgwWXIhSy5Pnhlt8lF z-<8y&_fcS|LP8oSkVFJ>AP>tHINtC5VMgNXD|}vK`GrI8JUQ6|`wTE#+5*?FT1zUb zNbd3HmtLC_(6?YJ7c~wNGH+4X-F=r zY{=vJSBJN?v%7}hV{-aVUyF%JKtMDQ5-Y9z#8t|n1Mh4qVaQd^yvtb)P~Bw zUSOfVYe>XIXeNJnWR9cQ=mWV^5q3rY;r^iSJIwz=*T7``lMfhafCk+UvQ)R_*)2BNs}@BBy3KVgK2~1)w?UTgR~|M;FZk%l=>jq%A0X6zJ#a zRbDoN$SN@$|8wj9=pkYxXW^cprbBtqv;CJZu8UHtp>sARL~S#@hd(Mp)na&LAXDa3 zqwI`I9*|@cWyVgj90uNt%2$UNr2u*!*Tt7HBWNqtoM6LVM)qY4XV7vJ`n9K~&OaLWt78 zx`Fxl$sJ(d?<)(79eqqUEH;p${L4jOWHk^A_4yAe271|MOdozf0Lr$DN&`~-Q5gcK z1piOkW%jBy^Na-@@It{Gxf-K#pw|7@zq?|~85Y17_rcF$-l+z+Gd)W2HIL z>h4>U$&{?6yt$~?pTr}liJZ$M7>mavNW5XY4=A(8UVG`KmkQ&Pde4m6_c*++YTwN6 zlbff=?i%zh#&lDt|3?`BnY@7GP}eF_xf&IR>U?A4gS)g!FbYusPi6=JvdG;BH7If1 zX4I#|92vK1!OKk(uNIrQ23(XxXLo1NIoX2b57DqyeQF=Ns{J7qM?<8y-kte{ zh`k}>2$W<&oV<2t+ij5M#HdfP23uv9bcP4&7qe3rW*Cj$f~uXD zGrT^(h=0?pCOrg-S9a{@ARIfE0>2L!y?TH$Fi46P${=WvUa?$-nMpisT=oC{g^Fq9 z7D7cH%;?B#{1jMq&qQK;a29KIT=`x;YpC%B*y=XTLUOwu_rDRH!d}YWbb&4BpHX(? zdGXotdN1rPd~$YvdOQe?=F!2d`3ve9QgGM=SfnxBcrpyq@c_SsaDfkZ?R!JT2~SV= zr)Obp0N%o`ziaCOP~Own@*$KY z3Wo#Uf1eaatQ|nK-Tjp zZH$PCynSrjYb-Y8abo4e*>nR0I${ zz;;nh(gkw;fh#gSvz z1VrXd=I|mH1PKpO;;Xv}-dLR()4wVdi=X*b>(_{D+iET0puVuyir9jd=sO&9A?DWr z@3xE5IZ)C@s44@39FrKO19NOZmCJwSeJ(flA5Ve$u3@FxgXk}|GD!U`BD2kh5HheR z=#ZpCHWj=R|QTckk&woWsWuqdzLrLK$H{+%;g@L3hdUf<=dwzsBZyV3)BIGx5& z4npE&uI61}Q}-=t0OU;~5hK(>90gmoRj#hoQEe7M*ptL)$~eNv0cDK7GGgu2KPfbS zM_UYJqLy#h{U`R9UOCd^o_`6xn;w6-y^P?)?MKG`A6w`cQ}|`fepKfc0Y>$IjVj>f zn+VH)g=rdN2ucSEfYxm0>L@lkq`@~%2`ux{ZX=4)A}vk@@$==F`!);5I|yd`4TRz1 z#mNw06ekFJGXh!1kB5bNM!2pW_JX(y)q*u$kc;qHHz1_Fu5P>PQ4#p-V+rR)$Wv#M zPJIX>q6a3wtAwYZ7o)sxna#76))Ge)!Ld}V0nJIf``Pb?jGLG%so{eP0iE592hD#8 zP9^1S)TY<2e+Y(>(?wWwt+E;=A6feIJDC}jQ4rA+gwr1SPceV+a|Wa@ zJO#)Na1>6?&a%2z4n^A1R#9YMzVL*ZA4RBOp3Lrg?Ox794fOXnxX*a^BmuprvWI_f z8oA-ylQchw^uq$T-7MetNwj7$3fQ#lJCR*Anqp#%oQ0-3GK1SpD;FnJGP3-1hF;$D zpYgC_7)q4+jPQy0!?Szm0qcL-gC*7nmPfy`_dk91xaY$=?Kz2ZO)Z=Uu~=Xbt=g1mel;GGYLw z0}{T2)*9|aF#dq$5lA>e%q=(u+^Cp;lm~U1J$#}bC29-2pOfPeg{xpi-HTwm@YG7U z7;RL17YHl|s(u+Wjiz3>ag$5~KZpn{_fc-;tSa&=mm$lXYY{j_89x!3=hlmS>Wkax zQ9NkGc4R50IxN}!;a~F_dmr8FkV=2fiY90~4jRr{BIf$sV zTlr%)k_(cAVxG!+EnRhPEGa3u@e4`H2-q2mBNgb@#$^8UBmk|%W;5Fcu%pQzvRL>3 zX92iM!NonsyWs5V`X_Y-Xv;y2b&UV-#>=dmFA@E45ywfEKFI@PA28(JjQfVb{%u`0 zV)DWLR$``IUCN5lLAN}8I+^qg{Qa#%;yeJ>y&UvGHt+9bkhO#NaS8gsEr^QifT z$V4=)L+OUWPZeTfLT;KZtOGHleI4i&&`NkU{!EaY061}m4B@(zDN!Kha`W(r%D8g8 z`E|+5{vqitDWLw z1yF+{zqX$i#c;eSD|3GLde)JUj}*VE*R*;0IO9Xg)-lf*->5L+ML(@s>R4}mWa-%V z^rZbaHNUqmNoe!ctLYxf3_^dk4C`*cR}YufQi#$G`cJP5gH{krE_ zfQo&&3J#z!AjkgJGX$tBzlhI^KG}-UrDD0=vHny)ON_Wn-I2d)x8@TEIJx2G+Q1y3 zb7DbE(i}Bs+>DX`cKxu*DXTBG+?`*-&NwDaZaCZ@t6>BLxwW)x1rZ zC&d`g43wXCv_COn><@U8`tGrSNOJ(sULXh$P24RgLAK{~YmoRE??r6q#nM?JOryLk z1|pJ@x9+S7H}LR)T!U7-E)w%W4`J3k22e8*R6@)@PGPp$Q)d0xMh(g~0Ci&(8_)aH zscg&B}0;u*# zz$b(FI(m~Yr(Zu55MQDrPGAXH0MU>s0HX(GR%H;GjtzgB) zUhK4E_)}2C)0WK~28{P=xZn{6P3R9F+}B(bcM{jUh*d+FZr`$*rR52z^#Jt&D$837 zEzI6Pa0{n#HZ~pyVP73Zz_t=yMh)UuRg(9o&w!=o=rBCD;I7wYI(__e%8uDBKAt=> z*!~93e5fRR#yuT3@e0s<-u#=ow6tn-0u|Tum<+jCXbazkEoj>is$>bn#9WQV6r zerC?{VD@**(ci9IyJ|#bTmGtAnorP=e3gwnh41yJ)gCRr+pS7(Xo3-6X1_dvQ-|oK zyK>+h{2A`cr$aoN*5!>PCburN-Hm)l2jXk zYRqAY6Au%F=0M}1Lpmb!2mLe5-o<6ejNr7r+XkxmX=VkJ@;R?tbAeplh9k5n(iPRy zU!=d?#jjR$h?2CRhWHZyqrba@Im&6bIaNt5fX{JIIZ+|&cWl>{hC}$40bUOd-SaM- ze|&_PxjJ?RAMd;^m6r>CA`t2T8{sx^e0uak`vn~x@tUii+)Eu8ckEOUNoxW2ol6b` z`XSDJ@?foLdN3tBqz$q@G#3x?j(MKlXT?Nmk_6Uhzq8G}7iS>lT=kadf3dgDES3%F zGu;!z_uWdT{rtv?P433(_-%NWp#J|84JC(L$4pOiJd>2-xtCqn$a=*B))GG~Jr{+o$uR zew(z5_AI3#N=LVo+1}BQ(y~Y6Ve*Jc{u+*$&%ot!f`>#e70SFNa^a>H)ZKNz-fuDPFH{C!Dk zb-z^B5Tmas(6R1#_mz*ix(QzSg*fj@WQ|-f9=0lzZf36$=72y!EdRU)_NUyvs56o$ zam!*fcOq^%sRq9vbUlb#NScRC#8OdKMsZ=1-*Ftctt{^5U{Y8J4bHO0%=7xwqAwu2 zkl92K5AS~<9sI{Cb zRyEO%YgDHJRq3i&EWY~&jS*c*#U$M^Fv0R;jsJ+E$q4<7#Nut94c8vNn)QI1d-}g8 zRs-w~ny!vw|Du0!=WRURjH({fUv)YB@aI#-t)ri@Z`J&|Mt7x@eyVWu-@!q-Ihh|V zCFlseFEpV;55X`$=)&)k0737u7+CC`U;9CWM^C25e?f-Qre_&CJG6koFZ=b(w8!L~l5Cu*(}!GIZcgYOag zo!o#?5)Y5|27jqoh{UK{PqI(@0gLDYm(j)hEcBjA z@bsv+#TduVM4n{(YHG!*(cxRkraa5JeZgqu))3Fxdir-Q-}X*CcJ4mp3RzR>JMs8O z+2xB&BzhB0v0)%o5OjK2LodWL;q+2pTG!DFCO%k;>6<^Z)T@tdpqu-$^S_)J{yF@e z_AEfylOD0CJ*%U@OR{Mmew(_Nw~kz%M?(f~96YzabXv6Tf)&w~RbfOpm6wDY|1FW` zpI=+n;mzMSbt*2XFYFyVp~uCP`+meHA^nE4q_D9x79w!We!0R~2$5b5H3!Hzd?8I1Qk^B&6?sYAHL5B3Q@c0RcTi=w8C)${nlnI)!TQwEINUVT2VOB!kXX@Q z;Th<^i#prk?H2l6KjXWiB8MHLCtQC4_Tmfs-xZsCaRK&orH9M0jUF4N9@(}LccdR2 z?fl%mbhsDDE$RZ}+OQf4xRAE_g5@={7n=Mf39*Qkk~jW+)j*o@HR{c{hn1)A>!xRt zvMY&74;yQir z){k#`+m@>(DtRYx^mPR@%@FodnhkZ?8v?`Gn&zqF&o8;^5tD67%^Zn{n2I9e_JLud zfUK_UlJz+0U6PsMPGBQFzx+DsOhoWrw9fiYQGpL__TT>PT`2`4>m%i#!6k;0xAWw$ z&}%Kvwj&@4n>fO?ZyoPJd|bc06JFk^?@+~Eae@w;GOfrdG>2-m~!dkw^v zfEaO_$yg)hAQ1%VI5-VPRPrW<$mP=@o#BUQvT=CU*Z$bfB1(xpwl3e?&^)`m_mLwa zOPKw2SOo-oM^TquJ&G#9g(Sz6Mev1un1rz(42cQ`3Tmu(3C^td$NTcD{w{bUh`ryr zeEEH?5Cc)x*lH13VJhw_(Z@3H>c@5}<#cSQ1r6on5-~Y!4aB!ST>jYdXR{B`L;Pnk z5h>uZcC#9)cc1Yp<>zpptrg0ZI|Ee^qEh8E&pWW>^B@ONA5Rdbkc+h`CU^39%M@=; zU#Jh8aUv|=r$~KB=_uLo-98_axl$PF)i>4#va2 z<`Cxq#0&>PcIpDn!@Am+avo9SP|aJeA#@a~)M*5pnxqnb`E7l=#U!|awT(S zdKZ~W81{hU<{>i^?tOa^50a+@Y$HBjIBo91^GkmGNGS3S5tQwB+8zhL!`gyyC_^*`FL(f2c{q=^UJ!*`o+@aU*VosfvOp$58kB0b$^(j%Pm{` zh$bs1$(_MW>OIGU%EGtY`sBUL(5LpPUf|bHh|F!*C8O$}@0FWJ>E~H<-TikLl4h*z zR>@tjo49>RdH^O{51vOOnF>dadXX@CW2A~T1~?g$AY+D_GJQ7Kx!ti||KYmR@aXdw zH4xv~M(^ysc6aq3<5XdmCXB`hIV~9q6H(*&;31=l<13?;)jk4FQnh?vmXTr`9d@Y) zdXo$L{v#@m3#%?`+{;p9L(Z+mUb(Ju4pT(^edjOE@Nq5btfofP)n8h#@Nj7U5V+ij zj$fA5$wqL>X{E_B;LKp$gokOh)?9S>)#BTlXLAz`Ba3YlPup#K`siG?K37_oSZu(- zsPFk+cUTxTp2d9^j?}6^Jiq+8ecQ5PY+B`x@up__yX3s=y5+u6_@ytiURf{nGawT) zClsukF~~(40#3JJSb77@*PB*xHukZhp`ifV#pyIz58D@ma$!qV(^AiAH>wD8(;L6Uid-*p`yEJ|cy^Qn4?FRi;M0wxhNIb+m212E~EP>bOWndsV8a5y#&zyJsw zE`w$#@^o|+mN5}u-84MSjoXsMxrfO1dBG)hO}ZIN?XmCoAS!4w2QH$C*qB_o;8vf0 zs#Ei|YfatS$i;c%()Q<=TZHk~_5Jigk%8kcJ_(*AieZ~l6tli>S`43PH!I@@ ztx>(tXUF7XO+|po zc-hKAH`q4=7UEp(hH8V~+v{g4*)2}ly%;P^Tb?%_)QO_jHt)u*pah_&kn@`i+nsWY zeJCyilS{38!ZL>L0Db~FRG-?}v?~3F`%}$XEyNc3-rz>Mkr!R{db3bgdwyzlthgq{ zw9Rzz9?|7->5rNupx|o9$bHb4#~3R+uM!g1&FY_m!0GPcu?VzDNx z0weAXJGajlBxWx65)D?oy;zTml3(BVs~lKpbvgN{@A;mdXirgPA(3R~`+kA`J2=wA zqhEuxFOz^|>IFXSijP;T1UjyqXBlY!tu5}iMH8>DbYW$KLx$q0y*}AB{4+mHnMOpD zRD{*I^mnHS9HC!POWwp$De+W#da*i@xo#Ok>K)>lui~Y5xUg4T!zH6}Qcll8v2qFl zB|QQh0T*nAoT=n*Z%ifX*!1(|&a0McJt?sgb?b57oLk@6a${8pVZKKX7Z2x^q9h2v zhi-&}`=g!y^zSZiJ5AES4=zpQBEPcFPlj!tj#!Kal$ntt@zmxU$|oj4HDcCSlkx#- zNzM`Q?|3O{u@p}%njH|0YUOf zgOwA?g>3aqEN%oQxwp@@%x}3)pRn+{4*FhsI*|dK(nX{ltjd9;n5fk2=NWO zf>6R0{^Ht>VxKkrszme#ELRSLLi<(5UN-#g6;hkCkPks-3G|RYNzRRxX#|Xs zjt~46QKOZ$FJoNGo7jn0l0Gl&FmJwcS2E13MI!Pd5oUNf%c?eydkg0tE$OyhKn&j!L_IP5?~*>sq%^T22~ z$>J&VDdTYUsK|NV0IhE%o@!@mqZPScU+Kwo?b^U{<-&tliD?6qY(3F+E>5Vz190)s z|Dtb5_E$rnOxX)1huYsuDB@ei(VkKWSapV9&i1xWli;_Ng%R$$g~o)fdKp9(!t)t~ zNrsJE-p0eGuZ&*8RrGK)CuGKil@sred;GhK<|tW7@no53^&y9@VKpMJYQYp9{dd6) zz+qleXghu^9R=65&|1?MD*NuY5pBcB1qH9b-XcoYVsbf$!cO#bT04v0hEeHY=wvr`1*;rpk~`owPRX<=C1C;2bw?G~4UGh8AP zN>3Xr=UXWKuNd3JYT(n8i()#t%fs~Xt0-l!G%q@eP^b1x@&lGt94JX!_M_Q}{U8?- z&E@+ApOtZ5Z+H0m`L{YTv?rmv1&2S>E|aBGLBOxU?YqEwU=qvcSa>U}3U~vrM7f2! zoR7fYPcr}#x=3({i5$wbPW;vS>!qjcjQFdqk82q4RkhYYR~im`$lq^l)tH>-s#oAs z7*m$W^o7MqGnM{Vb6KPR{mCVzv|0$oHLlXKb?~WO-n%8ci`D!I7p> zS;S$P`aPCle6O5F6OwKnST{z6UG~uaDZ*@oK!5w`100cYYq+tU6c8B zJ0pWK{wl3N?aAx*USZh1W z(r`?mf8#*#`OV@)9&BFfcJ7TJs!OugK-Je4eUjFNw9AIx-8>si2{jOI8?p;EG$jg& zLBMFP{Wex^oDKEc3jI*Tm+FV0urwqemS_9~tsbK#^}&|;_PBm$j}%4DHL8gC^|Imn zIa#ZNvTJ!-EwQo{D^_O$-N{my8Tyd4Dapb4Ll zfXB@0>W~dE%sF_{A>vaHP|EXrc5*1kKk(k~6jSvaHP&gEp>i%tybK@D|!oRwh00LoQqzv*J z?7NqYj+?vSLm!PG4ku9b-KUyI?g@juyvR4-vv{RSQw-=OxbqSfw9rA_MU+{G=X*i; z!m~haC@gf+mP_+dSe>cVnjn?YzyiX$@+aWMX;@z$6f% z$EX&XLv$M}XB6<82Ru#7x8f|t@e@&enM|#|vF4f`ASG@xqMpXb_xmXDk8_PAPkg9j zHk&+QcJX{1^Y_9D?63N52zIIX=^__p&`1f}Z`o ztw^Ptq?rMNce+V77ubt~*&mP7Al#LA6anhE{N~*oqOBcvGLdu>A9LVF} zsr%atoKUAn<8nvKSo+@g6`;PQtV^>r^J`m_8-u+V1;~l)y@fDtTM`e_zVx;ANe(z@ z{XfdlPsaWQw>ral-Nae|YF$-uKEqcVcOyfLCvPlp_ReRg&Fp9>`}RtHdt(C%pZ|cFLBYSG?V2|lSCLI?vLy5#(~lp(i_~|i4Gq}5Jgj!pqd~v{T#@wZ5LaJ$r0{v!S75&oEB^m~} zOPu*;b}Mj8lx@X{P+0pvxCpK#s2&gJ8f&D0yc{v z;mhG)eEp?smcapL{M#)-+WuQ_-ovbvueS9*{f!mpUKL3iPl3i{zB!&1e5h?eBl#J0Wc|ZR?)`R(3L)`qWygcGX%@e|DCeel*p7>5kT^ zQl!#a@_007z8w!6u1exT+UW=2^GP8k4IQ?h=Gj^unUc6rDi)G27rq4c{?1$c|IMQkagm zA-IsOtk2EcGigKZU65)wDxmnL@v4oo0c=YNVCDA!w0>%yxY3lvRd85PJqm}AA*e7_d57nfQ0@I zfwkxBM}dM%UX!!%*PD+(%ES_|psJo;x_v8IkBiDOl!5_W<=jWrSJF;#Upg zRqj%qT~^;MgHy~T#5?57$(^Ezinid=gZ8Jvkh4F9uD~s3W#>6CJNW3#G-JA-+eX|+P~=upIX#Vrq^Ky+$YUGw z>%R&1fn#Suu#dOR_D+b7>$+6S(f)3i;2#^Rb;!)`f!p4cH-7PhgEPVV*(VS=Ei+L` zzG`mc(2=Z*tq>e1M%nZc?Q5nHRcM7$8yv#(!O*t5n)$t8v)8*b_-LPvrq6#jo;-m* z)0${Oph|*1=-=z_MeT4r%IbtL6EkwMKBZyUR;CWB(q2<;78sR1&uo$`TG~+0(*=)+ z1dn(JbQj&VLSAqas~2QB(F1DZ`o80^sk&pBr{NrDiO-|H(9y!O+MFWv5>77YF{3O{ccCXHs9Dv6LhEI zj85)`=^8r+ne5LBlnG)j?yYgj)dD6{v>5&l2gMQGXTtH(XmWL!zOG3=;#eWe3UEI0 z1&7Q9AnA`Q$UIG|nuL>&6PadmUzZxh)643L{0bYFa(VB1THr5wf>8Y}U@dba!oo*o zFXLliHP2R^XdnegEgj7M_o^@MGusf?KV;6^o>!OIbLG82=ec=z;;Sjt!XA11PWZ&x zIO4s+k*GpAlo&iI@x1DDy2ENDbyJa^9$wk(+xyX`Ke1Rmd}bX72QaN|I}O?(GoNAm zE;UhKsRVKJq}@-f25$104`JhF5cMc`G$h zTKEEK51TaE9(2@ld#;l0Vz$Y0GW`<)Idb|inw)}=6slNr)?lHcwCM|Kg%n)Nt8aZO zx+V;`QTnH*a+lO$yT3Jq@X^|!$M0UEe-b4QE1#C3sMg|se=|PugIFAGA3&9qo6YceUewlRyX$02uKtRW@*#;T6pk1rPs3h$i#g^#}ebJ@IW34;6p z3LM7trU~1?G6zbynEO}MMH@GCY~R~t>ewygonq>g3`Kwy1=d<0D$bjD;W`aHnxyv? z^)a5hDP4Utlct$rw3r6~H$rr)9ya|0ncSBCCgzwI=REuP@Xc`@-%E-Wq{nKL{XNpy z>s0c1tsiKIpIQtL8&W|<)vA+%Nc8*d+^vvwWe_PH_4QQZ#|OgtQ>r+BTX?7J_LJn9Gt({tu8WLoh{N@?#}rTtyf9d>w!(j+j1zGoE*aOjLK!^QKu5 zg8`tOpwRU;`EBM)**8C#U)Q=vGL`RR-E3c^(#QS+i1Y%PRr|ZR<)uxiZ;})U_x|%% z?eH>*0z45ShMsA%&5dAyF+kVf;DTk)<9C&`th1c;B+Nd2lGNs!K66NolLmNYZ@U($ z3xw4NJ>jZ8*P+#yW=(u!ZS)doZHAN^!4bM&#ZL4YHti06@1C;;@;q&FM>ykmUTxxsci=pZPVcPXL9r3F};Drgjl^m24v?OAVKT> z)@^`-z|7nmHhDMg@Ds5ZcQc5i@fj0w5Ofc3+a7ZrHyh1PJ?wU@ppB%od-D$UAQp`} zA=M9f?ur^(HIT2_=&jq?yLjlg=dE#hTBhDSH+^>OJgrXW`QlfYZZ;t%*vocC7 z;!7&No~91vZlX61Y`s{>W=SC*4Crrk}Q!pR(IaXQ_N{J^MSt zwaB#dQ{P+N--;R;66>~ z0=sbZZ?`Sv_+q|&SYA;bx@%h9x7En(^J`w>e$kIdAjPTZR@qsT3^gUTd6w6;V?lcN zc|4+mHyN0Sfv%bEP+RYeU zM~|OKgD8NF00E2meS7d`uh=YBBU|4}#PIyI@GpAMbj*h4hw)~E`3P=QE*AQkyvk%R z@e!vCj$bKZ77YosH9F@P^lix9P^NpvRht?KZu7z$p>5nBt<}elt3Sa#664BL%OU6- zfk9{f^PX!-&JKvot4TyuQND~+==hg^mp=Dw{pq)aH>p>cP?jLBd^vySnL~AvMSkVX zBTH@4`jYBtTB3}r!~$)$;=`qVxqu0Yo0@w`Q^L~IXC6^f?aOzv;xrpK z0cK?Z`p(BhB{YC?O}R8Xxj8#xk&8x}58-bSm`s7QYXQ+X_hOon+lJjZ!&LpNx?UB} zN|!EQNP`ftUpJtqiPJ}NERlwc+$4laW)@7EJeJ-|8=gP51g zlU!bd53iF54!o}@0GA#88$5DkRVZQSPAaCG*LLff@AQPPT9M;&D$)s7-N}_5SMb)S zw5ziPMjpE%v@+6JST79;)ZFeZ#>5Ff>n=%VI?}^CvlkxJKkop^l6wxXtz6E}AN=hN zsCe1mF7<0C=+7gzgWKL`Lu8TE?#`=#_;!G9fVh8oEQpyHpdz%S!(zuEpb`S=?*Trj!D?Q%f_i^;HI*w|Iy@ zE8n~EZ!7$|n(hgVEj7J{Kew3KKu)7DJH@*MN@0D-Yg~4Bm5^_P=>`Ln{wGHQ+6B#^ zgdeDG64GN(2cyCFA1GY<@2(S->wsro{t+Y<9^MzZC+?xgb;~V9Lu-R}?IQ|p$;$0x z&L3~Q*`?*+tN)LEAn&FIwR7SL-deQQC?dyN>0HGc@?q;x;-a)?EE*-@m?Iz)cPxyUz$^>O0{V|7Rr1=(C0eqxbj)=<&5*Q896C8PtDVCZ+v){HSdm-F$ zKYegwENMB{hi#Npm0`KhA%yRvj}Fst<{dhc0oKEkO}!4GaB`PpW)Woy!!qDFJbE)> z9+H+u4}G-MlpMJiYE2Y&l>klaDQ@>)b*MoUS0{J1e}$a%nXiPF$cO}mmljX4D%bRt zbTiu_)hTi*qd=}T3qQ}fmGNRgurqj;UIw|S`pVQ#V8^XQ+(JS-r?5lwcj^%D-IkkI zqCzmiKpUn|$1l1!OFTUFuJ8;YJFi`fh0X8`T1*3sIT<1x3^2r(l})e`eVFB z{;#f_v9r7KM>i?ZJR{t+4$<^XzhTg6?qF*!wxh6L-?K^-9sk!Y1})tBA#u*|oGqYU z!N5Ko2wY<;UIBb)nTjAIIxh^!4LCcx7`6c?D&+SL_w=M)x9M0dPRk?h(bC$8o>J|% zgqaI1*~RLvKZGkTUKSL(uy7f<2v#6L>>A4M;WV4viAvR8LKxfpQhLW7bBJA+t1ZSW z>!zEIcF}c?sE?J<;IAllTfnE(;Gk!Lco9g?3Dzee6)v{;&|cG;r?34rVyNN5SE8q7 zR);J;rWa>Aw%}sWd$aJdK;}6G^*YryUGK_R_^{u zK9ZK3&Lj9!!2j|_UZOdbDR!8{u&v8V=zd@==|t{AFQMf731@YlSob(6C5`;yBE#uP z>yESfXGb`*i)Nj;ik>K*L|^!NsvKY4Ik0I}; zX?O@IegWuBKg20R2|{A}QQ=ps8pw^Kfc?5OCLmxEW+@i*|&&WJKnN=3NwrNp+fVY z77j?BGj}@{(d3&!)cb=Gk)UqFe*`tR+R-&W5tMScXe$qzhDe|L8I^KH<4jHg$vKPr z3}5I=$U~bbAkc|_l{vVU@DWtMnr=SRW_79i20$rcJ~Cu}QLImh-mVSkiNXd8KiH6) z*#Ubxh0y>{a$NF&)r#rkBKSW6NVyeJ$TR|+R)8C2TX}kt<9>kxD=rpO!AU)UOdvDf zy-nT%$|-`-gCB`9hD96%P-{jsl&U73iw21)(IMJ_eQ>J#X8#W@Zb>AQOZD zjO`5H;z1H{vj_|)K}GP-Pau)$OZu-S-07}E*_*g%=}Nt*k& zQe|Zge_=H&k55pe&NrHVvU@LtJU=-eE{P6G! z0A1Z%THhaCqCbA^F6L_lm{(P;(51S9i0AQ6)w5ZHriKzJ8r_T^ihHoyo%cY_7>R#r zAs|I`J|JL=xv19V`sEPW21B4?>MY8}Ae>S%`az`%3OKNHYm>{ME%&KQ0H~=IilKI2 zrOazi)_0ncoQP+qWd&cl#p4+5Gn}v<&MBoFgj__pHA0rOPIe^n1H}DBsSAJt%f({u zHyD!p@3q@26ro;)W%p(!Vo%S(3hZSJyAX>UqEKk{%n5>n)yd7PEF>)$DB9#fd&L-I zMTSN$nJr;7RGl=R-f4P%;~jF*79+?ZIPk)}vnuIWC);sypaT5`-*-*S)f@s_s-?T8 zU4*Iy8Z7cgCAFZkLrS)GNaQ9*ro>y5hw0X?#5*nhVM2w%3W9{S#|c7)u+L4rqyQ z6c;5(h5V9)`$``G*1=^Gz#oyiupno`E03Sy3g*#t9oXBx7fCkDEj7Sf*KHNYFnm)H zbY)(a3wm@Kkf`ufxs)q;j$zL0jFkGab|KwBol8O_fXj~No{*w^7RyH|Z!XbiyGDr_ z)&bV6=?qS~2#D#(p$G49)_D=pgRym(ZSGp@7pw#;II>a@n=}2ULb5FLf!KWg;u2guv72O-OqOXgFr37 zqw1QdF2Ft=Pg$p<02e#~SX!>UI+ytlm9+ju&$fzTJJ{_qI#0Jf6dM$dm~lXVgN!B? zgT!TmVbCW5P{Qh&d2hR|n8M<#;wYc%BKJj05%lfbT zm{HQP5;MR?(QRXXiVGVr?G>i|>7c4&7NenLG{e<3iE;g9u$Y90SR~UM4|8_o#%hJg zpantFAeVt{$vt>Dxx9OQoNU8kmSipKa)p2>C7)xUc2SRj8Yrer$f;#TbRpW7*Ju3? zS77*>0GJK;+*l%+tJHjeWBd#ub(|W|Lf+Pv@zUp7)5~KXqc*CZE?*1pvMx8~hu1l+ z9xrU|GKTAtjBnM%N!~zPEzW0-!EGjooYe z%lrKZIu(@u0hIGLH+)*wtp{n)aX(GpU&Pha+F8N^Ab`THq{6c7$H%oZ{+P^%prvEW ztc|SEMFnm=C-e&Ws;@umwV-crtxC8)<1i4K2`7kefN=QIdZ^|*ZH#D0}4 zCsyxbpAUcaTySeotq#u>qeBGRtZYSIqs-n=77R@ES-q7D`cGnjsOf!i?S6h0zma>6 zL^BUZyjg_)kIM(LN#V;7)FL99&^koY;)JB0+gsSqma|4v0bVY*@xAO?LIS?x?)BbJ*i)2w{NMkCqPgAQtGW@e8`-33A)TZM?DmI@HJa)(9lr-()ZVTA8a|`9!|HmrK})hE zS;o)`KXVn9c7vhKj^}K;JNV&E!qVwgpDG@#+{L_zq@JtzaF4oa#;26(%4|`)IoEqn zn9C~?1;nLe_|~^}vI>kf+LYOf0{D!Q#%Ki<7%?YcJ zIMe_8$;Vi6zK0R#aEDh+6uzK#p3@RV&NOj{>jj;^Kfq$ekLCE3Dr3k7BeB^Ou!LQQ z$w}w{0|y@xBW?2raZ|)F5`l5dY=2BlmZQf99UwtG zYik^WeWFBLq|y$txSFb!s^9Bi>LaO0HxG2~)=-N*gZ5343YyE;?E2UeY^^F3FBT(i z?&QhtL)@agGD^fta!+@2yD4;>;XlySQ|AV`L-idl-17@6Gz$dl3v;O?ww*$~b5#%BwWIj-gO3X|)a=GKcT<1bFbBB!E$Dzd+WgR^$(IUDI22(?$JhI9%BGq);)YU3!8_GC47);??)| za)=y7A?XVb%X06__^cx1;)P0bMls4}vuc57DN}Q?_Eoce*X-3gr}}4K{C_ZeX7G2o zpO}}IV=*dFpQ~<^J3rvUd9^91=}D^=$z7a@2!_etAbEolF{$|ZG{@WIH;Cs=$<`S~ zYc$x86hEN($pL8`O0Yf<`LMgK2?QcUn_R8;nkrvk+_(jZ{p{d*O#lW9sy%K#kV5cV zpCDSX0w%rO8H!etX`E|g(HE~<#fidSxP2whA+w8LzI_WO*KTYxaTlnTU4Mlex4H-N zCwdevyf3)6GwGyJpe_jVK3n^=+0flUaN)OmL-OfW}hmm|3&)qtbsmVIc); zj{$!IO+^!Zol1D$6)VI2rzt%4;-%ti83oTG1OirMdiZD=l@^{~uowP|L%qEW=KR%W zQU`$fC?JvIcMXBR_~0v}()ZGWc||Z5vrmZwzZrA(@-5YUFt1UpVoOZRtKvq+zvXm7 ze|wer*(JSSCB&11K@Afb0$b@Gfvl@xFB#l`h+Oa_O2S*4#t=gH%q;svobT-zG7L$y z2VnsbPx0FP0Eg!(W-PkS%NWPn^IoR&3(4mo_3OKlTTgXQFyKDrYuR@k++t*m2${v1 z;e@^QdWhDaUua3nAJ-U=1jcjM8V`sWAH7ckmYoI61iPIj1`rZCK z++d$S1JobX0A$PdFkftF#&;*dL!S9f9o0<^vw}O9P0BzA4BkUhx&GNKZlU_tMW%Qv zYM9ewkN}dKjs>ll-QoUW4!M&Cw=5MxL-)5?=<%nPZu`+*7I3+8&B#)>-E9k4W}kk; z0-e^g*u1??G17Cqg(ArF&W3u}KYr>hq{iQ$nr7o;^{+ua>6o+t6Ew>%D&_G+aN(?m zVjYQL0Cl~}p)l5tQ6;@&B8bRvZxxG7-_fqtTAwt8BXx(Wa`3CFLg|gbtjVUYn5RQe z59Pk6mA+t)4BLGvzw!X80$t^CzC0wHzH86I&%*=3JpC`MaB-Q_BKU(`_ zjn%x+%jyk_^s84i@%Q_!x!p7Lr?RuED9NJPIlI_W(+@=m*Wv)hwe}t16R1aG1F$uQ z?J9#zFDN(m#$T}$-+?emY*8#ocs`#QBI%RfZ(Lw63ylr^d@Q}Gxkihz24Wn()5XQd zLW)-Ul=<}N5l(~qFJ1Qq9w>6GNb%8ao3yMk5%)u_K$i*%rvHp1wd3b4(J)vELU2z_gt>h3+ zrxG!1zt8wXBctKfH0yjQi3aj7b`4}OxEp`TTq$(!!I?RU^gt~=R}~SYy3RTs6infb zj(F9mw4wJq+3*>W`Sx6Jf(-ihtYiNlUvCu@R}+Pc;tk5Zo=m;O-h6&i;?x`*7>NO-)Vp^h~eb-D|B+An?-63Iroljk*F40#}*zJFqu; z8Eql=gB*;-1o$u>aSs%8Kf{vX4d+ZA>am2Pa_u-#*Crcc? z-0|k!p=w%A^sGFk@}*vv<`D(D--jwtNTb@SQ{Nl`?1M6#wm*QZd4EE~gTyN!_=)IB za}DLzyMx#S%WVy40z2B&`vDt<4Wn-zmQj3$j<>CzS7Z}n)6d4Ag|9Fgp$$dSjNbG+ z`miHFd@v+t!7zl_qDXfpBAVfWZX>HlQ%I0z zBYhf?HqwFS4Zp@tYW@L%_dXj`InV=pzm`_PPO@^0YxcbT!=>0f`N*ZX-}lsozRFlH zO-&BvNOuFRRkjy4=n4J@dB%=&9tYF~;ICjj)^7w)N4`ZvhxrS4BBg*r*Ys4 zf%=~?L$Fkzpp+N=6{GT(SH54-+N0E}MHy!x`vNvO zrePD0)fk>~2mzWaoI3v>D)Mv=#EA%q`J)ub-mv>^01k+-8Uvu*FNpz+%oWKGb^o03 zK&0iUZDG<;spm&v|^vWsiy6PktB zR1E2tQpM}(&Zd^@|9o6P{KL=D^ z@Y3u6Vto)j!i?i^EI~c&fv>W(=|}f_~+XX+_nKqm=EM~t2YdRfIAbN#kVrL*U#}%q@tO9v0=y9WnWiN zv6v=uuHY8Xt~?&h+*&{0Uj}+m**E+1o~})JnSk1DfNQ6~K9RWXM%HoCkRyAQ#jEEd zQGVB`+2@o7x4z|uw>ML-Y~j{)7TWPtoX%0#)f93Ev<7JNC>B%xRfIOS@h1{$1=qk3 zzVBlNoS)yf+#xh*X`wjbCW4@$p&b!>|Gp2Fg!RFg!i1-jx{Up#e<2(l&6h@bC}hU{ z)`INFchk>!K#J&=OvpX;3(H!)Um!Y6(tfI|RAr)548*BDlh3?CPaLx{{|D;=&4q0Eb<-R6n=h{#o$&!=$3%l6xtKmStimnQ%ktG=|> z{i0R@rNvDT6I32`{=_Dq`NuKamENU{K6blbbaf6*3&MWtBx!+3y7LD1APBe?5T(Tf zI6lqz)Y;fxy^~JNM?|an@R3Ukl-x1gK<9$ovZ zr!#krTK2Ghkcv6pTqX;YR-9Kf=y{vp#UZD-L$DYQ?F-iFYcf2ZSAYNA8w~Lirpin? z1KpV={B=~EOUPPT7vO_dsHD&+=J2wy&(f*W>K1cg*X~}fIJvoTpKVr88kVU@^w>VK zv!%4hw}S~nzH7#Ssnj@#1a8* zswjLGXL@MwgXQhyr14HzCzk!C)5>@1R^tp&2jm)T+b{JnF=ILsp4vL+vdunB zrp(VL$Ft3L?ecsYz%MxIFniD7XOAPyX4$r`80P488;coPHMdnCG}Wwc++2fiLy22& zrPTIV3hrjM+>inu8xU=PyyuA{eK_FZw~Ce?$qJDU6xetd0E(&3Cfc&T&_OoUz;|>z zUFf#%c9mwwQZL|xrpbw6kxOnn&B4%Vov!juK9Alpc$@xTdH;961~lHRR^}Rpld%NfMAw8a}||ru7QO#$w7J^|ark z3C#Z9_GK0GpfR+{Su)%SMa|^?%)Q|7+|+k^<%gH)nPE z)JTA*93Q}5P2-?e3<^V{;l!V^yNnpWC(tw**j#}9g@dv%Sj{VeTg&ZDlI|SdVwq49 zC=di$yAQ2E9Qd6u^$PM|)NzB!U&`<-){5i$6nR~X?SF^B2}Hi6)}(7gh-CS-8r`x< z-_-1gXg5X02ma<^4l-W+DUWq0 z>G9#uk{UlyJB`@gP_#r1Nh4Nqvs$^?U7;M~(AaW`th}*NMLfb4nAiP{w}IKS-+z>| z?5i%N=3O(iZuFmfaE)m&cv^$O;Pa(i!M<9=3kLh6yUGKEd*2Bry;_yOnD$0cJkECM z&H+Mr{(4zlLw+>Yi4@EVDe`SiNs!Zs_;7(l+$ZNUTD*ZTfRZ};7L;a@ycf{m0p<); zO0vZ$@!Z4n$+jpSU_YayzBJeQ)uaw=)M z=Z84uP-%6Xq{`HL&^C>A^eK_!E8XWR=Zf3DnG#h`j8QbuL2i3%&k$JWLNvFyx0ZoS z=keVO90!d>wpZ|fx5pB=1iNKu%kv`c{yglh}eEbY8!F=ziu30%kdGTuZXF94B5 zK&j&jKs`Xq0VR^QPX{M}nG{`M6%>m(J>@+1D@;2po5~ZQEcCs{|82SF{T?^Lx2I?4 ztD!}B>Mu1DM#$D5CT$39pY1+5VgZ6*ko;DqZ&t*m#7?)o5c9$7m_XSSgJl=tqvSnsIUPnpdY`I%5WAL3_y$O2z*H^RFkUXfY)OhlI#r|Xn}Qnak=LsvnoZ6 zX(WSeo2bfxx{!!&QyhU`uV#`N(#tH3G{Q^ztk4i z(FupAuVMh;omOJDM_ZYf;%2eO6&;95R@o_!L$(TAFBHg$1R}Z9k?jJo?7lAQrI_=z zRv-ptqTkK>xFju+ZgeP)RKrP4if-!s?-_ZwfcEBP$7$^n+E+O{7+J{O>tpI&JF~{m&-qx~OeaB5^QQ)3$6^QV+4k~I>Haa-mOV8VL~0SVk2V8sJ=i8d{lDMhSWp-_XH%zi#PW)i6W zc7)eX<6uORGi@`9y^19I-uBi{GWtVi2%o-zAGJw3I(Dm}swQw;ir&*|pYA%gqEa6> z^u#QZb;M5W-7H3qI!+l7TgV@BWJUstF6fTqgSAXV1g=dejxW;D{EMp*EkM|@WVB-U z!dCkNwg?S0kw6zwZ;V2PK;Ee$3=+TnKM)((V-q}FKBK%^Q&`dDgRlM{HjAlzL2Yn* zg;Je2lSUChsw3Ow9SSSysNrg*2kb>nWRGCXfyr1v2q&2yGFFaymu0>YDpsl)fPI-O179xm zd{jK!<#*A3*vsJuWM7c%?13*Y5(}E5*u&J%NY=(82IgkHfU_I;C%xM08&?q*x`)XE z1P>*wiI*Cd-38JpSJ?T>>2nkmlP{psoO=bo3p)dpVM|O|&>TPr zyz~>n-Y#=O{8l>0%Fu~Y1qXmR+I|A?ghRz33S(}TK!=521=QHRpvRpU;J6S|3}zE1 z`2MrP1yTcifZu+k0hb{~i5Eyjzy{&s(6$}!E*wXP5}G6RZ~f>JcQB*O7aTlSQsHFl zeTD~UFbt0~EtvPzCrqS1J#9Ts3-L?*QZj{Vas8b}9^C@ioyS zN> z?@6`MEx$49?GY1x8*PheC-)6>*#2#Nwt4H*Z!xoaWBw|NeoL3m^o@kBi$$l&Q{~1i zWqD|HmSsq{PhOvn3Y=_jSWosAa*oHU4T0x~@tWQhWH&Avmd09VC|e}?OYGI%F-BnF zb+i&KQrP?&VbH$zOGnl&YMCc`y7y0avm;?)s5e2cnfnJkJ;y%tsD*p@)G<2DV;oD> zx9>D!O1OJ->bYBi_iK8Q0rvw2h;>M~Wmze|W}tl4Hc_8{8g`O6o*XsBhJ^A?i7J_P_Vr}2x<8*$Q0h#jn zYk!Zv{ovb|L$dBS74!qmYVGS$B|w0Xv9|4Ij8XW$LP_6kQG;jG_clf8>nF>Rl$0nV z#N1Kr-pN)+z6uU0=YVtsBbFab1K)e&H@duTfkX+wP(9w}fB3!u9D}8r5G3tR_j4>I z!E3;UeD{=)GJ&%%!B^lyL5$Rj^qpbjDi@g$o zmljL-5Xi=#;=a~TDRh(O;(+9@P^WhYSpjmwZ^+ODVOf}sz?RoRgl_4#AgvXwhr-$9<2Cgyzo&tAAqgSj1JM3PYvY>|Lb9bbGm?*prW+RD`vmd zY@-rZ^lLBm1nkzZ5etoS5G+c4%uu_M@EcbSaqV_Rd0ueL-+E(cy+!Yyt=5aLzDOHc z&n}PBgPU7mfD)AGmdeEl## z0Oqmc zVjL}8OgoY4!CqrUC7hr{r`((ts@a-Q09N-d4@}*KjtTVklR~^bZ128Lvqu=W@{*z& zeLMh|4hQHV5`0B)RedL0V8`&OH2tP^|C92ydl2?fYH<xioZByf|9A1u8Ht7p> z89W18_str4C(xO68o#J%f8nY4$j;bHDD)9BF2nyW7@B=-ZSz!~9(urDrvK8hJm8=Pl73)( zka`sLZB;NWMHPs%m}-BdAN>A^b}h7->~c-_3%`leA11et7vTi0`w!SMz&9ia`N80k zebJ{65<^(aA=6=7ZYMsZ3Zjxw;;p<6PV(c)9tdO_iMKv2U+DhDPqcLW2$SkNs>iV* z04Mh&p!59xa3(WD+K>Rj{>H7#WIgpP6Zlh^nPvvVD3B)gnh2&W?E7Lc*o@; z=29tXrS zds%{6vp^qqdpuHXXEB%8V@^n5sLlSKB8TH|R!C7kbnhE}1Cl~nKB|Of4+s~tj!F+& z-MY$kGvFhGjLS0Ti*I`3DW*kvNu&&i9BoiW=6{iFBV}|A6kvFEq@U9^KEk6eC0JLK zCOrgBEEBH^ap64zrrg$Abr(KzbXUyZ%CQO)S!UhcD0bvhgErRMi2(7JL#59x+wXi9*t!cy$_|`|a6@?~Y#kok z0yOWqs1zik?;*XrMz&&S;Y5p~1HapfMHzy6C>|owSL19jE~ZljEvUlc_YLzZg?%Z( z-AZVSkarbw3jv#I;{kM)?=iCBuHo*(%yb!J!G>>B2*g;z9XuS`&(oV*(?hUaz7%{R ztWq2&9|fnvTAb=s68ud(`oAQba9ZrXt~yVY%CJ1A(U-*J;2)Vu?NR^6V}&zxf~=p! zMP_kufeDc8W&Wqm^K90M@hm{4__pNEpaArL$cPa{>r z>Ir8C*&RKXa6|50^bh@yJ`+W>u)_}Yj3f_${_z`bL=5Y#Wih^no6Ip8U(r`se_tn| z3z4Y&$U^sN{Ch(`?b@{DcU#VXu>Mr7MD~N{O`M>@^PRu#l*2sHjMw0TmD;a<55+UO z9={fMC4kuTe6Z@^8ES2tmPDBR7W$jb1|uCCZB?}vTUNa39)P{^w7RRz97tZ3^3a(f z&HMP`2(%e)D?R#Df7|JtCHf5c9Oc9By&r)iJlH&js=Lno$_VUfTz_jPg9o_2bPEYi+u*7qMx0tShtSw4y^hHUiTm1RlN6xkFV_n;JVbOc%E*r2$ZgAyg%Nfdp zX@UcjYb~E0H%;CxP&?O0W9#a}dYAq7gJCp%BeY^0&Mp!JUQmY;q#PWT-c`UdT8u1k zn{C2)ol(1cn~f`?pzoVsFTmW`PORO23mA?{f8eP;RtLSVQQl_CN!p-6b9FLDt=O|$ zIor|C`M(o@>5EK zt*?hoVTOUvx(#BEO0*l1#Dgd|;79sLg2n6`P{+eiYgJE?N)c&?^ho=4PGN!RTvZ#g zf9>+b$%WhDU(m%7^yUbdb%)Yh#TOnUp%=v3FzaX5lz_e|89hInF|#}673?cg^!{S7 zS@zyzU!?C!AQ(S8tWF#N8_SvB^diCzg(22Rm}Xu~1om zngtTuV1w-uJ8!qq$(VQ@lO?(q>PDRYVi593E}X^N?Bm`|YxwPou^g!YJUBZ@y}$WR zWShDZpK*C}B-CU0tcjfN({my9VEt6(le@!sFL9wXXn*mx)A~zmXZ99;`-Ob7A6{CJ z@;z0cro0gX@a^j?t4DH;idn9-(%KvB)<#IPcqED+`c*7 zxnT0Y2{-%z$EdRgCctfypL}GW&j-BY{E-MA=@Zj+646>9ezd$2=+8Z-{3ze{cTKn| zV>Zce&9M3?d%OGwVrZcKkXt7^-|d9!{{2|kM!8BiEQgFO42zQ02Hc{qc?au(*`%Kv z6!NyUiFwEj!FHOC)SW9zu~)`tzcAiEzFPWz@NhpBgOst$m8G*=diD*CyRC%Vc!aVJ z3^;||76Lp97HDs1T-Ku^-qUu+c1giDScinFZ->&jreZHKrZ<$*Zy%<$7uO*@d@IkF z)>$uh;!hv1GMNO3=JsYTuFmGhcK^L{FtJADVkcuG`|pLIAgj8Uqd6I?wvvgpxv486 ztD2jM>;HU5*3Q_%9FbMa%FNZ0jDwR4kyXar%EHo>jE^0WRoubW!CB4G*wma%msM6= zT+GDI+R7p0%@WQ~`7}=kgSRZOp zT^~~2NXd&jK*8|qGDtAi*a^RdtrvN%zOKI@iqVoDO6?4r%IiR~xO>pTU?&;sAh(_L zBb~GSW#j3iZ#%z_?|!+a+fOO~EBFoUqLhE(^U>dLQ^Xj({yLt{+G!L*-9~B+=!a69-J}qs53hx|G*4$ulemW`&GKDOs>7Ro%P&X{^Z} zyJ;c4LcFZrb9UEH8DEV_i$X_X zN``4f>omcD`KqV5A~FraiUrg-+ic7JjSqD$#1?3+nlFBAFvzKo`sjMGyT=u97by#1 zo*8=}guY{-V+rDXIiR$|?;rE#b92iwL<%zLaCeAPmmMeX(PfM6J+6t=AKc(XzEI(z zst)Slxua$_d>9b+-Q5{mxcBayS{x+iD9&q_Md^yNwqvVmLZ`OM4e980I58>9F6MKCD$>hsc4U^&QcOio&?0ciZ_BigS&^J| z%lw8^wcs;)PCD-n*0P@tb<0ZhQPX324=l6ma)+>)Igw87j_FY~^S+N^?e0Cs+YYVD zhaXf^oH(S;MW2He)-t2=#G*mrW6{mfq|Q4DKKp#Y`Dd{&a*mmUxG1}B|1>(5ORKwe zyKJBW8dIvVm4b>Zyn={**Dn}-VmNd-VNJ{eaPqP~l~WV36AjQBRHJ(T7Z8w=Uy>w( zddp1|Un`QB{&!q_R2{lm4Sv8VlvDcjjKP+HBN-&v5!CVNK;0#4xFd_mE{?;%%09-` zuzk*zG`v}h@iGhBG#JA4+{^kmK@W5_ANUEQaQshDP}S0F-Ow@6VtLsNqzY0)cXoxc zx=NhKh6zEx@fxe*kFcE?(=FBGxVg*~h1I&(%;wQq(O5I>KT?VkpmyTO$b#&a4Nw%>&+1VXj~*SP z>_ei$k3Uh)FydU(8=;S5JnaU2sH{>*zf!$L;DbmhOZFLV_aoH|xL}@d}ATwliX>A zB%iDj9HPD%nZ2_7@qqbApP$+Bb7_!Hwt=ol(hAJ=n@1Yy?u)1v;nJtt8$Plo?N|TSOfX6^VZr%1>fp=*swqMbRxYDob+oX zGHfx(lqv_dQJegQ#sdt33XXhZr*Z{orE^WcWquI^koh^Pc-9vE^_j11Pi0J52TPtT zWMGBdNac*DtncNZ*H5yHNh?y9+JHmO3Ub07&LzTIXfSs?#FbXV;xEQ2LOuLIH|6Dt z*{xqPl8!iA+mSScO}5=lcV~c+02Dv*4A41t4VKPjvm)2mo41m*k8j_r$@-*j%mLz7 zCDqT}^K5pm;^g?Q>7trtrK8Q78Qp~fbWQv98|)n-npKwj-}{d8V=$7CLzn^~!}I;@WA}I*GgCP5uuF!K3bf(}p z@O(dcV+@CFd4Dcj-cD8bwT6{zVPzW-dUvcLL0 z0~oF{crNY&EB0hpDUk`DPm29CPe$&1nQX`%yj3x{P+(=^(|b)(6~SaRtv)#GOdW)$ zd+LMJ=yg~pYfe_yYEd%7s5A=EmoC7J zU}qZ}`%@YqUWyKcVQ0qaGO^7fk;An@kzDt`j80Fp&bMf(N|uMtFuESikUyiXzRtab z32*7w_tK|_H+Ui6az7$RCPbOFiw^$XXf&7q%iWc146K+1oz&d#Eb+RTx|4>Nj4q! zvazniU5nRaLCa$TKrAqzIr)FbC*rXe^tkvu!8*_*!OA1t`|6FtsugkXt2l$yDq0r0 zlNsqNTDPF#j#5`b6o@H#3DgyVWfZ?fIt(i06lD$hVZWeNr{?IUU5v+Wki>KYK%JWH z!5xh@p44Cnb;LWw+QCG9hJk?B09Lh=?V?0+8%FdS@UMC7#+VV3(16dvj>S%+8 zh+sxuw3XgsN;I|}{xkG#K1U>e&4>na>74(j0_?q|T7BLBGxfTa0l*u3{(h(<8xLfB zZ4hSw_ZgkBc%IQdWTElLWY+(lGXGulEgAkmC3}mI<0MobG#*obFzET7lnLEsjK+Qx z*wYEb0-C6H4z~`1)IKoLDn&;)K6GuEuAjWky{T(i?O18-_3E!y4VGQd9lnp5FxH7) z0`{=uwcFQfjn}IF^nWQ+*t}s?0Bso@SMsyKsO(d|En?$Ko$LmXCp1Ez0nBGMz_KPe zexqK+=HF!TP1Lr;Y<6xgv!==n_0dpvxaWB=s~+3ImVOPyHS?Fy@p?Y^Bar7cg>7Ad z)(Jyrc)Cn#!M$oblq3BNV;ch<9Xb+0kbytv7(_-M^kzAkHwMch^XLD^iP)_>n1FOHD)394O#W7TD5&AYubmWYzzOtI)3CB(jQTaASd8Tf34 z#B8VaVkPiY>HT+;%2SYj`Q0eL#Kxyx$M_4Mkq3b->l@$$z2RA~R53bS^p^Cw4Ohqpha_DTm;Ql07~V?Z0?Z0 zZ}3&vc_CZTEqF&mCJp|hP}dvv&0o4<6IvNyK%PW0)2k)8?P|adFo+JUg|h?X#%&gU zIqCYU7ylzx6hO1OpQm4b@BEDyPas34pF@84Q*v*)=w-kyds7XNpyth1{{2G{GAd@f z1R7v~3}kXz+ExDjvo%N!8bbPjK# z7xKHEaM`b~AZ8zV*(H1o*x~{x7#fK1UK5uUZb=!+Gik59*gli}nGNtL& z^}rCRIqp(A=hY>d^hm194>mx+&a}M?Di)C1SXfeEr4b=@bS=YQSM*qV?2?*Mu z*ECd6J?OPRWP_?=lgXX($Li(1H!A;uW3GiA#!Tuq3#=>j9cs$qJ$LZZkg;Ifee|4W zhg!u95`>oP9pW&HUR%K~X!T#TTil9i*=86IYd_~fqQsib4C6c(L7k2CWiEK`!rSPA z`}Lqikd230<|u#i>2Cn`Tl6jV$E4kQwW5)ahxn+f5q<*#alkLSBeyq$snt83?1vhK z+UEAlew%4>`s)q0yR_9S!n>9WK9Jo9^{2O06cGx}N7yL;%m%fx^rrKNesDwnx7#LW z{|1V97|Nt694n5-Zam&2gn*8fgoajb09VO2*J;IrWl!NLphDei=J#2frk?CNc zv7z`uy=w3VmN{b|z@vOp)t$4ebN2=n>IdCLVo(A+ji7l{V*Xeno~hiwR(EB0Sx#Ha z5g#s3F)(IzpKJ_#pRS5;-RVrThwi#?p8V#WKdttn&Yu_z8Ak-kKbIb^^O#w!`D+!% zguc=Ug1pw%vNP!?nuaCnhHI7)fL^5PDmNg7&0jUG?wm*B*v1LLyWn>L9*aygEND;cdcr z2k<)YW5xUv%x&lKd;hnvJr1v^+hetS%33Y7a;VZUk?A+*oHx48sP$5M*g~3kf7RoV zc{QVT2QL+v$gH$aW&=FjUXi=rm)le|^0gm_xIl-x5@3x}(?_Pc@O4K))P1 z#hHEiydIf}9dy`BoJ{fkB7Sdbbw~8lF6e`-4e)ET))t&B(1PG|cl5R{J^t^;@yLDoW#ZRs@$t0>Kl}G+(1VgTHLAGg1*E7ghPQWa?V*w+2~EEmg% z&HwWG@dJCEoIWo^=G)l&hH3$xrpMk$65xFsiv<$S{z>_-1>9TxetdesEP8jKYI_&i@t{6FmYagvjc>-GwqPHNbv>j zK=>=)#D52FWHEblU2=HOEab>e+pgB{E(;CU;`8{V6!4Tz_gCDuGVAzl^;PiYhBKpl zjvCUM74Rzm(v^HPUp@`A*h^IApiQAMZ;6)vAx0ZqTuKg3#PJzLn5}qB+?`(84=VVe z%H}i=g%}W+_to4P0P9Fa&LSar^tK&|(=#zen%H3cUKs+ltfI6B3A)uhGi-YU=252coqU)9C()Jf0+(M7tmG4BbpmTU8lAI(?i_wczqA-_Ch{wu9^heMt7)J!S zAPQ1an%?YMZp7WuGi`GKxEU>y?WhODslO~*#$Nql#?}^*u*IC7;vm_MPaDU^{u~ zbZ`@&*Mm?7{lss)bL6Y8Un-oQr~>QXvyB=9KHjOtv}CB%dGAg#LRMri5*HYUjF0wN zh~Eh4LJUCSjX4EcG|Lzv_=*O1h-9s2ettLWcFAPk^9ScEnE{^nOUsR|T~K$Zc`e4% zeFIo|8x$lr6R@2__mklFlibgZHJ6XQw-nYti79LmVM@ zy+Mxaot*gUprXxE^=ymQa9;g=MgMc>y>^* zL<_r{(4VwBj&M$v>+&JAN$xnd*%6m{H)mK!ayM9YwLQkLr4slit*bgAcy4uiLV z5#%Q0W6xUcp^*t{fAxn0?2C^%M>c81wm@=Ox^C$ zS$y%8uG_Tu%}eSlf2`hL6W8_+&vF;J`lZp3)>KSfg;dt2OM&+f3xzWQX>?HqgcQ=Cc_% z#Svlc4rGA)Y`AT@LVkQ?m)CX4H)t4>AtAbq=6@B!c83^8Dr}&K(6s;^tp;B?+r-;G zQzLSO)AREPCp+o`{AKg<{^F8Zee*JGbNqu~@WH2jk|}EQTLOIVa!L_99G6rv9`2*= z<})tjXTN0$x_Jc^=fbrj^&@5=*|_7-qd6MNw|5EoJ{Vw^&028M(>Z7O{wF3=_Vp$J zKd_E7ws{wfg4dSK~%Li;)-w2i0%ytgN4#(;~UsJvu!ulU-DsBMrOWm zA`)|@tU+S6mVBH>3y4f6OBfgt;r|ISCpk9gNYXumE*CeF5aw zYN2m(I}oW*AXo2!_v`39@zBkf#LG?Sep+JJ6-XQ0{k3D(gerCeP#rz>*q$`3C0il-cYoY3Hcg7^yc6DIyK6w)E;_dOft^mzTRf`0 z`=c8|2H)GzYd;mpQ%dD<+RzQurYk`xO8=B!Q-B;BxRJqX9S|w=Zv}FjTyi)WU#Evm zkfNRh)FtYQ+oQLuC4+^cW|t$i>sAn?`z0^)gnSZZWM!W&JbHXO#R1#pYTeda8!NX( zz$&_{TR#II_)Wo#ugf@;pkSd#hy!tcgXzM0&l!aPc$^QPA!<{Ccry>Be$JmV-bYy#ZWJJW=YNV(bP#%W?I1 z55iux&;OtrD6??gOv(C z!l`2XTEh#MOc|Lfnlv$6EDgY;CKYquKEZ+PLHjwFVwZr<@-e58Qe9phyC$xk=Qf|^ zAtY;?(=1BWFhsq==8x&T+^npuGyjf_y&c*KaX6?djVhWWoFfyh5|JigRiSqMUjE5O zbki0xta(ZAk4J`VW5s_pQ^+rry_= zFH9g=H-wLFK$*A>^l!DQuA3p{2yhmAG7@=udg3r+g==Ch07aXvVy3a>bjS=+(_Zq4*M7ULADrGjo+>i0@ZIIIcu-Q0Vj_n? zdG@~uP%9tI%R!LI$^4Ixi&Fj&ox(P*a+3*>u)Sd3O!u=tNo&(3l0yZ<%e7Rh41>{? zs=xyW1k@XO5kdD$x)G?v$+Jnj1)?D^9YB$;uIIf2nPB(=aGLy23%+E&WDWLLKUv0~ zK$5S%+HxSAnKg$V=DEE{2IXSSl{=v9U>|0MO=!)G zRK754$9CPG5&AS{{C%GlL8q5q90{090psUooT%TTZ>pQ0n8r=eZeJ2Hi^**GnD#RDb~8V*ONzRk4rWL1&EX* z6^PNRIHyioEYD5yv*FKBB;)JOl}I-X&WU8u-!iREppvkxsOVX|ATSmLMzN1q@LZdTE{9@}O z(+(ae;MqHhL&-^!Br=!+i_ncXn|e|<^+uM=BG`BYOCKS#-NNfKhf0yBe0oQQ-aAsD z%*@P6G0#kp*I_TA|Bm-sz)PT!G#Y_A95YRf%ovB3C)#v%)#x}{Ur6@G4kgm6Rkder zk7>(9WIrq^kC1YZ*Epbv$FA7+PERJ=yJF%l#frB9mA<%D4LqLQGSq$}+?{Bc@&rmD z&m#<=rdTElYnI&8TK%F7`Yb-;%BsS_DjGaKS{Vm5(MR=DaKbe~J=sdo#&h8p#A{+N z#-~-TynG;$+b z3~e&|`{??hS|@M$P?mCsoI$6QP|&Bzq(iTC5DBVAg`3g^?yyBx7L%O+Em+94%1by z4Rr9ED1aM8aM)V(Bq%NQ*ExDm{Zfh~Oag<+5u)EXq8SF@q^AjBv>Z!MtFpGhpXLNnvU1-3$G zR&)mY=cWrQ1XqY1dUg(->uOd?!=N%xRDV0F$9IGc43qSbZvxfAvc!7zqmS^`)38_{ zW#&FXF51VJ)%#|6P)Zoyr%`a`qyF4I8IYIq(r2BgAyp9+Y`E&&xG3J1v1s9hVRe z!yW}AjCZ@7LHa8q&!i%1O{=Z)Rfr zb^}A)d_v1mvc4dio}j+NDZlcUJ^4S5R&Q>?p4kp^1VQ}LxA?_-mD_Uej72`Z=kZ)E zgEdIYkD^3yrZi2yYR&N~mg=Xnq|SQtcC|M@V`x^dzt~offKu1jqIG!{zNcI+nbRq7 zR3rZRmVe^3fd4|Ftzu#cKB|*fk=0?yJwAtT&2PTxsNNvia?Yx~C1iB?AR5v}x-Ld3 z_Bx_H;gm34S;x~_2qz7(zQu0C{zmxkZV}u$r?>ctdDDl-Dpl_r<=_Izusi)hHbLg+ zefbj>DO6I8^pF5tN`GWpFVB;6@gG@BD(Cg~Jw^YZ+nx=bV+*x(wBva`lEo^%VTlTNm*rv5Ky{+lnB$3iCtW|@0RMl$dpQ0dc+dYI?1r6} zo8$ityP4EkvtR#$?zdSt;}SHJvptargADCtTkeN26BG20TPCGIGP}hJ>h9L(7x-ST zvqUR7tjQgHZNb|0z&{+@jbw8g;bUk5oW02tV5hg>@AiQevygguY3ROY__cdeuL~Lc z(!;awO>sI>BPbB1 zPzb^mf!UNvq52aElvVh7!Ms?endY7(ga-W4YZ-}ZzU2G82JR%i!Af7l8)r;+vq`c= z(BkQ-)|)^D>xLuWm?J);omLoXS)?WJ0~lVs-;_)-BinFm-~xz@STv8L3shr`{Q+W3 z4?N$ypPpYLYsR*j%O25}*reRmVKpxn9i1R_@&m`~k1* zZfzKrfh9+3%m8jb7k7I0i`vMXI!#V>OB;T-pWHV1dQmY(DZNOU;sy5#qQME~Mm7AK zJhro3$?Nxc$()GQUrBVWzI5S}F`PzEmE#ky$~1kLH*tF5A!}F*g%y744YT&K0P0S7Y5kc~v8|lmazIg%^(ioWvBkp=& z@OYpm(po)E6lNkcffs)%@$B#KpXrvOUE9ImgC*2{-l7_0mq$vE#aJoMhP7ZN{KSjm z_E2B~m$N&m?ogdCnM%Z6KXui3Oy||alYhv8^=HM>i1Ed$@8xyU`Y?U??V4{IvCSoZ zCe`7Fx#K@~5J_K5Rv$upORN6Ca=4pS-ZADD2z2AyBU}9SDjt@G{5){Xi)bN?l#+>8 zgd@QAO#3xWJXLmx7-J&XJGYE0BP}2ux7yBS*AZRPe|s<$kRGVE>}bs2*011Zo7`#q z-Kt=0?}Yx?l8!AXXZ$PMQ({e*bE5V~0aQuo`p&1}d|}S&obt@vL2SPKRnOxtOULL` zP&rlZ!J-o4ek6oeRWjJTjcL&p)n?CV(H;Ca%ykdJ}nc+;{`R(PqY<- ze^zC`!!c19$`=e#X7gUtsiw+&h6|CbDmUZ3m)Y;H$3?4{?Wg}5ib<1p$1bqo9ya?w z*n8`!D%);v6bS=eVjxOLij;t~faDU8lv1QcX%LWZP-GDT(j@|mMoI~3FhCj>-Jo=L z=QkJ6TYH>u?6LPA`;0Tj8SnF#59_|K`-*wZ{LMK8aeiz~|jwv#))@ zwRYSK_jAJoj)}uGE31Gl+8av_m>+bQz8BAYnQHhk6HTICDi;EWr$js1@mLdOB~(NB z%Ma|Q9K8)9#v{0}6YP*8eRAoLznpO)wCtaRJja54_p8eTbfk{s(jD3>EcHKFYMWd*`{@`Y6PYK7zO97(EXz?qn{v)-v z^P*?(PCmJ?S}o8^`tx+Rx7~|}^XjcD@rAD338JC|-#$&D z4XN8NFVg3nD=K2iDlQKcBk=G`+LQ{vA?)#_d!Wy9&TZ(kCh<%2=dU|yC0q)19zWH1 zW=FyKh2VI(Ncd*+9@A9;`&j1Z|XF@Wo#iLz&qOA zPa;oyfl^w`m&js|=<6jPW4*_$d2#m*${iT5Vvdywj!g zBczO^RatzPHB2{Uf#bonoG(>d%s_eXJ^u4J>aeF=I}5MgSooZk5t9&dJDTd8?yVlK zKAcu@aB^^Qs2iax&&w@S)@ArPG4XBq6HQ4kxkN=fKC#l{uWyZbPpzqx;V`>Il`bn8 zzKaddbYPqnFq;oH)_6BCaA`~PbPL8UQ0)Aa=!c!$c{yY11_Pod3^C#8rIa`J;nq%i z`*nF`L_78NFZXe@W|kEvbmD#~F1b4$?~V_8QSLAHze}ib;Pb$##k zTRske+x{EkSwF1c-RGmV;z9LJ5MrRD1e#uz-*tnW8oa+uzBTq85HZo??SIJY-hHo=kOeMH4Ycz*mzKKjMbP^gqN zl1hM#ERQ}NPnj%M!1l|IE@>uxI>lYvUBu1tDP1jTykFcK=w$ROtvBJ}o@s19-^iPx zkMjb4U%7kcg0?%dt|V3aP58+ptJ<7V;i#Ts{UeX-dmasp>@haqFdVkEPdTIh8bY_@ zYpCaG(&ED7eeq*<(nD|biy4{fF0~GfPY^2dd#JOh4ASe)&yE%o^l?-`(+|wGd$vb^ zp3mU2Wj=~P3EOfPnv>+E;B%)NM6;hkAA1B0k6E5dj@q8#uq|cNIa!H@VbjdO9A=v) z=-qzXy|k|a8x1r;O(H%P59DXcFL!#kH@SoaDBN%`DA^Z9Hkq$d-X?T=^t{+n3LkxJ zNM1+3xhhQ}na7Qc+MedhxWio-N8%yCWa6GYeOPzF&Dytj%;P~+dz0nNjY@Gz_H>5` zW;S>xvdcKM{Xo+4MuB>e?A-jj8RDvK(cM$qtQH4ui^f^0LtX+Ju?xBDcptkBR$=2CGR5MZC6M1 z-&wq}@&bocNRmCgon=lX75nq=SAUIsoM6Da&dwirI$5nAXKig*T+;ICtBbPZgNow8 zD!9t*KX9fvJa-Sn_W5u#H9cz+N+Uz`oQ1X%(X&;H#E;V1_{BjS`Zxt_wPqQboKb~ZdCfrO^(9M*{s28jgxx#+E(K&|1J}~Evu?+vKMx`~KU5`s zKBhB0?Q_rL(}D-_77by!QvH=Bw{IFqWSsc8;BId*LwkSB4aB(8@pjgktm>j1+pB8& zTdw-%vSc5JgO3_eh8lTAhjWh! zl1AY_a1wc;3M?UEEnZ{1#OF(2!`UUhSbmxTZ)mmli{{jqzMnJiZy{P9aTxZVRrnn8 zad-S)0FR#<4XTPLQ`XH!ZKgnl+bAb)i>tAmY|>4(zndoiZ58?EqNZ=J0y1p6?_uk4 z`%qT1AjNmLAVbk%<+h1SwfGOLqVkulxVQOZT_RrjBtOMYfVQ>JZ#qRhLA8=zS@7z- z5EbbkR9R{~6RpNM->%k)!a=3M^^CyM=L`3U=tGs44BQ|ZJ77Wh-g7>7hxNzU7doCZ$qw+WsY2hqvf zZ{1nMM^B|{UUtV0D#N$^gy`a{J%o^ufsN+#E==q~n51#1rBn04$jftfx9DD7O1IM# zui)qA79lbG?I^#0%JMQDsZ|577*S%7!(Q42O5(rAqJ!y7?!4y#M&Ub*|7d z*{}V{UF#mZJlqu_cZyNY#aT6cfv72lZ2lbmf>*BThZ)1)O5W>x@~7CYdzA8W2ZY>d zgQs%!Ia1NP=UyqWwCJf08M#X;zdFYoM(lRPzU)aU6e^Jr*;I}``k=m`DT7?Ux>xw5 zE2i{9+F_i<9j_R>bN3U7Icy};+?IclY;JKzPjZBE7k+Cfy|HfN)VzDakR$S$ip-ab z4+ANDDBZ%|@$7yuHC8$k2D94e&vq=ZjgjBKkhXNi@p(IY=6964-OmUH53f_Kz28&N z8dr;Yw6xuAguODWCO9iIBD%~)-RKf|cQq}HV-U9ykF`_{7py;St>5tZ;gb|_hO16m z@{a_k-2TYDyU`cYiZP1Aso!F%1EvoYPp2%@K^4ktox)xX&(eU=-jE$pap-T`-3^p> z|M^`1t}O4LyNl^J!wyj1-|u{Yk4tE*MehbS?*^prDZgp(=TCq-@Xw~Wg|S=yNJ}g- z&d^=Z#+_yyhSOe)&wdV-rl0HG|4H@;$P8nY^IJA}cW59^_uI3hb&n^(-QuI&9qhFq zGNac?AN}H56i3Py=nGM&qWOcsG#xNEmTxy?o^t&awklVS!6>tBdWU*W3C7PNO?VS6 zloB$dS4i6!?~uyC0bJUXix>^v@(f3j+>JMZjpZv$`yIn9a8;G`P9~Sgc&1?dpNmWg zgu$v*n5SvLbhV6(=y1|Kd5-DpRXaZ6t6ewStbD>zs3J%9KNsB|H5$J1Y6#xD9%16o zuWQyY9!P#iz0T$aqNT|L7v~bGo*DVc9nF5tCT*(>y7Sv=11t6BFgKi@6Um ze6&@bYuhX2^4Mx~MhQQ=N}n zFJ7biTo>8Ngm!ABJLrg}i{>sLU1(FDn8Y^I!FG4t(k5brR?KF7kDKNDysk4ySF<0k zQx`gDhxOcKm*g+YaI@^iaJAj%S}3PfYzZ)ZbyY$!yLjzw%tD2&G*+WE?1Z z36LEcP;g_M35pz`s~EJ)Y*%%?U9ZeI<`X%~kE>nnUP*4h zo0OYtF;?TZ))cy3&~q4J0wrOOs0!i5esQug>&eD0!P4oa^5v!Bat97(PjQUmgSh56 z9Z>_xkI|RrGkF%6rz9t*&L};8Jo(FYt}An-O|>eRs#vhHtW0Bdj@FSPmL{?FaC>wV z+Rxxl9{{-bkw|PDr|ZrNl+e6ZGFb?4X9gGdQMc_PDE)O~kmx2tqNbsF_V%S1*`^mK z$E{Sl>m8g>v>zdT)IDqsRm58c6km19^If|)y*yaFoN5~nWngRHT?fcr{^e|rs(tvho?DM@JGdzMFX0wV#-C&!=6gX?Wo%?^N8mW7)QxqXNT zT#_B%T(cR7hvw}zLWj9x*WEQ}>#3jsvp4HZlRw|@9tueYxlg}5hfbGeALMy#XB7L7 zAqY&osiE9{vNcQ8+?(0af1|zkLn7T>T_tbcR?_ufD@Q+vFqzhLH z8nwnXk0*MgAhmo5b4BjtJ~Q)Gi`MRlxePX2ooPa5lQZju7GT>PFB7YMgYIG;5KujH zks|m3Y}m)2C`4F6neA-8Q5z-y_G(SjI8;}m%_{A^9Z12?X>wi~vDlT_2_121(?y`d zFXRg978-|?XKP7x$iAP5?#*H9r8R3Tx(^LZE##%sHK+5N*fgOS5fpeEIvqv7vyIL$ zW~9xnL9l-Ri=wR8xx$hIe!mWmc$^!KN{fH2rZ!D^aZ}+H0kQQZ=Jt&|NFOldcLS1x zvsqK9n2I%4X%U zCckorMOL@g7Wt{Ht&2B))c$w;nY=|d2GdLq6*dY^p zzW0%W>zE(I!700mXw}xx=H24omc|LfZWH0aZ0>GGD1}<`o58(b8icwiKOXbUYTQbO zKzJx#2Xu!X5)47%N;lg5{VK)~9IrARCAKkl_7W4A&j9-Hv`1=Lne#xQNhm+R8*Kkk zv2Ph}nnmZ$a}e9+v)f;i?uO>xHPr+jNXx2SLw<>%rz-3$yO9MXtQr$lm6MU06N8?X zeyOk|t@MjI^OHOh?izH#VowpQs1j~&*4JYUW;nSNDY&4e-d zRyJx)-Q#3AjEKN-qt-LwyBexw0uD<0~5z zxeW64tx|=X%xaBBtqi~b|F-LNJH>5lFAejXdy_g8uQx*3$UB>Mr9ija)kBQfeK66D z5(M6=lq~7w@u#-(KGWj7HPlP9f-=nex~%sh!k)r66@D5j#7+v>WoR*Z?BDTgY-uwG z(7|_)#{B%o2x|&5{ir#!46D3jb%-Ay+f}GRFGRSIT-73~gfsCo6^C1chn56TODu2y zn+|`HoCepdhGArrx_mhiE48bhdj5x4k;uw6rci_o;ySB_EeL?h@SQkX{>bJEZ&~(A+vSnUrH4=0T;_9%Erv><$dqol zN+hdhq`iD;B;?)#5`G2R30w8jo{5((zvV#0Zw3dL&!D_c&G*^rUq0M(_J&?MT0Cec zRG57m7@Wd@wVS3@ln`Jx9MnxFfqs>bA7}uC23s8H$9Z-~Ga1@2;fUT4FI|%_6la?R z05e8tjdxxO`T9yCT9UW}6TzZJLW#-tD3;KM3P8a8hO0e}3q6YIZM-#$OzEo+W$Y(2 zc-N_JyzER1)Owe~HG4BVlbM;h%wakAsIF!n`HzwECv?PYv8ACa>1xIQ`YT3Jgx}G2 z`4)69m+sqFzQaw-c#6yvAF+9XN_a?!#|gw4kgRv5;6Gn@WM{WIQt1NRBWgjV6K_x&SGR;N-hF-Yz`1?OGtrkuQIUp(%v(j_ z0mo;CDR2^Bqk4Q$xHz22=uI>l$Ftek*v*&`hnkB_gNlq0r#|5S`Lj$X1}Y<6l&(LJ zVup=6C92KOvk{7)GZUVO?r71ptn5DYGi@+d%6o^x^(?bCor#9uh`}uI>`=texCj)XOq zRSScQ6c{dmw2dmnmOFcI zboG9sbv+Zg>%y11DPU|?zrO*#qTy5~alZj~Jqhek28$QUexdJ`Ga*06`-5GW4-_EA zR`$w$A6fx`C+P4z)1C}6CVumq6PA^QK&$aqkEEerP^SH%!ZPsp9=Bwe1X~qo2L&`p zQ~(8ker_&X5!yN_j-I97RJ+_hCnLic()|>~nv2jseTYU2jNTK!1buPo={+-1L5HO~ zEamU?@7YIEg;Bh2GzAcdf`A$9mi1nZ-_?R8V2L<(t1pdB`3hpWO*%YJ;oVEJ7cw~CKW`AJEwE`7HPz|qQf2to(_wO$5~I-SiZkKcXh6O1Z0chFcL^% zPL4-4X!8wK;AzVlD5}ju13er^yVZ0TKh97Zw5(jp&HkO^+QQRYVq?I7w-jGs*g{sS zDlaeJ!(#%wRnUpi`-z&|u(J4u3ZmfeAmC=w2kF*&2?K0R?#W8tvH|zb`J~eqDH!KP zR=(kZmepLwiCussnE>SYdFrj)DhBGKBGax zhXl98@pH}E`bnTX3Ds$4X_q+k`k(c%yzKte4HTq52|0jgD)_zm3RTh-y1To*yTSr+ z8M$C*N2}bt@Kv9je;RY^6gji{EAnyLisiA02L2a&0uSNuBfl({Jsh|Zq^C1Bv|OY(y$;toZoVK`;2{o9Y? zA#sDK8gkO4*4CyzSdQ2az8qR~^-S2Km zKeQc&C*7LgTvy~L!8Sy-2ez#D^R$W2@!1W4yV;$IkY=CHX=5!OqGcHcyqxs4)=$AG zsFCJa<9j)OI^X9C2KtR1G~rh4&1Ib@xkH_d(5Z_3OQe zgroN;jNRrP766et82 zhf5Zq#{&6ARzxzI$aQdP9=ei~t=RG8Yc!B)1bCYInImffFj#5zN1Q#GA`sX2-41VI zUwgysmm!*SA&8mUc8d(h#iCwE#p|LA7|hmvPWpWnM-f^;@>{@5UF_!#3Egh1GY;56m3n7 zPnHZ^^RWgM9^3{Y<1HWww6cP8G@)pgiQ=ecVIQp)q?IgECoTvU0;rd z9STltj^$yQwdMaB!4g_K7cO3I0I8TyNX%67&ypl0Oev8V9P+3mhcg2RFdZ@HTjBE9ag9m1)c&#YA zKTo&1u$dnSyR2(dcQob(`uNO(syXcMj$!_^YC4mjn8kW7c2 zn5~%3>pG5NLFl19>=_Ic&Tdy9AHsdZwn=BKKwE#FC1Z9WKTrm6x7Jtl7#I9LIByrw zT)nbO;lyFL_IhXg3)173j(%kw#QurTCWhi5wgXXz`2B{={9EA*WqB}<$P;BQ=?3B> zzl~uhzMuC4jDgZcE~1+P;akhLO@3F1XMHtQG;F}?D|;eyS(S{r&hwGbW;p&NirnI? zV+dBk2BpC|q>jqtK&^VO8w!t3-qWax&^<473esy`eh<2 z;RT@;meEE(z&1yCJ&9Q-=P}*Lv2>d^(``4+2$n3@csW$^rL0U~p2e5fypNit=~S}A zQ-)##xb4vGBH;psBya(VjnE1h5X)$N7; z@#-zl!R7!IuAZMIQj_|6twlyBAc&cK!y#!|sUsiicUFS^f`yL5NRyiUU-I+wUyc77 z-5=SmvgfWv`9q!jlBiE*`46cW9>s%%sO#d{2N%`+!eDXR`3yQf`}L_7Yf%uE><%&@ z>O)`t_Gg zd|YYfWu^DGmzPIII1L)ZVd=Z#RT2J&`*8pGs(0o6#-T$@J7^lTJcUlQPn8!zwD!6P ztbKcp2cOT%$XLnab=flKpN9;VhB7gAsUErgw^Pt@V=@~YVda+E$ON~o{w|(jO{LtD zF@LS%7*Yhm4{>B5qdnHkfX!nB#UAR@9r?v6yakRJKa)pXQc$Lv`kfusdw&kNQ(DajER`Od8 zka(%wu&+yQr%RFY%0FmBrc?3N3MbcTp#!YK{iyt-UAI*;IF2HDS#w)KiZuJ zePKP6r;E;IOsCHV%s*#_6C*vuYbN`;gPGyymxH{;hAXJkqlSju#AnYVc#^mgc1GBW z@tg_0-QA_>yH%@!mJ~M497}W;D-KQPqf$D>a~a1&v^=(;&p@5nf`boRL!kFdn^C$fa4ZvV z?+t?ZH5y(6$Bupr=<{szvERwbTtCFLD^lO#?(*&6+km@sgc3g58K*bS*r*qp?WY|! zx})WBxM{rvG+3#^%)&D7qP+y{nBHk*-ueuJOm)=En7;qTuYgsZ9OC~lGgBlzH*jkG z4qbwlA*^cU3YNPO95#5|>}pXu)(!L^d$REjp!)4m@5P2z?1r@Au2t(jP#6WZP}Wam zwj)iRe0hW1vZB2*sx#|DahiGmu}|*N_tr>$XH5l`{9`CZm&wr)MO<48f=#Z=xeHu7rbiO>V1%+`F>dBX=&8Ado zrhpa)U5zpX$+SUzNs@S{HG8Y%@#DwRW^k*wQ)`R}r8i0LJf*Jtv{YtMeQ0l4^c#Vw z`ulZx>x>jRSV3;1LfEe+yx9jyH)#3MIJ9ogopmbdk^D~)3RY9%=*!=St^iTfjI4%&fU}QA- zS>Kpn&TE{vo@72vM5(2z$#M)lkY|4H+O=y-?CJV$JXo^sj;ASfyARd8!5%KH-Qs z9DB#E< z!?*Pj*RX01(C(*CpEPbAosD&wD**o%wrF$tauvD$d2V9SJ0N>x`W2@m)O1QgRfg35 zRWGW8XL}p7$Do--ZDl@!=~cn>QY~kq{2nRnfH>aq5jps?^5-YNb};M+0h#IBC&LpcWEuGA!PQ4BnCP)TIErn1b8dzYIE?PmT2yVxAf6{7Y$gq&a^=VQ zVM)*kilMMZcHSq}8n~9cppU(Pa_lbj${b5uIEimiWJ+MXRPBsS`H9#wff@T5WE!Xn z9wdF~Qu`_Hkh!zh9K-cqaOvK*s0Eayh~<5$@zzBMXXeEBq}UF`Sdk~NI8AIoe+CV) ziQK)LlsLo?ULrI%2Agmq_-=JpD_tUV#ro0azQt9lyeW7ehcO8pSAB}p&vi)bT|2X!lI*av2{jW~s zf3kwNGAH(C6I*$9TH4en+<=>cUqOcl_4!E!Ii4sZYzKG(l9G~uLW7+{m2eDDaL&(= zAGl4FH8r!YC{BaXzW`w-FiXuqCWw)NB+CRM2GqfKk5uP-g_v>;^5$TLj4# zM=Ay3S>2Oook1j)dto5cO#^pt7wS^0dxA&S4y;EoKS}>0i_#sSe5QsE0(PG$UX#EF zvFoU21KKfKRZ23T_wQnSHc zDE(y(9uUo~4KaE?g&Brdp?!#`+@X7di2P0`7=uuP&Zs2=dYE8sJS>N)LAZ?v@_`-r z#dfx1`zrY4Xh(rvm&dTzVjuZS$YYrhw)_OJ?;yEoE~8c#uowj=K@x>n8Wdev-5go5 zo{Ugye0+V}f(VBhdo0`=LpE*Ts6{8%mCNFxCZ-V!dtrBlp1shwdMsBtX&PCy`kD z(@+(6ujuW4G64ua@KZ#Fn-3I9UvvRwl-;M0Z^UsND&aFRSd3O-ce~Z@L;EcV>&X&P z-e)|6UNb|29W?0>4a&EwMH+>hPo;zBIk-|0C>{HL1Dz>gZ09qlU*nybo12rQl(*)8 z@#{j&;QRlQWw<@;knmsvHu#}HB}g!93F}A-g6Xmrq8;6 zARB}8V5QlOHumPHQQS%!PW2p=qt^@@Ri5~T_2bRn;lC5nnJyP|MPCqyPz)KUXgPy5 z4?qflLLhy!B2*!kTRKRt@@HFNTU-@L3{XMs=fObI(pWiM_VB0=ra9iJ6Ct#mp^ws{ z-F4jRHv%Sd0sG_!^tl7Dky1% z6t>gFEvg+_yAjljrzh*V7enr0nYZtAZd+(bD1Q|M5A|hR_3)p~pueOZ3JzN*;Y$R) zABqM4=>652tJ*9OL(S+=5Gl%#cl9)L5YS~i^9pR>L|2yT z3m3q!uTC3kh7Et(>J092{T8U2HsW!7Xwd>%rDIkpYQ$mGEmVAV3bYFP5YS3&-LM`r z%E4wotvquv_b(00V>oX>ju022#apo+tH+PKp+ry0`zt}C$4A@1JnUJTuCL|9-LqeK z8Z$VFDFO{(bg!qlYU^4Kijbe%rbq+A+@NU}RK0olBrF2LapRSdkAw6eoJPX`3U=w} zT^=~Y6||~co#1LzT&3@^9!q{NJoj;iY>snI=+2$NkOyTi_ws~(^Lk}vXWLd2aa)eu z?`^`ZzXtrN4`GKj_NAi0*$Y}h+17v=I1G2Tz)uDL#CnV^Cm+Q8fiUDj<+9tdnUsoo zMzC_zsl@aB023*I(+B=N;PR&9k?E3Y#D?Leh8du|2~Smve8E(;Je~Y0Jfi!-WRq~b znysOl(^Z>f%;ej7J@63L5>beH(WX^Q8f@NQX<-U(9)9Rc;10xJYcUf{gG`BE^QJg@ z_)mk9nE!4#`@|rY`3P0-wphwgwA~xOU?LMj=Bz~f)VdPuHruwHfO`tm5Vk=W2kFZE zo2fY_!*F-<4F{&FCVTPt&HnuGUefHvVD%Wg7KlAGsB~q>jT4b+uI1?nGA<$OsS7OgkmQ>=j%j{zHlqy4nPPNU>*&P9eHD5 z;fY24RPGmYxWMQWdGucWDTEv2?g7Kf9t`<#+pIBUvx9g^=(cs?e2*=7Z__MlgnDNy zHJEmI3qnst;Te_IgHpKWh=1c`D^g7|cCZ}w+4nd8{)GHWrgk81uxA9?XB2fjG$@6m z!}_8@g6ia>oWcH_(&+;g{{C_egcjvzbL9txZT zXAaHaso*mpiDt>2SAL6mAi>SEdWBond4jS-ZlCl#)~>e!yv*3gj%p8&?J})WyCa^- zvH9M-s0ryyhh}MRh@7`BVDgS+MVwfJ^K^Be^S#sKoj++EGb1j&0$!5DTL^^vpBSUC z7e|nR9+3@#v-x;{#smTvSBH&G?T+51O)r0R3wz$jh=`@%t;a`-mh%1i#}a~STCcoa zs9NA64>O(E(I(wJJ&oUwjt=(?4!_aii(Z9oaztQr-r*!gJoaYObtzeGkkxXoD?gyQ zEO=)#_g!b|L+FOFHYpYsQ02NC%VRb%Fq~zpJj#mgumg5bnGX`6Al4xM8(yPbS-wZO z$!gfN#S5|13#&Z*#OHBz06ShX9}1z%ll80EL97tGFYNRq1qBQ@`GZ>RW^g9_QDo>5 z43WqIre#a_wlKl=vd3|`YWMMF4*b_itX6 z(l(=d(OeeHvr~*X2p;iDbUc_rDi@ucx0i%0hVIRV*Kpe)Mpv^4B;H~>b&14WOj!on41GZ*^) zZx$*P{P_RULPcG?IrpC#s2KKYXRwt8mq3TC!&Zs`g`7&jmT^%f0gO&pu%~2(6sNIn zmj9A{I>YXB5&FHd6wY(Oey$aWx5P|L?a4^AN1nr#=(9G%ar7KX4o z7}~8u!TVIlt5j=VY`=ynaa2JP5SnD%0|(wP+L*G#T|w!|=;4w>8~-%LKkZNqc)FUZ zcMZ7Tdr|^hf)1vU<;pm|cYs~JM%b~H{C)#9#P9WnPN}xRCvw3J3D`#EiZDFU5Ex$- z4A7PTiplvuvGo2MM(&?uF`r6y9L3uphX0$!@c+v*``@(N|2He>KL;k(1|K8RA znIG-nl=E-O`8VbKn<4)XT=4(DXB|9C_lec}MD*TzVM7K@$Ud}Z)#qgu#=xL>kQ}jlS zUJ+v>CxMB$^Q88q!PU63WLrYlk0B7Bh*k=75d9f{g!T*gbrNNfl%Lr=ARtWKFQ`w3 zS3OpbvGEn-lnF9L<)^&XtW2bV_f7^2|A9wlPpFvjgNtmd^2YH4>|pJe?b*uQ;7i_OIdsPT!`;Ln=$1ax5P!PSq!_|~&0s>6gBgSB(v=`Y|)PDEc; zauk&|8Zuxsp(I7@Q@;FbP7iVs^oa&c&m>KZp~?nKhnV<^;+(qs91Be6^2v1GWWysp z6R}EUWIQM9C)mCD_6lY#k~f+Era#K9nm6!d%M!nNVLmgs5X-*yNzxn5lgH}t7|i6h zDBCF>+8s0_(X=EX_M}ijp0owxoUpb*HtYuw+9L>ri>cHs2Ga*YDtL#$9DLD{2KR_l z(d{RzM>k;&yG&=I@76%>j4C1>k5B8g+!1+C1k7M8)eXm@Z|wYy?=AZ8cKY2aU+ z;E#xSz4^Xi4-GGJ{`I08;<1NVNLIQ?qWo3DxNadGqz{6;hvp9;Gr{Q35UjciUc2rx zHdgDM*{zr0>zhMd9e-M(;1A9_nNJ_pnHejyfP-R5 zAps(fb7*78p_z1C3|CQT_&iC)fZuT*%-+kTadwU4;9QcaA{;SMAoq5REqOMiWw6tcMUWxgZzokLmlT|IFC zbBD4yHqb{sbQ&hS%AgQ_IV`Z;GR)!!z%K7XM8nby8}fpjl>DWFu1?LIhX}h4%HXMD zCNi?~CkMqwhX6=bwqVmR{yD7or&IXlK&g$@ao z!Fnw>x@FWH{c0{Vy-{Gzqd$kvBHtZTVP71{teE#HO&|3>?>r?$@thd1t?96&C@-5qG;f0|BMQ z@W{xno)BAZC{Ll-Di_&jboxj8Sj`*&yLpstA6@UofJeQ|a(`oXJcsV*n2`89k1a%W zmWa2X-+DCo_IDdUNhW%U-`GRw{6WR-zpt$<8*!LAGCf&#uHqp(2VoyZMS;K8g~flr z-WxV$7gEk-orP5`^0u8*z!d_1$Wn*p5iY}KtEf%BLI{H_5%UvAkcxgi2EwV|XG8(a z;`Gq*b8gkumCio}M`I^<9I2*@R3VZmI&w7bI5i98x&EWLpE{}zi#D3*semG(X>u`R zV-dC#1JtX$9Vxx%$^2DuJJh>MsuY8^L<; zN9(;vvNX}_DON`$+z=|ASGCth^wRXR0N4VFsM4lDj_t;Pa;&@kQ0S51Vj;No(s>KT z##EdbRWc9r+4TsazPeZi9SFq-S|sNcy}oN(JpH2>1xPX>duOy zaNi@61^n7mNXa8}o{BL-mAIU8x~9@kh6oG+JTrl{7!r9s1wHr3PcmW1bz6T1+uI01 z)&&k@NNIAmbUb$?gSHOcB#_|d7C2#Qw$rKg7)2G*mp$1r+j^Vs5TaeOnU&96rVi)( z`T#uDAk5g1NUr;FX19{TpPZeM0bo292r&cX3sN22bC^RplF4C3PSXb{FGVsk^}jZ~%jtc<50!_aCdR%eGa+0vW+W}5=1HV@ z`-=ekT}iPil%WtYeX>Er@p=mxy!m(8EEFdz+4OY4b4}DDpr#nTKHa7pNv$gv!^*p5;ic>vyyLihk5=|;OFTg1@C%ar9Ukh z+2vB+@PNn!&r_-BFN*H)wn;n2_n{zSDz-f(^h)Q`hkGHZM{?_D89~~q^Qx{ST3jI` zy9qA9hthr~_^C$$`8_b^!%|*b2*K*9v1k|@+v1xW4+NyKnz#u`*miBJ$nMiAR5D;N z^~}8}YsT2OC`p->fGG{-8i{YtS1`UT<0yp~PWlBaiL;2n@IAxYI8GV4amJtTInzBm zFusiAC~U$5-k29kBg{;aD~g*)ukO}}tp~~GVa++?jfhvsC{|4p`x`c*$ef0|LXr6? zXVoY-Q_vyeCsH!pLDJ7;$l$2q3g!mWIBM-F8zQhOyTG6)RJnWR{L$45)*ff~h2JHI zzd}06hfpB`v#C1`73d`>2r9_mx>-u(b6PMizg$g5E0I_o-AbEeNT+h^+lIysxrj+F zl2FjenoQVI)4xQHwh1h_Fm|xOA}_%re~G&nurzA+ZOiB@N5Z4iD(#pX=UiGPRo}Wj zdSE}K)(B;5T0F|Pj&mzTCH`CbCfKmJopJE9U< z?f3t`)K%j7Z`M`f#@15$TkBHJyFCBvb(QXM^8SY+ii;o36p7Ua>ddMqWxsCCYi8!; zn>vh71(s*}YZ^;jk`NWezg%kaRFRli=`y|&K4O*RE@PH4x8%f?2wz$q+8a_dhDyB9 zbm>~4B!`ZoYQQ$V{wPCSOU1{fc=&H=Hh$@N*{gPA!!|)Uy{&C}R(N>2&Eqr8=+#NY?{XS_I`yPo|xI8%7= z!&xWD#!yyKfgCq;Hw@KA2#M*JJcuDOG5QaU?fu$eXx8*= zszW!Q!}rfT`$Kxz^sZ$l`H;nK+jrg^2PfxCzxkbKSKVuW@M?TtPqn@ljllWlZQyNU zV)7%;m}bA{?|-XZH*^8OW#?X^NY}va=F>R8$Mgz9TIS~G`weG_iY87MoWdz_>@(kb zDYKw<3cG+W7Q+I6X0)U*jh_~XVa>+=t~r@EN@>Um;Gb-U`&>)ZI5>q5*b7Wf!En#M z!EDyiQT;2n*KkAyZ8^x}eb1ac_){|g2PZb)VOeKaBRS%6KNEH-X*IuoHHMStNU~%V z>1r9lj*{Me=K22pBT-Qtg8PuHpTKD#V`w<_hTuHlO#u=46<~aC-{My`&6vPZT)60G zhDj8A{rWXvc%7h`RcA*B;LHgL2_xBctDHCI0IczHune2G7Z@1W($WIqbV0zO0&F7J z-C+|V&;VE{=r>xH^s!noTQv;pt9HDD@%R0D6H zHxLrd{truVtZmzBy!enkhj(5pG{|1!VF9=g6KQ$8)65{>v z64Fwfsr)UlLkB}HRaQksMR~bqOh21$Ra`>CBUIP^JzKAsMIsy=FR{zlYqbe2sP6Cm zQL?k+DOFmg!(0U#da<@QpV#(*?-+uBY$kzneLX#q(a{+Wt9GcEmg_ib&luj1q+Y74 zH*m=z+MRz-&X)@{5g=!8t;K#HNazI~9-cPqhmd)BASFf55SP~>O;exxpFe*-H)ns} zWlJutN2kK^6=b-;nGzUqSywXQ7~&D$wPwF--9Pc+B>-HpX{8W|horR8!9kWzbLh6I z1>iYI`neX~Qooso6OCZ}R$uw8-XQD+uZPERN=gdg1woP6f_u2Oj)C(5j1;JEOe`n! zXf|iZV^0Ch(VVX1Vlo&g9=lDtuCA^WA9*F02qf7)s1!&@O0wxxl$4g1W@X7%b^2ms z`VdHj%q1l)Ub@~7xX*amQ+27xVp=Zl%5M~MoXqxCGs%q`(IFvUKowiA+Rsf-_hHBd zRJJxisGt}}P%}0d2&K#+3y6j7Z!={z*WoBbwc(Q{_@=*tIK~j}2F+${n*owV#r)YZ ze?#En8;2Hzp~~rA^KgEJ`1_@xcvV(v-+R6Yi&drRBH`fRus^40TFLuzAAWv*OHuOk&f}&C zL%g8i7hmztTntXrB0DY0()nn{XN&%h`8+@p9eCbFvvPCWLNb#NMdCBBlp;tMvs5p@ zVu`xj+gBkZe3@CX)s@90=ELz3|t5R%CKur)}E_~brY(;P0-<9nq`8@xn=dRZObjz6)-S>(1~_d?eSNS^ zX@GJU+^K}5a!W`Dfb>Z43@G0hhI=Be6u7=5MqFJDIo2vnhWYMc>@lqyfB!AX}tR%u_tlt9I*xWLt zW0O?f-u`}gCcKa2+BM*+4eWYiVyC2&(dgH-q@3hT678>#2x~WizdZbbzT07PwW*iP z+oE4l5vtZjn_oE>dWmWUCbvWxNCmyP>f1@zOwnVAz6-`$g9m#N40r? zPMU;UDhu_Q^OEohbZa;|-pgrrr@d^nx4*AaD4C^H(zO4nurNFl?m(}}D)wkv%((>w zVg*~}_tZ>d0@qq#*(&Cehiv!mK{?BKCU;FFZ0K@`273()0vyVdToJadV0FRGBcR-U0uef zPf5C|sj2+hR}I&di9ZBp@$fpga2c*ImPhKSs_IkGNP1wTT&SiA%rE%QXx-dNNi-)c zlr*_*Ne2=dfQ;bfng+-VK3=!GyK>@}FC1K4ovA-**h5Z7m;}1dD2p=}j4)0cXpGO+ zx|52jnx#08oE0q}nixHLC?k`!xed9R)89S*qTBeM2fgj}=C&GOo~t>ob&Bsb7PDJJ z397cYEWrq^fsiZ%l{6mzmIY1f*NXm?kd+15@6eGZ`uZm!!2AK29a6{@_-iX3dPa|5ogaP znx;gk2zky=+oD*>#QAvswB1$~&yvG;Zl7s$U_mj-`hT z{15fmR4&MngpROpu79#~a40S;#B7zaUspEgee*+{U*miqEIS~3Zoq{QyVX1K$nBmpu&*3In&*?5Ug~eE8r*V{r=lTp18Z^iK^-?DYC*? z%h7Y}2mXz89@JT~2FVXX9qLqqdNRU()zpMXMt&_Sg7oU(Y3_UwFVk35e0VtV$Tb>= zW)cdDJ9Xy%x_dO$Bapw^#H{~+So`asIGZN`5Dpexg1bX-3lIolA-D$j;O+zsWPw0% zcXxtAaCZm}!9BPJLU33DAvgQ}uCD67`l_z3>i&Y=eV&<~p6;IZ`8iYM=PNW%?(Bk@ zoaifH{hjis?{x9#*qH4EIAX!!IwD31DB!(?AbOQA*Xhp>^Am#?BqcSKL0HUxPC%|V zqnp=Pt5Lo^t3`b|Ik~rQV-A0?Cq;vdFrY}R0ez%E>m+6w$idyTXl;!q-_)aMyUok4 z_pQJoa?{)9S*7+v4!sZfrJ9`96y#Z6>PJRJSs;*}E|fC>`DSZbuC9anvJYUPVL1r< z#HKY%V2wZX(C`J_=z0#FRPP#giL1GOp(cy4a60Av#LEq|10(MT7;mr(C>#gyxVUS~ zP}Prg$SYK^u69*i%RI9+H)m*0`9hOZfWg7aYU|`wEPzWXM0;RnVroiP@c?X{I~4OC zFrkbULD#_WBBIr0auUTXdFDsJihH1!%IlKrt1FN>*a|8$MMVvLFwDKi1wpE)uvOuF zlHJ_cat8AIC9oh;zQOF@-)*5xuw&WE&AHcPDdQ(e3Q~A76X3B*(YttPj+5_L`lwfEpc-P?j#Wt%pJpmK+mZK z=2u5`lA)bj*?N|d0fAZlGD}`^SsAz_=N0bH=Ro(uh#4TUy42_AGtkgTJewv`S%2d~ zGhlT|H0%hxZNOeAWP1KK3orZQ9tb>eadWFEC?wp;wVTxs3=XagNcUo$`eFEf3BDoP zea*uq<<}|fvq6sHFc${QlY)OJH`nJQ&vU0_fyf`p)rs5|O=*A&+iFr*t~U5~c`h!0 zX!*AcelTp**56^1yXMrC`$+Tjr3Yw_dxRvL*Hx$ z+_Z`RsLIodg3kg?IGQOR`9!fL2ACHWmXTp$c~j>gi4Lflf3Kqh|9$TSbp|O>QCO+e zC-0HR_j%Kn{~(<1;*v8&Esz40|6&XbURG5_1U3(3zY1oa0Ili0?QbGrV8$ZUdkc`G zR{=9&mw&xcfD1hDI=X*wKte*IqpdAtTU^+x1FXo@tgOlp7@TV0jo$HMFV;_~rn^R) z4Gs>!li^oLe(!Vz8rGjbe}4UPu)G|TL#p@2`k&b%Uf}RA`=jOMq+7MB$sp!D%LjW$ zM_-VgYG!UOKtB28|9Y@TZ5hxzuxcMcgQhSr;LjN*J|lhojH5!;mL3Oc;EjNrO?WBb z<~o)aFA`mqWWkVD8M(RDn_9wO5y3!Kf*xN*kW6^s<%Mx^aYXFel>aVT&f;%C{dGfQ zBd1{-KR95VQ01Np>kIsY5V|Fc<{^MEStI zaAl>XLcop&#?OcRl|tEAmLPUc&TJ6Z5*Pop9*mL$jK}r2?(SC?dt>lg7@Ob8Agenn zDk?4x4}km{b^K`uNrD#eCjd9VSEJ4&@PKqAaWuFE{WJkl1*smvIVfSYdBg7`STw*2 z6bQdbTd;}X;0N{h@%UPun3(Qh5+hjv8xU~==GocVfKgNlQ?s*0LHrEN;tsaLNJ>?8 z3gnB6ffEcE{9PaDAh)9+FPVgfMhpsb0i#?Y15hR2y?bYC`xDf@x3#we+^ql$myK=s zV{dP-Pb%Rd?)cLv6=1PBIsFFqEX(Y#zwu(K(G#5sxw$lCWMy9R0cxL?KgE;rntcNa z-(sNT6CeiTKbd`gzB4p7ItsRnR~6eOVEL12ctixKMU!`Pa|6Bu|8M(7%OEiyR6qj9 z#x)l!4<$e{Ezq!Z2XF_?3Vuci920R!9%u2dIAp&M4 z6SNSBa!X*?0^tIjFg_&2q$FwBq@kcXh+DS?Xi4XQIr>_4oYp(^jE!>>6A7rPzXG_Y z0YuO8o^S{Vz~erDCECF`2bxZBva-s{$@Pc}(4wYc9|8pjORu7_5#AH+bxCcj4shnr z&d$olP{NnK_@o|z3LP7J;`?`}rTMApX}YwJdnLfxuWqHUs)|QMR0JMMMW{zGL_xcQPpP0SWO| zR#qXw!9NYvH>&`VCdz^H1L#Bh`}@GlWAdA5lbMI&3=0srikuuU=i$@qg?9_yA5Phu=h4nn-Mj^#~_5lbx>r)_rqkTimMQFjJ*r+q{vJ8~(Li4;X^= z)vW^9R^UJ4xy6{mNy9Ex6c-l<0u2nvnC+ z5)`h0Zx0d2pAzzbHeMj?)6~!aR1I*S-W_O41M1iLKs6h{+ZCb}5?3z}&J`65zUZjs zp#9s1RXtGXOn(#Qar02bp#wdlqvHV(KcF$dBAy`feoX^5#stkokQP<&Rso#W0F^-8 z^nkUig2j;;Ja=qh%cu#mi3@j-#a}{_N z;cZG#an=8j5fc}C{jfq ze1y63OczzyO0opc5(HvvYipl2li@Zn)S&TAguLwyJ_^VxZVcu}JVC4c1g+24;9=2J zR8d*fgy?_vL@pj;0$;)I??1LLkPLD$B$p~8mvzMFd+@gQ>;&K3{4ZK^krp3hmiMhY-CMXvof%= z={Y#YrvoU&^mok8J&{u+VwO49St*MO;J!Un_b*)9PT)a{jEPYInc~%lSSpd?#;}H- zx)U^xU}(KwlLoNnC9h0CI>gtqz=r+|`1gud^;RV=?q62YhmhbaDRP;2ApS*I%x_SA z4HkldVpXqS!c(#A!?_UCqt5aov*=ZD_{4&J5gdTp<;q#samOa3F@RUX-)R%{*?m`5 z$Bi-~h>Baq%av2Cl_pI_nE(BlIdPTUM{jj3@Dco&AF({X##(9KWTeqtNVz_T|C{Sd zREa$=Wm)M5D~Dy1(PdL1-m8e7o*ozs2J~uB1+}-kySukHu^5&9!g_FE0GsL+q1ELR zwv36&>^?g%tgXmJ-bF06Xhx8>zP>(iCxT!#eDB{dLAys#aBeg=uoz=%C`ViIzBF-~a^NDR0Y4B!2peM_4~?!Xmq`4uGX2;u;!=?n(3@gdWO||1F#mHE_&?u>?TlP_&i+Km4HuiRQEa=y13%~dU z1b`*s2$YVluC5l9a;lZ|3I6Q9Z7`&wh-lz?yH@UvVHk<)-KjSwz8*TYNP1OcBT;tWvq4@80Rj{XGBPrW`su#DK8c1x%3%i(IbIyI#exrP zCAh%N|5s(>qe$?xahcyBEkkRNJ1iqQz|%eA*?7qP7k%@W6ouvG3hjPdMcER@^=g0g zfNVdvXB-dt?~%bCU-gsC^yt4O&h^XgzX*MF&}pb9;Kg()eri1RWj1#2z>`IU8j&X$P4C={18<;O%$!BCBLjY)Mz#q{lNI@}8 zm*LlDE7-t5QkWjS_PnaBY-DT<6qSKEMQ(9*I6)WR_oQK7gAhRmjFvSzM#f!uPcP>` zQ|$^ctmFQTjogdi@o}Z-aT(!4sXt{AfLCKY!G&<1xec|o@$}8W*5C#rz1{2Ed%E}; z|3aX?C@3pO$HwAfVexBxW0`e`0;gSB+FVT+9tFky{l>q?my}mu@ge;}$f&5#pFgj# z7hDg6@AsZ(gE@cwPn4fY_*)?%PccY9NJ##@l|rZ?ABAL9q##g){6P;Ye+KbWwyhUR zPwt`oW*AMKIZBdp01|hfBB?4oA<-z+fLbGv!cz@KQ*=SKr|j zm5{DilddxEUe8_uQ5)haM__*DQ|&#e7Y>}60ebV?oF@GFwB@Kxt#+e74`#ku-(#&u70gmHz}Fmc-#2 z`n<(BVZk2XNAqk>`u-9=^oLXHLa5a8+AEPH#}OsFjw0C>b3tKTLOIEaH|IIf$vmoT zL;%ei=fv39i(Xoz>6a=qtnjvoUVo%LJQ{6%g3RjGtPb8%CE%}7CI@&DqF5z{unkpO znX>@I6%-hOP6&WiXRpu(tPJWbMnI)A=z_LsN7d$CpMbg&k2=mA{c~b4&VNmWmGviR zz+h_KnC=qRKAX>~dn+fpApHn?I|+zWH0iy+#60 zWBBosf`OTNuxz8@W1)Wa9(>lfbKW#BXU z5}I5W|76Cu7b+?t0t|ZC{!tHLb$&356<_8oN$`7FMBOS`8%M$Pk+Q5vXB%1J9*3E< z5r>&nvkwdil?x27%>_fQhVg#xYW@iZKa2eD&BEWbukd^9MEer0E)!q!1zjlrj>+nB zraT!2`!M*!Dv=c3q>*~dw0`+7^K)SktIF7ZMx|~4zt;saXXBrAgI*1wFTHg(b$<4; zgrp?v-z42PkLG$3Q~v|MX!(mQqN;J8X4u>PKYx6Vuh34$Js)82^{bkiwELXj%7%o2 zyQ!Xl*_5pA#b|JREC2tbJqZ54OnVUG7y7@_9$vl>{Qo@d0eFsu1pjaMN&V`)Pc;}w z7JTTx^-Juqx4f{d+NGMy9yj^b)NUzh)#FJsl7J>i-Q(H5iqrM1?7iY(!jCG!MR7y~ zbHr062|3zNt;iu`yNJA>5zO7LtM-iS`Pk{+-_!glzo|Nsvum8Inms5j`^-T#Uv_y_ z>|TEJ)%tWrYy3drg6;6uEN6X-(STz&0!4_7q6pvc1!l8_qWK>#4X3;siMJTfBZ9@x zkN0S}iP;9AF0e>i|2$aJgC$BbjaK)x6mRv{6wGB;YRvi8uPO(`e(Bt%!hW2WI$V=S zVxurdHy+pyDrUg;;#l>H-L^@-!Ai+fnc?^HDf2ho?`%4f zmJrnk<_r5GEI}o47{W;P;MV~#HI(1n0?M^#yU9707i|``S9!q^JL8k5eGod%678|c zUG7DGR{v(R!kR<%VR=`ULHN&!#%D2v38K-{itCGBiEpbV)Vcn91CPF^v)aZ?@LyvI zB2@j1E$zy!m6ES?QOmp9`jJF`l12YykHcLkkk`)gQ2ig+{DUu;`LJAWzF<8yCnChO z!A-kMGL6P~m&;OExkRdoea1c@kq2`djr}I!0?SVJFGw=py(#OZdHFgDt0PFYNoErt zcT9*d^#hL6Q#<@v_DkAoQ@x9r&BBJ>tw~lfC3cK>$6TL>DSzWUvy1=!b@2y%WM4(O z8(XZFJl#m#A?&REYrmeNO#G=yT*c=X;uq3zfJr zZkC6)BVq{dhuh66*pEWXJVN#hr#`ftQQJWoq01b&--U2uM|&cj$gi*Cq6W?R(=KU} z-}~jmc=FNkx~1=?3kiE~%7&`ruIoxqG-9i%Jp>?9HNJLd0XWg$E^)OtEap~9sEw8N zCH)^iCbgDCb>p6-cEijxdv!mIPO^Z-G+C`^Fy4( z;a~}lBC!xKqGfreHRbAj}4X`bp8IocUtxb^JoQm&%5ndZ}#(nL51*vXdx z>^3`Y;;FS5VGQ`j`I9^~^xwjiFSKa;k@7z&VG?#bAkOT6jy)C%?9UB-pjFodc=7K@ zdR~BZhP$Qo&c8^lc^qbWGmumnU^wXMyGYt@`zt>8>BG%gqa=^qL-4RzOogxnpm->! zIzM=qhD0}b6><%OEy?El9-W`I-t$!cOpBVktS%1CZ^l*q=o8L$=N3+Tj*p(JuT(>z zq{;3*rzD=V$IOszC*JV|y}=8H10)(H(x9hAj%`67OWWNqUA=Rnl!4OC_R$4;#Cfeq z8|C*)x_B^M?*Lc=Ej{gn=U!^1z%$$25Loi(B{R~upPMbbTbN0d$T*a+T5Dxp$4L6Q zC|=B6-syhzc)$ooM+x3|c9(vPneR)!t+QkkD+kDf-QJWk`@3m_im0EoHjPvv_{qKk zH>*SK!(;d}>_%T7Ix0-+ls`(01b)64OzKarBBpyTxNX;zsp|*r*{HhuV_6`qx4aG5 zJTS3PRuRVEiUqB42XsxSugFs^dJ8@!BchbE{?ZeNHL=!-KZL7Ozkc^-tC^*n54~U+ z;ESNBBRl9z(o4_6Z*T8WO|KSrM;%Du$46#zf#OV%yAV9^O2HXN`Nh-t=@jd(TBP|}^_4AE&`iT-* z^K{y43oJF4D;s`-#KO=k8CVP7OQDC&T^j2O=kvW=%g`7*06&dbT5iqX@<)tBz1wR}B-}z@7N`pM;vw&=xmO7tA=BzDa`i6wnD!+|WpPU6j!{}Cr~g6U zJ={jEjHrF3KSuFF9N+y=c#b7rEX99HA1!agdm3TOopQquSI#egg;?2dg3D9>ew?|x zv4xNQbJJPGYe||$oz>~h@%rmOxAENv19lGGnPvGo~N_KuuXrJsmixhijFz@wsJq>@oZ^~nS8Nd!`LKg zh=dXlFCWC-)46LU}Fl>NNp@ zwAZF-Ei-gGodOOcq^o$Uah=9;KvchQyNa_-w>whnlMG_TM^Us?q5ctFoMw+hj;>t_ zJxb_i?@itNq*}wyr{o_J#5w69q53Fc8}sm4&3;{4{RnwO(N{lW+)n`|-M}t- z8~oE-FT__Hc|oTx24~qPe->s*r9_R`Wz>(^Vd(-hTm!hY_4-A7-rfdy%X}1)cORHw z{9(FgMRwI&8$zrZv5Rnd<)?Mir1{~^cK$ait8&#*%t@VI*Gyq(D9!ta0vqW06cLNv z*oQ0HsBW9;^CW_tLL%?a4jB8B8agMmUFg!7NX8RdpL?3|e|vbrS!1Yki3cllfB*F` zfi+q+;&PnjCKg?Hioh}VZY6InKKB%&3*{@2s5b7%R_+`w@qPUKk=BIp1JHBnOJCaH?m7G}Pc8+u*Z+$kwxwW-(wDx`5Ko6t6 z%h#$u)KK{1)vi@k=z9@w$l7-T&oN{3cfu%UNDA+w&6|Y|$}_%u5pHyZhH@_-yb2cc ztEowXZ+YDWTakqIdDVp5f1}8@RoBA?=c(22ryj|BPMP#hr<*j0PkTpa&9%~dq@(?u zf&?mxJ`g~Nj)C?zcV4y=rXNbJnS_%(DX;0TR+eHH=tS46Gz3N-G zB*IfDNbU3GL|?Km^dX-6#`M{2s!T2PVD^#RSp94S(-75{3Pz$|G7Y4>|3+B1J;gdg zNu%I0q$CoGZp_DKz6G=Vvk{92(H*-(*}hMF%3TIuqRSV|o}mLr`6jruT6di^3?&IW zHEt%7xWC%`O+LC3dA*GxJ*;lpI)P7mnRLY}Q_jq&IJ2WjoWHHMj#*iB>hj+q#x$C$ zc>FAHCycr|ji#_yFVkGJ`)Cd|e3=2+6*D1N+=lcMFE_MEU&!zRb5;6n}vP;U4+^tv;|(6+1Q!F5YAWcf=f%dB)exkFfM48e)j@)XLDyyW13ZIB#XTk0Wg;#F5543}#~9P}@uN2f?V!zDO;nr(78~%-hYB z#J3gz=$;L6#}7FYk}N3%Ifgfx45wJgGcP?6haGl}FcLjCAsbr`c{6>wDEXjn)ZtgP zUC;T<1KLTUF9O@y#fgb{Fx{f~>df)nT009Lo3Hx|=e)mprS3iktbX?I zth|1W9%;RHRGZL1R9d7Us+oL-5Wzn(z&wtza{yPm1pP)zZ8f(G2~aPYORJeF73+RU z{KcT)ls>;Tts&;F=c|kfuAb}sF;Cg;u=gY!()AWLslAZ}BU)Ewcy$pP#=RPme2G6U z`B`xC(6&y@lwaT&(SE69HJy?tn4>=nR>Nw&xH&fHU$t5I;-xxxp<2hVR$zv*+w21j zZ3NIm-Jea5mVtv8@9sYOm}R*N@6-7w?2_0>a?q}^jeNHW{&I>J^UXi8bqHwUhdYWo zfsTYVLqFX_N7eKNe=6h^4-F5!Gzx;cqqwhH-euiU^5bK)E}xB=kn;CFrzl)KOCAXg z@K!z>TF#2e19(nt2z{wi7GfD^t%DP@l?S~%R9>k4k<`TQ6(-}Cwr1}16>;Njay>N1 zF$S0LP3jhsX>u+>F0Au#goYmS+hUQD=4E(T@dMqq^dKm zoklz#qNCO678+(-=C>mErtoq}LOq!PYBCydzYK&mz~LT}@y2FVd8oQ;7c=ZuMLxvp z`$Jh?)hSl^MQX5xd^1(KLzT13#FGJ{#Bb%8i;m+HRSBT%XG_ytwca+N+XkptF{qyi ztj^$icNhNSb8L|Bl&iEss(PzRnNN#(vS#Ay&&YrM0L!I2^^X&E&fi@J5zs#iw?`DT z)6p7Z$M(!Dy%lA2tP|gyJNnBEV{%3LbJiPi=mT9w>Kj~yY;eVkaXh@oI;ux?Q%12c z{VX^}ra3&KYK*^@|Bv3sdug!{^&dy#Q$01BYnWzz=J~R+UZj{2hkGy_Oq@@_K2V<{ zdT#$P*!cDQcBaYaxZTib0hbpE=q z6)9G=ccP_FHcb_}HaiozSJ__qjN3u_sn0kqZwO2WpTgJ+1^rs%49H<~rkv1syEB{s zfw<`g7(d?4xf?P9FjtOde`erf`YHc2gx)n4l_Z|hb))_W+fuIVAd)E|mp)=r#uD|9 zn2b7#6Prj>fa@~kxywWb|L6^0u=$YrdUkvhw@;)$a+=y_PVmRl5|rLgm0N6%aLzCLXe>GF}8M0!iaa5}r_RFQt-^=H|f_H;1h2nn|njtsrYX)f8-FW;go66cSru zg10dWSroVkjTC!3V&J;7uuuL!IM3QOxDP-1y7VP=qvF!S;$FQOKz$vaN+|u430A)D zP6$c7Na^{&Qf#>CH(ByISo}RW7FJ-T1_2di^DBf|#%HV>{X0xFMdruv`^Ob)w3|4g zU2Xj5xGIG0`j0hdybr&=pBI!Om1}R7iQ+yosbSo$#8Fqegv}%9jBTGVjV!k9o(*}- zCb#K4x|}DEcr8kmecZ!+_OGj*`Lz4stIM8_mC^Xw$1aBDLxZ~gEU3ABdy|b8_ejLz zfteeCr47auS;QL2MO-Dy?5k=l@jPC>pHD`3LCAvrSm*XeUJ>?3zzTtj6Ka7qsBCo1 zl_(|!9Up)9OqL?{$a1uy`lSJPA(pxDibnVs{F69ybiHuGN`e|4T>0#x;MjQEq_sB8 zJzSD*M6CJ|k~v4Xh$6@KJQSi#dFbRdyS4cDE-dKQVV=dfQ{O|cLOij{jAiRd#TQMu z3D^hAkRUUss;bxCkNA;Ky9BXsQZ!LneQ|JdCpeh2L@gfSlGI~o~Z49l`j#TA1r zC--c?B=zYpBM#D3zeeujlH|&?@~)(wP!F#(r(N7$-MBnj6Vxo(g#nhP)t-3u3zGo9e ztI}WRw+7?8p~>;(Jt#iS4ZJ1o?z@M0MFNmj(x9>1TLSEPh1~uvu$?%Ak-_fUuMz7R zj4C?ImTS zQo{{0dhd{=Am?@LmVI5J$+PzqT%^2k7BFc0372f$buA|LYl9Dz`A-2Cn5{URG`CSc z-~wyb!as>e-U}v=w15_9ysucvDwn@BKl(+ajnuzG(0|2VH)mPubR)J@tFIdDCa{a^JTRb$j2N7#*{W z(QtL;*z@-d{i*vz^8DVtZ$~T_GqH!wYMh=}x37)}jWJ+jT-co*!vDPzM$mxk><$Rk zSkT@nY;w{F{{M!9#~Rwdb-+mN_I#>$A1nJ`JaXNiVMX?JglU;*u)gaz68eec`k$V+ z@cXH7f2~cheS_dWHka#jw&hBkkK3y630seABPJUIx!c_Zdb#l}=X_y@BiRHr**njY z#NI)FtmUCYpcCr9pJ}US@S5z_U3)23zSA8b)z2IEpDVE;&1LhVdlcIJ=n!MfO7uYi zUl<^-gsL}VJ5y?6IuQ`hdkjXK5P$BV8`geqw7cYVlKyM*+MJ+0zDp^Zz@wy==jRb* zjtY;wm0RKK*J&~NjMlmn|GC{#c~%|Yq!DAaP6lQybjqm59YbAK>Ax0X^ER+Cutn@4t_3%znmn|7hy_wTO+iMV_$O+Fol@4!ZtgDm|x4*B&F9{X2jhZO2|~?3n$H z*AC*yH2$gW@K+hti7%#1q8V#1Z7YuPPjJWB{J5Z=^Nj(MDKaD=F*?g}(>&x~*f7nj zuJzVxOXwVJ9ZBZ+Utvd`m@HY8V|#>!+kE|if7yKdCo~MmGED-SO$KL{FH#rI8`sPn z-MSlAt=~dBIZBnGzq+3RhK1^%%-}rw)(9As?4p{*I&>jVGlLs&vWK0i2^i%o7)?v@ zRr|lCelm!js~y6FA5E0G1s3uiF)P1?i?Ug9pWeQAABVeW&#yVN-1;vTqL0UM59#be zxP3Ydp*Me}sc1Nw?=}tEYdH6AY-4bLDSU(p4bcoqCs~kLogDslUGIuS3*h6#B})!6 z*?WDTKuY65upWpuJw(!0W(a@;n)WNm&4%~ps?{aFQ|>;j=X3(q__;HJ*eT*(NfG(V zkI32#rI|YEnYbF>H+*z<$wbzH<^?XXY?|34L9Mgu9R>0Ksjm=JDY7aBO3d-A$i|5I zh#GpqL7jlas(9zOd@o*p;|@1=?t)noOc6;#^@@4Lpn9F{nyb(A4nkp}yn0b4w#4!X z2(YAOEq|seH7h7O9-Cfsq>}{H^@TKPZi?U}Nq1kXY>M6eczAcu0cyWOs>}WO-2(?6 zU^$!(uf;9zvDoEXv(8_WOo87N8KHK(y>EHcs&>_0AgHBeM!AS@n zI$nM~%Mq*=an+RV^~;_;QhmRDv=UldabZ;25L876Y1<+W+mL#kI-gE&7l%@54b)yK zR{Cb_m@}?-oLfHR#u*BKfnrVaQldGJ)V0x(sKfoUWOV}Zdx~bMiE3KYeX=2LTd)>C zC@?CsV29&|LS2obLkV`d!sm394M?!tpD0wJ2Xy}2U(9>^)pNrw`l?m(r7(o-$=J0Y zQyYFW$Lj3}B*V#pFgNLx^vDS-XYr^}og>YcKrS6WUcw#2ou4a!qn*^&m`6Z588|Z+ zH!J#ZK-3HnuHen9P?c3Tj{KW+f{nLkFPx!*IQ2VOAHGPbNv7z(G3@^*i+_KjL%+|( z$NCqh>dmhXE1&hi%S|=o%m%N}j3>uz9ec}`0k;7lW_$K5oXs7D^`>1^&)B9VTG2zj zSG=aPOsX~m5(27SHz`->+h&y#AUaYQGx^Au!IbRc(5zJ@w1zd5aDC31z&25b5Y|RU z;7=36Yw%jD|4 zKsD-S8sLtxv1cxbyaA1V(lygWHDq1q&I($z!?GY>6JZ2(@$YY&RoG!Q7KMqhw2r;? z_U8uixvip9g9r1a^0}v&$)yv)bB7A(0TKd7i=%#j2H=O;_E;ZFk!QSEFO?wc9?DQp z>e-LVBz~XPH0!S5D-NNRL7SKJX;&%xifY0MXMDeoN3(N=)R%fub0$ypl(jp%zx8;fga{fr@{19o@^7LGpK0*Cmf)Sy;Ob9y zKCD6}`$k&U)WA1iy>T)9K~8bNi1F3{+S(G>4vK#sCNQDa-!nJ1D7o*J0=7074C)2? ztbJ74-5Z+}_3u@~C6vfd^(Kh^Zq=qL^M4I3>FQyLI>fV68p07gbi^> zTk;68nGV!lVLHoVtb^4K8$zO=-W%A9tN+Lhi}T}gxf`+%>-ue*t9{1a!Rj`2{^s7o zG}dDsHxkKDSGh)P7)O?SDjMoU5m(tdj=psd)ryRa{V_*Btdio^+z7Va&)-S)f?-j% z$x;XYF?q)9iaWdF?rZ-wjIfPl7RPWQRNlBpqp<%4ubMk5HL-o)^PJXRKmvz5_85l8 z6KXt~7t-IW7};hRr||x=>k}n&RP6Aft{(T*QaVMN?4;VZ!RxMjzRzGSf<+z@%&GZDC)oy@DVAm8;D54HVAAZ`nbnZl}DNQ ziG8}eu+ovU(SB>k^R)o1j!ae@KyUoH+A&!=HTN!n%Lbsk%0skwl)D`D9}DE4y5I1E zNS-@wN7jRKM{nr4sM?WE9?2-P&+8$7#V)R=5RL_1hr8weRg{xijK>y^N?oZt_Hkj^ zZeRW0>LiRvZm}uf#84OCugS3}$@JbmuRD#CJ2{Dr;y$pv4^3xFy6C&qs`+8{-|5yp zOztM}+6E=UhmV>U?KAg^J>wp-ahytjkbd;DWA&6LY)>7+#NP9$SgG>>sJUTrF5LWg ztio6_#U= z=N9xyB;+=yggM8Qtb?J6?d+FM1JU)|FAvQfW6A&XEAhSw`a6cKJn|7?w(uu)ke;WS`S$2g!l7zw z@LwC~wz+x6=CQOtDrWB%DrfgXz3CWkxggCl*j_QhyD=0q3WWvrn7UG2S}c#@DbfnM zk8TL~vFNB{8y4@>h`Zk4V2NQ|=#1WB3?0c<`RXT$U2S+3WR+x1PTS`apR?zWs@&T@ z=}xuilwEzR*MZ8e602p2NFwm3_1QvY%C<60vl2k5HdGRTGJCgQhDuZ6fOzVs1kI)PBW)M3ypc~@tP02&An9sH#YbOfZ(%cL2m4{#K_JR))Dt4R4wGq8n$ zUx%^0gK;jv^m&@U$=^W#(s98fIu~(*f15+r_zZZ^l~w_shlM?zqa1!e;9e$zM!g8{ zJH$S()*zlJDdLPTKRrl^s-onO$8Ro9wL^n1@Ioc$Hepkt+3jC^}pq^ z95BG{?Z#Rur#G7EAO{9G2e&8x&o0}8Y+8=5pAR&YGUZ4+N_A)@F)Hmiy(nh5TiT;H z@&#$2KT1V~K)S``7(X$5n(E~QhF9Ir*pM%1F;k}o^r!f&3@2NAcz^i3W2Cfd75efD z1NYMFG%40~*?8)=hPcVDn`xm9XP^6x^B!K&a?ah@KJ%}8hgJkcglrdo27D+K>7c87 za%ZlkkCo`y1lwL=@%Wb3`ZNC1+7OjMNhmg`=o*HJbSyr2Jai2MkMe91Q=h3wDZ6(KC2}1|Be;^F*zj zvct-|ZAJ-qunMp4S71aJ+(DQ3H;B!s|9te0z43`B+vd%gY}r*6lYL6rwvY&I6C#z< zhz2L&X1(J>f|^??vaDsIPk1bTBa6OPv6XuLuy2hn&sZhaaUBnoy?`4Jo@wwM*)|Mt z3501Ndi^vKW`iVka@#nsKxTI&SMJ)YGt6g#LPt4D>C0$=;=rbGzRQH!J4oVc?gt~n z5yC?2*0`8ekCAqUi-jTye*X=h2Zf*F!8)l@2e*61t$1elK%@!nxsFRdGLCRhh3Ja6 zX(eP&W5{pNQHK9xc8djqVtOd`EAlFyN$l)_r7E)A+4sl}OlVCHW$s?D6gVt`R>-k@$yaOB_MP)_WJkT!wK-&P=2c zm&c&3{wt!cLr1vFGHEtOV6bF$^;@nKCL7N~%oy(1k-maPm17t|nh@8o8(XSwbqJ3h z1g~@BRy^#a5W91oTB$dcM}NwE=G);g@(WgUjU2)uawo^lkyOTa=COJS*X92c#qt?^ zH~#2VbQ#ZV5-SaHl4a#bR$}UTxxQt1k~h5FRH5Y1WP#(_bFZYOWw-%MNhJTB68kOy zHB1#mzEiO(KHc@oycr>8vpSe{@OEf$*pAn|=lQ29Xc0_Qo+N9nZ4UL_0ZA56dy&uL zXGjUg2EJK8gy=6+z%7q#C$q00OOvPtm-pp3_-6Nbt*A8U_j!8}OD1H&XV-x1+HX1{t0n@Ju2S4|qn|n_sa*SFJvDT)0US7`FR{acO z)9k;pudsBKVrab!g**2yU|%lM#H??SlJkBGSrJ)44kn$M$(yW-7?l@3;geyb8K2IT zMF-`b8N_$H^1#nEDu2W}iy(HfgeTJuAPbM28Nn7+lXvBaL6AYw4wE2kZ~0tvc==E1 z?ijIRq*TUc>hk0~@*>A%sF@AeEA4d){kGP z-!7N>^`}kABV;0)Ge#qI!lKm83ZGdun9`^L;!b{=XTZH^E)Fpw+}g0&Dg2G}wNU$5 z&E;c&WG05|e1VjMsC}9@dhsY1V%PeT+4P>yj&5@U*!r{{|MR_SIr0=60Oh0wVy8l- z6YT}%%V+GQr!%Ck&-B!+8D#>>(6x=2)a;bcdpnaWhEPe2KEV~d*y>z65spd3zYkFoUY#@qz4jR$Z$glwwxWu$JNeNOQg ze293}WToivV*iuQtjARhA z$^C5)wvAQM?(D)oqXBbO_P3?LxA#(;&TI$5Nns=dHK}%UB{dhq^1Nz-uVVNO(@CR* z&w7!IT8hJ6=~N%`_MBsIdntkk;y1e4rM%#xq$NZr#TAvU`;mRCQANcW_h93=cCIK^ z1Yt2%IxwwmJlIHZSvDjwm3EwPaQoUa=}XXGH+ay~ur z@1Y{|VW01<3ij>_+5qo}j(;n^1s{5?t+d$U=jr`IvW} z*&Mrn+mm1MIPx2L@ENlmKkG(iWJgKBK&LJS*?d`Ob5BSyoD`^y-E&!_ws?6oMY zs%$aQ>gYcqhn6+CF|03D$J2uXa4XeRVJ#;*I?VtX$p!0*^r{bEooK#sqrU;+vGi&a z<)jMT2a41}r@mEL^hEdE+^aN9V~OvGeI&sYC=Vli7U`Jy-4&}PoY2ibILJYhDQ-e1 zg}*v=xtT1^mu6A)6Kruc)ALw@(GjA5E+6T9hgLAZDN`ushii#^i>ZFW>XD>?$Igc`*QdMXf35l zzvGc0%gGH_e~gJ>UFt}k!lVhruMpBF6z!q%3g!$Z89F9GzjTK*7YazTUyy%5Q z!%_=~rS7wvB%*NqkrWQ>(u&}S*dLmx=kY%b_)cbtA%iO(c-@Ew+MMY(tO@0O(~%_s0-f+NEmo=b}&YmBy;w14w#N`PploP5UAFaEP5EWpHKxef9iMTfG?7YEvRC%cJU7uP&Dq7q@ zD>2QQ1y!#zi~pCiYw~qhkklbz$z22=jl|s@xm#itFLV|nD%v96OtNsGU!D+{DP`)@ zq>U2u?LZVM`{!~*WARkWHauB|r%PL_1Mf~U=@O1JFi!-?a-l}=p$n~3-%eA_ZyhiS5B#$LVt5 zJpD$!3`H+c#zN_P6~FPCSuR0azE71YFC?5Bp_#nQJeKaiv2@7&Mmc5M9CVVe{0Mo(tB9+87(zZ1~B-cfAzb?D1iIu?;( z+R_!C>|!ewmXE?%3ly@f0~gl0`v^NL4g_I2%DrTV+;#6qij@GG7~*id9L(jQ(p>?R zACy3!9ACh;EC2)7jI2Y9kZYl67HZRYpM#ED1n)^aJ{`f0Z)SMJb3IY~^!-ms)A4 z5Q{wwV<25v-6|Jkza)=FaU;kvDV}Q$D+II;E=60~%gCE<0cON)2;^>SG<+b2GnQSV zyq?7z4&?l!=@<>`gS-*>EElB63rF5>yo==6(x2-YqY?dvn+mKWe$7nFi%096EQv1pz{htEa*%oDg`V_>L zXyYQdF=8X-L9IWPKcQ-;dG!$_r8#cx6gxOJ;#QAf$UYj$4F!f4K?p{IjB_Qz30)TW zv@0L}e{*Q7dFOmdp*j6YgjO2}>Ym@s@iGypq7`WsIc54tl$6zipM3*T%IDh29+qqd zvAdAP&P|bM{-pAKC~Sm=!*6{l8sFfzj{4!`=+;M+1oKa=EFqS}vy&;68L$i~?HVd5 z2e7fI!D}=#a@01@XzXPa9!s~HAE5KSO&E)WPZ9PdanCZ`weg#95Mlc6*|ILyS%ecc+LB&H5) zsC4Fnem@2wrO$)hiGk*^L?{K4bZo48u5z_1DE&qJ#gICt(T3MF(0}&YP{S_9v-7(w zf{lG71}6Wxv%-H+zUGpCSO>Yl&r9EY{KnQeazj=+)l!+i`pYWaaKE7*RFAZ1j(Di{ zm|BXJ^;0`bV%L}+J6W_*|J3b&X|b};l1!#!!XtRP$m^K@560dzs;Mn{_f}C*sS47I zihu}62dOzo?;>5AQUs-mNKc4>fPjGXCPbvy&^v_Qd+(tmodAIlLUK3f{NMMEaX;K~ z$MBO1WAC+BnRCr&KEH>2oer3_=0*E6O7;?br~kGo8lXfRLdr`*>Bm_t}=+)z8nQ4ZTjQ)+3eo%|5G8H?*(M ztfY*4+MbC@n=l<=WA&L)SoQYYCjD`Q@xMwmI&BAR3UVBJdL@+LH}d&7dA?sBj<6`Y zM)MtKXPJE)N?(u#?;Y;lKhFrFJg>hiib?FM2PI}Me58*Z-sK^r72BdHCXs~hLPN4JC(Vkj5msu0F&L$Rer1$ zU11_O>R?Q%I&@!kK0nPslC!2N%kmOC{qIspIC`lt>@L>&CYC(Kua5HSCiNfYK9}a` zzg;8I|1hjiLom6oy+}j$V?h3x2cK43)kceBLAP=8Rpvxl#LGKtLo4L6Kb*r}_uYW_ z4cR88+Pvc9*(FKiy@_!6gLJjH59s9I-QUXFj|F|i#8gNI2^9oCMpPXaf_ZakdWbf* zF7tOC^A7#nda9Mi?1N!6 zYfMu^u%G22Kc^v3f&$_B;y>B%KZR~3fWd}3{-Yl7kg zKb!n_*mSl1&F`(c?n^Y{s}pO-?V)*FKLtB?w!#_cfmrRoX9CfHvz3-qW-acUDsA7Z z%r&TWzcoblJgv4(_Uiq%G1L5(^Mc-*XQ3S>!5t+z=+h&TGo^k1rio3?-JH{J(-NMgFPG3xhCHC5s>yApU`c4Rcr8L1>SyiR})Y zrFOf!=~6EeaU10cg)%=!Pc7~pUPr*4C(3LL(ZjzvW2Wg-TmQN(WSfPUopF>$a(o5_ zPOW-IuTe{Azu)@!`oVi>GBZRv3L>4Pe*lV02J2h2GA-L=NZ{V0sAn=l%s{5qUd|^l zZQ$|hX`t)9aec*gWnIvvlH@}a$Nf?3@<+MMF(qU|J#=fFA<0K$&zl&p4lqVUQ^C8O zBXrG+vl`!hFVpuP3%>i`T%PVH%rX12@aS(+Oy0^-i#=(EZ`eE^8s#He$W0+!biK6)iJJI0o(s!e`k?<8sbtd^jU+1_ z-WE7epQ1G4{^$e0aGzhli`z3nRXz|CrwX zC+sl~amb_O`_|1Raew5PeY-I{1}8m`8;E|Zx8i9S6{l59i3$NF<*y(FSC&U_Fzm}Ns zyBNS?;D#$dZ9uJ^ZFEbj8@j@_OR^->0s(hej?Z>hJs;zUR70>Ag58I zBn`DD>ro024Q`z8B6gy>!TFXno7o{)5*&=60#{5`tXIVpN^l;#M_&fDFWx@(P&nPv z7CY#!o8xwe(b)Sq8=M=SqLcEz@W%f?T(CusY4n4ZSCf(nZN*f!z7|-?xC==L;cFWp z!K6)SQGR@3lw3q=CGq{;qu?^u=AR`F9+Sn|FQ!*xY#7nXyA;Mj%G^}Y)%wr>3# z5NJUcG5zg(B9kc{+YeZ-n{ct zKr@65vQ^mrxg^tYJ-1nGcBInJ+ccJ>e!$JFr2F{?jrpcMYyIHT zmDo56xJCWwUHE0mcYAFHmIMlA+ph^Gow`M(byjZdK( z5?bNxr=RVaX5LuTxwr)&_ za^wcHpL}SLa*%%N#?jbavz4>2Oo9y|Jti$lynyGb&Zw0keY?6U&S!K!{!n`L{eSm) zFEj(}$c2T&&dx5dB^rk{t8LQ7pyw<_7R9Hl1OM!fnG9n9gJ4A2BZ*moRlcMmwUEe{^;9V%QhmE)sXzbm_u7C?CTOHUPTo`%f zYg{%WKW2B?*`jmj@k{BG@Oxa3({+W%P7C1??7A{Z)NTQ=GY)(&F2cs>)^l4g04Mi9WJ`m+AiygSvpp?a z2DCF&&^=-9%pNwl!9;;ZSyjfq=CryioP9%rRoLAz7!b`I`IwC$nDGg;&^>JBRa<)g z>CZ0tg4Zm3E4ss}HuDUVPC3RY=^G<}3piF=zX0&`$klr$V{ppV%Y;0AIted z*5C?1*ha31RRg_i_xNXw<8lm0{OGc88TtX`Vi!1KJu6;ekTdZ$BWTidK`kT*Q{`73 z^8>+R*%8urN{~g}q%fP{zB~xwN_z-;UbG_`v~pX@{a&uuMYDn8+PF<+*S-Bh;F7QF zAnn~N@LiFlk}$W*C-xivpR6-K;KVii$X6@zj?!ypD^U$XWiZvw-{9Wtxeoh-+Y|)A zT+42Q*=y~dDE?uSE#tTZtFcdVj+Cy7IJe#n+!@`JXdA=vS3P4Vcds43cth)51fJf=mNP3Gt+i}E-W zghxeEvOGfq81`9sk{Ul1$m&*4-o@wIU(q+%C17h%;L`wX=4lZEQxQ6zV>`aL327yD2LrUUe=y*+=Pe`atf0S|RZ5^QZ)5MXrKrdCB{D6>tB z_yBuBb}V}vJM&*#gF{P9+ADC0wN++14$B{aOGXj}7cZZ^V*kUg(e;u%)PG`!fkajK zBgZxeu?(d+@9*`Wta+N#H{MaXJ)2))@a+U**4wLdF>-0NN%AC0Dv_4i%u08P1LcXo z2wimgc_L(Rys~&AO6lK)spT+ONE1%_`UEe`d4h|cC3=gmgW_fCc({FOQrDuXM0Uab z(cF!Gz}{P(X*Qo6EMlDf{Hf2uC_fZk5R7?&oz6gbov(uk=R>_GKAWi?LP`BE4@an? znSgx<+gpG*?}*X9QTS*~GBDC3f7WB(B{-S@SXEwM7XDa-s{)B*hjM`PMGSvQDJ>vl zCgZxH!GjSE6H;nc5k`4#7bLpwZcTh!)H<5k|4C@R+0NuA$lP)b!FW!xt0H^2+3C$K zIs0bzw$EUBC=JJlW6qGw;84A8+FT8_c;=5w{7f|e&YLxqm)>7-NT<1{4a;Q^{~28$ zZ{%j6fuGETi5~8C?YC3ylXXC}I8V`hZ@E>Q;D7CR*mm#e+ieEh=P#1^>L@|^bV%=u zBEY{i%^|}5(699M>0)3~QW9Wb$$r355zpvCxIhqmHb(m!&lc<*l21{NP5qtKvh60b zsyM0r7r*g{2m;FUA53`jEvclfmr4qsBi%-+&?WXrc3Is9- z2kTy19w`Ocga=j8_1qYLl8$GZ&6xYgSAX7$g*8Ztp)zDMlPODOr|%|z*vTue{7AX{ zYj0@T|9t#?wS&sa9;2okFS3xQ$6q3%1>T9W-o)BSe=X*7p}w4?e`)(@nJ%p3gMhna z7rH)eVMzB*`9mh4hSxs%>JX_kQ#|#%fC{l)? zF)DO$Wx*J{3A^opv8&T-fz?9^iJly&WJxLPUmdrtNM}U&S>AmcBd3Kbbasl<%X7eO zw7@rPZ!X9I7oH-vw@@#h(3h9ooWStUtv~mwoEq2T`Qo9X?3k4C#*5R4hzO^JvMjF) zhuK{FMOp6)F|;8s7<~Xhw5IFa=GK!1=ie!eC~LcgB(bMR4rbg7thKYnI;c{#PBJzhmd^dYmvQFj|9s8B0NIT1Nm z8xYcXuMF*3OFS?25*j|B9t8yq?Oc-8+PuS!DDAb2ZpzuV*1ir4V6AWeZ8~q2tNG#1 zle{Up(WY$&synnt4dmWe;Ts$DpP*ZRg-22LcUOtrt6O!x8no!LReQ_ymukQ%Z^5|I zCn#1dJQmyzQavf(Y5#iRvM{?N`4|L$GzPPz|H*fqi$U;dVb--J1`US0@MjVqD$uq* z9tD$|A`|)MCv<~Bkv6)zbg^b4VpX7lejpvBpF=?3ZyW)pp^D=GWN)C~6uT5a>Fx{N z%+qCiIgeatlR3QBy7;+|kH7I`ln#2nQbuuWJnHKKZVI~Kd-2p*^DDySk!jr*z#Fi_ z(I86~AY=e#wFV5}quLzKc$VrBaWGd6Z-O{AZEes+HewPGI7NN``~^ij>CXGQG+jTgwGg@H(;3x1Gi=s9?Z)fon4l4 z_okONyL&UofaSaaIyrnBKtP=BE|yy*7XJwkmhSc{%Te9>RIJ8yMSwB_H)y>`8<{O)n#YTpTtWnZ~OYw+1Y`;50`FXC@$9zdQxp2cs>Ce_V9jks> z)w*L|ZEg2kbHW}GSYLa+eP>x^E6Fl^e)hJ5n5;PW7PoKeId!D8Sz1Imn}Yw%>L*_< zhbv#k*>xnfn^Z7rz5V&1Ot9x0a3YL!<@l+1L-6mLre9lP!ly%QKuuT_nw4?F8hA$WV1`m z`XR_J#31zHggS(tHLn{gInHU=u3(aPEkJ!m!0Hpyvp*xOce3e`mS*pKEg?;Dl${tG1#pLRR2sO!oMzn*Ey z&)<%Cod4MsyeRK@Gf{GuW=d%7J4))Po1Jb@I}efW*hH!e`2D_ozVL2ZGJH=;+J$*uQkoS3CwPh}x0V)2V<^f7FAe)z6*b6vU#y!*n#67p@NrcjauHJ_i5tJ8P2 zy)nUT16=mA=Z!z?2U`W*xw(J9R>I#?6TmTwb~*f>@->uVm0erZ@dx9arg0Wsq}s&R zRzx>+!EGw-h0Bc=?O&c}03>lo<)Yd18<|BvJt=2EusPSmY=z#>kFt}H<@ZZeZd&*N zExNLA1nEQ&;==A1_*i52{P2VnyxZ~l@hpwrs<57-`@X?pwfXD!DSXi92}JLmy~nb{ zMKJ)MDzxroZ3w0SKDiB(@ZoH`?p%GjM3Udy#Rk1OnGA<40*8ir0zcAiGxa+FVawm( zu(|FuTcdi|;?&o0IXTA$JwgE!; zd>;Av-z^Onv>L#BDlW0$H9O605&gsp$9muw7C%qH`^xs>bYI5n$XY*VsUS`Iyag~{ zJ-l!1!CYJo@svu%~#VsMGzbT^m*OIS6Gv+AQ0ae{*x<{mb$62=6muW8*(7 z%*-_CXJ=n2l(ZQhg%tePonmFNJd-La`7i_NIY2l57tR-TBhfth-qYLQFFIBJk@EUE zYY2)d`&hN9BD1-?)AZp+`h9py=XGzIovCl3MECY&SltGqO^vD^J)h6#oy|?ZmXqVf zJH=MMyecRv>gM%+BOQjzyw>FhPWLSN0sw{pw#U=eZGc?a_-?gR#P)%bb73nyiU62V z>aAR4H5A37Sinae9;j+dqd`qhs zAtu?N!}J}u=|4xFqQ&m3`)if8+tYxbF>r+AVqiE01|O*ont55W$%Vu3f){URIt6C@ z>{g3fAJnyPFNZE%*PkZcGCEme42w!uh)n$3`G${lbJiuE{)w67Q^Ni1=k+}*)yY?l zs83ffh)H<7?$MlHkggKFj_G2xmm~=^;8h7|$#KPq%4<@CD?I;jA|Quv$Smntc-GW*YtN~B>`Aj2R24E zVZr#v?oJzpWs|rg1Rl$j-yo!u*F{)7YdWh|Pr#p6t1~>6yIFPiWn#+0$1JV^9b9d` zCa*l-c3fK1R~MH97>^Vx{o*LK}8$dHe z*eJ&Sw`Z(khVM4zd%gSE+j(G7nNJLh&izp`!*%CGX}U~l-tcgBM`kBwIr`n9!t;P-pr75szb&ia@w_PN%#$UYHxMWdaukDF>@2ZwGEqb^m%%;89>gj;%kVFL)aOo{L^X4}4; zv4vh@RwFmdB#gVCIoCT);7yU+Z(c~?2VHDTAKVS}#%Vk|1`&?AY_t~(#qF%1#Z~k@ z5#B&<^64I5VMe3Ej_U`vB0LW6u_z~P=K7u!HZ--AL4&_7Wplvt>YrHW87dFQ3aBeM z3PB8;+*GRhZz@Jm7A@`Mt>{%sG+7(Q(|~*5mmhESgp#EnbdS!-YH;&Wxsv)+!Ml>!p zV9P(cd-9Jue!3?02mJtSR|;O*J4T#{gA$+2Uy!k?%{n>h-cco%D4vs(t35`9AQn?g{y5)l_}^q<;ceS8l&);R zKsWrdHi18?(H7-dR1I~-1?^>oVzCu1Q+b;?=f^sYtnaE zYas}x5mkFAQWlP|STlZ`c$nXc#ysF}Ho1{|e-d#=CpBK4{6~fquJE(prxAXBUk^i&_Se~pk|Vm|O3Wg^1bPJD6B&74{4OQg zGV&f4XzpiWkmnaPi7h?d^3QlvsZy7}SDEE{bYQ?gd*B}Z-T^jq*upr5H|?$l`v8q2 zQI`B%CermzSq{%ck3i^g{`b#^q|s}-XiW71f`z#TX7#A>kRyl+1;cjeZZEP&GAETo zXqx=7=R;7Z)v&Y54Sr<$lzp@B$EuD=Enr88*I>@xbE%`pK}!yjHr8-PTBt``gB~-v+XaGa-+E%mMR647K9FMmG-ole$eXtW(?(cwqEONg z(7yVgD z+Tb16JV@-v#6o7kR9;xE0?1Fx$oPMO5H4e!bJzN;zof;F943~5Ij&S%sMDAKy=mgd$8X*FBewKU$(^P(oe^5ra__Zpn`o^WKhWitc3f=^ zZ=H_kU>M;s;!Pu)p#zf*|-^5KaMK;Vo_GtZW52%42V~sIVRgb|2t0HoVD}eE@r2 z=Vm>hPq>FE6HJ)F{&|-!mZylaoka;>(*-aQ~qE=R)=AR%_Zh*#$8RuoG+o$ zGTj=Q5YHoPdGNz|5m_p)HoM~%v5)uRTm=FrJ%CSlAgONeycArssl5AOO9ixa2C)AU zr`U-;+06}8(FeR;DT@6oV{`d|^ydo3!0euI*KfOVISTK`;xi9(Utyk1Y!z8l`^pG$ z>=;|T3DqB*q@4Xv-+V^SSn&j55k$EouN5+42&soQSMyN<%|BGN{I_wy1o+kJlJ{{w zv9o=7S(V8rM=P&4H5&0}^^9gM57@J{Dk(Q_PS98caQu!Z{Jhax@>*@vr{K+bjNsXk z$gwBqBlWS<7ArBZ=^shO^Rv!T1LejLPITi@L(<3Qtr=0?cQ> zmQwbG7Z9_B@i!&@`qd69JRG=dL240BP-DvNo!A}+%%o3F#Q!$~NZbE7OiRI3a3u3f-l*nTw-g3qew z@Z5cmY0gusDGzE{v+aUcz;Ig?6?2aeoD{J-&<_71>RvX4CP%m z>)!QU@WlPQE^UM|tHkG{07|V?$;(6b$Xs}D`k z7XQZm_Scc^(Q}FSKbEM>?fbXHGlEqYWS0&%+c#+r8%xaD6|cW*CL3WT&!dS?M8g`s zE&UN(c(PTd+8_-XnSJY6_nfXAg)^{i>+;Xb2eS5b5M5&|x8R5NbytJ zOtYukJa8Nu4sGn;@zhSkEdewbcmfodjV8{GZd=he>TzuF#!06O6S4Ftpie)ye4Ph7 z4qZ69s(FIAWF3u3jSq<5-n;b`9n(eIb}$?qwq@Y{ianM12=xR38*+P{wz)9MfEJ9=IKS1`Af-os!PO|{7Wg0&> zu@w2Te^M4GIxJQ@!-S53rJURc1t`5p>}4vGcyc)CjRWDll5*ImPYR9uasfmY39Pb( zLlf>NNO?!Iq&3eF)^OnV=y`Arn`%|HjSgn+mSVx)hpoDt?vG2;m_#R->%KRSx zKoHy;P0t$7kO!~NH`^^Jh%Kt6VL*ai?LTzulBl68sWrh-@MM;B5Y_=uQQrIThq5W()rLE?F=w|vu0Q~+%?m1*t>&VYZ>%1ijM$l+3;x?J z_d12v>jo0|qU801|G!<>ZsX;APLZk!sOX|A@7JPXux#NQmtTA5>cln48aG&cg~5%^ z&$&#iZl}|%-%>@ryooQd%OLCeHpB6unDdFAQhbOK8x$Zt0+AwiJt@hmEA2q(+@wX> z7ye&eH)Ux`;m*q48BgZ146=vS)sVz$1!0tTZ(2a6x7|4Ta?ek552+{k*>{eUj>-m< zMGboZT|hp@d9JExn`W=BDQ?@SV}o@D%2V0saSQL!0cuwWxF6=vhGl{sCAjTO`cpgb zSumN_59VvZ?59fOCn_gH>&!R*Ee;i_CM&p&Yt3PIw1_oSf@PT6TOLvLRjhykpI<*` z1N*o5#vd@TGP@@`FV!?|hlRgfeL~cy&_PqWuVp-``~*cAN4Yg1{M-!R2SZHPS}9NU zARpuJuAJ4aURCxHtWbSWir1xqVrRk^&oW8*(SoP!g9)aAM5j-lQO-z}>{L-lkszxJ zgugH98ZKo~I!^iJBMW zu_k|A8)FlR?)_T=^@SM5Go&&IddhaO`z5W;$jgH$A8VX;UDC1mT7iX5m)@5DV?xUP z=v$(UrFy%7V*IU_#-P`oGj_^}S%u|)R}w-=hEhR5cz!9C(C3zc<6DuJjbHz7L(%Ta z$1~#e%hGQ_3!|kyUj>FY?gdRT#pArJ7w)x@7_fZuYWv%$A81mYzAXNPEqQ#d>H#8O zT#8b`#So2` zuF^DJXJN?+oa`3(2h}LTdSb$Ulj4y`Ig{O&7q|_KpyLkuCB|G^iH!UWO|Q;d?wP$u z9DT&orz)jUmQ3{Vc~LLzZ^|5{kRBd8NKB1=a^RF+i49jhtubkK=Iai*xX5r|&YYDR}5j765)b?kLK z3wU2rM(Q8UIgFrma>P3EWIVnZrsp7{+&EKmW6ybfrirP ztA=STCf6Hlz-vL_Q**`_%n$kb&_SY*eK&yd69pHR&EZDEcghJk=KN+v}8zv_*YRk)Gw z#Aou7mr!1dh=XkkH1adnV?rN`)Hhmv2C?bT2aplb1$u7iCWjdS9&o<5xJWoIA{?JV z31@^0DB%EcQIEKQ&Pn49&3Xt#T?GF#cq=Ln4qh)n2kT=V6feVb5fh6S4_-83A240T z#!pwzmW}?A_dl_~t`JIiPL>7UhI8ocGwH>{KVe)~x-_tjtYtKb`bSVB5-fb7 zhNh@+XYlHd1;N|G!h$nqrp{((->m-ZJhBmhO_Ck~E{4#a)w#P`V%J`E{Ugn4!9qv* zK$yDw(|0BHbN~3lOzS^{_if4?5+E?MuRqXQ1>9;G70U^N7oH-nuD08iO`aqlvCGpA zoGMdCR+08+a>qYX6f9(V&GZ_OPw_+IB1BAohj~6Z*M((9>&0qtQg0)JtRF{R0S^dU zF#MJY5*z=LHav(+)WT#6LJpq|1P&0W1j^>A0Q-$!Jg)q*R9sL!wP!7yhtgGtZ{Gz` zy8cbg)%f8fBRiSICaWa@e9Nn+8f?q~)#>;G^0kbRNM=2e^#da0hnlNP*w3DC1+qUL z>Nb2FY5uhOx)-CYs_4}WKr6fI;M5zA6S(oK(eokHp>)IQ67$e5QR_~!i zd^ZNbK1kk$F?iX3KVS-&ZSSIrzUrVt5F>K9CQ0%0^cSx6>i<0{g4r};B$n;*hJix) z3t1sEy0)_$b3eyBdT3ByvpDr@IV1|O4Ro}s%^+k`3aKNh zV(eY-zV|vlt!~Xh+M|ZOhCNlwq?U73=)Ik{Gx}Qio%er?gozWK83@fwOi;#Uc|l5P z$CZo}&KE$J6ud>*b*L9HWpi{Tlhy>2Nrz?QAe>J*t{Pq=apvT{YW{C}y!y=Nzr1Ov z``}0{)R(g-d9svEmXq+L!1>dsLs&763vC!%DlRSE;B~wK0Q7>2$QG6(IDFrdR4Wl_ z$P(k{J(;~5G^k7GTyIL!bx-N-eX{;Iu{xG0%nxRb@Nd2Pb~yTaSD(tpDaa8&1-=p} z-~v)lBz97qfPh0U56&^zsS~Eliwk&5N*zuV_yggzK)i31SIi5nrv5JHfnjN_&cJh= zLTd{jEk(;(2*w`136L#s|E(QQdjj7^Y$V$^w4lpnKq9Xcj?TL}C|Z{J6?{Kd>*ce8 zDz6h-&d51GYL|Px-Ee7N40>|$nOarov^#`!%^cv&izChl7J%#7*~&f!pZ!l=E6gvP z`!c<9mm(07P2LZiX31!qmRrR=zAd*VTfhH!VE3JJnIq|ZS=Uqb?NxuDt1_?Bsd-^_ zHPgDqO|Z>Y6n9Hd*HmQ@%FU_})Bj-ZiCoO;PUp{a?>!ca3&sUtnlvLm&y>l=kbZM` zF?UVxy*7*&Yu!fKI)~l0$%wC=w&iEgw?#H?)RV*58dwK5VI-w0C;UvHTj$OCV{@1? z3idcHlX+IYITfN(E z*Di2DhcPI>>lN(YKHhPPzv&|2#*NHZjaGU+geWHHWu;>w^&H$GKtA=MmsV5&m37_jVDjGWq;WHFt}KhR=^q$0rARFv}6Q%RDgBqHG*3ePEG zx53?MvYa#idaPCORW|6Ys{<3lw|rz0H3G9*o$5nS;Y8L%JU6y-TxvkCUJSc{>mgsK z!$!QrDVBxhbQIAvxww{PN{V6&Vr9ZVh*F8+5`p*Mjh>@K-2&|()8I))3qMU83Ebc+ z5yB&GttDme+-t3y9R@z?iIvQ!8`o+4t(E;;W7*}p`8G1}W87W6b?8MkG-k8wn0Q11 zMNHxdKCdqV!Dwr_WbCk(6NixA`@7x8{BqonPT@0$f|f!y#@|&|KJ=BIPXDa>Rm-hW zmhh+Dr{RVCzv*9^ep)jSJ!NqS`5d`jqP@Q^TaRtr^jiGIxSiY{ao~g>@d&oS z>=%Q4&q!cHr*-cI&0)0(K5pnfGScDWtkte}P;&16?ia0G^z$t?+>H938{Bfu+m3d2 z7a7^G@nE10T zOWU~|bNtdFpd^mxGs(_jWv1o~B6-8|+RiitT*leN=|mP^<2Z3mdA<50v#$WA@9UDu z^^2^*Qydt(W!visY)$q%LqkUmS0LW5nWif!M} zoin=(a-;ZqKeIZf;i7^clD(C`))e~gIPP3;Nq9xH0Mr5F8mGWnouWHp(6bG8O$TY) zU0}2-(L%b4$#+M=PUx?UD3VW>2`es-Z1`^XWq$Dgu=rZ~`hXEf(g^^F(-FW>u%*#} zKk;O41V@^9lKRS@ZwAA)e$KS{U2a*(q}Iv;mOLBK1u#%blt%Yi$P(tuBLRr4WA){4 z^l3Su~Il`ytoBNSuE+-wBcJd_zq4enCAJ>G5ZnQVQ4Pgqn`GG@Tu2i$t z*@ZKA(ukwLPztwnD2m9L+I-2pz&kW_>-XU-#CI6UGZc9vh`cv`4pS3aU9{;s%R3<; zoc)3O8fgjp5ENFLM>~#inkidv_c?fkHmYookMUY6U@Opejci6{aes@1RG0V?jh1B2 z3eX~ok;b>LHZ~$VWzz8HFhXrosrX4jH@(l`qANV> zaK$`QgEZn!-SL=rc(OvOh|HQCS#SwW&gVa>4{37pfUljd9BKEN63?Pf?JC?8C{XLQ z1Yg=?Dbgu0034^P=fUbWpO1Pha4SXCi<0Q&t*bj~Dd;``-gQP?o&t#1_L$>>99R=; zPP^+eKqEXX*9J}4pH)~UrUkW}om+K@0fbN6i`>_myXelZOFNE=c!OEwT>+R=HDneG z*&M+GefPVI`UR_N{6Ate%id6AryRC23Mqb?d20OB{$WY~w3tJUipDHsFdH+zj0gT& z>EcM%YHl0^`rG4pb}pAte2E)=_LdYHIGw}?f{j9FvI_RHw=4KFDN&>(cY&+O#Ixfe zRbpV6fw_ro5;`5OJB|cBCP*KK&<#+P2NOTaGHa2tY3U65a(C+e?`uo>E2 zEH?w$$$l;B#XU^f%Q(*Od0%G9q=K^2Wf;5^{CWz@3PmbCXc(H0kN^O$zxHDOT)IXrhlwU2(5EDkG*sY;u%hZAX4+0w%KiKs0Q|wKL79)-cCnI?K!5 zRuaKjL?VJPtlm&(rplYO6h6?%a@nr|E_sys+$kfAOsW|o+Zzr=O-f8hc}0Ls018B4 zfQacah5ujB5!b8`L)AE{AD%Xt2?xm0hkg8rAMX@350Rd$Zf<=e8Oo05)s74K#4`F# zwr6RadjN*zuQ}P-lpQa$e$S#vD=gw;7G$=gdYzJj?MEO_kNbC0!nZ*sQx!&Jsg=Ua zM9z}u@^oobRMZ4#S-+8}Jugvkd1p~PqvqkIz1C`WP@D60wmli6{aVYaO_C{#>7gN# z0|cD+AJZf``_DPqi%p%qFe-?SI%uKf{4UvuG$$;LyIH7xB`V@la}CR-AGYRGL!tt9?C$G zj^}KWU-suEMBf*bs`Lm*NO+AT$_oXO1glhH3v5qrgr}!J(l?IFJ8O%HDPM*j_#g|k zKBz5qGC3DU5de{4mVYBbnQ|P+1g>-zi?8ssz>>`&R6o-`OsN`@KHOt}0set352v<@ z`3px~?AJ$17>|x_hdrY~W-1z{ds$kJv`c^RKj1fIuYQ4}MZp_FcV&!HhB49|?W>(hxO3eBQ0vTC@Ks;}a^TH#=^i77iWVFhEP!7pl6t*JEY{G;bN}zc$MJ84K ztLI&E4!)P_cYZI_=o_oO93mVKfg(kL>WM@YLJ_Q*oj@#QOrkP&!xX=Yfws*#EGge_ zy4qLAhGeCDHs*u5wDFj9lD_paPW0Cq-7e*~yWGMD0cRl0(pnE7QoIljp?7bxSFU&2~?2(f!$`Q>wI`#NS=t;)N zoHL9Z)<`8{82>>kQjvU(t`xw~hGsXr4uRmEs`?o04xGvLySWIYYoZ2JvnLk#TcZgm zqB3-i0&sJY_N(%!O%whw7*yyC%ZOyv{~0NbQ)$F)Kq=R9_FR{X%z+aC!3u-C)~7#A zsnn*%VNXkIZU#U{c&|HA0_Yx3ubH^rAsbTv0WwGZ+@4mElFGArSIYOe*Ie5L(aPmu zStNThn;2bSRcIwyB?Z!m&llTA{0TFFs_gJx7n-N>sM=)Xs}33(DmHQMXaN8GzV3gC zdHY88;(X*+Y!dOuo6`l3iY7w{Z_h>Fi#?!fD|Hc2m>jxz0vF$#t3ODqKY;8Mfqu#O zIN8mK>2FRANn3#kL(<1PUebj215pT`DZ<&rsdl~bOsl(zjrKA(EJB8Wa~+bgjdb2y zDJXv8McT6#K<$oHTWO^6cAKC&JKy~H26qnkMOP#rI8T=>10}R^{Jb{k@^)gQZqE4J z2kTQ-!j^VpOA8#bXYV@g^Bf%tY;f23BkrIEFyOrk`8rWt`Jx;B~V}Tj( zFza{EW9_MltEqzn$i{!>8!X@ZOPP?@C3pdUV+Kk-kt% z|A4T3`4}C1Mtr!l*2Cjo!HXvC0=_3)_3#;EFij0{ni-` zLoXjn-`~IOvO6uv(X((i->75|*lEZUqix@4)m8JXmYolXpq^?yw%ilZ3E)?X47YH- zy2>>)%i^A0=W&2K?Hy~MnlBu?^3ePA_FpVrQvO^o2eR%yU*Bn%{b37#Z9~x};?>js zQ?S!5tAnaJ-&e=D0%&y}gJsGP=*VXP26bRF@fEd#95kNgK6RXg?bMSr?Ke9Twdp91_^zzDT?dk z2%D2?wO-V*Ize6Ncv63cn?4UvuV!eHFMF zj8#AuWR3+u$i0%%J{yHNzCgmTJY{-O#x+OqVBo}W2teY7Z$kkki@3wQ5kCS_eE8vF z-h3WZ&4H`1)+>G!gL@XdtI{uF{1N{)GoL@iAqHu9nGFcCU%TSy%soTM&~>NI&!NEI z4!WHz#u*kB1G%$j-Ns4Yn3_L1lb8T9lRq<>nCK~YG^Z?$r-#`+9i;&kW{`Ifp9g21b!tq+exxb%R8`ezFaQqM<-RKL60`YAowvn z%CV2HciIP@O$c2MX7QOt#AC*{2(ZSpuMykbN1apUgv-u)DdBqCp_$`5r~S4})1`io zREvD!9F=c0spM`vQ4mmh81=+IT<)u5NeE|vAg7e26_p$@Z?wkGZ%;~EZf3-Ht-8=} zqNZ0-{Vz1ye-Vo6^v@=ISjO+)kNzF19PEwuHOrrZAP*Ld7-f7d{S!Ii7UOc(Ec&rt z^oMfR&b_hRHa6>AT50qkU$B3F1=%;s+=US8E(_v(!@C-hwRz4^SvoY&}f zutH=c`NS>N5Y;aVhq&dfX1~jaH*5!gZ8ak+?GqZPX`A?YhgipUHQV<8m?@eIzFA^l zC;I0BvW}x_^^9$pZQt&?9Uwb?vl2Ufx!DB5V^TenSFIev_SV~U+u7Y;qMLd7g1_Y`uOAwDhTT_S?91TQ?xNJ-4AfM zd9LEm6=k*&!A$X3KrEUaGKz^1z{IPmwsUsH%}~}xrA0QkMg8~1qob6Lh6ZV+Guq#bln=c8 zuN{$ebT}`!&Nnn*SGewU?VjSCW|*Athiv=9a)0^E>c;&nj9w6>jtoC3-$%I(-?2XU z;`U&XnT~dF?ayp0Ox#)aq%E~6RGV5K<#W1QYkTI@4E=HI9I$UwGiY_56ALoMY`5n) zB?BNi|Dlx)yoTt5o4K7U2eCp1E-kwAwd{pZq%TBWP0b&rPQki82e_V)yRM-YIXmqg}2wnr32T^wjIF6?1=@iCLr9*!vTuo&QDCRR%;AHeC?`QD6m> zh85}VQg%T=LQ1+zI;5Lr5s~hY6i`Y51p(>q2I*!Okd9@kUHZGe@B97b4}0%3cg}fc z&Y4+Ml4Fe69C5c&n3Q+x0!GM6ey_^jw7Y6RqmUPn1Nj-~A|_X1Rd)gtp5+#515*92 z^nqNr6aoHGRs;{y|1BSW#t9E_PLH{{%*c9y-=D+@oJ{f5+g z$_r`f+P^()U-?NWdV-1!&eB=V!9!mr&PrTnS<_F#5yw9%&4)8^qK)Y`q!Mbo-!^l#6ClZ?{Gzf~lrYCrRz zHEb3BQhdMnXj{ylk3vOJWng4PH*)DiXsWKdzya@2|A;eHi)&zo4gK4jKM#(MHQtb~ zTi)x;x|&^U66Iavor*8cgya}`U3!dKHgI>ex?Fh98tE8_xOK=qK{y7y<>}sBsM(jm z_5tadQ9^=c@ru5;`?tL;FDwx~b=rQer^odi9~zWlXJ$%O!Xxy5l9e4v|_hmD2BdU1)vf%;V9 z%(3Pwae8q@@@jlhCX?){Pfh$T`e3VN zvaymmU1;*2TuB364buv{v1dIP@df1*X27HV0naf-a1@fzToj(z9Ku4dLd3w$VNmOA zl8vs9m04-9NqZ$xX*AORX(8zJpces0hqsn2yZ{GPt)ACrhCc0HPkpy^yBjZcTlMa> z+4U*5e)KxIl;%zyHpA?hBIa}I2<9lwtfSaw`PQLIy{N@#v{)y^wt1CDd}>dKu->56 z+ml|cBLKqJx6}CCn@nQ%eApyO2cHbVGH-48Oeua6TKtgRJ%qa<+NzJY#o+fZbq7S) zisf6KfLf26D__(#XmON>ZwgQZ9kT``CpT;J?yGp6J~w?Ck_fSXh9j^XZ>y)60)BYh zjjpm8CF#-I1YrRg%ZEhZ61$r`&(?9J1M_9+W6(Q()fNvAOdpZcY;nkV&-M30E1&V93a>ASZ=o#HIj%EAJ%DY8dTY4CteUwy|YIxH#1E9 z@*s<@@!Vk-d86xN2TKgn*QKr!Pax9xgH1%EqywA@x>r(fgNh5}#9qvKUYn;sr?1f2Pj|NEwyjc7P$(}=YBIO)UCp1RIK3Z#Mcf6Z zOg{__z7>!f8WMEe7&wiQ&L0WetU$rjrIG7ZuJrTv5`^%jij1@79TvYSGw1|LBaoOc z17Rh@q2W8WTu|MS<%x+PeMp&rGoyxi9EgY@Wugfi?e-04E?--o6AeO(3fQ0R&3%$= zJQ|^YQR6iCdJKQpH>Y`TZ_kns%*DqApu3O73Q@V9@BI7Ew4Vih7d#fsxs7!XT5oS$ zvBI}grMkh35Z^=d)*IYsqhPfC&4&Xt9gOra-wa$HG5Un_ecvuQ4(1vuM}PnbP?^}X60v{x_{KW> z-4(MW)XOuqNnX5?qu1fe6`SHxA9zW!bN;jBhkTsE&&+f)YaK&FLqN7&5VeB|Z#vz& z%-7W~J-$4D>n7lE%U6y&WzE@mVM|CJ96eh0i)CbD-+sFWperK^G@OBCj!8hlq z{M$X#Su^r(CAHA7sk}||U-iR_1Q$jO;`ps0G(G5U=sW&Vs0QR+&k41)G*&GKwvq8| zh2#Qo00Y&77ybpF=TYlLy>!n#w*Z?Ak^&paWAxy`gGFbbCa#v^^JNbO=+!DHDCFJJ zfzPh+TG_%WNoDkC%hez`0UYAoAKDf9t%t83`!^U9bQ$onVtNmD>2I2n>%Sd%wFrV< zk`#%pe)ZU>i~5XLuG7M+h~$&PHnjX!J#OBu$_`3KL+V28&N4nOeq>fm5(^xE+P;K& zuCy(^q{;3(CB`h!MSt>FDOqkZEHe;vT)~dN)St{MM${V8l$e$z+;z215+((|!de#c z>2b+?KHu|eUIE^epl-RTiH6&b%BlS$Gruh>DhBZUhx20`@W=f&U|rbf*3ad>>S!93 zR!aO<)7`c8vBr)TS&D^-<9M>$V1lm@WO@+O>Wk@cw&S{NJA96U4=QoGVleyQ4Zt`{ zd2`YnaK@QZp3f|-4f&DGq%HCq7JWUtI3ofe+u`kiY`yTKj z`Va(OygftRE_=fG0&e`61AfJR_JIn{?FOogBsNH7InYz~`WXcUH^PtC%`{mb`Us9< zquQ&?wmsBET5{_4R%3b{!bTyql=1|#Jez;}{5EZ%42oH9teITyt(R&+=uhcn{9rG= z_%Z8C^}wjc0~3!qKaNo;yfupu%JyIFFc;*I9unmCN9t!>a({Ay$@ZRi%S5gr%R_pdzp}e!58??0&q#M$Y}h)qe4GI=rDH87Gk3#a@KXwN z5XSUko6YTd*(3(V5@pmar^1(D=kxA(b*ZG8&3@!F0n1NWJVYK|%_8i>EkxYbx$`i? zyIeNFK9dj<<7CYXpc^_*i#j4jW8Y3747HL!@%;&n1h7|3sw7r*pnK zc^Qa)LRk}cwkF&{oIAIMy3;cAa;LKE{3pd&^$1G6T8D*a9K-fbnuh2F{OaSyb)uo% z?j@6ySP2T?f8F*PIeRgy&-8fSiptGo0C`{j{Kn|{=*3kFO#1xIU@vO3;CT`x4WN>J zREtQ2%iFwa+H;9vDbnGlBaWB4nwBx~pT?~DrprtMR`2n*kn;&u))HMP#L{G>iHSlW z5Rqz^QPbw}2?85iz&t7Oa03A-Pgqw2Dl9p9TTTg>11<-oVpYz}jR3N!$Wudq@$~Ph z+vOO@t*sLXMMxIrnkKfy2y>emLG2`hx^hd;&b?DH=eL&+f*;ezo_CkGnqnuj63hFP zYYE@hY^Ak}65&Ebp~ z;+6)HxyI=_JHdi##YC%5J;s+}8J&d6SmW|;x=QSv z4Ma|UL;G=;6gr;u$HNDkz=I<}E8>r)3S5FtV#QVKtsCQme&iJW%WnQ(a!Dd>>d!u7 z#b>c3+O2aEISm|Q!GEXPbNHcp2a4`Zzaj~Col_YL&h`i04|twl?td_-vLU8UB3$^9 zQAMX@sHvs38=d?2w31&^SsS>zhTcyWywCDFtwUJVB$(}{bXQ$S1YBu+MqlAU&s|$hPcLP?`|VnCsYcW9YdW?$~ae}u9a8_+o)wovRZ0b zJD${^JP{~_W3ovk%3Z)q=G(ZTpk$kMOY)3guSKuz&yZ&$Tf zZuCEG8*m*SU z!z%Xc-vQBv^50SayTphYK(pVtsvGrQTM6A5O^|{A!JKJ3*9rjJvGbvfGVVH zxya(X{Qz_6X_$ZB&)~GVjD1k7p-9f)Q~_)Nw@eR$1fDAs#GapiL#*?(Jj&H0p+l++ zr;$MUZwW0PmbF~1ClHv1THtZ#)>CNACs!&MRdQE1~%Xf44A zY-$W4()eTVic;|mJ5}RcNwBa04#d^7eacVDo7iWy?2A$-p%L;cTsI~dy7z)oif5>r z>iDu0)T+e$D@7SoRv*(@G*5rL^MYkQtbcT=;daJBYM$DHX>eQQ2r|X_+_@ibZxMZ@ zFe%|x{nXwH9pnD>L#-W_;gO`LuC|xY!(1|B=Sj()btGTl=9&v#vXs}hkFV%h)j=P% zcjof#uun0@kT?49BG7r6CI39O3-gBa4}|?eNt~p#-hEs$GvDZ~!Uf$Fd(E0dpEe9W zG2P>56diG(a(ogR?KcT#W-x4T)U~+gM0iK|H-Pegoi(jJ5U`nxC6Q8+OPG#Dygw!E zQ9v};$h6LgV{Q9>ZhCrYjkazdzVO_-14ETH9_$osCEOc(^zj2(AKG+rB4bjm=Iem| zU}sEbkIRFr1@RT~MR|$VbQlZvvU8$RTNHVT+msjB=~{m3=hEc{b32#P9%B|rw)`5sJ5<&6z0M!zq~UdX z)U(VaXJr|g>gsw|Isrp331hbX4s&~jBTxbZy*Kr12DlxY7b~D3@wZ(OVd&<6h)+dt z9Cx{diJF(PJ?2Z;3p||jfqct}3)^cyn+`{Oac1jaNqTWlkh~za2X?Aw>4X;>Nv{U+${6c3NN{{uwoMw0Fh#0CC^POYzU+b7Fa`0ieMMvT#Tymav}QlnG&U+EH$& zf(j_9I1Y~0{Wyp;=s1MfcNhBeXdmj9`^M&ImG$&3QU&A>RrEaDxa(?Eb{JP^mkN*gm8-CWP+PcfD{1~bF`4 zQPEfusnU^}VE+z=RjE^kzeP-`i0!*niw;#YFYJr&8e3Z{+BA6L*#@eu*NR&}l2O@m zCE4IXnV~A=wx|_qlDV3!j~SUdB3 zT%N%u|7ey;Ja5%W{h6vEC4VH2Cl~z=w?#^t?D=n(+hbmnNfZbKj4m!m*&d`Xc7}tL z2%s-d-(!KgvpAnvEotlRzMQa|$4v_zmT>&uQd}Wo_vwkRQXV{$|KNc&xazOJFWZ}z zMS*;QH#2_+AhPV3E~beCoEeWslbHSP##NJ_hq>yDcmrm#OJ=;onI zvuhcF?IeNPThEQ_tz6?Laj-HTe_9_4?_Jt3RR0g=GuoCeIU|V3W%E@Io6y=)G zSC^KO@?A71+Ti1sKUrVQpl5&_M*kVb*<+M^Pxu97+WC3~L_35DgZjpi<1+*sF+OvR z9)x!1tZhO;TDnXp>nQUnm&4~!yJ;>)f`s-Ig-b)>u)G(tGy z3-3P5mvC=NzVGrBcl>B0d=i)iW}W#W30bzu`_o*p>>p{u5OG7osv(HUV;|(PEAdL_ z49zB@0Gs{7XM;sQLuZEb@Q+f$V__c6q4#e@3}m%AE&Z}MPUNcPy~nY!aq*jKvpQKD zm$)gbwo;eWNT2Qb?qEJa{@Om2RIX8@&23<4mrL(MhG=&u(*l2`f}P{1oyrL`P_;9C z<)^Z|8SYDY-5%qcZ>>NKa9?6z;UaZB9((6<)V_Q}$Xv+&R~{Fn zm*i~TOeld1*=1%k;~PGjkVPhDr@!yolVu31^H1`T&C*hE^lmxg^6x@38?_*HL>0rY z?%)oiz1(V3l^1k?cXzl)QkgSof)l$;PVmtKnPeNZP0hF0t+gJ^i69Tc>IJdPygYUB zTWgjIBrT!qc?n< zE+*a*sK8AH>}Qd*7H^{3(&{m@T8^KCwAOGZI%Q~q!D~{DyCaRk}chenDp1%xQ;sQtDlSuam4NcO$Wj%<Z*vZ+lN{HHzZf0@7~V42RO(4-mED*HzT;p^|?9U zuU(4kholc|iN@f5!5poK*5@|2?32_-vt4&L*RHhJ9%t7^n)cUnaBH_e4K49p=|Nny2OZ5T0M#v+bPKbfKh1T0^gZOumn*!@XuG zHY{6h>NS1Vtjt_w>o2~r)?*CvSd`s_JcT*?NQOCnu^C-|`@F?*64gG<(|Bb*Wm~Ww zvOigfcxelqi*rizz3*!+N9sUR>4npX!Ba!+{<}4J>W1wF8sq8I@l(i(R@m(>P^Jo& z`ODWT7YQ|Y`N%;!de6Nw|AYxC)mDT&)T;b`(3lI`U6uT1MDkp?a>S%pY6M`&H70^6 z@ZK}FHsl3Ou+Pm$Z31@o!%;V7UUTov)K1LwzQ65!ei}Wk&F=;ME8aMqyD*IGX60rp zYvn#3x|vt-KBZWmYn^J^x9y+6j1#*-+I-5Ka0Q|ai=Z#=nRy1hv%U%9yTa+v8khgV0C+nmDX9F@ z@a+Y%ap_IAF_)I!znUg3dg=rMhGp8_q2`FioQ z9SnY(x70c<7_C6_LJyl0Xn&|s9_T3<{y`46W8Slj^M>P^QWn0iv@~L~^vyRbLnbdD z?KtYQ-YtK``@^CMKaJfpD?Q?pXJ%PWl2hY}T$FgFJl@*FjESc(j=0PR|H(D+i5rVG zF{tX6b4OS#LJ%GL#V+x9lV;#-xDm<;Z1$qvsy?E4y923Ew!@IUzwx`bOD-b%@iM0$ z+#fi~6Np`1dPW^hdl~>BJe9zzA(p`@@Bk86w1GjvkM@6@w$FXq^`ZYQU7hVH+Ml$xf5~3HEE6B-iMs`*T`}s zF<$m@F{50)&uKz;xgFAs-$aTmO`7Y--)*gQ`?*CqP@w^DO`yH97k4`OaGY*VErmH* z=TrLj99Gu+%fy*lW1S&imK&Tp!!o&2v?yV zN1{LddZ6G*nmnH*=<4SP5zirMx>SMbzqHH`asTLOVl>{R#eWVE4bq;NObpEtL_yb~ z7oY3V@N>B;<0TKZjyob~^M`X*i|{||^H8&c+U?OA=;B8}$Xkj)GOzYiZ^;c}%TtU+`)r1`) zEi*8ZVZxi4f=mz9+cu+IxNjlDm=oPi-NA{U$TQFW%VB7)4Wc0aw`b&mIbMqdP0aSA zNsi9V`LQv2C;F2Dbmyu^w<(ck%d~hQ)7zH_VW zRdPF|cFB9;HYBL2J^B_ik$W@PdZnCa~mzc+r7^l<-BPrVgR#~LdA;z9XZp6uBgJ6C2|HtZXk3CXqYVQIK_CDPI}gB7EK zCWxtB&sR)x@4q6;KHKL>nlXjqR9ohYFRlF^Wfw6$z)als(i}EF-`ONtD(%l% zE(?qv`pmO|Hq z6+OELn=Ei&wUv+EY!bwpl&yb-;n9mowdIv#i2z}^306Qr;9cmYDG~X2(<7E>_v8C+ z@9xiynD+UX^EkqnbYV0_uzna z#|jUpa)MKhg^>;23`0Mkk^QK{TUiUP)n`cSUherC1st7doIs0y*-G?oyHi8OV(P=6 zMt}k$=IGSq3lLN%({)A<&eJ8ZgofPIX@sjhshgQzDP}mx9iC_$`>Q^zaaHdP?q*ei zkfBpLw^&G4FXN~m$m?(+VE2LaEo5Mf#%v7$FECl|P+ft#a9_b7A$aOq9;we6^j;{DbN~PAA@O+e{^;T?~oucKpJUMg^|g_THsnK~%xc{iXVz zdB0OK%)A{h7Hh6SxMW&B!>X+NTMw1vetLKoBnfl5pReh3;7rr1 z24fD8q*tbW++-}iK<7bJ-)ULfOU2#_8DO6{Gn{qNj{MJ)&tP9-r8m1AO-YBD@E?tJ z{xKemj-Lz=6r6d5uD2SCjrAhjH|36=mM(Q}J_%^pE;6#LehlERjwu^N3G0*#dADr1*cuyK* zy|dN(bRYFhPt|Eh{Q<~>xdfGX`?FP(bQm8+GEY7{-qsRlT2CsV>_1N$%T)qPkycBh z4Pwt(tv#kILUu6yGW-ZEK0PnCcuDAtodhH+3pKHflu!|(;g!Qr`)+uFg`MeN_0 z;e9?Wf6V9 zhyKXjcOiEdY97eF;Qshj1Q>5$TVzLJk0w-R@tP${=J>5A{`=gwncrwmV8o^mF&yRc z2TO_;5KXJ5Vko!Z6rAzTOY3-xTe*;6pwVsG5_09XCGHrZSpVg}(QbiekrL*Zs!R$5 z@ifT8n%al!UcaVD%OY(RkD5(jHHrE2gfML`c>{Rs)7-a0WhV z;K<>RT7t8F{jCgKGyx_5s>4GI6>jD*?iBY=4yGQPe(pFm{=DChU7S5wOfNnC_pgn4 zy5F*s*b3!d2ODv98&!+`xch~(#rIE1$=wO;$|2s;ngmv*(OmT1vm7#KH-oapnF%<@ zjw;c772=XBso4NcU~4s!D;9L~;rvgLO2+uk#y_|wj^`ue_PB*m7dkd@IE9cyzfO~1 zKoXOS^ydHYEK!>c^5)$|5po#zwHt*T!hveC01P2q%jb;d9G)%%5mz{MT9{kQENgqTPvp7AUqH@TTeALH9+g4g#;K55HT)PEh>#~ zGu%07qm5ePeUwMoLPV^NAR3xHsbdgIG(@E)hB~bpt}eEeS;$C^RvO7;{|$SFiXeK4 ze89mCmcQU?Ju%cG%AhDjYj}qA#J?Zi*%0jLwaazyd>7I5(6@AChc%Mz)kwgh^YXIg zfQ4r=O5iW=liNp30ewfa9wJwzB|pLld^HRJ0nU7>9?G#DByn3%(4}Vvd8iUE_9ak6 z3zab+fsL($md;2Hu-mJe4`+&~1_Q;Ue*pIyRy$O_SMdk0WvFIIYQQzwD~%i|WS*_! z?XCsae?ICyCTsYNs(4SF=xCt*XA1V8=iqqFe#GfP!xZ?EuYh6j>;CF}xD-g1<+!k? z9Je*rPT<*X&VJc~@TUb&>+okeKN|xd-!M1_p!?_t#=_iB`Bf*^Lc4+h?N0E0K#j0% zcqlv4`sK?VodA&!D7>7YgLC;Vm#$Ns%y5UksWwdJ9pLu#FF0`z4Wz@~Ki$vqElrti z)8@u!v2Mp2Z**;XTdInTV60wKP?R&46nmk*9#?D?86E#NFIgq`XA7-R+)uH3p4v0u zeE68Thc6}rehKIqY0MHJ%LTKiv&=QULn9+O!uI2TKPWY+QrxnslF{$+EJ$QvR5q6K zD?MaP$hl$8EkBDu8fDBmErFM**^>P~8_0F;$NLJa6pKf^FCWQ0%ndv8oVx(k<)k+HGq*&53i5)ul8n%%pS1H}x{LHvm%JI*W2aI&%eEh#^KaO=+k zde|2ms8iN&MP~-B7Px9&ojt_GO*kzaQtEc{WRh3pbBjrVEskbj0{F-OTw@Bbf{L;g zyFE;6Lcz!k;#^;DxU8I;4hFe`x^_x$&g8PLe_JVd@^)~>P{mKzSScR6JHVNkUJ3~~vz5Lv`G_BcP9P=`S5kZs6Q zgX67CJ+r`1M&@&UgXL!Mi|ucKce4va%X#x22R8?Hq=d}C`Reck;{V%oV3I@lmH_Pg z-eYWjURsss(hf#aP*^y2(MA|rFR&o&|7`jmuUT2J`d^Q#>a7_Clc0$P?-&0GrTn!< zKdZ8Ewp}fBAtHyvAlK;QR+KcctSf{vbu_ki^KB$h50bxV(2}V3pB{bJFb&uTvFwII zBm4pa73#Ur?d@&XD#CAn4Zk3JMEUqYe`p+?RcE%EgfVi?Z4_P5s}FsB$h*<&1&U8T z68Qa2CS&BS57SHvTX)UBn&bQ1pe z8YqTx^*RX=%aDM5OX}B}nq+JraokTkZNHfDlOmlyjw+;1x@tyX#cA~Ci?u%4SDzSpfwQTZ|OyaI%=RimPwU7I#W zv!4b)>fkP25v0Q-80j;$=)7Rx`WufBqcyj+9tj&k>wy&D3lJl61(d4q6A}~0>W-bF zrmJk#uL6P(L@~xYOURjogoHa%A2xCnw^(E2RqWT3DU`~793^*;^VLTc@Is4!SBk%R zeLr4I3i2=Ue?I8$I{e6O7r7#~D>&EOm)nL_e+h?v^TxBQ)rqOcOD+(gobS4VQE|sV zPQYiePr&ve?jwHl5{GF&(4V=ZU$FzxR8KkJ3Y_;Rw z3cseq0s*~YTvF3e((p9sEou@fTUqJ7&;7R35jRWJ`PtQ6mCaW*K0d#5V+TMl69k5?0vimVxAPKe%(#G@F~se90{`(}O3B+r zkE>(HTB%#ujhz1El~pBf`*SFsRMMUHL^@j0WmodIZA|h!Kqw59gMMw`cj9c-{-!pA$vH`Po)0vtW z-;Kg3bYhxJQC>OvfDi-lOf9<}2fxtO@4k3j^Q>hC=+W3%&CkptGw%wGj*K`TM1FME zoUqcfQ&LU2R-#8@$munCK0#j;GbVoncr)JzkMrb&t|PU@8!HkTVMYF?5B#21) zTj96-D$Cb{QA$PEVL5C0J!;*0_`U^!5oJ&E%$n`A-I87ZMfc^&Hjo^w|N4aBlgM8s zC53#(nI6d!8x02DzQ1rWuSreJA>g2{b(*Vm?~bAtvUzlK(L2MmCn%~pG?BJv$)s2I zSs)O9`1WQF^hMAab6~P_p_0x|>gDnv6g<*QY~LolQqsF-2-PA zMO{ql$u@N{7g;YiYg=~zVUHH_p|?V$!LO*YdEYwTqNTL~h<$%76H6}lPtsl!4Aq*W z+f4v}vQ;55gCv!`yuW|LJOaoWyAoXbQ^0Ud0^5H&g~>^6eN}z;^e2gJ_;%;Qh(_o; zz!1cL{Q2|ee^FgTMpga(UeaS0Rvfc0_|ajr+2hFAdJ&@?_z5mO)mhh5STv{>GFYz)1O^HfZKpMn2&x#`^QJH&GVh)jTySisAN8}jcUu;?Ky}umW#LFi(Qvp2;pAiHsd4!RXb(X{V zj{H%;s7K73<6xS{mfM=2jgVM0ITNZS>u~CPrv9dVGwrZ*7`R1V?Y@r}sBg;rhvHPk z$pr7ZqDyEhyX2cx+Imj{+-eLR9%m0S_@NgMctma)iZ7+fO0HQ!)FDz~{_g=b5v0wy=8_+$3#eL zf%mIF=w3URCIP)8dH)|&__1Id@KiukwFvPmX{{Y@*#XCiEvYZJR;0Nv>ZL9GCw+f& zP);)ph8sZxv|)eUKeoEBmkhk-qRMX=crbQ<%oo1laI{uh_S}1qsxSWjv6L&4#m4I= zF}HEuqjFVzQ1_5mG!{$eYpx3RQXfnT<_HZY}O~;4xhxoQc-bj!N7_j2w9hCQe zF@FvCt&CT!09J`sGqOuU=##Zl32*88C_oAwgcr3kQ87h8!HjJk*G-F~$gzSnqEL2ujey;+c zRlL%y6+TI*{lNcy{K^!VVFe_Y8cJ*`8~>U#g!YhK(h6|fF&2*jzQ~aJM?-YO5tHv9 z0UCNO$RD6kzqg_&u1k*}#Og`$_%YH*+)LaEFa`0mJVE?DW1Tu6(gSOan>=^zOJGzC z{@$(t?Bvrpf?)5j_Dssh(u8sm0m0$!>|D0i2AI3HawYu)TVgjEg4KE|;pcgTS!ppiFO(R7H&k_}Q zR}V>>nvq63QHfHkiX9q%0DRP8FVIiE+Z*sZSe|UrN$O8dJ??dF=XJRx1l!ZfghW^p z>T^a8>M&wTe^59`Du2~8QGy%=lePl5kZda8dt+~J9~0virtMd9Gz)+Su$=`bo-Iy@ ziO&|MoW{LQ$?bTP@6nr*mgX^{L;JnrEt_}Cp^Srr*^{85bb%-Qnq8`s)8@19o*s6L z&E@f2k@7dc6>T|p2jb>4zK@mzm_yvT89IS=d+kO1V1++nyStaR>%=(k121R_ znW0~o%9nbMIz@_juK8|24q9gBPXZJ{@84*E#)!-*CkdPG`UA)~^C_8l_6h8?SN7R2 z`Y#0H+>5Js`d@aFHhq!pyD1gj((acRZ`u43+J`~Dl>t*8m8bV)ymPgM!{ONkYNdk= z^ehNkh`*wbT_S4Nq`M{z_57c=@Rs$3-gUL$SRvDHJ4f_a>$aQdl z(%>J8*73sM3H7h81{glw+5!AQ5%bGHFiXjTBmEsK6*>H?_orp%%AK>fk$Z}iPeIxz z_c-C!B5RHjLBQl*iO0TEI)L*l`;*M^x2}Cb$Uh}0J$C1clE?5d9jLVFduVSmoBDWY z^ajYCgDv1>+*&z0DrzJLqFnK-ySqE?)Pl*wJ(c$f6CqfhjCIJs=6#0Foytf&i#E%b z@R!i)$E{$3a=52gTM?p4MEwujI6~97Ni6deXhHu3MxdA+Uf986gZ8nUGg{~h=Q75J z4sT>AIDdV8Jo-d6brvaVlH*2AWd_O!iccGT$8i%7M-hi2rV+k>Ic9vH&iv?a|12)^ z1D>qIZ;ZDuWj^KP#NobYytDm5;82lMe3sUkM9vGJgpO+X^NNSkKK?J?2U`<>8qgF_ z16teIbQp;*IUxD@#nN#-$IZeWluo71b<+zHC7J2FI2NDDAp?Yiu*TT3Jg$wdNb+wS zoVroR^!(;`=+jeEi*nZ4N414vAfrLRjMYgv29U$m->uwk%-r?+rXy|ZE z#Sf>L&^)kJ*~7@BxDK}INbt%P{u;U3$)+9vbNFiyU*$v$tQTnhflfTunOdyu3A6IQ z5{RqIQ5HWueZ&CcHV+Bkd=PK|xPvk;cWfP3unfOQKGCqSvZ`s(ker>vc8uy4nU*xO zyO1L8Rrjs#Y-uUdP?U`=YI}I-DJ^IZc*eritD}ngBt>_q_ke|H-g&|4B=_P9c7)ul z%>_wsz5^OPSmGZ8jj1J^a;?$3F)wY()EKI(@$m4(OW8*!iMueczn)>;W{J&Mmb40N z9N8=ZEVj{M#!Enc!Kzm zZ8x}eLHqO7!nc60zUlk-t-i!gYU&IiWN01hzSfAod12>@zgm^}BANs&ao6WdSd;6D zqE#3*e^PnRV!emo{HHsQq$CV#pv?iN^O)6^O*3B{);md$Hd`I09Go|ZotB&CDsJ4J zSIO&dx*vJ0VHXq2<4)sU;q<#9*rO7<{qrd-O)DIG1X(fqBvQbW7Uc|7920{^Sq#iR zYopwHAsh8>c?vQ*&pU85a*F?HwVD{mb%vjDLtkpvwI>PC zed98WdQyt~<};wC!K1r26Fk~)N(9xvOWR!^EVgKTSD7O&j0#I{AP@MAkUu%_4Kc^~ zQm^YVVmh#|2q8&;%HIvsGx?b$F1RxvBF4$X#e3A^(1TZc(CA{w4;==UazCjy9t-Qw z_{0%4_-8SEBX9k8!;bL%XN&dwbMLsH*Vde^kB>Js8Dmvrsi3R%n>8zaQBg-9yY6)% zYW5Af_^g?ZFQOAizXn$4<=vHE{nmcbX{3^5+*IrP7Jl>Rs{uwO`{HP@a4U=eFtUYj zLWb&$RQE$4ZUUNoQJ(|4OyMEocVpPd>kCYXk;z%)BGWHAr~u>Fk<)k>eCm{RX3amx z3@BRChK(k3HlpoJy>b0=xpS2>A)WW&3eTYjEo?`qZuDM2g_~Ov#)6Xq-Rd>x(fC>q z<~gGv1$jzjxA}^MA7JJ7?s8rhY3 zE^0+Nx$=Nh$6|H%>ZLJ;*|!Y8feTT?MSgD1%r1)&r}a?hF7#?;7W{cH?zd6g5aZ;1 zA^o+-m5EE!q-PIZDsuOlG>)2qts^xLS_n23S$thN?hTl{d4og-m{{Mn65h@epk#2< zJF(?tM&x%I(Icwfs$VD7-5aCNwI2QLvmlt{D)h~4%)%^p~WBYSE+iOL(_`{_?;eDH-^jHh%I^@re^BROEI3_&7#KujI`1?6?2a zP6BoonFW3%@Ixd7U&3*46_1c1IyyQr;N4PF=!TTJ^zG^HOfJDVi=_u&Gu#dECw7Jt zd;9<`wm@2L&<`m`$OCYft35VHEIPwV27`BTuMUgZa$9`SdHjMi_ePq%Nuc-t#sFeZ zAw+8scvwozC*~s3$$8Uk&GQ$&RE6E<$ck+EtNI(S95bdo?_iH^rNdpvf(Lu_w2 zwk+WFWLU&hLXR=S_rXYp_z9h3RKM{c#4sOHTk3AD5ICR0q1Av6o0itB)WCnj%+UUd z;)`u&(mrz?DeEJ}@JNbsZeBzGk!ByaCwyKwC1=J;#5vF?+O z*R+m-K)0<`9+nsM5j5kecKxd+?ECMRF!hw-Cv&Mwf@_soZjb<9-Zzkv3LN9|tfAg< zvYpN6hWAICh7XpSJ##UP&z_+Z)&VWqLp1`iD~a@jz4i~D}IxBA4U{O z;5UpT#fXo`mry1hf-}2~4Cv~Wk7rQDx=y?54{iq7N#_=^i%t!3aw3@Wd0wGP2Bz3Y zAD`otbRs)Q;{erNXqMk`&0{6=guq7?Zk~okug^P1#}gkh9u=}@gw>M_*)&gLNe?aO z&EN9|u(tDBi3F`M2CL>7ile9wqj#6SfjI9dmgIjCohp(h{jQQg$1 zsZS0?kkUW+wxOD9KKG)nKX%01+*Fn`{%aNhr9O%l3w>37 zlqaVj{QyyejSq?k!iH)W=}oYPY7h~H=pjsB8v&1q$deZw>|TcHABWQKfd7xNw~mUs zi`s^7ML|iGZV{zJB$W~o5Rh)^k`4h0XJDkIrMnxElJ4%1ZiWV_VW=7UJ>1^Ude-{Z z_r2@mKNc*Q-}#-h&))mG_H|_m3n|YSMl;;y*W{NeeF0`(L1D>&#N=dZpk!YAy85^! zt~1jVav&K1Gb9}@04%uE)6;jKzZ$lt7Bbk_(m@G6&I;zGGelNTfFjV-yXAsev#kYH z+xu9@8o!KsE&hqO2;0LF!MHYHa?Om9-i!L(;7EaLwzlX&)f$PA90;xO+&v7k|CLFA z7cbIcqo{A1Uu|uDPmt|y9Pir|sm`!~fTbB>*KcC{)`ZmdLT_{^-&C&H)O`-(G;M@F zU&X7H`oqPp_D>V8Cc4JW7@zi)GPBSLOOG?FTvZHBal$KWdFVK(sQrpv1a7#~H?ZdQ z3{ojNciEVim+%nrX`V*jyWx9}CN6m#>!bZ!myR8Eu0^J{Lu(HyOFwPakV@U6EM=pU z1vx`O8yPVheb}Xim@kW}M_#2l%T%j`7&~gwJamMhR^d&k)rq7#j{;gxqVsNCRfxQYiwMy3P#f_K~_r&F*^hZdauy|04 ziWRg;$wofo6k4jnls0-RVn@-056zjf>WhMKX)4Io9L7`~DvM<1b(ILw0W~ye81Fl5 zTa~j_gOI27|4*XCeXXoW+Ty&v(SraZOJp#rX0FVGgJ9+XUG0-=8vgaphqD;9lW%rw zqQp6~KKQXDhg6V`zi}TFM$HlGFyU3p>FLpoJaYA!eE5Q0@WY!ox2H|=Z>=fj`@SOM zE?a&Mh-f_#L3s1GsV$PQ2xtl&I>5ww2IrG_dgd^me4aXB3X{y@^5oGs9oAJ(a4C>$ z7TtqE0{ao9Wy6!54^weAe4{?&i$9krZhj(gJHAu`tjy#;v|>*QCw|(&8`0`(4X>Sk zm6ZV~(yMpsI|_ebDyvbNO9flr_%z}4LjAFq0+-UOM9z*ttHsrByFb5pJT##OUu% z>aW;|8FwTfZcFN8Q>W+9V}Kjq*CLxYmdnxE8`Q3fAuk?&i`S>{mLrj}s3qgJ;p>f5 zd%A{D*|-Ss+1=Qi0k=BqhQ?KhEP z7-AQ9*kjU-<$D|lUiB9LV}Bge@fE;6(4uMMYd3d1=Hy``!dL5|Vb7C<$6D+VKUx2| zL@`PIhY7!fzF3J684euz927y>jnf}((ql*C;gs2tEvrrE6<+kh z>7|F$r+U-^L=6KqFio}EZf49T0?8%*CQ~U58Rlx*+Riz$ym*Ixq3$hA5eVh$$;#+; z6L+&IsM@YqK_1_yyBLqy{!20m6EH9NEc=-@G=mHeIYJapq0{I8en{dI#auvGy#0zU zQT{`zBm%-RC#lBCH;3%o;Qt3px9~Y_{$5X!KjfFSGUb*&qjE3!BD2OaQu)Uvm@zS@ zvzSmnYG{o>Ie0d^^72F{dT|*H5O2Ct)kWj@{Kf`F^i8wZ6>8v+^L$69*B@KBZNkTc zGo{Nq4^_qv9hoDaj2#B-yTjQ__@!l)Sj4`k^dhRJ zuo+n^QI>~6uQ8+d(Q9I4?PxNvuO5X?Ar)1F#gmC?iLW|AGGx&+5<7vSM`JX;*IRw4R~A`!wVq1kxYdK^hM zp)dXB49CSXJP77DCfen0%zHM^8J?KN87vCUKm1 z1(T1C{EN}c1#vpC(vN38c*u&R@mo(=?U34PaIHF;{UuXyUz=m7qqPE3+8P&xtN{y^ zGkXlwo+YgBq&!PcCmkCO2bKq>yZ45_Qz4UGEJ1tQ)CSKrG(1C7#<|u8^}U{yS;_lO zwOmvm-SR!_K$0}+{d3edP%Wx9>7lQ4Cf+r);lF4zGlw9xj&?l2WaQU}x7Vy*{=K3S ziIVzc@{nS-OH2L|eANj<%6m%Pn8ZWG#N$S$)sj@>L&SeSllCtj5`IKrNKodT_>As~ z{ql;_`C1bxHrPj$J%>=B|NJkrSSs?MTc;dg-8^P1xmH^leeJ$cI?Pgwz*uePgrg`^ zlS znB&`j)BjF<0Oy(au_IRHHuW;)o(c#fRcueRPSiTOewBWDs(;a|HE9{cSb?) zy6wPSvEDegoujQ0RNmex%||GEB;jV1ALS$N_b%&CAn{wt5$eXaXAmYyS#XDSP3cS+ zxz_vTck65i=wS!pb=!)6lpuzj;vaOSuN2ZfS$m_hw4u-sv)=t+2tp6&7B*kCVQ ze^Pf@!!8re@PIZg`T*@4)};GUc22*B{#{TKcr$=krbYIO{G~PLywLF@jzbTQ$4s_N z``kF?m%cE;neOVlkQ5H@=8Plqf7WoG*Uo3L4;nE=Of$8=bz7W}LB@ntY-pAh#%sP~ zoG@Jxr4SWrOoIqvrCN~o=g76Iw6b&bMW26};go0y5QCgRU=n5gcpf4v5G$ny7h<`} zqpuTltja?!zGJYROOc$lL}n)au|owJ8J2F&uL{t!crzgKa?ripA?{~Vv3guXa*>oY z3^WH5xy|HRlbbs2bkTXs;@86bT8{rqeBrM}EOGbn$A|hxt8IJU<*WVNbPV+L9l&^z zipYFICSboPzkK-XQ@)8rUe42+0JY8~WW?`#j1`(4&iR0hs#1?Rif|(eDEel+#i#n` zJ?e_jlOxF~u3H0(YyuBPxWs+AU~JW6gJ5AfQ-r%nIO*3ssoU8DiGPE@uM&G6?j1?q z>-zfoz=OcoX?)GslM~I{=hr*GW-mNoeLAUrOXtu=AhSC@Ha3Q7Ig}zidBft-rv6Kd zF@5zhKD@)^*by)O^GiJwIAM4Vy4}uAw;iQfwnl^gExwH^;YD@mS0xtF35K7%Q)BkM zGB16jMjUjv#8|gTKw@$&Lc}JJj*vN|#GsbPBN<(2%RvtfK4Ipc^XsnrTD4VZ8{w+F zT91WT-xbcGlH@<75#O^^z5ricG`g~ug5?{%4?=!u4A(=VqhK$*#1BM2PMLmaRP0{s=dHDu}c&3~Sw?mlW~HrThZ3 z+P}Mkjb+oaF}HF(=bzg7w{l8vI%L|iQ-5>RQ>72mk`o+IcK^M7Q!8(cVUvE>>ba%zkkTMUVOLFW&DYw2K(IwAXzgD z_c%V8MW5|f!H%n%u61`}KZqE3_Lm4cu5S+4gl_0d{9#(-bvsx z{(_^EdbT{2z*~novz7p4AA>1)3-}Zr_jYR3d0-)M3grKVbrWIt0{&v_WHCK)9TGY* z$M(7^G5f9x@rb5Yu!Lpq)6r5M_0md=nkGNfepTVAn`}w!#5K}r(l~PJph#Qh%XX+P z@ueTp@<`lDDMGd5OXT9l`Z};nItjENtaI@7aw_&9paSTw>1-;5CVn2MaR4WxE1>NA zT>{0D8}R&0A|}gJ60otVPjk^Kw>@V-Va@oI0doij%sO*_3^nt-85Hf<=%aYW0~zZE z8?<|Sj}v9H%aZOKMmU0v?+KEUw@Ts?TN+RPtyt?L3=g6|?q98gg zU6F`%qE+>I%_E0DbZ?v>bM>d1PpZ}#Zr9EevCBUojg98!P-h@gn*aU#T3s7Fj1XPUP#FCPL10rLpkXr zX58f5!t%1Axc?6{IXSs!!fR=1>7kT{N;_C%&iV@%nnGcq_BQv@(Q7hSqj-Um-=^Lv z9QFmzQGktOLC|Tcr2CMT*D`7-*6VPHBH}I@7|B|79{xbu89@2N`j7Wb+*ulrX1Skj zI|e<_eVAu>uc(U`qTXwojckW}n$J)A!2c7DGoO%%1M*D%Gqs3NnFr+LI**T4>Pb=6 z?|zz676exafgP5NwjrA*6r*G^Lhj8yHhjNKigMkoT;NkBxB4*bTo#U>_GjM{A(s>f zQ+rW`Xgn{-VEJy9v`WNVsUrbM|4I?nGx<$;1Qsh4M4xyIU5D5q=D9CwSzD`Qx~{Z0K8+nu;S( zKa@W+!4{#T=OULyX%;OS^E%aCD{dIyA5BQa-*g5hcF4~3-w_E+=aH|PpY6t0*0-vU zJWRg~9K9i$FX~)9eOg*iSS89V=d+SurgJpwQ%lFn5Pw2>meaCv))4T74|R@EQC7yx z$|fcv`X`rlreb@$xT(^MzewluOhF$pSBkN@*j*=e1;3^|9J;<3g5xl_W>c;}L<5Dc z|BU9Q3PURWv_As>VARFQE?_4C7*b>Z0_a~nyf+3S*V)${=0mLBD#+xXahKj;cmLaq zr8a+z4**a0Du)iA)mPim?$mCiKuYM1?LV?>tl`m$ywL>;@OrVFP576WXU82F{$XT# zZO`>%=-MXE5VKXqm|U5fupGEC?2pyokm$uZMg8n!3rnr#%2I8da~LZs>luG&6!ht8 z^)`($bOy#*FfkYWx{9(U|4t{%Hk^jatG$J%8PC`K-y*LS0QC^$P(mk)3KktI)v{)}iCrSU*7 zZ50&2K#YT3cZmZ2%kgh<$HPNLAmD<;sw(M3)8tsx90>0>q~5!3cz1}H{;}arFv!fz ze1B{#jqZ4)BM3kvC)wz+(_)>{+nEwuN!JIaeTJ)4dtWy+Q~haEu5Q30@zrcD9aj;;?u$6&L-)7E{0f*(X=c! zvP{E+v=CP%HSw6KZ?X5$aUPt5>armjj??sT`I&$3`q{jdV3Rbu)XWwp zw7@=~l(oxVeRd!JtwD6>|0|(n;Td8!x*j*?eN`y0Pz*s8^n{1m!=cP>*(V@MfwKcE zm~(9}+=YPhRiQ?4%JlS0?i>aE3d`|cVpar@KR2;EXu3$fzA?KOy)mPW;O+9><(ZCRk$6- z4E*Lp=Cbc}qzKn}D%-*vsVA|}dNye%CR$qiXK79FZ%gzKQr*A&!}W3^XW)_3Vahz@ zYgS{)im&;E{w9h^FZ1QiqR}C_MpML)wAhWz!PQYtRk_GlvWjY0RU&Dv1kEPf@!#Sg?F$PVZzgHVzf1=kRL&}FtL(;EQSrCrK z%sOhh;}H${8Ac8}VQ}6RY*HW{lLtO`oMj6FX<)h>`Vb5eYI<;wx`!$5$Vo|E#mbi zRAwrV!Id`_o`}bVYg&%;KxWv;1&V|eBdCv>J7@6pS9txnjIZHq)!lmr#9=`F0PW_j z1+|rXx!;g>{PM$>EFcF`_pqtjl~c5m#QtdnUaFyF>6N%vJ?4JHf0>Av-N7PmsG-_q z+5>!ycs*#n*!!VpESm?4A=LtZ+Pa`A#*j2=Z?lD!J;;Y9QVAF=p|*i1&e zUX|5R9N9H*8Y0`JX1JbR4}<6*zGwK$gfFsH`T6Yo=LNwojfVnyIBlU))bmd0%_b@4 zW2nc+f#fFpTYiR6AVLoIhJOms!_Rg>EFc;W@$rqJh#1;54}xFEpROBd`K_Jpr+F+@Y>5oEv_WMLtJ#SX&6@Wmc2TrEZKM`enhc-gK+ z=4I`EL93L{8);r#({QH#xI}H)4h-Np)o~@t80dJ-G4V(|A|&NX)+F`iXb&7~tIWIW zi~sU|L?=N2E&V`^4LO`!RczqdtNEN2_Nt?j80w=5`#`JQe^67kl(g_J^unEj{99xA zWN;M)VYy=mPrq#Rg=qC!DzL_2Wwp|2fSs@%!Q1W(X#7q&|2N?h^SBzxmK_$M6uRyY zrmOHwC@5fg<#1dO-5jM}sHwDZF0&{(pGQb^l(FZ-o^J& zm==T%1H^)`Om+n#j8^dKFx7>`m#@eOx);_qaFd}|cs7^DhQ+7&ES?S}Ha&ij$Ih`J zdpI=MlaDv1p7JU>DXDOfwmC2`FqB3(B0M|*o$284T!cT|)V%bk0wC4)sN^(f-K?qD}=+QTR zGUG4HYVtj8`3)l7#EsUxpR&C(GxBYT_7W)YS%XXg z{(ZkhSp(({c5&Q*PXrAr&b>)zy$wy%S?C@%5f?AEWXKxg8ZFq@0A1UHUtd+RUZcCl zf8@bsK@6_inQuFS!2BN;269kdS2bORkQ&wno}x6NwsEJqIXSmOaK|RBr)$AIn0tyL zRd;=LUh3B&tb!>0xPv5W>ZUpbWz)KxOwOrj89F@Sby`M-S2luMNoiTkMew!!0mI1I zoIC0puymT3!0ZgA4oV~U4o5^|6%PR!W~=WAo{6m|FVIbsmU*9nbP1#SMDpC~ zw(()7#IGx|)V{+~p4q>7s6xR`XQtaU-l}^Ul*8f#mk>?;2VH#*mY0+fW8x{IO_gmK z)>pbAAOGU+=|YKNThS0BsEW0@`NoIe=lvwI<58CgCJW5J5P8H;}9~<({V4@~cG_`V@wIBGTM4FO3l%UHD1Hs)WdP$}3<4 z!!EROdbnGtnervnn2bU42yS1BF+x3<*A-sulvj1&@HTSkmutvH!0S0zNs<$Lz^4>( zKQll6=NR~gsb$R1bgC!Q()p`@u8j+ z4Iijdl`zMK~HoK!K6kMunL z>TU>SKaU)~zdZ2ZmZTzR{f$9`tA<{fOous@Q%vu|#uj|n>xVNYw z@TCS0Of95kEmoVJ9$h6F85t-zEY;8b>U2ZUWlz=r?xAosK}Snh7_G)|LL$YcGX`JF zE1jdACkz?RH=wiE+GzPbrNUmz-==MymOLJ=7jb-KX?-cuB{*6G`kH~y9*wuU`QNEb zm-$RImNP??m(!*oFF%+X$LYCBPTg6nq^r(XO&bD}Gm#x1vQy{c& ze%QBIVq7K257Dx_0VcH??~MbO6oo($S0x=CDhEvoiMu+MpW?g`HuJT_U8?HptujAS zXXnO1)>yKH7|#ApByU`eLBYsYO0p#89-X}$kt8M3E^{|d_Rlc-^HNuPyZtVWP zWdOba4P6l)-7KQCtkmSAEO`z)71HIOPK9It-%S`Tvx*d;eNQF|}hmi>Z{c93ILr#3{dRML!ooahJK|#tqJAkfI zc_puOhO`;7z-P-$arW3*S-sVqptg=G81uHx44POu_#{a_JPKKZkG$`Ux0I;1d_{-N zn0}&^4({HNyJ49TL2Tk3I5D^LnOznRob`Imh>po(7@zVe1S3i@rCV%dQmG2VePS^c zB7tsh$_(-Hr}w{HqRFU_^e?aaIAPbPcA}(D9|#tAA3+ZUs#UIr!(h1 z!k8rUN0}B2HhpecUQQxae14wzS8+e;h}6hj96`yxgfVJ0O2P}}ig`|AL~2^O_RWSY zr#5J{@xh=aiCWlyv-zW{=pGTyb@+<4{6~e4Nqa0x&8!-Y8ZOLiVLvclYPF;1wncAL zDD32+-k~G~!tV>Ml><$&7wr3V`u1F(8DKba{L&1mc8!NBM5n%3b8uOxttfI0ae=#& zViJ*(3Z4yr!GVk$0<0tG0tk=g-I5LdC3s8O>TmM4+}*_%bEJ?L-1S;ns^6lJ_lf%M zH%I0>QXjK_;ooHOH6Oz+H;#F;oFs$fHsb_ETJ291{rh{L-L1PoD23W$LsRooA!;9- zrg4I{^2b8aDXnLPP&VCKqszH%p?}pg8UvhlY&)O1XAozD5qJJ?h-o7y(?Qck_cyhm zBXXrbHWXqbaVFdZ55oY$%G*t z7?TIAYu;H0GjPbOY0zDkLg&p(I>UlFjd`as)4RF(42u|6V)A=~Dw@hqg_7K&(I~n&2?E|$k*ODxu*=Eg)EmkLKV4(M ze}VzA{|W|_y~8V76VKVm5Ww4O?=sP@?=x+}3eEIkn-51+FhGhXw>xfaPZqoEcmZ{` zsHmv8xNpTkoR;jAkQz!b(;v?*{yzyb+YfpPlJ?G5T0y$P*$%`Ov+bT+2qE>05ndpB zkpbm^eK(rpsMxLAD7Zt4f{%!GtxWyYdF-Q}#*741C7^$|N5f^*aj?d!QRq`?nQgvr zbl3T(&P)7R&CisaX9owsKh99k<9ZgCAxYX+WSH<)y}5 z9w5l?FGnhf^z`;(U(NmZepogNHuH5jf_~bU89|EqV(*zK-Vc+d@xhe_M<1ROqPPd_ zN9fzjx*{fYG-nrn4A4{mB=$svW2N7Toe!h|z11a=$qi$-6W4WO=+UW)H3F7a4!hdW z)?7AqZJ|xSoNc;1AM!Z)wVv=VF9uF;urS*CIpoYU@sA8Fy=v4BUw>Ajc zCv4kL!HaAFPM2+8heXb;SG&Wq=HRb}6>ugTbKL26hj3=3SNu^EGjeof1Qe?}0>;32z<2+f%`3PoJh=6b@2Kf@ z9GCHjnSaB!1yaJ+H0uk|6QgGfKq?`!9{sL?6cOVAQJ3bW+j8X~DVh%=?wpGuPsO=o z{Xwy>zu$-JalU=Owp{~gQ4x5Kc}cm3B$tq;142QdFlbHhwff?CJ^&(Hr~!U>`yidT z-{Nzrs|MhOzVa1J6SSV?IsQ)+SbjBIurCj!XNRz`#Sr!hU!UZbB_6~ggZ^Jrbb_1U)dO;GAoT?nhEDN zQK(-fX{yA~9_VP_CZoGI6vO>D97wm+0JVPFRT3HxO%v}Qr)+C9zSfFJ9VMlZKK}QW zY}*@w=sA*Z)SF5SEd0giX=Zd-0=*eOb!uL$mQV{i|0ctNdrM@hdOUulR{nR}0Rac2 z9zi+G^x8K_qS)k(YM5hoD7*sX#qlVSSK%^2Hr`K)YRxu1U|AqnlF z9)0tdzVOX1!M3#uyCD8WOBwl2dlyc=;R_Kb_{v9}6E<1vyn~QjW5fIC*SOAhhR3{} zVYIZK2%tZGdA-x%X0Iw0!~bf}1Ci))_!*TL2NP*&Z@=JrNXT{A$(~nR04a99|CFTk z-Q)HrJ@3AfGohuqZM zgvE7S$QOsJFZu1FV{X$&>#UaMKeJoVH=SomK3mhWSnrKyO-xK|0%qgD{PmaiNnFTV zjK+(DJ~=tL=OJ`HZ_>Y60P{yKO3>CL4q4sUxbd>v54>TXBVAQ6*zYspTOeJ-2VB23 zGPwxJiD8;FKON?L-(&V2t2;Tr7BpQM(ccsMo=LA-X$B5{-rGz+ntDEOW2C0)rb?CO zO7&_e{)sGy6RF2%wc}1a$|oxJw6B<}e@kI(afv=~0boVa08?toL5KV0zqA3INM7sH zcXA_(JPNK(15rdO)t{mx*!;o>MLA8a1f5KI<`AnaFeJTPlZ2c)@bwd&|F{6l1aL43=~v9_U91 z8?I+I)J%EJmWi-9i#a&)_?ff5f;*L0sl3u3UVl(KpNv);AE)H2XZW@tK1{Q_RQ20> z#nU<+smRi1p9x+2q<<5b6xbW@U&|_Rh%&Sur`-!ucqEELk9t3?fG5i- z*aFe>-S+gzd<4C8Et7O8E<+rD4B+Se9r1PfGnvfWlK3un`ugaDd`~*_f;4`W&SG+o zt4yR|XRMRL*y69bz-fnkSC;B$PBmr4xhr{F?C)kA5tqaR3&QB}B!o?x@|WK)#23Eu zqi)Td(lB)Z=_yEr_UZb&Ued#}(a}+>0;?(3J&(hI-Px+9GNX<>rA)}-v77Zp(oWLp zAq)WVF(5z`>jLFrG!kBXY;}R47KuYY_85{{(2<#a;G>XmoaH`*1zH;lXLx<3`<0fL z=`E5DO$(#?nf$W#`JOR%i2TC@7#NNC{AAR#EUUtss)fKgMAoeA z&r^PyJ4sNFmgHNS&79W})#8pUvFIloyrR6aZQVF&2%%)MM$nO+H81$vjL`@+)4909 zdmz*(*c%Qi3eMg2~IL@yV@hBBFu;vpsDq`AZ z(_4#EqPF24xtg4@VKX&QC&iW|nFyV27oQItitFi|cf!wF7cAvjwl9lNkOciZ9mHCwhwL7`Gd2e?c3AR~h@oU?qMpImwN2L~Y z#g5)vOv#29Dm-1s?u|9-rv5qg4=T1I#uQ6;0A&>Z_lO5R#QqUfUHHNN@qy=~vYZcf zUoXg>;1U1SqG)2h5K)dWxfGr(nk|d6sdad*G&E>S8`Z=6jf0F3#MQRbRrl|OP z`0STsnRxpC?(ENe3ya}x{aU9#JEk~Rj}4J2*{rQgGu&5%VIh%BI2lgOuh*GlKQ7lNS~4WZf>WgI@jgpx6?Me?Hk=qeK7L#WEt8Tx>?Rm`*Spx`dOS3_ z?&DAbH)L=Jjk=WnalWgx>vdV?fY->j-v@f~ic$tfoG@K4KKzGr!EQbiX`158h?dZWQV~ zCZOR#1Joeh;Q51lJ7C~#JLcyGT(IlG%#5u+qIj^~Z3V4RGb|F3m>@wvak; zGD{Pg+)@ahDl{&IXoPwnxxnRmS1L_e2tr{Z}?>N7^1b3OlU{StFS z0U~@X@#Ev;=t!1PWfehv*kyVZncyo|1K3A(lJ})Hkb$j=;AGv#xu}E$8MHtCLA;>z zZvVTXwC%%@gSB8*y&8vS^upBpLD)FnPiWDg5(M)I_U=IK) zcU|^oqGM9zC$F>DI7^SY`;UT;q#CXWSy`rj-1$)S(d6UN>c$TwAtf9uMz}q<3_`+S zq1jr<80`Kr@JC_--J#>%KW&eo%2IE*VmxRJ7gvOx&M&izx*mD|-J0Rlc296Wm)K;`YL?|KjphRC5JcgBTnjj2S2(h5jk=!pHKZeGpoZ`t z5j8b%kHq9;;qtV!oE*PTwP$Vq*T9}zpofxx=4wgw%6{`_6&W>^sO`n|nZC5>#S%eN z^!t6W7aI`KJ?JHf^7Wrq{PGkY)w->$UtwT@rmdhs^ViMeFm`fEY~M={7zsV3_l$f^ z&;i|jdWawFO8dLNdj&W)L%f|X_#hjqNEvjAS2URFv-Yo*4zMb znMQ+Yjm7qe(r03!r!+aZf|J*PQzO<4joKD9S8VK-zCRepmFX|4A95YFDC;cctCbd1 z#PUR!w0H0R3e6XX29s6#XN+%90xP3PNEpN4)KT^J3eWd_nTXq0?|P-b=dhUt%9tjZ z^Z8!oEy#z!MMO5-$lA>Ex5zHJMqY|*yO{+R^eUOvMTv^Ekfqsn4E6rg$T;9S{c>7M zEBMHiG>qo56^A>9P3w`l#pz+{^+m4e^(n~6L5I4asOT&gc9u&k)CO=brqP2%?M{>j z%L?k1Hf-=p{Qu%@8XEd7j5Q<;)!&57vZ@lhI?8 zQRLr*;jILf15m?7}{B5^36ql)(uVk&>w2#+hs zTHWiEV_Sy*4A$iAw%e~X%>P8k$7`vn6y&AoIg=Per@eBrlAE*HAOQGaE7Ka7BU(Ir z-bjk?%c;QW9j$0FlyyOH7ak$6!%~YYJn??fV|C~*W}f8!r(MRQ#2W34PS$ zH|b?Lyz-}MU&*m&8eSnk()dm+ia$fVeop>QMa?RxTZ#tPFp{65$q{J(M4Vy6I1t6R zwx)S>UnTJE=S_8p=wi+aB+xn|`0i-VIvI-z;oF|q5r|@3)(}XnOH_Ve>x;eK0Id_S zdIyIGm}>+HLT(O*hQrr4R}1WNQwm&&7h$3oU0`fH?D$p8V z(n|${S?M=SWO~;#R^?a5U18iL{K%S(S{>0;=BL4!ZQplh9(r-m0Qm_2Ny?r>M{l`c zgoJMDuMGa2$&D$$yEV?i)6GLbTNNMl9x89L_O z-~%#w7d)Az=bnGEv*Oy1aHqvIvMgO2$lAJ$Q-m*Dvgue50sF!>@F2Or3-*tw5}sna zr%#l4faC*&4=`(bdTX4D5X=iF{XRpm4EzFW1 z8{O7W)2H_V--4{^binT8U{a4{3b2s`Z{Fjqhw*vHVS|~+KPSR@6O79!j|123;s-Uj zq?~$L+$X!UUESkjGcz2MoFDzx`;!IVbnl0^b+DgfVbEg|(BN)hpX+VDm_`x3?`|+{ zGW|Nl<-JsM_;mdq*861wiWdSg+f9FtC&}52<3KqLuIW;p)X@Ftx@vbAk4OE!T2bzD zVunCa03L7NV;tTo#e@x+77|L#Km48OGviC)VG)fkM90(r2}{QB95R0s0ir4wb(mN_ z4vp5AspyKd-|6qXvXHYK3U6F&^#@V!)s%2TSz)5awpu+QB_$I4N={KCSp_jV{*xS9FI zATl!IDO`4IHkPRPUQ;Qx>8|o*Q9~UwgP`SXs8CzG%o#&YvNNHFqNIgJI>P5vi-QYpeTjU{X@)V@^H^_s-O#p z_VuyVBoLP&hJ*9MC?F!j2+jS4dFGJ+$Io~!@)(6{;;zSo2xu0jsbLI49D{^v9a8%4 z?^+d70uz^~Y_H9PMhttfp8ipyN`7mM`z42}J>?OID}YNKvDm3Jl{P3hWsidw{9*1> z`*(_}Kk*I^thQJoupin;Za0rTBJ1SyMFrRuVR6I7;!^6>Q5P2M>afk<0N!`i!aYPK z*t-jX8F9Ok>WW;4!7eh*TTdQrA&?hy_!FtAsYB)_OjL7?#t?aD{GX|x$^p4UoFHf; zR}@h=RT-AL=;;PQ?G3ye$~tZ;TaGqh8k?Kz??Y)BFqP8Hh0^7kWep#b^V{wWQ1Z7f zDVmx-`$`ger*V?V-5rRZP~K@|oETF7?B#zK6M)4jDKYYgT_Lm(Z$nJ+XyV9MHCHOR z@(qAb^m9TRfL1~0H~Z}oR7rIj%PgppviZEkokQ#7eGpQx3<)BP7HlZ!zE$^m8d1s2L}q5%3f2MA^MCh*xgD(5xp;%n0xZ9; zg3MB0dR&;Rs1ROX37`K8iscZ?!A$Pjg@|5$V%$y@aerkjRxi=c++T)(-dtZW~kY5;116HpvT`~oP_fq~e>Fm%b1oZLW0!l$oX z-#OG~KYian}iTaYJQ#VHD1aq`9--KJ{x&C>Ha8{3cD%BwPj5GmYT#+zGjAN~4vX)Y$g&S4LiY0qS?n;wqt2RfhE1uk27C^T3l;4WE)=jbfEVNDX}cv2+n|N?Giw7M@+*b=!sn_#(F5tkN1s7~ zEi23Ty0KIAqD}N-5lhh(!Fjb+e6;}zl&7LZL|iqr#a$z(XJBi6us7A%uq&^9&(WN`;X}Fk!|R_N z+nH!b`r@IQ&R@7NkQ{uz#5lx!aWXYA(fQC@%y|^*Ydq6S9M*Vc+!=g70JAabDb;#S zgTv(xa{5=uzx9w$d#oH!#=cw?`mvKj&&>B4ip_rFfLgE3Y{d#UwkH3Ev2sy<=f4d> zuPe!m!?$|NVvf-!{LcsM!^xIk5KT(zDfkbVUli@13+y^ZzT-@+){T{@Y-TsEixSu} zqtL)9i{SIudQ=FGFE@A2HxJ8thZn}~tp=#mjV7%U1F=e``lQH6JSc)Q#SL>xZ#XYQ zLG<)!N&E#?hP%R;Hi+akH8t6$Yieq0YuTn?j$zl~(wexVBU4sZmTt6?@FF-kcv&eE zG^_kbaE%Lhq;*r3ZvQgS^z^+4&%B9JEF#0At!AroilUo8=>%;az$0xOO)E@k)pbrj z6>Z25nW_$LM+@_jP0CIjYZ}G;%_1W{lR4QvAXLJl)_5kec+5lnW>rZG&&Rr2Go%wi zCYn$1bn^hByrZlUgcWKSpjv^uK<8^y@PV4BB0KQ*xI4%p;{kCdE=kOt&vma8h$r;* z^?4rO|FOV*eK2(Gu+-|d>Pq0cBy8e-84-Cy??qjl0kj-uiIf{{;VqY0IWgNbI#@20EmriJQHE2&rLYo)aq{}Bcz zZ*;`Dm}o_c-oH&|I+3^oRp@5UN3ly`!&z_BeJsu3j`nsPt*>FjqV&F9Kgbq`qR;T2 zpz5PamX_{nHPXXbzq~SuB4Jnf@O1m@NJ~WI_qBk2Er*D#SKnh;$iJgA`KWBY+rJqt z@OY4-=Sn7HDNzw=sR?C=ox4I?W{St(V;FhMnS$*EBNabslSN_(M=a*H?5SViZswkR zb)~A>5q>J!)k*7d-gJ$+hFou-N8D*$#9$lp-+0Ezc#UQh{d0N@#)7Y%RtVkS64H{G zv^*9aR?>)CiyRnJ7C>-Ar$jZL+H@1?#&<9z)#w>Pg7@4tV7B!Engsvd|K*a*wlX~y zNt)bUJe+u{4-L&VUunkG5vj!81DLz+5_isIE~h3sg36R_oMY3P@xd$)`|*Q6?)gdM zECrQw`P%#Ri%Fxp=y)x1MVLa#%fgiYm-FHoUkPZSKWLOSQ=VgCm)$$95pMDTn0u-` zG;%N|N0_P5S}DL(W?|p(r>TBmM9D4QShpfS{<}el{4+vX4isTT;UO>RpVnQ0eBPD0 zvtRs#@p-5C6N|Tl`Cai#N?7%NZ|qB0k{ts(5qI=8Ptc;n`WO}5uGTX{HH6&#-47|6 z3v}|Pkf}eD=Bd$IWw1T*qs;>>{Bf0hYNW95@ylb)luPoXq1~mM)hkA_@2_C4h@ty2IBsBl)y&&3emmE zL@b^TA($H@K$v=Ud1L9r9ItZ$(Hnj-06+SBmaxGm8|y_2k$tW0XTXb4i8Zsl$4dKL z*!zIz?E~6L8)mS8C>8%_{*W?6!wdy=uR!?h}qTIfh-x zPJ;K@cT%&AIbN-8RR{2J%-C}~+~1}2_^hH2Mp%9)qx19gr>8-|7^>S~Ii4_X6<4TB z?<=f{CvG+I{&v3Vbt+pI3(M6N`26u^DW{+u%1!wfpP@+QdMW$LIsCv=j5C8uAN$K^ zx4D!@&HihexJqV?OsM`M`=SWlDi$?@PstymnPwhX@2}_TlAlN5ubMr$Tlmc{u zwmXHXYXpm^SaGnZnTn7=!2P@VoO3;YxS4%+2#`xa{~ed7_2sl;#Td^ddYHRUlePYV zs6%SKfrz$q_a*yyRsms!rqAag`s5n8uqcZ8Kcqm9v+QA*Wwt}t4>yya4_j1%oy#81 zIkSfm9!lP_r)Uy~?)+@oi&P}e{FrM~CwHb7^EO0zkwYz1WtIGKAo1%PCx~L?Y*$y5 z4(;>Td3F9Ay0USnR4X*u){2KZ(@Jui{7jX>myV_$MgN=A8s;4+I#DpBQ|uy%+6EkP zEvjFb#3e3eo7_=y8f1Ncb^}s0x<@kk5JcMvQaViH>kCCwx+Gezs64M^sx`eI8xjx? zB^jLf`_%1KY_v;)c;jqrCDsyH$N))8#bAO~ln-Sr+&BnNttr5Pt^OZc74J(YSV-Kn zg|XMNw*$pL2w z_=QQB@4ZW+Cb1d~F%}bz|Da<|cLdJgC5O|A9Eb#(puBrq3@4Vm5GmLm zYoZW$YHr6-xmZ~k7O4B3iaLaDCUB6SdUrQ!P^0{%&;Mxk6aD{%D9b7l9ZY{?Nj<)J zz&*ahYCE4QWf9)zcjSnVKK7z{+d4oI+l7wU3!s3;Xme?E5G&lR^5M`pId-B_Yq%>7 z*=uOZm`i9R`vI;`>J$_DAJ*VgtW9sw@67zcb9Zby1PT9L%K`4OOWoIbazgA5B@*b+WW=X z`>egs+Ut1vN-r;*^E!b~`m zX8XIJqypFI0`%vQe#BFy%We44=|cz7yN;kh3=E7roCrR&z|?ysb@lo79m5hL0Tll> zem7F|;GEr~^-1=#!!sxH8zrSCR2l{b&?ZmX-90WZ2cUEJ+Q~Rf8jdGU-Ab+YYZyLF z`PT;b;y{IXIlVLPgakAPoj%iN(3Q@(>-Nxr!O*|GpGRE$?pY==$31mqTKUI(aAroN zBGvS8x-;0K6fy)`tt>2V@vsIC1l6t~DaN==*pz;LNxx^gbTB6n6HzxfX8CRSL+04|a^doE3 zO9P+`R1EI>T+0qSS0^V(AVklme}=U>`4bEzb%zv?a5-R#sNd&Q}iIxVr<;Wt`??t(}P?X^^0*NooN_^Xu*HtwU+9pCk>{2!JNJ=4fjJIx7H0A=NAmM$Q)i8tY{oZ-K>6;oDr694^uHz^7EhJ`D3X=4M!#NpQL zF72k_Php|aO=02Zu3Yf43=V|dxyX%j?1D`D#S>!gd`A$$DTs*a$IkU_k?(SQYYW|d z=5%XG<`DG2oi0KDzdz`!7=?Qg)6btT5ZBl2j&H>!B#Bu~(j_A6btt5*g)->C69-#E9aE_GG@iQd){Z;w>)-uQ={atD z9=<;vm<-@`i4fK1h>Zw;%>g`e)szp-OY(uUD+-ieLp0&1v)01Qs_&arwsqx(htk^C z2Iftq(`vlX2LszuRk*7BW?|V+b`n|gqL@OvyO2aJ_^xbJpS0Y0z!0dS21~x)`V7X; zHPO+9VqmBpro+N?G z;OF%k`LG_s;CR?__uAY4nVX0R5CjkWz4*`NUb?!{tJU-j{NOSFftnH@ucY*AWgxkp zpM6^0RJAmWf@PN$t%~} z(-pb%?R(Uj&+b=NX?y6vfDWb`)Gg%V{>2>98T4|pL_JCzEOT9DYB zO3-Y0_#aXkM{jL(|90F@mBiWJiR&@eKH8Jh!4AXIo3NHtSpR@!M$jzbVllZx6V1lg z*|tANuK#%(H*Bi1$N~Jdic~c*>hz;Ghn&+ZU)o8&t+U78Yu>3jSy=4rE}{i(_?ij7d6~$Jeoaxg;0XV#=@rIcyP1FWe$}0DUde}uA{O` zVqG^s@Src`&_u%u3uZMmH3z>F)!W($WWR3IqrH7YR*=qEp8^yk;F&l9L_GfPUNu|f z{*sIPB3)&5^*+dI9nT-P++T3L7*Mb7NdTGx9yE3T&duZ|r5hQz;rkw4<5x=j+spi!vF)A> zdqNHPtH1%Byogp8A485>?m;PHHc>X!SeUA%61L4|HM+lPgywJ@$7~U;+l?dgI68$2 zR;4wyVv9-QQ5FsjCuXN7BU_0)gfpr`6I-72S{;Ngm-6wVM-wJg{Qw)AU)z$C z&Bn-?;aFrORhL{D$&ZFR)6N3Rg>vCeUguLPGA*QY(w>3dtTP6+%7~)-iGEdJIrNGS zu5k^YJtZI{K=s2Jx~iRLz^-$-EO)-8p`n=`%lxR>Zs+=x#5p+hV5Te@OX|ucoO4aq?r|f z(deZ~!hxdQv(=2< zzJTE?(ID-aCr-pK*S?IdCJvg%Ey0GKeO`lgOc5DrUC;FFjeU=5nN6W!mV?p z9Ce`5q0kdp)V!=V&SpkIkl3VeIi#(|KShlJ|5EIC;1Kr!%=Ts>^Y2YMwuJ+Oydf&f zgEz|hc@}kFLdF+-${&*Hy!pT6R(^#KI+1i8D(8O?aSyQ_7*bqF$jG-Io2Fk2mu5ty zBG8<<=m=ePjJh8WxW_*#&=<;0OM8Xm(#<}*{H*TLaid4@?e9`<*39;Be=f~?nv#a+ z=)Tov^8%AOg@sAO--B`KMbsYF>dz-G{JHg%851C3bad@Zx#k;IqKg&?BDpGY)JGtk zE7xaMzb`L;&t|=}jBs{UsNCq+I8|4*lW{cdQXK>urEOQi@Fn%mXC@s066S~swQ(;# zzEo6{_XCicQ<*}^@h8_{9NLgOmj}_aKSi~=?F!#tk70gdg3W5#Mtb#nbpD^OEh3tx z&R9kkr)RiSXhbAvH{X+v?Mvw+(q9Ko>WyPmT=CGL_1at8+s3JXY8zBpEdNLj_rlFV z_#aPhy2@0k?Zh))U^(V6QTp?*ICx>MbH8?_lan2i|IjDoO6T_tT0Exz&9=^_*qDoH z&e2kIWm4+xb&4a4LOD-DH@~QfDb2fg$Bw;*hOo`1*j+k46#Qim&EI3lLX+mQStn=A z$N1=oCF+fFaX(O%^nn>6grMA_J2Ev1bH+Cz_$Kq5I4LUi8l$E4 ze%0-r9efBnnu(*<{o}JMOd32KRKya^dc*~3ibS>wCCo-Ze{P>aD;%Ubu;Y&G_Rn&D z{wzC#g40sryLL;H!=<%c$A-k6+S$p=vkL*ya3Y10!39^y^x{&x=yvt^xOs`U`z}*G zm-8-FW(~;HOFKJoilz%uO0_UDQ>!q*AUpZEerVOqJAZpS2AdDXj6MI{doOcHx7G18 zqi;yJAAVo-PEE_h1fF!#O`y59&h+%+(B9 zWKpQ94scm*ea7Ylz{u`{QOdlM!;RxRg0vLL(Vv;daNypvJE~tAnt7>xVIk;ICCQG{ zv9egr`y_Rf2=QQiqBmszN1mhDP|HkEV8{W+E|DgMj#e+?)S*gGO-_x&?bnh{XvPfu z?KkWA9bT(ta$Sx^Pf1A$kn{3xMN7~h9n8W)=OsU8t!v=1F&H#{&Z8`Kgoj^``_cXG zCbC@=Px%8V!#d7*-In1YoOj#1f5yfN9y{A1-2*#e>o?>-M#DSJb_KDGGGJD~+Y{EL z26Po(QcSd_9G50QnRs~}1`A0mBQ-^Vjq3IF=gU zrE!Iu+v{_t;?6yY?2h5$ToZ1C>FivO*Lh>#w#3vsO!)~lWlofZ!a6{3UmQKBx7i58 zW72^kQv(73_m_(ibi1{GkkfVW*d=x+50*_e zpra#*@vcVzgDqyg_DgA}AOBS-Q`6m7cj?N(J>eq=D=RA$*%%a4dNW|!llDv;oodSN zlc`jaS4v7sqD0PPsn%XPmib({UUBfxVq5T{fPow#Fp&Rqx30{#muMV`y*`Mlr;2)I z3~sB@L&>x8C){!hV*;6@Y{b=bc-ow}qk*IAdE;6)e?t3Ox9UmYWjhBN+UO{AwIy_5v-eK|gNP<6 z?*@cF|JU5`75;{{%a*35)7#~%23Q+#X z>?mV`SGx_~eiKx9hnMd)a4DK{3N;T~zKyO)Y}@7L#;hu;-`CmQRK?M$UpYck0jfJO zF`>oS;pRnp_Roe(iu)gseP@GjYR$OLF;K6W%^aRWa8uo`sQ=^@6hO++v>;u8V0Q;F z5GXOR{7KGoT)<8D5xBUH6U*~UJPCjW$E;I^FMUrJf+Z|Pyu?+vN~mPie=Hi(!_4Bg z0TUOGI(U1wClRFN1Dy&hx6d=3Tu`sEkCAL5cJd31J(Z-b$zP5($Bbsljxx7Q%I!WD zgXz_y!QhI^dt`bu%*Y+NbHf7bJ+lunVK)KpaIMI3CpAYrsQ zJJAnGzRhLwLajgx4g!qP2j|n=fP=vV<{W$hItI}bqkk)N+q*Y>4s0j*H0fFAzlA`` z?@~Mq2ZPtSv1+=>F7%EX-HeujwdDX?h(_`nD{Qu0n-^?z0&tl) z4n#~@UFHw!^N*|E1!z#0zYaw1H5M*+ZOE&@p>okK2lhT+SfdAR^Yd?h^Zl&YPd4La z-O38xM^?p`v^HnPchY4dm~!n)Fvp-YGz>c~wQ{Bp2FBo_)e8%*Yk_XP0)x;;a$4=;hO1KnRh zQZk-wLi6j_)!xZxFi?IOV4tq{Cpy8M7~Srk!^&3DwT!OMKV+Y5oe+bN#?TAP@*U>9 z^5+xH%WE%ptHOx6yK)AQ!ig9eCn3C3r7}Jf>SI$llMc@~DjP}pGpNO4a2Syu9?D?DI+WH;QwCoe zPOxrpc;um}?<;kO_=$GM@TzDnh!^jvzoSY=2Y9A}Pkq}nnnetE3{gv~s$#LyL7qV4 z`HbJB24pY!7jy}~#D5#Vsq&!qXu6={?TJ~yH^*h07qgwXx2%p&ks+d7AfKn)=_Fy) z(5s=ji@0ajckoxK*wh6*t8iiJELa-A47wMIdejyc|e~2^j{wpLbPC z3+llRi2>Acc}>y=NO+O^b=Y3z9Y`&dh)--=dX?A!?xl97lZ_xb@nU~r<)^ODXv6?7 zH0A!`>ezwwJb;_kQ7ctCOgTdz1lB8=&?D|sOS1XIN}GJ>Ozqp$C<%gs_~d9HNsE5g za`)bbk3En~c)ykz&hsVw!Al1WS=lJ9S`iov&F-46Ibd`1{|%T)J!y8csYmiJ!pe8h zMZvx4cG@r%%UV^HP>upv0`BJz+(f;bVVw-}&edS6_VCI9#KUd2u6E(`?6UAJuu{y{ zY&X3zc^tvfzS1IGY1{mY=Cx&J{BsGijuja5e0xyE^IeTz50l;z*xm{N>*3EEd0abE zRKyGAI&5z^_q98f;jZ2E`R039(Ajz3>dPxAfq6!4VJ@Mf?YOK;LK%p{TiRj8g12g~ z-9Qn?;dQAVvZDzKze@MDK+HXLQolhenS{-f;mfP(^K;Mu{;0`{r^jCOOEsQWx$T6t zhS$Npep0i+`LOq8vx!Pzi{Xt4Wd6LG%UmAr5X^g7)7RhL2)>(>z=91K7NKlcQo+3a z(RtL|zgDgy?@w3w7h}2?5zm5yo9?;gB?g%tz~8+FV7I7dP+S}5q0r}9-a6?&j2i#QXq)YK2t7*&DcuhBy)2PP0l(bhW&NAdX zjGGIuC^DXYDE{QqvSa+grzf+V0D1ab89^wN$?XfN*wETV*3{2p989w>Q(5`?hfk@^ z0;O}2KhYw)T2OX4i8!;I-2ednxqd} zGNQ|1fL%jkJW8m%ZL45LrldckV3*O{l%$!fwK3?r$p&Yj;x8@@lb1# z6JY1IwnxjI-zvdCF)zSGZ7bmBvb)Kn74ejR;P|LWVDOz9AoCo^6MXhs5} zE(f43c}gg0-}Y<4IT$h3{mu!mu(;SSa^RArex~{U_5hR_B-8|lU^g^djc>mB`D27j zw+o%mfZ=Er#XqhF^@f`ORT7Iu1U?O=ZP^MLbE<&o1+Fzo!%^DfKQbaLZc3nl5^t;P zwuqvw>_+|-EI*k&wpVKIPz}pHIqK@bs4EKj8b-}`gKoYuho$is|5!;+P zC%7P0c0M>acR|5+Ai5l>{EKBE{NH35oL@uxdU~en9T=W5pW}sr(>ywKuE{l#J8Tj1 zYIpv@B$S*2N|+oUFDev?NL~6c zI+Sq)1*&_n*^bG3BT`fJo1|w|srl-q#~*pUyPHeK8mE(WixqHCl116^ZE5`$?>QD) zr{xs?LM$c`i!OE}OZfB`zm5Vg5tABPDl?oVdurv?A$L)((mwOnGE=-U=4u<;UXQH2lSt>Aui$SCjdKdTkY-rL@@aKcc4bYJKz|$aQK2rXZ7qq zg|W!|XAt?*CUHZFQ4(y$>fDa^Z!KY^6XguT;!_?!`aUYPCwg^W$lnIOjlV__-}k*F z&>F3`UPFZtp(9Q+=(@Uc4^c`~sst}WHU?8EHBOJZNw073y1Kf;U<)92(Ae78IL|p3 z2}YvI{70?Px$48f!uz7_4ArR~kXkrds647(DJYTDA;DxoQqJqiMDkpm%vy?j06qM} zA}h9+x58GE91Ae4wv~6^2i_6nCb#O2x(y_M*A@vI@|arSYJPM&sQ6bX23NA*p%Ujp zloFGasus_nHfx(Y{rX+kS@zMFwaclysXJTA=Z#>tPVeqxy$WzFAsOVmFmQ7dt5t%d zfq>Nn`x@VuGeQun_2SJon9%aO7>{_*qDfTS?V#aA%G%nxqlA~k2^eyQ)lUT^7^0D_ zm%lw{KuoMPiLAj3ohIOK>wGe_A0-|59Nvu~%1OzBriw?L-do zT1mCqzZR3JOUq;bBFuhQ#$aofRY3`Z|2c)xIF@={SZ_yw%&H8^lUiqo-6?*V5SZ}n z1s*NAl~4N;SYmDPRgt2=pvZ=F9DFcWt2=9dTEa`_2@P=3K4&wIC{wO(15iEit?@dw zrN1p+{y)Sy{wCKGuP{>>ig(k0;bGiNppQIn#Ez^;M?BQ##1R}zI5@D1U3|f+90j3+ z>Xz&G;k<$L_xH2NtwDC%TyD=Mp~%wvA^6OA%0GDB?@HWncZ`}Y&Sx##7l952eyOUc zEN+3weJz2DBW}b_Ba%PbmNvO>UK`)gA6}nEb~R@4xMrzT6Pwe;RPEHhV?$J- zL4|fj^Lek7>L3mM3pN0Ba^4;4VsMgLcR@k*AnQ=-&Z;TZRp^!JY4lm#_5G%3WBkF| z#)jMnx)miB<~ME!^A6D4Y`E7=?1lqGc$}a?AHryWEOZ2RCq1Rh>HSs6>Th$Up0u1} zRRuu9_VE;^WV}{!7Rz#%)h8!$%w2u;s%+_X4T<1eR+2>qzZ3L>HQi9?Tb>AR*|49O zR2u0?>YkhwB;T%H4cACsyD%T47grAV3I4+-lpJLq>YMAQImf(N#$NCqQwz?3^)$pU zx7Tpz{QMzrmw^*py6=?5{7Y$VL%_HmgpOUriS<4*HZs!k2BA$TsvH;^Ac&7pHV!|f z{FnV=izh?#&GVimOZRJ4`%O^T?ON&TlKt`A?rpw1D*>P)iXo77`Eujv)q7KlRjRfL zwJCivcenZH(o9qVYWO7XtDKymx6l*NCDqb9+a7;}Af0ETSub!q78M;m9jH{S<{7Vb z49epo&E^!7Q)6-RBj?CaM|ql9E{%RN(@^9ToyzFY#cEThr0?vg9-}bgWnD`Q`&6HN zJXS9AS@V&La&nWHCk}bmNpA)p$re8P%V_ZMWUpR%JQkSew%t-``?G;}L8b5gW1~0B zM4*|!On*0(=EMeq3B3+$>-l0~Vd3W1)Qg@PbISNai*yfgx;EC<&M>>G(=24cSpSSs zMwD@*@Ekmg6kW0Q*w^iI=nQYk0y1ngv_~Fbr;EKrq1P_CF0<1S_4cskyt$fLdBRk$ zGT~u5i5j!de^x8L{XH+pn)EM`p~kre+MfC}9|ELVzIf}nJZqwytGOXjHOsw{jL}^} zfPE*}o$Lea%iu4?6oC_tJ00S`8EoA>imk_o`1$#5TZ{{$lgC^_e!bj7)4H}+ElQ}# z@8_o4Mi|MbaE#G;3Ps^q(ay&~-RUPu)a9#Qf6uFc@SCBGFMXfRxDPjfP*Z1rTY2|Z z8J(2UO`~_}+bQMLg=4F}RnDab?LXGrP9|A?6VKE40;pcEci?n!8CglkTc%i6>7UXa zI}f$3p_|@3J$gI+!hmNcF_^A~ZYNU*mt0g-H=sJ>L{b#O_4DMV`P1~-f1U}cuVvf) zNOWI#;5Tlnw&m!K!L}Z5xpTSpkv^%z6s0BSG-xioCT~h%WZ~YwW5*@!GH<;4KkgGQ z6~#znp!3TiG$y=v>;HBHj3Z1TcEubjX;c@2vg4QfL%|0DCCz`O zZ{7{B`1{x4p`ocK%eePtkY=LX{NlSXSmhnC&HH<&%ZS|oWBG2_{R&*G)*pZdwp#0btg7XwH9e7+9JQ^i8wXH6Jn;0*i}g#_BoAZ#M{{DhW^M2 zswM^F*pp+M%Py>cq-wXo{+pgIIU+i`Xw!+%{$wAkFG@YmDB3d7QLK}<{WnSJM>kTf z`zjdRt{?Z|uOu!9ixv-0C_?JQAWVY_@2O2x_W{Q&8fp`z$W%YLsQF`(-c7-5T@O3# zi!oOqhe06pXy@t8f%U^su5gC`%XWBYNxJyu<>k{;7Ex}ldRLjdYO(f;Baf|?@4Y4d z`c;(?<_yQ4r6-r5AUIb)6sKTnqFmjsGS$t(md?vpY2L2_y6BS&F8dpp?n&J<#bVwX0?BfQ!Kb|M2o=ygRO?U}deE z>~Ido|s%hM1Mfb$pRr!|iIaT5-ktjr-&7=487Z z#PSbMO;HKl;Yp%Z;P0-1c~Z|qdG$7j)VZKFHWyD35CA%H>vA_}p16y*-iCno+Tw3vxbq;M?##Pb@~mhnsnI)=dr7Tf`Wlmcj>RxoOiR7 zP?8U}o4|h&OmLbucd>UchPYCO0ySkf^#(ED(jUmrkQ*k9H; zFw_9k4%W!g{MiQK?XB#Q@hwQB1G6@L4qhbGR#Q!YNo8{>zJ-I7;NFtqM_ro&1|+_X ze~IOL(QuoatYW#HI-B?pJ%oCz5Ec6AIsRvQzUvq~UoymmDmA&-S}FL%hg^->v#Z`L z44JZ0E_QMJ6*voMV$l5w;lND_4Z~pnZ@a<2zWHZO&BFbI&oFQ3y-9Vw-I~)p?{F^E z_^~_5x>`T9<(!?sxKnbmbSRbiTFnf%JJE1kesVbs5UJeHuoehfMB+ScSHS%4`lQdIiAHuk;07StIGCmO&K{!Xhm~_q=5b$v{*~Bnm znUGdo!=~I~cMy4SBNmZ;h-+bHG!M=xIfcgMWHg0jci(IR(hAcFE2^!x^Y3Z4?& zJ%|hL94Us&kAG*phc0_U6+6EAl=Z#}|LLiQkQa;;s`wP?xcbGBL4@wZ$W0hUex6A>zd4Ltew=wC zL0U|De@QH%!AJD;)lzh&^p|!%QE;kI&oy;Zqj5bB#nQ4ZEw88mHv_m!LIpXB_TKK8 zm`9zu{y>FL`TF(iN9VBt^?HwCCj0mwDToilA8;AepNCL6f-CiOg@I&#QY8qQ2b&)v zg2WGCbt)9=I5@)Ux|k}+%FZTn22{T9ARF7dp5gEnQ0aV_-n--!ng5P@>nprFF8UB@ z&#_TeFgY^vMf=tp5fcR1P^_kwDy6bJNGaHz)!FUr&ox3oi11RTfr}-Udg^INhy*VE zai^akVD%C{8b5~=y_JZsT&P2s^bc)0OJH}PFXVk@lvi9lFi6PZe7f0w=I?P~K3&He zH^_cR zx5cQsMh3~yT^jr?nw!Uf-zDN7zf1Xwqz^wDo$NlBotNjGgp#^;&SpTRn-eP_KHqaH z&528fa9Ld>D@c4{wtF`PeVxEuvQkF>vr^yW(_NXb0gRsexH39L3a zMZDuujaG0;iRnz8osXC;?_3z85Q;T^KH{a2z+rf~)slX7mt-YUX2b_UB>wdz&|Y?q$2+fZX}`Xbv~C^B5Jua& z`r|+Dhd0etsF6w?ozudhSrpNqSlx1^>3XJcQ?#D$H@joc$kvC0)Az4kg<@*+o{U~= z_)7N%Z0ACq@w;KD6@0kr3PN@CG4n6FxKCG|fA@%flJeQN>dINUAo*(#&V}zXezg=! zj&Kth6a7k3c>p`Mmt)ur^;Whq`N3m0aKh*}_+^~wN;k{KZ}y_4O^FQd5_T__Gx5%# zX+}k)IOa`PZU+;hSSO_bE?*Pww=!P*>Kp@Gn1XeH^uu;ybDg5aQ1r_JM3NnAPP@}6 z0XzO0$XZL6!Ba~FmzatqBXSz&TW}N}Xu6Z`x`9@Whx=-eEMt9RgO0Co_R~JSF{|HE zoxAsdP-~Ya-hi}))PR5uC8Z)S+(x%s6_8;Pzhj6DH(k(t&-1M`@Z}5bUQono-6q8j zTV@;o%*jE+Wu|l)Nu@aqC*pasv%w#3akEaN1lxtQR#vi9yV<*XFlp3$v%0CVI(}p8 z>YVdr?dh9e z_S;-i+t&9VJBh=1@vk>Zv!dw2=OR2wbUTXQbuX z#*Ou|Q98<*dY+Zd68LTji*(@c#bY3-*S=9l4T*eZ$qxLI80s?6!r;9OTd&m!7BlAWCm>`;8 zd2PEtX7lKT$fUpe0SKg{)ogMBJ$qZ%&$GKXC!u$Dw$`q1gjaZ3|0|sT1nh@+Wt87F zB>UxcoLQ@|XS$zGd}~rlh>u6=XQd11ZZ+p!ipIdKU?x(cBR%h{RCf>FZacG4%&oDR zmIRx&OJqsTYb5v7iLOHDUS1SB=Q**R^HGP}He$f_8t5)N2sA>s~vqU;!L{2T12KgR^_qE5z z>)8}0LS9M?u!(eUKuDKcn&dnjiaF6cFUs6P?^#w<-*C2<5Bcv%{pZF6;QPy?rOalr zzx2K|0h0QYJ2laJ8v7?Np`>YZA@=Bwp1D|mc9A(=yDoQx9U9+aH{&A1`=za75E*eH zyfIrFa05Lj&wIuXzkqlMkl@(Zczu2SWRRWv74z#99uL`1EvRnHh;{I6mNvulbMDrZ zHb8Hinwr8PRym(-2b@`AmkiK(niuVkZ~RGARorfHrV={m-T_>N2Pa4f4J>}xYHc@`Id`(m!qLjGBf~1tZ|5E@9zh(3z zXGfLf*U@=4dW08Kw;1`;UtJjul{ko8jG$TlhTL}Je9dK0{$*9@V-B66j?Wmjs}me3 zgr7m8IZ(CCWLc)Zco0>1m`(|x5i2Mm+Ni(cd+hAypi&I&#K^Oj087wqffx5S>P-~U zAmb9PoH}Ja*zi8!byIx)o>ttvb*#_$N8G*mTjCQGl}@tF7V_33d{gJZ-^cOm{i&F+ z5C@ccjohQ%>C;o&JNZItJais*tBg7~M>~q$@$nt^gys+Kx8kuc8SjDe`NoamtCCVq zv3rZhkHMXTr7P(aj>kK=b~iJZF2(>9pk!sWJBoU%_mt?P1 zJGl@B45#-rN)eyLuW2Rh6;8CnG4!05CnVG`ZQsPGfRPAfqc&Cl?qGX!_vYv2Z(R-p zDl9hx4!urcB{d4V&&n-WWXI?anOR?=Rw3f9*HOzam)NT_3w>2Y28;TtDNT+giPT z+&Mj?x=7P#-O2l{<Z z%$r}*tr1^5*wz`W9f8#>&lL-9*ZzR^o^xMp0b^=>3}+WAjpbZjT|q~W?9kSAYNKzG zB5CPOL`m>N$+RNl5mw^93ndLrHG;vC4wDB6Hv4{N6NXtyPe7R?&YXrDD4$$7uBOm7 zO=3#^4^?fcMF5gzMCqgCBQgKCq!C%7R<_SWnKnT_0p!y|*aO0}gcqTxw)Y2i?RGkr z(%lmNbmwDjaPIDVs5#+?ta@Crbt(hY=mjXIkfgYoz4}^dk@S%i0W*J|4%T22Ur?0;P8Cl#k`^_dF`@ zFNFGiy%X6idk6c#WlFom?Po1#NV;u^d0Rf+PGYj+TXG*%@Aa z5fA`!cE(P0a_s!v9Q+9goKRGk>o-~j*Rtk|1Wo?s{CE=3gKCdMb{~wcv?tyCyaO+CIm|< zY~JYfVO@2V2@<{=y_@?t3lD)2MZe-F7J`E}A|Xh`5z8fbl&z^OtCWt7+X(?6I;!Qs zdnA5R)xN4@jnHhQCp#38yGw^-Y2|hd5wrEVO7_2nAiQfLefO6GLB`pr9N|;hbP&Eo zND?6;UK+hhceh}~^V8EfqVcX+r0i~V>x#six3*^W0m9B1x+Q0;4RKFupH^20Fa(+CFA}Z@kKrGknyX zQ64p+DgSlMM<`~*7-V>zvD+V@N?b3zIlGFHqH3fs=&d;vOe$f+B>9L(|GQLMP+vIy zm179;4KNdw4s!pi_Rmy19w>GXd05Df|Cb^U7>gvT&L4b4A$X7HHImgo?qA0~wxbDW zex{jjJ0&5>dc-M8Y-qtJBh(4qL28<$=Rvu$@kZx8R<|p?yF6L9d4aD;=XNnx55jmq zYDjT7*ZNAf`ny&AnzbOaLuF-WXHct=Y2-m;($u|QaNRze1|bS#2F8u9^|`so6Za>V zCvZw|9jxG<+o^F|Bxw1Vr%aNsXJA0N!EqP_LQALe+)lK5Gc+-Jdp4Arvil$jaO0d^ zVWSh*U5B#I>{nvl6{)hbTP`GVy_&Rdre>R(ai&4Xr}o+!C;cG*Fn$@-rB0pO#L03iKF+{{AmUG_aM^4Ol3??jYok=noG_N@Dh(g zoh?2fvy_qDY@UrCM2a^n5ts0SGXWNWrT&4{ zZP=vd3NQD=q=nQ-cK+M6Uw*rl{4Ubf{juY5u3xRDyK7UpxZ`EHOO(#1wA2`sCv^jr z|8X~pO=u4_@_!a0wpELTc}$MlB1ekqvpy?d(qScN*1gH%iAd`^5HFKZrz4rlEtQ~* zInLJo(%1J6^@~5v--rZZ7)B_@_p}0*2|gr?7Sh9l*z+V2La&z@oh4=I=?`B37cZ-{ z&aG89>38;T>wgkvx3>Yqn!miUu_ha3>7=Y&qW+-N`&j;X+7y`VB>bV+L&k;;q^L== z%`oa&V*I}HI6B&6q!w1TtR?k1JezSdF`H%8?~QpY5!V1H^R>0L-CLnSPP=VvIoF5T za-L@$$0x~2Nl7ofJDw=0s`f3+0Dma{y2A>#igQT|G&6k&n_pMx(T`WOB>smUjlE1( zuQyr=F$r4G^@Ua(d{bj$$O|F66ew+_bjU4bKkL|jTKJpSS9FRdhqF^y44Lb9Sbu)J zY}nG8`t?YyJwc>7WU8HnP%7U~w;OH9gC>As=v%huFT|Zk5B5Qa8{pzZ5|&rzz@DkK zQ5uiQFCQ5J1(!yR<73pdWcPyx^VvrGhhSpWIF_|M*pOtu*A=9#8mVs)vlK5lFtE1# z5oxBo2x_62i>43gr@zQGG1O*RGcfBN*P&c_*yw$OTioz%o!G&CY1T>g}bn3fYoIi)A*Ev0xq zoN->%<3L`M_9Zs45UO=akU}$v0dcLhVnDgdqvo=FS82jEXqNE*l-4i-lg7E5d8v(cWUo}B@!$ny_?I;vm1kI#vF zvC3v`2|I7AyEiosjyKBLCmvu$xvMn>cTS5rjvsTgu!R_EIh`d4;1V{Ue>!Q*;U6OS zSwsCc$4aN4XtaSvM%X7^drM1u|I26XH-Er?-FNW2MrxDRl7w|knS6a(MKYh+Xyuq- z*2Pb2#e&=!>l};La1WcXF83(dU~GJ|-KGqvpueuVGLAR^tf9(u5*?3Y5d!WBB}bAU zRDYbOm6i*?jiYvI_pgd`rT)~0IHtBZ*_XV|^jnpx<*-jSR+L`!161T%9q+%DIq-21`zVirm2dKy{x=v*MgWdwd10>~G|$-^aoQhL?u-QT4c zbVoSdJ7Nh4B+}%jR*5O4-*fMhA*-5oz^JTQJ@}G6w`*Qywvk(jwPwy;*CUMB z>g%OvgyLhc=0IM<0p5SllNhuOtUUM`kc7eNf!SAvmoebt9$o;Sn)^Fy4gy;M9Usqe zbdrxX^C%!#hmMvuD|6Vz5=0p_-|ksv3a(G35x^Wka}MwR7z{f(E6^OSjMs1uTqojj z;UMt;4R~yw8`gt>1JJ{3QrGJ;n120T(4|COyGQ!FlTn~Q$DZ0tu2^jjGvSDrlQs(I z2|a1gN#LZP_+>p5YNMhA7g1NIktWmjl~pnm#T)rkct%B3E`-ugFv8k{i2o)saCPTg z(ReUeTN9TQ&+tt0xfe&5q;Ilsmkdi!sP5lqlAhr-0Y9JQqE>!#(Tl)E$nLG{)mBbR zfMT&)!do9RzujkvE!kiKX-f|n3)xqUNr=yE@CR4ZrB!6X{od6>rP>Vt4+{JfFw042 z)Wp>EwLZ+D`C1)_=d{i1#7B)_r)+&N)=ne+8Jbe{h?GoZYbA<3Fo!mA1J6AWjWqAi zG__Yx26+uNgDD4jD^Cl>?lXGDJ|4>a=I7IC7yQQU_sQgAizfO8{z6~(gD3VCB!ftT zRHi|Iv;K+A=Z}l0{25k>=a&Yn_-e+P@%}&_{1-SQy3{w*%Xda4+(F%dlvj%*d=%-# z(CKdlk&c(0o-2DlD#H{S0ftqgKs%sJ>2sw~s}mt}5P94tW|$YQ+cT0j;* ze9DEWw-O#`V8Zw3WI&g5B(|iiudhdK?E*|CncY&u&CLz|J%`f)wE6xG@Ru+HXbaQO ze4&9ePV;2ogmr2|7K|>DlLJt5(gTzE2QYaYpIoFdwsi##^npDZnrvwlSn~fPCc&7E z^mMfQWMhB69#@RurP*U`>DPPlk{*#jT6SB(IUk8YY7H|mG;|-$V~oT5*Ruyh-g7R&$1#6?@7TGbWF|9TigxU~TqDymKbY3oI< zeX3H&R%EFI=8743#ev2%n8Ho!a_IT#mpx#~3v)*W$Z+S|M2a;U>kRsTe0A&RTK*So z2ZBNGrVcLH+1TtmE)X#YvQqjM(5xmYJ3mx*Nc`svR4Dp~50r&b+%Rz0-h4-%^nOFj zBYs00GRLgX%Szk03xDABvKZ+q^wZ(l%5Q?-jlE45BN7ace{%vf_H-DufJ7c5oGTu- z3o&>onfnf?w8vje%i*Ph)yP6iCJzPVbExRSt9KN!1KyR zb!OJT`LVRj3#x7x@e3ggp1z{*bn!tsUaA7|G}q1_!txiqvbZl6G>3Qp@F!O@e7POwr8ax=*FA+}omL8Di$7rE02#H=W30{9H1u_}9m8 zi!A|7FJGBO5PNZgaoh6Q3<6;2E58g_@Z=-Iwa&m2x3!0z}eJ~YJivt&!>7==GFPl1NTCSnt zFU(#jjY`O0|o!NS0Us$Z$t;~Q`vX<7=&XRr)Q~yNZjLM&DxFn zrF&(G8uH-?DJ|0-X(6O-<>X3Zz|XM{9AQ z9D|9-<^j|73|Zg}tgco}Zg&gAKL*_CL-i*X3EQn0oG8&Gt=HbG-AM6f1vAYGR?wF~ z1};O=5PX#;;biG=^@A+# z7Zk#UUshMQwFz{!;+g80(RHEH>KJbBDu@0hn8Ydgyz#DaylHawui5(pY3*ebsM+*c%J&(gx5lh+G0;I-eW$7 z$KvM`kRikpdfe%mCPaa69`P#rm5{hM9zVRM-ZOaqSA2It4hN<)O5^yR^BeilCXr9wqjgDLG@s_Jy0bFs(%40K$*3N2i9x`^Un{fm~G?vy$C z5SNVASGEj{O;+JhLU!XH1m8cW89jMHD3&GkWr&oT6Gg8#NRA~oaM &d4%n>7ons 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%0D~0obU*t&YJ6m(tDQ&*C;H@_5 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 z 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�G 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 S`$lyPCatZ#ef*SL+WrLzA((6c literal 0 HcmV?d00001 diff --git a/Assets/OtherAssets/DEV/PAID/vFavorites/Manual.pdf.meta b/Assets/OtherAssets/DEV/PAID/vFavorites/Manual.pdf.meta new file mode 100644 index 0000000..a486e69 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFavorites/Manual.pdf.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 842468ccf23564770b4b1cb9f7eca30e +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vFavorites/VFavorites.asmdef b/Assets/OtherAssets/DEV/PAID/vFavorites/VFavorites.asmdef new file mode 100644 index 0000000..8897fa1 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFavorites/VFavorites.asmdef @@ -0,0 +1,16 @@ +{ + "name": "VFavorites", + "rootNamespace": "", + "references": [], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Assets/OtherAssets/DEV/PAID/vFavorites/VFavorites.asmdef.meta b/Assets/OtherAssets/DEV/PAID/vFavorites/VFavorites.asmdef.meta new file mode 100644 index 0000000..d92c7c0 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFavorites/VFavorites.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: d8266c7db84a045d3b706b23e60aa197 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vFavorites/VFavorites.cs b/Assets/OtherAssets/DEV/PAID/vFavorites/VFavorites.cs new file mode 100644 index 0000000..128bd15 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFavorites/VFavorites.cs @@ -0,0 +1,1976 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.Linq; +using UnityEngine.UIElements; +using Type = System.Type; +using static VFavorites.Libs.VUtils; +using static VFavorites.Libs.VGUI; +using static VFavorites.VFavoritesData; + + +namespace VFavorites +{ + public static class VFavorites + { + static void WrappedOnGUI(object _) + { + if (wrappedBrowser?.GetType() != t_BrowserWindow) { originalBrowserGUI(); return; } + + + void createData() + { + if (data) return; + + data = ScriptableObject.CreateInstance(); + + AssetDatabase.CreateAsset(data, GetScriptPath("VFavorites").GetParentPath().CombinePath("vFavorites Data.asset")); + + + + // // migrateDataFromV1 + + // if (!EditorPrefsCached.HasKey("vFavorites-guids-" + GetProjectId())) return; + // if (EditorPrefsCached.HasKey("vHierarchy-dataMigrationFromV1Attempted-" + GetProjectId())) return; + + // EditorPrefsCached.SetBool("vHierarchy-dataMigrationFromV1Attempted-" + GetProjectId(), true); + + + // if (!data.pages.Any()) + // data.pages.Add(new Page("Page 1")); + + + // var guidsFromV1 = EditorPrefsCached.GetString("vFavorites-guids-" + GetProjectId()).Split('-').Where(r => r != "").ToList(); + + // foreach (var guid in guidsFromV1) + // if (AssetDatabase.LoadAssetAtPath(guid.ToPath()) is Object obj) + // data.pages.First().items.Add(new Item(obj)); + + + // data.Dirty(); + // data.Save(); + + } + void closeNavbarSearch() + { + if (!curEvent.isLayout) return; + if (t_VFolders == null) return; + + + if (GUI.GetNameOfFocusedControl() == "navbar search field") + GUI.FocusControl(null); + + if (!wrappedBrowser.GetMemberValue("m_SearchFieldText").IsNullOrEmpty()) + { + wrappedBrowser.SetMemberValue("m_SearchFieldText", ""); + wrappedBrowser.GetMemberValue("m_SearchFilter").SetMemberValue("m_NameFilter", ""); + + wrappedBrowser.InvokeMethod("UpdateSearchDelayed"); + + GUIUtility.keyboardControl = 0; + } + + + // mouse input on vFavorites throws exceptions if search is active on vFolders navbar + // here we close search to avoid this + + } + + void pageScroll() + { + if (!curEvent.isScroll) return; + if (!VFavoritesMenu.pageScrollEnabled) return; + + + + var scrollDelta = curEvent.mouseDelta.y; + + if (scrollDelta == 0 && curEvent.holdingShift) + scrollDelta = curEvent.mouseDelta.x; + + if (scrollDelta < 0 && data.curPageIndex == 0) return; + + + + if (scrollDelta < 0) + { + data.curPageIndex--; + prevPageButtonBrightness = 2; + } + + if (scrollDelta > 0) + { + data.curPageIndex++; + nextPageButtonBrightness = 2; + + } + + CancelDragging(); + CancelRowAnimations(); + + curEvent.Use(); + + } + + void background() + { + var color = isDarkTheme ? Greyscale(.2f) : Greyscale(.78f); + + totalRect_groupSpace.Draw(color); + + } + void pages() + { + void page(Rect pageRect, Page page) + { + void findSelectedItem() + { + if (!curEvent.isLayout) return; + + foreach (var item in page.items) + item.isSelected = false; + + if (draggingItem) return; + if (mousePresesdOnItem) return; + if (page.lastItemDragTime_ticks > page.lastItemSelectTime_ticks) return; + + Item lastSelectedItem = null; + + foreach (var item in page.items) + { + if (!item.isLoadable) continue; + if (lastSelectedItem?.lastSelectTime_ticks > item.lastSelectTime_ticks) continue; + + + var isSelected = false; + + if (item.isFolder) + { + var targetBrowser = isWrappedBrowserLocked && isOneColumn ? allBrowsers.FirstOrDefault(r => !r.GetMemberValue("isLocked")) ?? wrappedBrowser + : wrappedBrowser; + + if (targetBrowser.GetFieldValue("m_ViewMode") == 1) + isSelected = targetBrowser.InvokeMethod("GetActiveFolderPath") == item.assetPath; + else + isSelected = Selection.activeObject == item.obj; + + } + + if (!item.isFolder) + isSelected = Selection.activeObject == item.obj; + + + if (isSelected) + lastSelectedItem = item; + + } + + if (lastSelectedItem != null) + lastSelectedItem.isSelected = true; + + } + + void rows() + { + void row(float y, Item item) + { + var rowRect = pageRect.SetHeight(rowHeight).SetY(y).SetX(0); + + var iconOffset = 6; + var iconSize = 25 * Mathf.Min(1, data.rowScale); + var nameOffset = 3; + var deletedOrNotLoadedLabelOffset = 1; + + float highlightAmount = 0f; + + + void set_highlightAmount() + { + if (animatingDroppedItem && item == droppedItem) + highlightAmount = droppedItemHighlightAmount; + + if (item.isSelected) + highlightAmount = 1; + + if (draggingItem && item == draggedItem) + highlightAmount = 1; + + if (mousePresesdOnItem && item == pressedItem) + highlightAmount = 1; + + } + + void shadow() + { + if (item != draggedItem && item != droppedItem) return; + + var amount = item == droppedItem ? droppedItemShadowAmount : 1; + + if (amount.Approx(0)) return; + + rowRect.AddWidthFromMid(30).DrawBlurred(Greyscale(0, .55f * amount), 22); + + } + void background() + { + var evenColor = isDarkTheme ? Greyscale(.249f) : Greyscale(.82f); + var oddColor = isDarkTheme ? Greyscale(.228f) : Greyscale(.85f); + var highlightedColor = isDarkTheme ? Greyscale(.335f) : Greyscale(.9f); + + var rowColor = Lerp(evenColor, oddColor, rowRect.y.PingPong(rowHeight) / rowHeight); + + Lerp(ref rowColor, highlightedColor, highlightAmount); + + rowRect.Draw(rowColor); + + } + void icon() + { + var iconRect = rowRect.MoveX(iconOffset).SetWidth(iconSize).SetHeightFromMid(iconSize); + + if (item.isSceneGameObject) + iconRect = iconRect.MoveX(1).Resize(.5f); + + void asset() + { + if (!item.isAsset) return; + + var iconTexture = item.isLoadable ? AssetPreview.GetAssetPreview(item.obj) ?? AssetPreview.GetMiniThumbnail(item.obj) : AssetPreview.GetMiniTypeThumbnail(item.type); + + GUI.DrawTexture(iconRect, iconTexture); + + } + void sceneGameObject() + { + if (!item.isSceneGameObject) return; + + void getIconNameFromAssetPreview() + { + if (!item.isLoadable) return; + + item.sceneGameObjectIconName = AssetPreview.GetMiniThumbnail(item.obj).name; + + } + void getIconNameFromVHierarchy() + { + if (!item.isLoadable) return; + if (!(item.obj is GameObject gameObject)) return; + if (mi_VHierarchy_GetIconName == null) return; + + var iconNameFromVHierarchy = (string)mi_VHierarchy_GetIconName.Invoke(null, new object[] { gameObject }); + + if (!iconNameFromVHierarchy.IsNullOrEmpty()) + item.sceneGameObjectIconName = iconNameFromVHierarchy; + + } + + getIconNameFromAssetPreview(); + getIconNameFromVHierarchy(); + + var iconTexture = EditorGUIUtility.IconContent(item.sceneGameObjectIconName.IsNullOrEmpty() ? "GameObject icon" : item.sceneGameObjectIconName).image; + + GUI.DrawTexture(iconRect, iconTexture); + + } + void folder() + { + if (!item.isFolder) return; + + iconRect = iconRect.Resize(-1.5f); + + void drawNormal() + { + if (isDarkTheme) + if (highlightAmount == 1) return; + + GUI.DrawTexture(iconRect, EditorGUIUtility.IconContent("Folder icon").image); + + } + void drawHighlighted() + { + if (!isDarkTheme) return; + if (highlightAmount != 1) return; + + SetGUIColor(Greyscale(.84f)); + + GUI.DrawTexture(iconRect, EditorGUIUtility.IconContent("Folder On icon").image); + + ResetGUIColor(); + + } + void drawViaVFolders() + { + mi_VFolders_DrawBigFolderIcon?.Invoke(null, new object[] { iconRect, item.globalId.guid }); + } + + drawNormal(); + drawHighlighted(); + drawViaVFolders(); + + } + + asset(); + sceneGameObject(); + folder(); + + } + void name() + { + var nameRect = rowRect.MoveX(iconOffset + iconSize + nameOffset).MoveY(-.5f).SetHeightFromMid(16); + + void normal() + { + if (isDarkTheme) + if (highlightAmount == 1) return; + + GUI.Label(nameRect, item.name); + + } + void highlighted() + { + if (!isDarkTheme) return; + if (highlightAmount != 1) return; + if (!curEvent.isRepaint) return; + + SetGUIColor(Greyscale(.91f)); + + GUI.skin.GetStyle("WhiteLabel").Draw(nameRect, item.name, false, false, false, false); + + ResetGUIColor(); + + } + + normal(); + highlighted(); + + } + void deletedOrNotLoaded() + { + var labelRect = rowRect.MoveX(iconOffset + iconSize + nameOffset + item.name.GetLabelWidth() + deletedOrNotLoadedLabelOffset).MoveY(.5f); + + SetGUIEnabled(false); + SetLabelFontSize(10); + + if (item.isDeleted) + GUI.Label(labelRect, "Deleted"); + + else if (!item.isLoadable) + GUI.Label(labelRect, "Not loaded"); + + ResetLabelStyle(); + ResetGUIEnabled(); + + } + void crossButton() + { + if (!rowRect.IsHovered()) return; + if (draggingItem) return; + // if (mousePresesdOnItem) return; // idk + + var buttonRect = rowRect.SetWidthFromRight(0).MoveX(-crossButtonOffsetFromRight).SetWidthFromMid(crossButtonSize); + var iconRect = buttonRect.SetSizeFromMid(16); + + var normalColor = Greyscale(item.isSelected ? .48f : .4f); + var hoveredColor = isDarkTheme ? Greyscale(.8f) : normalColor; + var pressedColor = Greyscale(.6f); + + SetGUIColor(buttonRect.IsHovered() ? (mousePressed ? pressedColor : hoveredColor) : normalColor); + GUI.Label(iconRect, EditorGUIUtility.IconContent("CrossIcon")); + ResetGUIColor(); + + buttonRect.MarkInteractive(); + + + if (!mousePressedOnCrossButtonArea) return; + if (!curEvent.isMouseUp) return; + if (!buttonRect.IsHovered()) return; + + CancelRowAnimations(); + data.curPage.rowGaps[data.curPage.items.IndexOf(item)] = rowHeight; + + data.curPage.items.Remove(item); + + data.Dirty(); + data.Save(); + + curEvent.Use(); + + } + void click() + { + if (!rowRect.IsHovered()) return; + if (!curEvent.isMouseUp) return; + + curEvent.Use(); + + if (draggingItem) return; + if (mouseDragDistance > 2) return; + if (!item.isLoadable) return; + + SelectItem(item); + + } + void doubleclick() + { + if (!rowRect.IsHovered()) return; + if (!doubleclickUnhandled) return; + + OpenItem(item); + + doubleclickUnhandled = false; + + } + + + rowRect.MarkInteractive(); + + set_highlightAmount(); + + shadow(); + background(); + icon(); + name(); + deletedOrNotLoaded(); + crossButton(); + click(); + doubleclick(); + + } + + void normalRow(int i) + { + Space(page.rowGaps[i]); + Space(rowHeight); + + if (page.items[i] == droppedItem && animatingDroppedItem && page == data.curPage) return; + + row(lastRect.y, page.items[i]); + + } + void draggedRow() + { + if (!draggingItem) return; + if (page != data.curPage) return; + + + row(draggedItemY_rowsSpace, draggedItem); + + } + void droppedRow() + { + if (!animatingDroppedItem) return; + if (page != data.curPage) return; + + row(droppedItemY_rowsSpace, droppedItem); + + } + + + if (curEvent.isRepaint && skipNextRepaint) { skipNextRepaint = false; return; } + + + GUILayout.BeginArea(pageRect); + page.scrollPos = EditorGUILayout.BeginScrollView(new Vector2(0, page.scrollPos), GUIStyle.none, GUIStyle.none).y; + + for (int i = 0; i < page.items.Count; i++) + normalRow(i); + + Space(page.rowGaps.Last()); + + Space(60); + + draggedRow(); + droppedRow(); + + EditorGUILayout.EndScrollView(); + GUILayout.EndArea(); + + + } + void curtains() + { + var height = 25; + var color = isDarkTheme ? Greyscale(.2f) : Greyscale(.78f); + + pageRect.SetHeight(height).DrawCurtainDown(color.SetAlpha((page.scrollPos / 20).Smoothstep())); + pageRect.SetHeightFromBottom(height).DrawCurtainUp(color); + + } + void tutor() + { + if (page.items.Any() || draggingItem) return; + + SetGUIEnabled(false); + SetLabelFontSize(11); + SetLabelAlignmentCenter(); + + GUI.Label(pageRect.MoveY(-13), "Drop folders, assets"); + GUI.Label(pageRect.MoveY(5), "or GameObjects"); + + ResetGUIEnabled(); + ResetLabelStyle(); + } + + + findSelectedItem(); + + rows(); + curtains(); + tutor(); + + } + + var spaceBetweenPages = 10; + + + if (pagesScrollPos == -1) + pagesScrollPos = data.curPageIndex; + + while (data.pages.Count <= pagesScrollPos.CeilToInt()) + data.pages.Add(new Page("Page " + (data.pages.Count + 1))); + + + for (int i = pagesScrollPos.FloorToInt(); i <= pagesScrollPos.CeilToInt(); i++) + page(totalRect_groupSpace.SetX((totalRect_groupSpace.width + spaceBetweenPages) * (i - pagesScrollPos)), data.pages[i]); + + } + void widget() + { + var widthToAdd = 48; + var height = 24; + var distToBottom = 13; + + var color = isDarkTheme ? Greyscale(.1f) : Greyscale(.85f); + var shadowSize = 10; + var shadowAlpha = .23f; + + var chevronSize = 15; + var chevronOffset = 12; + var chevronBrightness = .5f; + + var textSize = 11; + var textBrightness = .65f; + + widgetRect_groupSpace = totalRect_groupSpace.SetWidthFromMid(data.curPage.name.GetLabelWidth(textSize) + widthToAdd).SetHeightFromBottom(height).MoveY(-distToBottom); + + + void shadow() + { + widgetRect_groupSpace.Resize(1).DrawBlurred(Greyscale(0f, shadowAlpha), shadowSize); + } + void background() + { + widgetRect_groupSpace.DrawWithRoundedCorners(color, 1223); + } + + void nameLabel() + { + if (renamingPage) return; + + + var buttonRect = widgetRect_groupSpace.SetWidthFromMid(data.curPage.name.GetLabelWidth(textSize) - 2); + + buttonRect.MarkInteractive(); + + + var activated = curEvent.isMouseUp && buttonRect.IsHovered(); + + + var brightness = !buttonRect.IsHovered() ? textBrightness : (mousePressed ? .75f : 1); + + SetGUIColor(Greyscale(brightness)); + SetLabelAlignmentCenter(); + SetLabelFontSize(textSize); + SetLabelBold(); + + GUI.Label(widgetRect_groupSpace, data.curPage.name); + + ResetGUIColor(); + ResetLabelStyle(); + + + if (!activated) return; + + renamingPage = true; + prevPageName = data.curPage.name; + + curEvent.Use(); + + } + void leftButton() + { + if (renamingPage) return; + + var iconRect = widgetRect_groupSpace.SetWidth(chevronOffset * 2).SetSizeFromMid(chevronSize, chevronSize); + var buttonRect = Rect.zero.SetX(0).SetY(widgetRect_groupSpace.y).SetXMax(iconRect.xMax + 4).SetYMax(totalRect_groupSpace.yMax); + + buttonRect.MarkInteractive(); + + + var active = data.curPageIndex > 0; + var activated = curEvent.isMouseUp && buttonRect.IsHovered(); + + + var brightness = prevPageButtonBrightness * (!buttonRect.IsHovered() || !active ? chevronBrightness : (mousePressed ? .75f : 1)); + + SetGUIColor(Greyscale(brightness)); + + GUI.DrawTexture(iconRect, EditorGUIUtility.IconContent("NodeChevronLeft@2x").image); + + ResetGUIColor(); + + + + if (!activated) return; + + curEvent.Use(); + + + + if (!active) return; + + CancelDragging(); + CancelRowAnimations(); + + data.curPageIndex--; + + + } + void rightButton() + { + if (renamingPage) return; + + var iconRect = widgetRect_groupSpace.SetWidthFromRight(chevronOffset * 2).SetSizeFromMid(chevronSize, chevronSize); + var buttonRect = Rect.zero.SetX(iconRect.x - 6).SetY(widgetRect_groupSpace.y).SetXMax(totalRect_groupSpace.xMax).SetYMax(totalRect_groupSpace.yMax); + + buttonRect.MarkInteractive(); + + + var active = true;// data.curPageIndex < data.pages.Count - 1 && data.curPage.items.Any(); + var activated = curEvent.isMouseUp && buttonRect.IsHovered(); + + + var brightness = nextPageButtonBrightness * (!buttonRect.IsHovered() || !active ? chevronBrightness : (mousePressed ? .75f : 1)); + + SetGUIColor(Greyscale(brightness)); + + GUI.DrawTexture(iconRect, EditorGUIUtility.IconContent("NodeChevronRight@2x").image); + + ResetGUIColor(); + + + + if (!activated) return; + + curEvent.Use(); + + + + if (!active) return; + + CancelDragging(); + CancelRowAnimations(); + + data.curPageIndex++; + + } + + void nameTextField() + { + if (!renamingPage) return; + + var textFieldRect = widgetRect_groupSpace.AddHeightFromMid(-2).SetWidthFromMid(data.curPage.name.GetLabelWidth(textSize) + 4); + + var s = new GUIStyle(GUI.skin.textField); + s.alignment = TextAnchor.MiddleCenter; + s.fontSize = 11; + + EditorGUIUtility.AddCursorRect(textFieldRect, MouseCursor.CustomCursor); + + GUI.SetNextControlName("asdasdasd"); + EditorGUI.FocusTextInControl("asdasdasd"); + + + data.curPage.name = EditorGUI.TextField(textFieldRect, data.curPage.name, s); + + + if (data.curPage.name.IsNullOrEmpty()) + data.curPage.name = "Page " + (data.curPageIndex + 1); + + EditorGUIUtility.AddCursorRect(textFieldRect, MouseCursor.CustomCursor); + + + + } + void cancelRename_button() + { + if (!renamingPage) return; + + var iconRect = widgetRect_groupSpace.SetWidth(chevronOffset * 2).SetSizeFromMid(chevronSize - 2); + var buttonRect = iconRect.SetSizeFromMid(25, 25); + + buttonRect.MarkInteractive(); + + + var brightness = !buttonRect.IsHovered() ? chevronBrightness : (mousePressed ? .75f : 1); + + SetGUIColor(Greyscale(brightness)); + + GUI.DrawTexture(iconRect, EditorGUIUtility.IconContent("CrossIcon").image); + + ResetGUIColor(); + + + + if (!curEvent.isMouseUp) return; + if (!buttonRect.IsHovered()) return; + + curEvent.Use(); + + data.curPage.name = prevPageName; + + renamingPage = false; + + } + void acceptRename_button() + { + if (!renamingPage) return; + + var iconRect = widgetRect_groupSpace.SetWidthFromRight(chevronOffset * 2).SetSizeFromMid(chevronSize - 0); + var buttonRect = iconRect.SetSizeFromMid(25, 25); + + buttonRect.MarkInteractive(); + + + var brightness = !buttonRect.IsHovered() ? chevronBrightness : (mousePressed ? .75f : 1); + + SetGUIColor(Greyscale(brightness)); + + GUI.DrawTexture(iconRect, EditorGUIUtility.IconContent("check").image); + + ResetGUIColor(); + + + + if (!curEvent.isMouseUp) return; + if (!buttonRect.IsHovered()) return; + + curEvent.Use(); + + renamingPage = false; + + data.Dirty(); + data.Save(); + + } + void acceptRename_enterKey() + { + if (!renamingPage) return; + if (curEvent.keyCode != KeyCode.Return) return; + + renamingPage = false; + + data.Dirty(); + data.Save(); + + } + void cancelRename_escapeKey() + { + if (!renamingPage) return; + if (curEvent.keyCode != KeyCode.Escape) return; + + data.curPage.name = prevPageName; + + renamingPage = false; + + } + + + shadow(); + background(); + + leftButton(); + rightButton(); + nameLabel(); + + nameTextField(); + + cancelRename_button(); + acceptRename_button(); + + acceptRename_enterKey(); + cancelRename_escapeKey(); + + } + void keys() + { + if (isWrappedBrowserLocked && !totalRect_browserSpace.IsHovered()) return; + + void prevPage() + { + if (!curEvent.isKeyDown) return; + if (curEvent.keyCode != KeyCode.LeftArrow) return; + if (!VFavoritesMenu.arrowKeysEnabled) return; + if (data.curPageIndex == 0) return; + + CancelDragging(); + CancelRowAnimations(); + + data.curPageIndex--; + + prevPageButtonBrightness = 2; + + curEvent.Use(); + + } + void nextPage() + { + if (curEvent.keyCode != KeyCode.RightArrow) return; + if (!curEvent.isKeyDown) return; + if (!VFavoritesMenu.arrowKeysEnabled) return; + + CancelDragging(); + CancelRowAnimations(); + + data.curPageIndex++; + + nextPageButtonBrightness = 2; + + curEvent.Use(); + + } + void selectPrev() + { + if (!curEvent.isKeyDown) return; + if (curEvent.keyCode != KeyCode.UpArrow) return; + if (!VFavoritesMenu.arrowKeysEnabled) return; + + var iToSelect = data.curPage.items.IndexOfFirst(r => r.isSelected) - 1; + + if (iToSelect < 0) + iToSelect = data.curPage.items.LastIndex(); + + if (iToSelect.IsInRangeOf(data.curPage.items)) + SelectItem(data.curPage.items[iToSelect]); + + curEvent.Use(); + + } + void selectNext() + { + if (!curEvent.isKeyDown) return; + if (curEvent.keyCode != KeyCode.DownArrow) return; + if (!VFavoritesMenu.arrowKeysEnabled) return; + + var iToSelect = data.curPage.items.IndexOfFirst(r => r.isSelected) + 1; + + if (iToSelect >= data.curPage.items.Count) + iToSelect = 0; + + if (iToSelect.IsInRangeOf(data.curPage.items)) + SelectItem(data.curPage.items[iToSelect]); + + curEvent.Use(); + + } + void numberKeys() + { + if (!curEvent.isKeyDown) return; + if (!VFavoritesMenu.numberKeysEnabled) return; + if (EditorGUIUtility.editingTextField) return; + + + var i = ((int)curEvent.keyCode - 48); + + if (i == 0) i = 10; + + if (!i.IsInRange(1, 10)) return; + + + data.curPageIndex = i - 1; + + curEvent.Use(); + + + } + + prevPage(); + nextPage(); + selectPrev(); + selectNext(); + numberKeys(); + + } + + void doOriginalGUIFirst_() + { + originalBrowserGUI(); + + GUI.BeginGroup(totalRect_browserSpace); + GUI.color = GUI.color.SetAlpha(currentOpacity); + + background(); + pages(); + widget(); + + GUI.color = GUI.color.SetAlpha(1); + GUI.EndGroup(); + + } + void doVFavoritesGUIFirst_() + { + keys(); + pageScroll(); + + GUI.BeginGroup(totalRect_browserSpace); + + widget(); + pages(); + + GUI.EndGroup(); + + if (totalRect_browserSpace.IsHovered()) + if (curEvent.isMouseUp || curEvent.isMouseDrag || curEvent.isScroll) // prevents these events from reaching original gui + curEvent.Use(); + + originalBrowserGUI(); + + } + + void originalBrowserGUI() + { + if (origBrowserOnGUIDelegate.GetMethodInfo().DeclaringType.Name.Contains("VTabs")) return; + + if (isOneColumn && currentOpacity.Approx(1)) // to optimize locked one-column browser functioning as a dedicated favorites window + if (originalGUICalledOnce) // needs to be called once to init stuff so pinging object won't throw exceptions + return; + + + + if (origBrowserOnGUIDelegate.GetMethodInfo().IsStatic) // wrapped by vFolders + origBrowserOnGUIDelegate.GetMethodInfo().Invoke(null, new[] { wrappedBrowser }); + else + origBrowserOnGUIDelegate.GetMethodInfo().Invoke(wrappedBrowser, null); + + originalGUICalledOnce = true; + + } + + + + + createData(); + closeNavbarSearch(); + + UpdateMouseState(); + UpdateDragging(); + + + var doOriginalGUIFirst = VFavoritesMenu.pageScrollEnabled ? curEvent.isRepaint || curEvent.isLayout + : curEvent.isRepaint; + + if (renamingPage) + doOriginalGUIFirst = !curEvent.isMouseDown && !curEvent.isMouseUp; + + if (renamingPage && curEvent.isMouseDown) + curEvent.Use(); + + + if (doOriginalGUIFirst) + doOriginalGUIFirst_(); + else + doVFavoritesGUIFirst_(); + + + if (isWrappedBrowserLocked) + if (!totalRect_browserSpace.IsHovered() && !animatingDroppedItem && !animatingPageScroll) return; + + if (animatingOpacity || animatingDroppedItem || animatingPageScroll || animatingRowGaps) + wrappedBrowser.Repaint(); + + } + + static void SelectItem(Item item) + { + void openFolder(EditorWindow browser, string path) + { + var folderAsset = AssetDatabase.LoadAssetAtPath(path); + + if (browser.GetFieldValue("m_ViewMode") == 1) + browser.InvokeMethod("SetFolderSelection", new[] { folderAsset.GetInstanceID() }, false); + else + { + Selection.activeObject = folderAsset; + + browser.GetMemberValue("m_AssetTree")?.GetPropertyValue("data")?.InvokeMethod("SetExpanded", folderAsset.GetInstanceID(), true); + + } + + } + + void selectSceneObject() + { + if (!item.isSceneGameObject) return; + + Selection.activeObject = item.obj; + + } + void selectAsset_twoColumns() + { + if (!item.isAsset) return; + if (wrappedBrowser.GetFieldValue("m_ViewMode") != 1) return; + + openFolder(wrappedBrowser, item.assetPath.GetParentPath()); + + Selection.activeObject = item.obj; + + } + void selectAsset_oneColumn() + { + if (!item.isAsset) return; + if (wrappedBrowser.GetFieldValue("m_ViewMode") != 0) return; + + Selection.activeObject = item.obj; + + } + void openFolder_unlocked() + { + if (!item.isFolder) return; + if (isWrappedBrowserLocked) return; + + openFolder(wrappedBrowser, item.assetPath); + + } + void openFolder_locked() + { + if (!item.isFolder) return; + if (!isWrappedBrowserLocked) return; + + var unlockedBrowser = allBrowsers.FirstOrDefault(r => !r.GetMemberValue("isLocked")); + var browserToUse = isOneColumn ? unlockedBrowser : lockedBrowser; + + if (!browserToUse) return; + + openFolder(browserToUse, item.assetPath); + + } + + selectSceneObject(); + selectAsset_twoColumns(); + selectAsset_oneColumn(); + openFolder_unlocked(); + openFolder_locked(); + + item.lastSelectTime_ticks = data.curPage.lastItemSelectTime_ticks = System.DateTime.UtcNow.Ticks; + + } + static void OpenItem(Item item) + { + void openText() + { + if (item.assetPath.GetExtension() != ".cs" + && item.assetPath.GetExtension() != ".shader" + && item.assetPath.GetExtension() != ".compute" + && item.assetPath.GetExtension() != ".cginc" + && item.assetPath.GetExtension() != ".json") return; + + + AssetDatabase.OpenAsset(item.globalId.guid.LoadGuid()); + + } + void openPrefab() + { + if (item.type != typeof(GameObject)) return; + if (!item.isLoadable) return; + if ((item.obj as GameObject).scene.rootCount != 0) return; + + AssetDatabase.OpenAsset(item.obj); + + } + void openScene() + { + if (item.type != typeof(SceneAsset)) return; + + EditorSceneManager.SaveOpenScenes(); + EditorSceneManager.OpenScene(item.assetPath); + + } + void openSceneForNotLoadedGameObject() + { + if (!item.isSceneGameObject) return; + if (item.isLoadable) return; + if (item.isDeleted) return; + + EditorSceneManager.SaveOpenScenes(); + EditorSceneManager.OpenScene(item.assetPath); + + Selection.activeObject = item.obj; + + } + + openText(); + openPrefab(); + openScene(); + openSceneForNotLoadedGameObject(); + + } + + static float rowHeight => 44 * data.rowScale; + + static float crossButtonOffsetFromRight = 23; + static float crossButtonSize = 16; + + static Rect totalRect_browserSpace + { + get + { + if (isOneColumn) + return wrappedBrowser.position.SetPos(0, 0); + + + var treeViewRect = wrappedBrowser.GetFieldValue("m_TreeViewRect"); + + return wrappedBrowser.position.SetPos(0, 0).SetWidth(treeViewRect.width).SetHeightFromBottom(treeViewRect.height); + + } + } + static Rect totalRect_groupSpace => totalRect_browserSpace.SetPos(0, 0); + + static Rect widgetRect_browserSpace => widgetRect_groupSpace.MoveY(totalRect_browserSpace.y); + static Rect widgetRect_groupSpace; + + static bool renamingPage; + static string prevPageName; + + static int lastNumberKeyPressedIndex; + static long lastNumberKeyPressedTime_ticks; + + static bool isOneColumn => wrappedBrowser?.GetFieldValue("m_ViewMode") == 0; + + static bool skipNextRepaint; + static bool originalGUICalledOnce; + + + + + + + + static void UpdateMouseState() // called from WrappedOnGUI + { + if (!shortcutPressed && !isWrappedBrowserLocked) { setDefaultState(); return; } + if (!totalRect_browserSpace.IsHovered()) { setDefaultState(); return; } + + void setDefaultState() + { + mousePressed = false; + mousePressedOnCrossButtonArea = false; + mousePressedOnWidget = false; + pressedItem = null; + doubleclickUnhandled = false; + + } + + void position() + { + mousePosiion_browserSpace = curEvent.mousePosition; + } + void down() + { + if (!curEvent.isMouseDown) return; + + mousePressed = true; + mousePressedOnCrossButtonArea = totalRect_browserSpace.SetWidthFromRight(0).MoveX(-crossButtonOffsetFromRight).SetWidthFromMid(crossButtonSize).IsHovered(); + mousePressedOnWidget = curEvent.mousePosition.y >= widgetRect_browserSpace.y; + + mouseDownPosiion_browserSpace = curEvent.mousePosition; + + var pressedItemIndex = (mouseDownPosition_rowsSpace.y / rowHeight).FloorToInt(); + if (pressedItemIndex.IsInRangeOf(data.curPage.items) && !mousePressedOnCrossButtonArea && !mousePressedOnWidget) + pressedItem = data.curPage.items[pressedItemIndex]; + + doubleclickUnhandled = !mousePressedOnCrossButtonArea && curEvent.clickCount == 2; + + curEvent.Use(); + + } + void up() + { + if (!curEvent.isMouseUp) return; + + mousePressed = false; + doubleclickUnhandled = false; + pressedItem = null; + + } + + position(); + down(); + up(); + + } + + static bool mousePressed; + static bool mousePresesdOnItem => pressedItem != null; + static bool mousePressedOnCrossButtonArea; + static bool mousePressedOnWidget; + static bool doubleclickUnhandled; + + static float groupRectOffsetY => isOneColumn ? 0 : -20; + + static Vector2 mousePosiion_browserSpace; + static Vector2 mousePosition_groupSpace => mousePosiion_browserSpace.AddY(groupRectOffsetY); + static Vector2 mousePosition_rowsSpace => mousePosiion_browserSpace.AddY(groupRectOffsetY + data.curPage.scrollPos); + + static Vector2 mouseDownPosiion_browserSpace; + static Vector2 mouseDownPosition_groupSpace => mouseDownPosiion_browserSpace.AddY(groupRectOffsetY); + static Vector2 mouseDownPosition_rowsSpace => mouseDownPosiion_browserSpace.AddY(groupRectOffsetY + data.curPage.scrollPos); + + static float mouseDragDistance => (mousePosiion_browserSpace - mouseDownPosiion_browserSpace).magnitude; + + + static Item pressedItem; + + + + + + + static void UpdateAnimations() // Update + { + void calcDeltaTime() + { + deltaTime = (float)(EditorApplication.timeSinceStartup - lastLayoutTime); + + if (deltaTime > .05f) + deltaTime = .0166f; + + lastLayoutTime = EditorApplication.timeSinceStartup; + + } + void opacity() + { + if (!VFavoritesMenu.fadeAnimationsEnabled) { currentOpacity = targetOpacity; return; } + if (!UnityEditorInternal.InternalEditorUtility.isApplicationActive) { currentOpacity = targetOpacity; return; } + + SmoothDamp(ref currentOpacity, targetOpacity, 11, ref currentOpacityDerivative, deltaTime); + + if (targetOpacity == 0 && currentOpacity < .04f) + currentOpacity = 0; + + } + void pagesScroll() + { + if (!data) return; + if (!VFavoritesMenu.pageScrollAnimationEnabled) { pagesScrollPos = data.curPageIndex; return; } + + if (pagesScrollPos == -1) + pagesScrollPos = data.curPageIndex; + + SmoothDamp(ref pagesScrollPos, data.curPageIndex, 5, ref pagesScrollDerivative, deltaTime); + + if (pagesScrollPos.DistanceTo(data.curPageIndex) < .001f) + pagesScrollPos = data.curPageIndex; + + } + void rowGaps() + { + if (!data) return; + + var lerpSpeed = 10; + + for (int i = 0; i < data.curPage.rowGaps.Count; i++) + data.curPage.rowGaps[i] = Lerp(data.curPage.rowGaps[i], draggingItem && i == insertDraggedItemAtIndex ? rowHeight : 0, lerpSpeed, deltaTime); + + + } + void droppedItem() + { + if (!animatingDroppedItem) return; + + var yLerpSpeed = 8; + var shadowLerpSpeed = 8; + var highlightLerpSpeed = 10; + + SmoothDamp(ref droppedItemY_rowsSpace, data.curPage.items.IndexOf(VFavorites.droppedItem) * rowHeight, yLerpSpeed, ref droppedItemYDerivative, deltaTime); + Lerp(ref droppedItemShadowAmount, 0, shadowLerpSpeed, deltaTime); + Lerp(ref droppedItemHighlightAmount, 0, highlightLerpSpeed, deltaTime); + + if (droppedItemShadowAmount < .01f) + animatingDroppedItem = false; + + } + void pageButtons() + { + if (!VFavoritesMenu.pageScrollAnimationEnabled) { prevPageButtonBrightness = nextPageButtonBrightness = 1; return; } + + var lerpSpeed = 7; + + Lerp(ref prevPageButtonBrightness, 1, lerpSpeed, deltaTime); + Lerp(ref nextPageButtonBrightness, 1, lerpSpeed, deltaTime); + + + } + + calcDeltaTime(); + opacity(); + pagesScroll(); + rowGaps(); + droppedItem(); + pageButtons(); + + } + + static void CancelRowAnimations() + { + for (int i = 0; i < data.curPage.rowGaps.Count; i++) + data.curPage.rowGaps[i] = 0; + + animatingDroppedItem = false; + droppedItem = null; + + } + + static float deltaTime; + static double lastLayoutTime; + + static bool animatingRowGaps => data.curPage.rowGaps.Any(r => r > .1f && r < rowHeight - .1f); + + static float pagesScrollPos = -1; + static float pagesScrollDerivative; + static bool animatingPageScroll => !pagesScrollPos.Approx(data ? data.curPageIndex : 0); + + static float droppedItemY_rowsSpace; + static float droppedItemYDerivative; + static float droppedItemShadowAmount; + static float droppedItemHighlightAmount; + static bool animatingDroppedItem; + + static float prevPageButtonBrightness = 1; + static float nextPageButtonBrightness = 1; + + static float currentOpacity; + static float currentOpacityDerivative; + static float targetOpacity => curEvent.holdingAlt || renamingPage || draggingItemFromPageToOutside || isWrappedBrowserLocked ? 1 : 0; // holdingAlt instead of shortcutPressed to prevent unwrapping due to incorrect event modifiers on key down on mac + static bool animatingOpacity => currentOpacity.DistanceTo(targetOpacity) > .01f; + + + + + + static void UpdateDragging() // called from WrappedOnGUI + { + void initFromOutside() + { + if (draggingItem) return; + if (!totalRect_browserSpace.IsHovered()) return; + if (!curEvent.isDragUpdate) return; + if (!DragAndDrop.objectReferences.FirstOrDefault()) return; + if (draggingItemFromPageToOutside) return; // to avoid duplication when dragging item from page to outside and back + + + animatingDroppedItem = false; + + draggingItem = true; + draggingItemFromPage = false; + + draggedItem = new Item(DragAndDrop.objectReferences.FirstOrDefault()); + draggedItemHoldOffset = 0; + + data.curPage.lastItemDragTime_ticks = System.DateTime.UtcNow.Ticks; + + } + void initFromPage() + { + if (draggingItem) return; + if (!totalRect_browserSpace.IsHovered()) return; + if (!curEvent.isMouseDrag) return; + if (mouseDragDistance < 2) return; + + + var i = (mouseDownPosition_rowsSpace.y / rowHeight).FloorToInt(); + + if (i >= data.curPage.items.Count) return; + if (i < 0) return; // somehow i = -1 with mouseDownPosition_rowsSpace.y = -20 when dragging in from outside quickly + + + animatingDroppedItem = false; + + draggingItem = true; + draggingItemFromPage = true; + draggingItemFromPageAtIndex = i; + + draggedItem = data.curPage.items[i]; + draggedItemHoldOffset = (i * rowHeight + rowHeight / 2) - mouseDownPosition_rowsSpace.y; + + data.curPage.lastItemDragTime_ticks = System.DateTime.UtcNow.Ticks; + + data.curPage.items.Remove(draggedItem); + data.curPage.rowGaps[draggingItemFromPageAtIndex] = rowHeight; + + data.Dirty(); + data.Save(); + + } + + void acceptFromOutside() + { + if (!draggingItem) return; + if (!curEvent.isDragPerform) return; + + DragAndDrop.AcceptDrag(); + curEvent.Use(); + + AcceptDragging(); + + } + void acceptFromPage() + { + if (!draggingItem) return; + if (!curEvent.isMouseUp) return; + + curEvent.Use(); + + AcceptDragging(); + } + + void cancelFromOutside() + { + if (!draggingItemFromOutside) return; + if (totalRect_browserSpace.IsHovered()) return; + + CancelDragging(); + + } + void cancelFromPageAndInitToOutside() + { + if (!curEvent.isMouseDrag) return; + if (!draggingItemFromPage) return; + if (totalRect_browserSpace.IsHovered()) return; + if (DragAndDrop.objectReferences.Any()) return; + + + DragAndDrop.PrepareStartDrag(); + DragAndDrop.objectReferences = new[] { draggedItem.obj }; + DragAndDrop.StartDrag(draggedItem.name); + + CancelDragging(); + + draggingItemFromPageToOutside = true; + + } + + void setVisualMode() + { + if (!draggingItem) return; + + DragAndDrop.visualMode = DragAndDropVisualMode.Generic; + + } + void setHotControl() + { + if (!draggingItem) return; + + EditorGUIUtility.hotControl = EditorGUIUtility.GetControlID(FocusType.Passive); + + } + + void resetDraggingItemFromPageToOutside() // delayCall loop + { + if (!DragAndDrop.objectReferences.Any()) + draggingItemFromPageToOutside = false; + + EditorApplication.delayCall -= resetDraggingItemFromPageToOutside; + EditorApplication.delayCall += resetDraggingItemFromPageToOutside; + + // DragAndDrop.objectReferences is unreliable outside of delayCall + + } + + + initFromOutside(); + initFromPage(); + + acceptFromOutside(); + acceptFromPage(); + + cancelFromOutside(); + cancelFromPageAndInitToOutside(); + + setVisualMode(); + setHotControl(); + + EditorApplication.delayCall -= resetDraggingItemFromPageToOutside; + EditorApplication.delayCall += resetDraggingItemFromPageToOutside; + + } + + static void AcceptDragging() + { + draggingItem = false; + draggingItemFromPage = false; + mousePressed = false; + + data.curPage.items.AddAt(draggedItem, insertDraggedItemAtIndex); + + data.curPage.rowGaps[insertDraggedItemAtIndex] -= rowHeight; + data.curPage.rowGaps.AddAt(0, insertDraggedItemAtIndex); + + droppedItem = draggedItem; + + droppedItemY_rowsSpace = draggedItemY_groupSpace + data.curPage.scrollPos; + droppedItemYDerivative = 0; + droppedItemShadowAmount = droppedItemHighlightAmount = 1; + animatingDroppedItem = true; + + draggedItem = null; + + EditorGUIUtility.hotControl = 0; + + data.Dirty(); + data.Save(); + + } + static void CancelDragging() + { + if (!draggingItem) return; + + draggingItem = false; + mousePressed = false; + + + if (!draggingItemFromPage) { draggedItem = null; return; } + + data.curPage.items.AddAt(draggedItem, draggingItemFromPageAtIndex); + + data.curPage.rowGaps[draggingItemFromPageAtIndex] -= rowHeight; + droppedItem = draggedItem; + droppedItemY_rowsSpace = draggedItemY_groupSpace - data.curPage.scrollPos; + droppedItemShadowAmount = droppedItemHighlightAmount = 1; + animatingDroppedItem = true; + + draggingItemFromPage = false; + + draggedItem = null; + + EditorGUIUtility.hotControl = 0; + + data.Dirty(); + data.Save(); + + } + + static bool draggingItem; + static bool draggingItemFromPage; + static bool draggingItemFromPageToOutside; + static bool draggingItemFromOutside => draggingItem && !draggingItemFromPage; + static int draggingItemFromPageAtIndex; + static Item draggedItem; + static float draggedItemHoldOffset; + static float draggedItemY_groupSpace => (mousePosition_groupSpace.y - rowHeight / 2 + draggedItemHoldOffset).Clamp(0, 12321); + static float draggedItemY_rowsSpace => draggedItemY_groupSpace + data.curPage.scrollPos; + static int insertDraggedItemAtIndex => ((mousePosition_rowsSpace.y + draggedItemHoldOffset) / rowHeight).FloorToInt().Clamp(0, data.curPage.items.Count); + + static Item droppedItem; + + + + + + + + + + + + + + + static void UpdateGUIWrapping() // called from EditorApplicaton.update + { + void wrap() + { + if (wrappedBrowser) return; + if (!UnityEditorInternal.InternalEditorUtility.isApplicationActive) return; + if (!shortcutPressed) return; + if (EditorWindow.mouseOverWindow?.GetType() != t_BrowserWindow) return; + if (t_VTabs != null && !EditorPrefsCached.GetBool("vTabs-pluginDisabled", false) && EditorWindow.mouseOverWindow.GetMemberValue("isLocked")) return; + + + WrapBrowserGUI(EditorWindow.mouseOverWindow); + + wrappedBrowser = EditorWindow.mouseOverWindow; + + wrappedBrowser.Focus(); + wrappedBrowser.Repaint(); + + t_BrowserWindow.SetFieldValue("s_LastInteractedProjectBrowser", wrappedBrowser); // so vTabs can copy its layout setting + + } + void unwrap() + { + if (!wrappedBrowser) return; + if (shortcutPressed && wrappedBrowser.hasFocus) return; + if (currentOpacity > 0 && wrappedBrowser.hasFocus) return; + + + CancelDragging(); + CancelRowAnimations(); + + UnwrapBrowserGUI(); + + wrappedBrowser.Repaint(); + + wrappedBrowser = null; + + } + + unwrap(); + wrap(); + + } + + static void WrapBrowserGUI(EditorWindow browser) + { + var hostView = fi_m_Parent.GetValue(browser); + var newDelegate = mi_WrappedOnGUI.CreateDelegate(t_EditorWindowDelegate, hostView); + + origBrowserOnGUIDelegate = fi_m_OnGUI.GetValue(hostView) as System.Delegate; + fi_m_OnGUI.SetValue(hostView, newDelegate); + + } + static void UnwrapBrowserGUI() + { + if (wrappedBrowser.GetFieldValue("m_Parent").GetFieldValue("m_OnGUI").Method != mi_WrappedOnGUI) return; + + wrappedBrowser.GetFieldValue("m_Parent").SetFieldValue("m_OnGUI", origBrowserOnGUIDelegate); + + } + + static EditorWindow wrappedBrowser; + static System.Delegate origBrowserOnGUIDelegate; + + static bool shortcutPressed + { + get + { + if (VFavoritesMenu.activeOnAltEnabled) + return curEvent.holdingAlt; + + if (VFavoritesMenu.activeOnAltShiftEnabled) + return curEvent.modifiers == (EventModifiers.Alt | EventModifiers.Shift); + + if (VFavoritesMenu.activeOnCtrlAltEnabled) + if (Application.platform == RuntimePlatform.OSXEditor) + return curEvent.modifiers == (EventModifiers.Command | EventModifiers.Alt); + else + return curEvent.modifiers == (EventModifiers.Control | EventModifiers.Alt); + + return false; + } + } + + + + + + + static void UpdateLocking() // called from EditorApplicaton.update + { + void unsetWrappedBrowser() + { + if (!isWrappedBrowserLocked) return; + + if (wrappedBrowser.GetFieldValue("m_Parent").GetFieldValue("m_OnGUI").Method == mi_WrappedOnGUI) return; + + wrappedBrowser = null; + + // nulls wrappedBrowser if it was unwrapped externally + // ie when locked browser gets moved or maximized + + } + void markLockedBrowser() + { + if (!lockedBrowser) return; + + MarkAsLocked(lockedBrowser); + + // fixes marking getting reset on scene load + + } + void setMinWidthOnLockedBrowser() + { + if (!lockedBrowser) return; + if (lockedBrowser.minSize.x == 100) return; + + lockedBrowser.minSize = Vector2.one * 100; + + } + + void lock_() + { + if (lockedBrowser) return; + if (!wrappedBrowser) return; + if (!wrappedBrowser.GetMemberValue("isLocked")) return; + + lockedBrowser = wrappedBrowser; + + EditorPrefsCached.SetInt("vFavorites-lockedBrowserHash", lockedBrowser.GetHashCode()); + EditorPrefsCached.SetInt("vFavorites-lockedBrowserDockAreaInstanceId", lockedBrowser.GetMemberValue("m_Parent").GetInstanceID()); + + curEvent.Use(); + + } + void unlock() + { + if (!lockedBrowser) return; + if (lockedBrowser.GetMemberValue("isLocked")) return; + + + lockedBrowser = null; + + EditorPrefsCached.SetInt("vFavorites-lockedBrowserHash", 0); + EditorPrefsCached.SetInt("vFavorites-lockedBrowserDockAreaInstanceId", 0); + + curEvent.Use(); + + } + void wrap() + { + if (!lockedBrowser) return; + if (!lockedBrowser.hasFocus) return; + if (isWrappedBrowserLocked) return; + + WrapBrowserGUI(lockedBrowser); + + currentOpacity = 1; + + wrappedBrowser = lockedBrowser; + + } + + + unsetWrappedBrowser(); + markLockedBrowser(); + setMinWidthOnLockedBrowser(); + + lock_(); + unlock(); + wrap(); + + } + + static EditorWindow lockedBrowser + { + get + { + if (_lockedBrowser) return _lockedBrowser; + + + var lockedBrowserInstanceId = EditorPrefsCached.GetInt("vFavorites-lockedBrowserInstanceId", 0); + + if (lockedBrowserInstanceId == 0) return null; + + + + var window = Resources.InstanceIDToObject(lockedBrowserInstanceId) as EditorWindow; + + if (window && window.GetType() == t_BrowserWindow) // prevents iid collisions + _lockedBrowser = window; + + if (_lockedBrowser) return _lockedBrowser; + + + + _lockedBrowser = allBrowsers.FirstOrDefault(r => IsMarkedAsLocked(r)); + + if (_lockedBrowser) return _lockedBrowser; // todo set instanceid + + + + // EditorPrefsCached.SetInt("vFavorites-lockedBrowserInstanceId", 0); // one attempt to find the locked browser isn't enough after unmaximize + + return null; + + } + set + { + if (_lockedBrowser) + MarkAsUnlocked(_lockedBrowser); + + if (value == null) + { + EditorPrefsCached.SetInt("vFavorites-lockedBrowserInstanceId", 0); + + _lockedBrowser = null; + + } + else + { + EditorPrefsCached.SetInt("vFavorites-lockedBrowserInstanceId", value.GetInstanceID()); + + MarkAsLocked(value); + + _lockedBrowser = value; + + } + + } + } + static EditorWindow _lockedBrowser; + + static bool IsMarkedAsLocked(EditorWindow browser) => browser.GetMemberValue("m_SearchFilter")?.GetMemberValue("m_OriginalText") == "asd"; + static void MarkAsLocked(EditorWindow browser) => browser.GetMemberValue("m_SearchFilter")?.SetMemberValue("m_OriginalText", "asd"); + static void MarkAsUnlocked(EditorWindow browser) => browser.GetMemberValue("m_SearchFilter")?.SetMemberValue("m_OriginalText", ""); + + static bool isWrappedBrowserLocked => wrappedBrowser && wrappedBrowser == lockedBrowser; + + static bool CanBrowserBeWrapped_byVTabs(EditorWindow browser) => !IsMarkedAsLocked(browser); + + + + + static void RepaintOnAltUp() // Update + { + var lastEvent = typeof(Event).GetFieldValue("s_Current"); + + if (wasAlt && !lastEvent.alt) + if (EditorWindow.mouseOverWindow?.GetType() == t_BrowserWindow) + EditorApplication.RepaintProjectWindow(); + + wasAlt = lastEvent.alt; + + } + + static bool wasAlt; + + + + + + + + + + + + + [InitializeOnLoadMethod] + static void Init() + { + if (VFavoritesMenu.pluginDisabled) return; + + void subscribe() + { + EditorApplication.update -= UpdateGUIWrapping; + EditorApplication.update += UpdateGUIWrapping; + + EditorApplication.update -= UpdateLocking; + EditorApplication.update += UpdateLocking; + + EditorApplication.update -= UpdateAnimations; + EditorApplication.update += UpdateAnimations; + + EditorApplication.update -= RepaintOnAltUp; + EditorApplication.update += RepaintOnAltUp; + + + + EditorApplication.quitting -= VFavoritesState.Save; + EditorApplication.quitting += VFavoritesState.Save; + + } + void loadData() + { + data = AssetDatabase.LoadAssetAtPath(EditorPrefsCached.GetString("vFavorites-lastKnownDataPath-" + GetProjectId())); + + + if (data) return; + + data = AssetDatabase.FindAssets("t:VFavoritesData").Select(guid => AssetDatabase.LoadAssetAtPath(guid.ToPath())).FirstOrDefault(); + + + if (!data) return; + + EditorPrefsCached.SetString("vFavorites-lastKnownDataPath-" + GetProjectId(), data.GetPath()); + + } + void loadDataDelayed() + { + if (data) return; + + EditorApplication.delayCall += () => EditorApplication.delayCall += loadData; + + // AssetDatabase isn't up to date at this point (it gets updated after InitializeOnLoadMethod) + // and if current AssetDatabase state doesn't contain the data - it won't be loaded during Init() + // so here we schedule an additional, delayed attempt to load the data + // this addresses reports of data loss when trying to load it on a new machine + + } + + subscribe(); + loadData(); + loadDataDelayed(); + + } + + public static VFavoritesData data; + + + + + + + + static void BeforeWindowCreated_byVTabs(object dockArea) + { + if (!wrappedBrowser) return; + if (wrappedBrowser.GetFieldValue("m_Parent") != dockArea) return; + + CancelDragging(); + CancelRowAnimations(); + + UnwrapBrowserGUI(); + + draggingItemFromPageToOutside = false; + + } + + + + + + + static IEnumerable allBrowsers => _allBrowsers ??= t_BrowserWindow.GetFieldValue("s_ProjectBrowsers").Cast(); + static IEnumerable _allBrowsers; + + static Type t_BrowserWindow = typeof(Editor).Assembly.GetType("UnityEditor.ProjectBrowser"); + static Type t_HostView = typeof(Editor).Assembly.GetType("UnityEditor.HostView"); + static Type t_EditorWindowDelegate = t_HostView.GetNestedType("EditorWindowDelegate", maxBindingFlags); + static FieldInfo fi_m_Parent = typeof(EditorWindow).GetField("m_Parent", maxBindingFlags); + static FieldInfo fi_m_OnGUI = t_HostView.GetField("m_OnGUI", maxBindingFlags); + static MethodInfo mi_WrappedOnGUI = typeof(VFavorites).GetMethod(nameof(WrappedOnGUI), maxBindingFlags); + + + static Type t_VHierarchy = Type.GetType("VHierarchy.VHierarchy") ?? Type.GetType("VHierarchy.VHierarchy, VHierarchy, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"); + static Type t_VFolders = Type.GetType("VFolders.VFolders") ?? Type.GetType("VFolders.VFolders, VFolders, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"); + static Type t_VTabs = Type.GetType("VTabs.VTabs") ?? Type.GetType("VTabs.VTabs, VTabs, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"); + + static MethodInfo mi_VHierarchy_GetIconName = t_VHierarchy?.GetMethod("GetIconName_forVFavorites", maxBindingFlags); + static MethodInfo mi_VFolders_DrawBigFolderIcon = t_VFolders?.GetMethod("DrawBigFolderIcon_forVFavorites", maxBindingFlags); + + + + + + const string version = "2.0.10"; + + } +} +#endif diff --git a/Assets/OtherAssets/DEV/PAID/vFavorites/VFavorites.cs.meta b/Assets/OtherAssets/DEV/PAID/vFavorites/VFavorites.cs.meta new file mode 100644 index 0000000..64e3f6a --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFavorites/VFavorites.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6651039474d594cdd91489768cf293f6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vFavorites/VFavoritesData.cs b/Assets/OtherAssets/DEV/PAID/vFavorites/VFavoritesData.cs new file mode 100644 index 0000000..c2dbe74 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFavorites/VFavoritesData.cs @@ -0,0 +1,225 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Serialization; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.Linq; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using Type = System.Type; +using static VFavorites.VFavoritesState; +using static VFavorites.Libs.VUtils; +using static VFavorites.Libs.VGUI; + + + +namespace VFavorites +{ + public class VFavoritesData : ScriptableObject + { + public List pages = new List(); + + public Page curPage + { + get + { + while (curPageIndex >= pages.Count - 1) + pages.Add(new Page("Page " + (pages.Count + 1))); + + return pages[curPageIndex]; + + } + } + + public int curPageIndex { get => VFavoritesState.instance.curPageIndex; set => VFavoritesState.instance.curPageIndex = value; } + + public float rowScale = 1; + + + [System.Serializable] + public class Page + { + public List items = new List(); + + public string name = ""; + + public Page(string name) => this.name = name; + + + + + [System.NonSerialized] + public List _rowGaps = new List(); + public List rowGaps + { + get + { + if (_rowGaps == null) + _rowGaps = new List(); + + while (_rowGaps.Count < items.Count + 1) _rowGaps.Add(0); + while (_rowGaps.Count > items.Count + 1) _rowGaps.RemoveLast(); + + return _rowGaps; + + } + } + + + public float scrollPos { get => state.scrollPos; set => state.scrollPos = value; } + + public long lastItemSelectTime_ticks { get => state.lastItemSelectTime_ticks; set => state.lastItemSelectTime_ticks = value; } + public long lastItemDragTime_ticks { get => state.lastItemDragTime_ticks; set => state.lastItemDragTime_ticks = value; } + + + public PageState state + { + get + { + if (!VFavoritesState.instance.pageStates_byPageId.ContainsKey(id)) + VFavoritesState.instance.pageStates_byPageId[id] = new PageState(); + + return VFavoritesState.instance.pageStates_byPageId[id]; + + } + } + + public int id + { + get + { + if (_id == 0) + _id = Random.value.GetHashCode(); + + return _id; + + } + } + public int _id = 0; + + } + + [System.Serializable] + public class Item + { + public GlobalID globalId; + + + public Type type => Type.GetType(_typeString) ?? typeof(DefaultAsset); + public string _typeString; + + public Object obj => _obj != null ? _obj : (_obj = globalId.GetObject()); + public Object _obj; + + + public bool isSceneGameObject; + public bool isFolder; + public bool isAsset; + + + public bool isLoadable => obj != null; + + public bool isDeleted + { + get + { + if (!isSceneGameObject) + return !isLoadable; + + if (isLoadable) + return false; + + if (!AssetDatabase.LoadAssetAtPath(globalId.guid.ToPath())) + return true; + + for (int i = 0; i < EditorSceneManager.sceneCount; i++) + if (EditorSceneManager.GetSceneAt(i).path == globalId.guid.ToPath()) + return true; + + return false; + + } + } + + public string assetPath => globalId.guid.ToPath(); + + + public Item(Object o) + { + globalId = o.GetGlobalID(); + + + isSceneGameObject = o is GameObject go && go.scene.rootCount != 0; + isFolder = AssetDatabase.IsValidFolder(o.GetPath()); + isAsset = !isSceneGameObject && !isFolder; + + _typeString = o.GetType().AssemblyQualifiedName; + + _name = o.name; + + } + + + + + + public string name + { + get + { + if (!isLoadable) return _name; + + if (assetPath.GetExtension() == ".cs") + _name = obj.name.Decamelcase(); + else + _name = obj.name; + + return _name; + + } + } + public string _name { get => state._name; set => state._name = value; } + + public string sceneGameObjectIconName { get => state.sceneGameObjectIconName; set => state.sceneGameObjectIconName = value; } + + public long lastSelectTime_ticks { get => state.lastSelectTime_ticks; set => state.lastSelectTime_ticks = value; } + public bool isSelected { get => state.isSelected; set => state.isSelected = value; } + + + + public ItemState state + { + get + { + if (!VFavoritesState.instance.itemStates_byItemId.ContainsKey(id)) + VFavoritesState.instance.itemStates_byItemId[id] = new ItemState(); + + return VFavoritesState.instance.itemStates_byItemId[id]; + + } + } + + public int id + { + get + { + if (_id == 0) + _id = Random.value.GetHashCode(); + + return _id; + + } + } + public int _id = 0; + + + } + + + } +} +#endif \ No newline at end of file diff --git a/Assets/OtherAssets/DEV/PAID/vFavorites/VFavoritesData.cs.meta b/Assets/OtherAssets/DEV/PAID/vFavorites/VFavoritesData.cs.meta new file mode 100644 index 0000000..92b497a --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFavorites/VFavoritesData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 066cf82f8f80d408c856e48fc8f1127b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vFavorites/VFavoritesLibs.cs b/Assets/OtherAssets/DEV/PAID/vFavorites/VFavoritesLibs.cs new file mode 100644 index 0000000..ad3851d --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFavorites/VFavoritesLibs.cs @@ -0,0 +1,1816 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; +using System.Linq; +using UnityEngine.UIElements; +using Type = System.Type; +using static VFavorites.Libs.VUtils; + + +namespace VFavorites.Libs +{ + public static class VUtils + { + #region Text + + + public static string Decamelcase(this string str) => Regex.Replace(Regex.Replace(str, @"(\P{Ll})(\P{Ll}\p{Ll})", "$1 $2"), @"(\p{Ll})(\P{Ll})", "$1 $2"); + + public static string Remove(this string s, string toRemove) + { + if (toRemove == "") return s; + return s.Replace(toRemove, ""); + } + + public static bool IsEmpty(this string s) => s == ""; + public static bool IsNullOrEmpty(this string s) => string.IsNullOrEmpty(s); + + + + + + #endregion + + #region IEnumerables + + + public static T AddAt(this List l, T r, int i) + { + if (i < 0) i = 0; + if (i >= l.Count) + l.Add(r); + else + l.Insert(i, r); + return r; + } + public static T RemoveLast(this List l) + { + if (!l.Any()) return default; + + var r = l.Last(); + + l.RemoveAt(l.Count - 1); + + return r; + } + + public static void Add(this List list, params T[] items) + { + foreach (var r in items) + list.Add(r); + } + + public static int LastIndex(this List l) => l.Count - 1; // toremove + + // public static T GetAtWrapped(this List list, int i) // toremove + // { + // while (i < 0) i += list.Count; + // while (i >= list.Count) i -= list.Count; + + // return list[i]; + // } + + + + + + #endregion + + #region Linq + + + public static T NextTo(this IEnumerable e, T to) => e.SkipWhile(r => !r.Equals(to)).Skip(1).FirstOrDefault(); + public static T PreviousTo(this IEnumerable e, T to) => e.Reverse().SkipWhile(r => !r.Equals(to)).Skip(1).FirstOrDefault(); + public static T NextToOtFirst(this IEnumerable e, T to) => e.NextTo(to) ?? e.First(); + public static T PreviousToOrLast(this IEnumerable e, T to) => e.PreviousTo(to) ?? e.Last(); + + public static Dictionary MergeDictionaries(IEnumerable> dicts) + { + if (dicts.Count() == 0) return null; + if (dicts.Count() == 1) return dicts.First(); + + var mergedDict = new Dictionary(dicts.First()); + + foreach (var dict in dicts.Skip(1)) + foreach (var r in dict) + if (!mergedDict.ContainsKey(r.Key)) + mergedDict.Add(r.Key, r.Value); + + return mergedDict; + } + + public static IEnumerable InsertFirst(this IEnumerable ie, T t) => new[] { t }.Concat(ie); + + public static bool None(this IEnumerable ie, System.Func f) => !ie.Any(f); + public static bool None(this IEnumerable ie) => !ie.Any(); + + public static int IndexOfFirst(this List list, System.Func f) => list.FirstOrDefault(f) is T t ? list.IndexOf(t) : -1; + public static int IndexOfLast(this List list, System.Func f) => list.LastOrDefault(f) is T t ? list.IndexOf(t) : -1; + + public static void SortBy(this List list, System.Func keySelector) where T2 : System.IComparable => list.Sort((q, w) => keySelector(q).CompareTo(keySelector(w))); + + public static void RemoveValue(this IDictionary dictionary, TValue value) + { + if (dictionary.FirstOrDefault(r => r.Value.Equals(value)) is var kvp) + dictionary.Remove(kvp); + } + + + public static void ForEach(this IEnumerable sequence, System.Action action) { foreach (T item in sequence) action(item); } + + + + + + #endregion + + #region Reflection + + public static object GetFieldValue(this object o, string fieldName, bool exceptionIfNotFound = true) + { + var type = (o as Type) ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetFieldInfo(fieldName) is FieldInfo fieldInfo) + return fieldInfo.GetValue(target); + + + if (exceptionIfNotFound) + throw new System.Exception($"Field '{fieldName}' not found in type '{type.Name}' and its parent types"); + + return null; + + } + public static object GetPropertyValue(this object o, string propertyName, bool exceptionIfNotFound = true) + { + var type = (o as Type) ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetPropertyInfo(propertyName) is PropertyInfo propertyInfo) + return propertyInfo.GetValue(target); + + + if (exceptionIfNotFound) + throw new System.Exception($"Property '{propertyName}' not found in type '{type.Name}' and its parent types"); + + return null; + + } + public static object GetMemberValue(this object o, string memberName, bool exceptionIfNotFound = true) + { + var type = (o as Type) ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetFieldInfo(memberName) is FieldInfo fieldInfo) + return fieldInfo.GetValue(target); + + if (type.GetPropertyInfo(memberName) is PropertyInfo propertyInfo) + return propertyInfo.GetValue(target); + + + if (exceptionIfNotFound) + throw new System.Exception($"Member '{memberName}' not found in type '{type.Name}' and its parent types"); + + return null; + + } + + public static void SetFieldValue(this object o, string fieldName, object value, bool exceptionIfNotFound = true) + { + var type = (o as Type) ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetFieldInfo(fieldName) is FieldInfo fieldInfo) + fieldInfo.SetValue(target, value); + + + else if (exceptionIfNotFound) + throw new System.Exception($"Field '{fieldName}' not found in type '{type.Name}' and its parent types"); + + } + public static void SetPropertyValue(this object o, string propertyName, object value, bool exceptionIfNotFound = true) + { + var type = (o as Type) ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetPropertyInfo(propertyName) is PropertyInfo propertyInfo) + propertyInfo.SetValue(target, value); + + + else if (exceptionIfNotFound) + throw new System.Exception($"Property '{propertyName}' not found in type '{type.Name}' and its parent types"); + + } + public static void SetMemberValue(this object o, string memberName, object value, bool exceptionIfNotFound = true) + { + var type = (o as Type) ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetFieldInfo(memberName) is FieldInfo fieldInfo) + fieldInfo.SetValue(target, value); + + else if (type.GetPropertyInfo(memberName) is PropertyInfo propertyInfo) + propertyInfo.SetValue(target, value); + + + else if (exceptionIfNotFound) + throw new System.Exception($"Member '{memberName}' not found in type '{type.Name}' and its parent types"); + + } + + public static object InvokeMethod(this object o, string methodName, params object[] parameters) // todo handle null params (can't get their type) + { + var type = (o as Type) ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetMethodInfo(methodName, parameters.Select(r => r.GetType()).ToArray()) is MethodInfo methodInfo) + return methodInfo.Invoke(target, parameters); + + + throw new System.Exception($"Method '{methodName}' not found in type '{type.Name}', its parent types and interfaces"); + + } + + + + static FieldInfo GetFieldInfo(this Type type, string fieldName) + { + if (fieldInfoCache.TryGetValue(type, out var fieldInfosByNames)) + if (fieldInfosByNames.TryGetValue(fieldName, out var fieldInfo)) + return fieldInfo; + + + if (!fieldInfoCache.ContainsKey(type)) + fieldInfoCache[type] = new Dictionary(); + + for (var curType = type; curType != null; curType = curType.BaseType) + if (curType.GetField(fieldName, maxBindingFlags) is FieldInfo fieldInfo) + return fieldInfoCache[type][fieldName] = fieldInfo; + + + return fieldInfoCache[type][fieldName] = null; + + } + static Dictionary> fieldInfoCache = new Dictionary>(); + + static PropertyInfo GetPropertyInfo(this Type type, string propertyName) + { + if (propertyInfoCache.TryGetValue(type, out var propertyInfosByNames)) + if (propertyInfosByNames.TryGetValue(propertyName, out var propertyInfo)) + return propertyInfo; + + + if (!propertyInfoCache.ContainsKey(type)) + propertyInfoCache[type] = new Dictionary(); + + for (var curType = type; curType != null; curType = curType.BaseType) + if (curType.GetProperty(propertyName, maxBindingFlags) is PropertyInfo propertyInfo) + return propertyInfoCache[type][propertyName] = propertyInfo; + + + return propertyInfoCache[type][propertyName] = null; + + } + static Dictionary> propertyInfoCache = new Dictionary>(); + + static MethodInfo GetMethodInfo(this Type type, string methodName, params Type[] argumentTypes) + { + var methodHash = methodName.GetHashCode() ^ argumentTypes.Aggregate(0, (hash, r) => hash ^= r.GetHashCode()); + + + if (methodInfoCache.TryGetValue(type, out var methodInfosByHashes)) + if (methodInfosByHashes.TryGetValue(methodHash, out var methodInfo)) + return methodInfo; + + + + if (!methodInfoCache.ContainsKey(type)) + methodInfoCache[type] = new Dictionary(); + + for (var curType = type; curType != null; curType = curType.BaseType) + if (curType.GetMethod(methodName, maxBindingFlags, null, argumentTypes, null) is MethodInfo methodInfo) + return methodInfoCache[type][methodHash] = methodInfo; + + foreach (var interfaceType in type.GetInterfaces()) + if (interfaceType.GetMethod(methodName, maxBindingFlags, null, argumentTypes, null) is MethodInfo methodInfo) + return methodInfoCache[type][methodHash] = methodInfo; + + + + return methodInfoCache[type][methodHash] = null; + + } + static Dictionary> methodInfoCache = new Dictionary>(); + + + + public static T GetFieldValue(this object o, string fieldName, bool exceptionIfNotFound = true) => (T)o.GetFieldValue(fieldName, exceptionIfNotFound); + public static T GetPropertyValue(this object o, string propertyName, bool exceptionIfNotFound = true) => (T)o.GetPropertyValue(propertyName, exceptionIfNotFound); + public static T GetMemberValue(this object o, string memberName, bool exceptionIfNotFound = true) => (T)o.GetMemberValue(memberName, exceptionIfNotFound); + public static T InvokeMethod(this object o, string methodName, params object[] parameters) => (T)o.InvokeMethod(methodName, parameters); + + + + + + + public static List GetSubclasses(this Type t) => t.Assembly.GetTypes().Where(type => type.IsSubclassOf(t)).ToList(); + + public static object GetDefaultValue(this FieldInfo f, params object[] constructorVars) => f.GetValue(System.Activator.CreateInstance(((MemberInfo)f).ReflectedType, constructorVars)); + + public static object GetDefaultValue(this FieldInfo f) => f.GetValue(System.Activator.CreateInstance(((MemberInfo)f).ReflectedType)); + + + public static IEnumerable GetFieldsWithoutBase(this Type t) => t.GetFields().Where(r => !t.BaseType.GetFields().Any(rr => rr.Name == r.Name)); + + public static IEnumerable GetPropertiesWithoutBase(this Type t) => t.GetProperties().Where(r => !t.BaseType.GetProperties().Any(rr => rr.Name == r.Name)); + + + public const BindingFlags maxBindingFlags = (BindingFlags)62; + + + + + + + + #endregion + + #region Math + + + public static bool Approx(this float f1, float f2) => Mathf.Approximately(f1, f2); + public static float DistanceTo(this float f1, float f2) => Mathf.Abs(f1 - f2); + public static float DistanceTo(this Vector2 f1, Vector2 f2) => (f1 - f2).magnitude; + public static float DistanceTo(this Vector3 f1, Vector3 f2) => (f1 - f2).magnitude; + public static float Dist(float f1, float f2) => Mathf.Abs(f1 - f2); + public static float Avg(float f1, float f2) => (f1 + f2) / 2; + public static float Abs(this float f) => Mathf.Abs(f); + public static int Abs(this int f) => Mathf.Abs(f); + public static float Sign(this float f) => Mathf.Sign(f); + public static float Clamp(this float f, float f0, float f1) => Mathf.Clamp(f, f0, f1); + public static int Clamp(this int f, int f0, int f1) => Mathf.Clamp(f, f0, f1); + public static float Clamp01(this float f) => Mathf.Clamp(f, 0, 1); + public static Vector2 Clamp01(this Vector2 f) => new Vector2(f.x.Clamp01(), f.y.Clamp01()); + public static Vector3 Clamp01(this Vector3 f) => new Vector3(f.x.Clamp01(), f.y.Clamp01(), f.z.Clamp01()); + + + public static float Pow(this float f, float pow) => Mathf.Pow(f, pow); + public static int Pow(this int f, int pow) => (int)Mathf.Pow(f, pow); + + public static float Round(this float f) => Mathf.Round(f); + public static float Ceil(this float f) => Mathf.Ceil(f); + public static float Floor(this float f) => Mathf.Floor(f); + public static int RoundToInt(this float f) => Mathf.RoundToInt(f); + public static int CeilToInt(this float f) => Mathf.CeilToInt(f); + public static int FloorToInt(this float f) => Mathf.FloorToInt(f); + public static int ToInt(this float f) => (int)f; + public static float ToFloat(this int f) => (float)f; + public static float ToFloat(this double f) => (float)f; + + + public static float Sqrt(this float f) => Mathf.Sqrt(f); + + public static float Max(this float f, float ff) => Mathf.Max(f, ff); + public static float Min(this float f, float ff) => Mathf.Min(f, ff); + public static int Max(this int f, int ff) => Mathf.Max(f, ff); + public static int Min(this int f, int ff) => Mathf.Min(f, ff); + + public static float Loop(this float f, float boundMin, float boundMax) + { + while (f < boundMin) f += boundMax - boundMin; + while (f > boundMax) f -= boundMax - boundMin; + return f; + } + public static float Loop(this float f, float boundMax) => f.Loop(0, boundMax); + + public static float PingPong(this float f, float boundMin, float boundMax) => boundMin + Mathf.PingPong(f - boundMin, boundMax - boundMin); + public static float PingPong(this float f, float boundMax) => f.PingPong(0, boundMax); + + + public static float TriangleArea(Vector2 A, Vector2 B, Vector2 C) => Vector3.Cross(A - B, A - C).z.Abs() / 2; + public static Vector2 LineIntersection(Vector2 A, Vector2 B, Vector2 C, Vector2 D) + { + var a1 = B.y - A.y; + var b1 = A.x - B.x; + var c1 = a1 * A.x + b1 * A.y; + + var a2 = D.y - C.y; + var b2 = C.x - D.x; + var c2 = a2 * C.x + b2 * C.y; + + var d = a1 * b2 - a2 * b1; + + var x = (b2 * c1 - b1 * c2) / d; + var y = (a1 * c2 - a2 * c1) / d; + + return new Vector2(x, y); + + } + + public static float ProjectOn(this Vector2 v, Vector2 on) => Vector3.Project(v, on).magnitude; + public static float AngleTo(this Vector2 v, Vector2 to) => Vector2.Angle(v, to); + + public static Vector2 Rotate(this Vector2 v, float deg) => Quaternion.AngleAxis(deg, Vector3.forward) * v; + + public static float Smoothstep(this float f) { f = f.Clamp01(); return f * f * (3 - 2 * f); } + + public static float InverseLerp(this Vector2 v, Vector2 a, Vector2 b) + { + var ab = b - a; + var av = v - a; + return Vector2.Dot(av, ab) / Vector2.Dot(ab, ab); + } + + public static bool IsOdd(this int i) => i % 2 == 1; + public static bool IsEven(this int i) => i % 2 == 0; + + public static bool IsInRange(this int i, int a, int b) => i >= a && i <= b; + public static bool IsInRange(this float i, float a, float b) => i >= a && i <= b; + + public static bool IsInRangeOf(this int i, IList list) => i.IsInRange(0, list.Count - 1); + public static bool IsInRangeOf(this int i, T[] array) => i.IsInRange(0, array.Length - 1); + + + + + + + + #endregion + + #region Lerping + + + public static float LerpT(float lerpSpeed, float deltaTime) => 1 - Mathf.Exp(-lerpSpeed * 2f * deltaTime); + public static float LerpT(float lerpSpeed) => LerpT(lerpSpeed, Time.deltaTime); + + public static float Lerp(float f1, float f2, float t) => Mathf.LerpUnclamped(f1, f2, t); + public static float Lerp(ref float f1, float f2, float t) => f1 = Lerp(f1, f2, t); + + public static Vector2 Lerp(Vector2 f1, Vector2 f2, float t) => Vector2.LerpUnclamped(f1, f2, t); + public static Vector2 Lerp(ref Vector2 f1, Vector2 f2, float t) => f1 = Lerp(f1, f2, t); + + public static Vector3 Lerp(Vector3 f1, Vector3 f2, float t) => Vector3.LerpUnclamped(f1, f2, t); + public static Vector3 Lerp(ref Vector3 f1, Vector3 f2, float t) => f1 = Lerp(f1, f2, t); + + public static Color Lerp(Color f1, Color f2, float t) => Color.LerpUnclamped(f1, f2, t); + public static Color Lerp(ref Color f1, Color f2, float t) => f1 = Lerp(f1, f2, t); + + + public static float Lerp(float current, float target, float speed, float deltaTime) => Mathf.Lerp(current, target, LerpT(speed, deltaTime)); + public static float Lerp(ref float current, float target, float speed, float deltaTime) => current = Lerp(current, target, speed, deltaTime); + + public static Vector2 Lerp(Vector2 current, Vector2 target, float speed, float deltaTime) => Vector2.Lerp(current, target, LerpT(speed, deltaTime)); + public static Vector2 Lerp(ref Vector2 current, Vector2 target, float speed, float deltaTime) => current = Lerp(current, target, speed, deltaTime); + + public static Vector3 Lerp(Vector3 current, Vector3 target, float speed, float deltaTime) => Vector3.Lerp(current, target, LerpT(speed, deltaTime)); + public static Vector3 Lerp(ref Vector3 current, Vector3 target, float speed, float deltaTime) => current = Lerp(current, target, speed, deltaTime); + + public static float SmoothDamp(float current, float target, float speed, ref float derivative, float deltaTime) => Mathf.SmoothDamp(current, target, ref derivative, .5f / speed, Mathf.Infinity, deltaTime); + public static float SmoothDamp(ref float current, float target, float speed, ref float derivative, float deltaTime) => current = SmoothDamp(current, target, speed, ref derivative, deltaTime); + public static float SmoothDamp(float current, float target, float speed, ref float derivative) => SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + public static float SmoothDamp(ref float current, float target, float speed, ref float derivative) => current = SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + + public static Vector2 SmoothDamp(Vector2 current, Vector2 target, float speed, ref Vector2 derivative, float deltaTime) => Vector2.SmoothDamp(current, target, ref derivative, .5f / speed, Mathf.Infinity, deltaTime); + public static Vector2 SmoothDamp(ref Vector2 current, Vector2 target, float speed, ref Vector2 derivative, float deltaTime) => current = SmoothDamp(current, target, speed, ref derivative, deltaTime); + public static Vector2 SmoothDamp(Vector2 current, Vector2 target, float speed, ref Vector2 derivative) => SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + public static Vector2 SmoothDamp(ref Vector2 current, Vector2 target, float speed, ref Vector2 derivative) => current = SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + + public static Vector3 SmoothDamp(Vector3 current, Vector3 target, float speed, ref Vector3 derivative, float deltaTime) => Vector3.SmoothDamp(current, target, ref derivative, .5f / speed, Mathf.Infinity, deltaTime); + public static Vector3 SmoothDamp(ref Vector3 current, Vector3 target, float speed, ref Vector3 derivative, float deltaTime) => current = SmoothDamp(current, target, speed, ref derivative, deltaTime); + public static Vector3 SmoothDamp(Vector3 current, Vector3 target, float speed, ref Vector3 derivative) => SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + public static Vector3 SmoothDamp(ref Vector3 current, Vector3 target, float speed, ref Vector3 derivative) => current = SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + + + + + + + #endregion + + #region Colors + + + public static Color HSLToRGB(float h, float s, float l) + { + float hue2Rgb(float v1, float v2, float vH) + { + if (vH < 0f) + vH += 1f; + + if (vH > 1f) + vH -= 1f; + + if (6f * vH < 1f) + return v1 + (v2 - v1) * 6f * vH; + + if (2f * vH < 1f) + return v2; + + if (3f * vH < 2f) + return v1 + (v2 - v1) * (2f / 3f - vH) * 6f; + + return v1; + } + + if (s.Approx(0)) return new Color(l, l, l); + + float k1; + + if (l < .5f) + k1 = l * (1f + s); + else + k1 = l + s - s * l; + + + var k2 = 2f * l - k1; + + float r, g, b; + r = hue2Rgb(k2, k1, h + 1f / 3); + g = hue2Rgb(k2, k1, h); + b = hue2Rgb(k2, k1, h - 1f / 3); + + return new Color(r, g, b); + } + public static Color LCHtoRGB(float l, float c, float h) + { + l *= 100; + c *= 100; + h *= 360; + + double xw = 0.948110; + double yw = 1.00000; + double zw = 1.07304; + + float a = c * Mathf.Cos(Mathf.Deg2Rad * h); + float b = c * Mathf.Sin(Mathf.Deg2Rad * h); + + float fy = (l + 16) / 116; + float fx = fy + (a / 500); + float fz = fy - (b / 200); + + float x = (float)System.Math.Round(xw * ((System.Math.Pow(fx, 3) > 0.008856) ? System.Math.Pow(fx, 3) : ((fx - 16 / 116) / 7.787)), 5); + float y = (float)System.Math.Round(yw * ((System.Math.Pow(fy, 3) > 0.008856) ? System.Math.Pow(fy, 3) : ((fy - 16 / 116) / 7.787)), 5); + float z = (float)System.Math.Round(zw * ((System.Math.Pow(fz, 3) > 0.008856) ? System.Math.Pow(fz, 3) : ((fz - 16 / 116) / 7.787)), 5); + + float r = x * 3.2406f - y * 1.5372f - z * 0.4986f; + float g = -x * 0.9689f + y * 1.8758f + z * 0.0415f; + float bValue = x * 0.0557f - y * 0.2040f + z * 1.0570f; + + r = r > 0.0031308f ? 1.055f * (float)System.Math.Pow(r, 1 / 2.4) - 0.055f : r * 12.92f; + g = g > 0.0031308f ? 1.055f * (float)System.Math.Pow(g, 1 / 2.4) - 0.055f : g * 12.92f; + bValue = bValue > 0.0031308f ? 1.055f * (float)System.Math.Pow(bValue, 1 / 2.4) - 0.055f : bValue * 12.92f; + + // r = (float)System.Math.Round(System.Math.Max(0, System.Math.Min(1, r))); + // g = (float)System.Math.Round(System.Math.Max(0, System.Math.Min(1, g))); + // bValue = (float)System.Math.Round(System.Math.Max(0, System.Math.Min(1, bValue))); + + return new Color(r, g, bValue); + + } + + + + public static Color Greyscale(float brightness, float alpha = 1) => new Color(brightness, brightness, brightness, alpha); + + public static Color SetAlpha(this Color color, float alpha) { color.a = alpha; return color; } + + public static Color MultiplyAlpha(this Color color, float k) { color.a *= k; return color; } + + + + + + #endregion + + #region Rects + + + public static Rect Resize(this Rect rect, float px) { rect.x += px; rect.y += px; rect.width -= px * 2; rect.height -= px * 2; return rect; } + + public static Rect SetPos(this Rect rect, Vector2 v) => rect.SetPos(v.x, v.y); + public static Rect SetPos(this Rect rect, float x, float y) { rect.x = x; rect.y = y; return rect; } + + public static Rect SetX(this Rect rect, float x) => rect.SetPos(x, rect.y); + public static Rect SetY(this Rect rect, float y) => rect.SetPos(rect.x, y); + public static Rect SetXMax(this Rect rect, float xMax) { rect.xMax = xMax; return rect; } + public static Rect SetYMax(this Rect rect, float yMax) { rect.yMax = yMax; return rect; } + + public static Rect SetMidPos(this Rect r, Vector2 v) => r.SetPos(v).MoveX(-r.width / 2).MoveY(-r.height / 2); + + public static Rect Move(this Rect rect, Vector2 v) { rect.position += v; return rect; } + public static Rect Move(this Rect rect, float x, float y) { rect.x += x; rect.y += y; return rect; } + public static Rect MoveX(this Rect rect, float px) { rect.x += px; return rect; } + public static Rect MoveY(this Rect rect, float px) { rect.y += px; return rect; } + + public static Rect SetWidth(this Rect rect, float f) { rect.width = f; return rect; } + public static Rect SetWidthFromMid(this Rect rect, float px) { rect.x += rect.width / 2; rect.width = px; rect.x -= rect.width / 2; return rect; } + public static Rect SetWidthFromRight(this Rect rect, float px) { rect.x += rect.width; rect.width = px; rect.x -= rect.width; return rect; } + + public static Rect SetHeight(this Rect rect, float f) { rect.height = f; return rect; } + public static Rect SetHeightFromMid(this Rect rect, float px) { rect.y += rect.height / 2; rect.height = px; rect.y -= rect.height / 2; return rect; } + public static Rect SetHeightFromBottom(this Rect rect, float px) { rect.y += rect.height; rect.height = px; rect.y -= rect.height; return rect; } + + public static Rect AddWidth(this Rect rect, float f) => rect.SetWidth(rect.width + f); + public static Rect AddWidthFromMid(this Rect rect, float f) => rect.SetWidthFromMid(rect.width + f); + public static Rect AddWidthFromRight(this Rect rect, float f) => rect.SetWidthFromRight(rect.width + f); + + public static Rect AddHeight(this Rect rect, float f) => rect.SetHeight(rect.height + f); + public static Rect AddHeightFromMid(this Rect rect, float f) => rect.SetHeightFromMid(rect.height + f); + public static Rect AddHeightFromBottom(this Rect rect, float f) => rect.SetHeightFromBottom(rect.height + f); + + public static Rect SetSize(this Rect rect, Vector2 v) => rect.SetWidth(v.x).SetHeight(v.y); + public static Rect SetSize(this Rect rect, float w, float h) => rect.SetWidth(w).SetHeight(h); + public static Rect SetSize(this Rect rect, float f) { rect.height = rect.width = f; return rect; } + + public static Rect SetSizeFromMid(this Rect r, Vector2 v) => r.Move(r.size / 2).SetSize(v).Move(-v / 2); + public static Rect SetSizeFromMid(this Rect r, float x, float y) => r.SetSizeFromMid(new Vector2(x, y)); + public static Rect SetSizeFromMid(this Rect r, float f) => r.SetSizeFromMid(new Vector2(f, f)); + + public static Rect AlignToPixelGrid(this Rect r) => GUIUtility.AlignRectToDevice(r); + + + + + + #endregion + + #region Vectors + + + public static Vector2 AddX(this Vector2 v, float f) => new Vector2(v.x + f, v.y + 0); + public static Vector2 AddY(this Vector2 v, float f) => new Vector2(v.x + 0, v.y + f); + + public static Vector3 AddX(this Vector3 v, float f) => new Vector3(v.x + f, v.y + 0, v.z + 0); + public static Vector3 AddY(this Vector3 v, float f) => new Vector3(v.x + 0, v.y + f, v.z + 0); + public static Vector3 AddZ(this Vector3 v, float f) => new Vector3(v.x + 0, v.y + 0, v.z + f); + + public static Vector2 xx(this Vector3 v) { return new Vector2(v.x, v.x); } + public static Vector2 xy(this Vector3 v) { return new Vector2(v.x, v.y); } + public static Vector2 xz(this Vector3 v) { return new Vector2(v.x, v.z); } + public static Vector2 yx(this Vector3 v) { return new Vector2(v.y, v.x); } + public static Vector2 yy(this Vector3 v) { return new Vector2(v.y, v.y); } + public static Vector2 yz(this Vector3 v) { return new Vector2(v.y, v.z); } + public static Vector2 zx(this Vector3 v) { return new Vector2(v.z, v.x); } + public static Vector2 zy(this Vector3 v) { return new Vector2(v.z, v.y); } + public static Vector2 zz(this Vector3 v) { return new Vector2(v.z, v.z); } + + + + + + #endregion + + #region Compute + + + [System.Serializable] + public class GaussianKernel + { + public GaussianKernel(bool isEvenSize = false, int radius = 7, float sharpness = .5f) + { + this.isEvenSize = isEvenSize; + this.radius = radius; + this.sharpness = sharpness; + } + + public bool isEvenSize = false; + + public int radius = 7; + public float sharpness = .5f; + + public int size => radius * 2 + (isEvenSize ? 0 : 1); + public float sigma => 1 - Mathf.Pow(sharpness, .1f) * .99999f; + + public float[,] Array2d() + { + float[,] kr = new float[size, size]; + + if (size == 1) { kr[0, 0] = 1; return kr; } + + var a = -2f * radius * radius / Mathf.Log(sigma); + var sum = 0f; + + for (int y = 0; y < size; y++) + for (int x = 0; x < size; x++) + { + var rX = size % 2 == 1 ? (x - radius) : (x - radius) + .5f; + var rY = size % 2 == 1 ? (y - radius) : (y - radius) + .5f; + var dist = Mathf.Sqrt(rX * rX + rY * rY); + kr[x, y] = Mathf.Exp(-dist * dist / a); + sum += kr[x, y]; + } + + for (int y = 0; y < size; y++) + for (int x = 0; x < size; x++) + kr[x, y] /= sum; + + return kr; + } + public float[] ArrayFlat() + { + var gk = Array2d(); + float[] flat = new float[size * size]; + + for (int i = 0; i < size; i++) + for (int j = 0; j < size; j++) + flat[(i * size + j)] = gk[i, j]; + + return flat; + } + } + + + + + + #endregion + + #region Objects + + + public static Object[] FindObjects(Type type) + { +#if UNITY_2023_1_OR_NEWER + return Object.FindObjectsByType(type, FindObjectsSortMode.None); +#else + return Object.FindObjectsOfType(type); +#endif + } + public static T[] FindObjects() where T : Object + { +#if UNITY_2023_1_OR_NEWER + return Object.FindObjectsByType(FindObjectsSortMode.None); +#else + return Object.FindObjectsOfType(); +#endif + } + + public static void Destroy(this Object r) + { + if (Application.isPlaying) + Object.Destroy(r); + else + Object.DestroyImmediate(r); + + } + + public static void DestroyImmediate(this Object o) => Object.DestroyImmediate(o); + + + + + + #endregion + + #region GlobalID + +#if UNITY_EDITOR + + [System.Serializable] + public struct GlobalID : System.IEquatable + { + public Object GetObject() => GlobalObjectId.GlobalObjectIdentifierToObjectSlow(globalObjectId); + public int GetObjectInstanceId() => GlobalObjectId.GlobalObjectIdentifierToInstanceIDSlow(globalObjectId); + + + public string guid => globalObjectId.assetGUID.ToString(); + public ulong fileId => globalObjectId.targetObjectId; + + public bool isNull => globalObjectId.identifierType == 0; + public bool isAsset => globalObjectId.identifierType == 1; + public bool isSceneObject => globalObjectId.identifierType == 2; + + public GlobalObjectId globalObjectId => _globalObjectId.Equals(default) && globalObjectIdString != null && GlobalObjectId.TryParse(globalObjectIdString, out var r) ? _globalObjectId = r : _globalObjectId; + public GlobalObjectId _globalObjectId; + + public GlobalID(Object o) => globalObjectIdString = (_globalObjectId = GlobalObjectId.GetGlobalObjectIdSlow(o)).ToString(); + public GlobalID(string s) => globalObjectIdString = GlobalObjectId.TryParse(s, out _globalObjectId) ? s : s; + + public string globalObjectIdString; + + + + public bool Equals(GlobalID other) => this.globalObjectIdString.Equals(other.globalObjectIdString); + + public static bool operator ==(GlobalID a, GlobalID b) => a.Equals(b); + public static bool operator !=(GlobalID a, GlobalID b) => !a.Equals(b); + + public override bool Equals(object other) => other is GlobalID otherglobalID && this.Equals(otherglobalID); + public override int GetHashCode() => globalObjectIdString == null ? 0 : globalObjectIdString.GetHashCode(); + + + public override string ToString() => globalObjectIdString; + + } + + public static GlobalID GetGlobalID(this Object o) => new GlobalID(o); + + public static int[] GetObjectInstanceIds(this IEnumerable globalIDs) + { + var goids = globalIDs.Select(r => r.globalObjectId).ToArray(); + + var iids = new int[goids.Length]; + + GlobalObjectId.GlobalObjectIdentifiersToInstanceIDsSlow(goids, iids); + + return iids; + } + + +#endif + + + + + #endregion + + #region Paths + + + public static string GetParentPath(this string path) => path.Substring(0, path.LastIndexOf('/')); + public static bool HasParentPath(this string path) => path.Contains('/') && path.GetParentPath() != ""; + + public static string ToGlobalPath(this string localPath) => Application.dataPath + "/" + localPath.Substring(0, localPath.Length - 1); + public static string ToLocalPath(this string globalPath) => "Assets" + globalPath.Remove(Application.dataPath); + + + + public static string CombinePath(this string p, string p2) => Path.Combine(p, p2); + + public static bool IsSubpathOf(this string path, string of) => path.StartsWith(of + "/") || of == ""; + + public static string GetDirectory(this string pathOrDirectory) + { + var directory = pathOrDirectory.Contains('.') ? pathOrDirectory.Substring(0, pathOrDirectory.LastIndexOf('/')) : pathOrDirectory; + + if (directory.Contains('.')) + directory = directory.Substring(0, directory.LastIndexOf('/')); + + return directory; + + } + + public static bool DirectoryExists(this string pathOrDirectory) => Directory.Exists(pathOrDirectory.GetDirectory()); + + public static string EnsureDirExists(this string pathOrDirectory) // todo to EnsureDirectoryExists + { + var directory = pathOrDirectory.GetDirectory(); + + if (directory.HasParentPath() && !Directory.Exists(directory.GetParentPath())) + EnsureDirExists(directory.GetParentPath()); + + if (!Directory.Exists(directory)) + Directory.CreateDirectory(directory); + + return pathOrDirectory; + + } + + + + public static string ClearDir(this string dir) + { + if (!Directory.Exists(dir)) return dir; + + var diri = new DirectoryInfo(dir); + foreach (var r in diri.EnumerateFiles()) r.Delete(); + foreach (var r in diri.EnumerateDirectories()) r.Delete(true); + + return dir; + } + + + + + + +#if UNITY_EDITOR + + public static string EnsurePathIsUnique(this string path) + { + if (!path.DirectoryExists()) return path; + + var s = AssetDatabase.GenerateUniqueAssetPath(path); // returns empty if parent dir doesnt exist + + return s == "" ? path : s; + + } + + public static void EnsureDirExistsAndRevealInFinder(string dir) + { + EnsureDirExists(dir); + UnityEditor.EditorUtility.OpenWithDefaultApp(dir); + } + +#endif + + + + #endregion + + #region AssetDatabase + +#if UNITY_EDITOR + + public static AssetImporter GetImporter(this Object t) => AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(t)); + + public static string ToPath(this string guid) => AssetDatabase.GUIDToAssetPath(guid); // returns empty string if not found + public static List ToPaths(this IEnumerable guids) => guids.Select(r => r.ToPath()).ToList(); + + public static string GetFilename(this string path, bool withExtension = false) => withExtension ? Path.GetFileName(path) : Path.GetFileNameWithoutExtension(path); // prev GetName + public static string GetExtension(this string path) => Path.GetExtension(path); + + + public static string ToGuid(this string pathInProject) => AssetDatabase.AssetPathToGUID(pathInProject); + public static List ToGuids(this IEnumerable pathsInProject) => pathsInProject.Select(r => r.ToGuid()).ToList(); + + public static string GetPath(this Object o) => AssetDatabase.GetAssetPath(o); + public static string GetGuid(this Object o) => AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(o)); + + public static string GetScriptPath(string scriptName) => AssetDatabase.FindAssets("t: script " + scriptName, null).FirstOrDefault()?.ToPath() ?? "scirpt not found"; + + + public static bool IsValidGuid(this string guid) => AssetDatabase.AssetPathToGUID(AssetDatabase.GUIDToAssetPath(guid), AssetPathToGUIDOptions.OnlyExistingAssets) != ""; + + + + // toremove + public static Object LoadGuid(this string guid) => AssetDatabase.LoadAssetAtPath(guid.ToPath(), typeof(Object)); + public static T LoadGuid(this string guid) where T : Object => AssetDatabase.LoadAssetAtPath(guid.ToPath()); + + + + + public static List FindAllAssetsOfType_guids(Type type) => AssetDatabase.FindAssets("t:" + type.Name).ToList(); + public static List FindAllAssetsOfType_guids(Type type, string path) => AssetDatabase.FindAssets("t:" + type.Name, new[] { path }).ToList(); + public static List FindAllAssetsOfType() where T : Object => FindAllAssetsOfType_guids(typeof(T)).Select(r => (T)r.LoadGuid()).ToList(); + public static List FindAllAssetsOfType(string path) where T : Object => FindAllAssetsOfType_guids(typeof(T), path).Select(r => (T)r.LoadGuid()).ToList(); + + public static T Reimport(this T t) where T : Object { AssetDatabase.ImportAsset(t.GetPath(), ImportAssetOptions.ForceUpdate); return t; } + +#endif + + + + + + #endregion + + #region Serialization + + [System.Serializable] + public class SerializableDictionary : Dictionary, ISerializationCallbackReceiver + { + [SerializeField] List keys = new List(); + [SerializeField] List values = new List(); + + public void OnBeforeSerialize() + { + keys.Clear(); + values.Clear(); + + foreach (KeyValuePair kvp in this) + { + keys.Add(kvp.Key); + values.Add(kvp.Value); + } + + } + public void OnAfterDeserialize() + { + this.Clear(); + + for (int i = 0; i < keys.Count; i++) + this[keys[i]] = values[i]; + + } + + } + + + #endregion + + #region Editor + +#if UNITY_EDITOR + + public static void ToggleDefineDisabledInScript(Type scriptType) + { + var path = GetScriptPath(scriptType.Name); + + var lines = File.ReadAllLines(path); + if (lines.First().StartsWith("#define DISABLED")) + File.WriteAllLines(path, lines.Skip(1)); + else + File.WriteAllLines(path, lines.Prepend("#define DISABLED // this line was added by VUtils.ToggleDefineDisabledInScript")); + + AssetDatabase.ImportAsset(path); + } + public static bool ScriptHasDefineDisabled(Type scriptType) => File.ReadLines(GetScriptPath(scriptType.Name)).First().StartsWith("#define DISABLED"); + public static void SetDefineDisabledInScript(Type scriptType, bool defineDisabled) + { + if (ScriptHasDefineDisabled(scriptType) != defineDisabled) + ToggleDefineDisabledInScript(scriptType); + + } + + public static int GetProjectId() => Application.dataPath.GetHashCode(); + + public static void PingObject(Object o, bool select = false, bool focusProjectWindow = true) + { + if (select) + { + Selection.activeObject = null; + Selection.activeObject = o; + } + if (focusProjectWindow) EditorUtility.FocusProjectWindow(); + EditorGUIUtility.PingObject(o); + + } + public static void PingObject(string guid, bool select = false, bool focusProjectWindow = true) => PingObject(AssetDatabase.LoadAssetAtPath(guid.ToPath())); + + + public static void OpenFolder(string path) + { + var folder = AssetDatabase.LoadAssetAtPath(path, typeof(Object)); + + var t = typeof(Editor).Assembly.GetType("UnityEditor.ProjectBrowser"); + var w = (EditorWindow)t.GetField("s_LastInteractedProjectBrowser").GetValue(null); + + var m_ListAreaState = t.GetField("m_ListAreaState", maxBindingFlags).GetValue(w); + + m_ListAreaState.GetType().GetField("m_SelectedInstanceIDs").SetValue(m_ListAreaState, new List { folder.GetInstanceID() }); + + t.GetMethod("OpenSelectedFolders", maxBindingFlags).Invoke(null, null); + + } + + public static void Dirty(this Object o) => UnityEditor.EditorUtility.SetDirty(o); + public static void Save(this Object o) => AssetDatabase.SaveAssetIfDirty(o); + public static void RecordUndo(this Object o) => Undo.RecordObject(o, ""); + + + public static EditorWindow OpenObjectPicker(Object obj = null, bool allowSceneObjects = false, string searchFilter = "", int controlID = 0) where T : Object + { + EditorGUIUtility.ShowObjectPicker(obj, allowSceneObjects, searchFilter, controlID); + + return Resources.FindObjectsOfTypeAll(typeof(Editor).Assembly.GetType("UnityEditor.ObjectSelector")).FirstOrDefault() as EditorWindow; + + } + public static EditorWindow OpenColorPicker(System.Action colorChangedCallback, Color color, bool showAlpha = true, bool hdr = false) + { + typeof(Editor).Assembly.GetType("UnityEditor.ColorPicker").InvokeMethod("Show", colorChangedCallback, color, showAlpha, hdr); + + return typeof(Editor).Assembly.GetType("UnityEditor.ColorPicker").GetPropertyValue("instance"); + + } + + public static void MoveTo(this EditorWindow window, Vector2 position, bool ensureFitsOnScreen = true) + { + if (!ensureFitsOnScreen) { window.position = window.position.SetPos(position); return; } + + var windowRect = window.position; + var unityWindowRect = EditorGUIUtility.GetMainWindowPosition(); + + position.x = position.x.Max(unityWindowRect.position.x); + position.y = position.y.Max(unityWindowRect.position.y); + + position.x = position.x.Min(unityWindowRect.xMax - windowRect.width); + position.y = position.y.Min(unityWindowRect.yMax - windowRect.height); + + window.position = windowRect.SetPos(position); + + } + + + + public static void RemoveEditorErrors() => removeEditorErrorsMethod.Invoke(null, new object[] { 1 }); + static MethodInfo removeEditorErrorsMethod = System.AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(r => r.GetName().ToString().Contains("UnityEditor.CoreModule")).GetTypes().First(r => r.Name.Contains("LogEntry")).GetMethod("RemoveLogEntriesByMode", BindingFlags.Static | BindingFlags.NonPublic); + + + + public static class EditorPrefsCached + { + public static bool GetBool(string key, bool defaultValue = false) + { + if (bools_byKey.ContainsKey(key)) + return bools_byKey[key]; + else + return bools_byKey[key] = EditorPrefs.GetBool(key, defaultValue); + + } + public static float GetFloat(string key, float defaultValue = 0) + { + if (floats_byKey.ContainsKey(key)) + return floats_byKey[key]; + else + return floats_byKey[key] = EditorPrefs.GetFloat(key, defaultValue); + + } + public static int GetInt(string key, int defaultValue = 0) + { + if (ints_byKey.ContainsKey(key)) + return ints_byKey[key]; + else + return ints_byKey[key] = EditorPrefs.GetInt(key, defaultValue); + + } + public static string GetString(string key, string defaultValue = "") + { + if (strings_byKey.ContainsKey(key)) + return strings_byKey[key]; + else + return strings_byKey[key] = EditorPrefs.GetString(key, defaultValue); + + } + + + public static void SetBool(string key, bool value) + { + bools_byKey[key] = value; + + EditorPrefs.SetBool(key, value); + + } + public static void SetFloat(string key, float value) + { + floats_byKey[key] = value; + + EditorPrefs.SetFloat(key, value); + + } + public static void SetInt(string key, int value) + { + ints_byKey[key] = value; + + EditorPrefs.SetInt(key, value); + + } + public static void SetString(string key, string value) + { + strings_byKey[key] = value; + + EditorPrefs.SetString(key, value); + + } + + + static Dictionary bools_byKey = new(); + static Dictionary floats_byKey = new(); + static Dictionary ints_byKey = new(); + static Dictionary strings_byKey = new(); + + } + + +#endif + + + + + + #endregion + + } + + public static partial class VGUI + { + #region Colors + + public static class GUIColors + { + public static Color windowBackground => isDarkTheme ? Greyscale(.22f) : Greyscale(.78f); // prev backgroundCol + public static Color pressedButtonBackground => isDarkTheme ? new Color(.48f, .76f, 1f, 1f) * 1.4f : new Color(.48f, .7f, 1f, 1f) * 1.2f; // prev pressedButtonCol + public static Color greyedOutTint => Greyscale(.7f); + public static Color selectedBackground => isDarkTheme ? new Color(.17f, .365f, .535f) : new Color(.2f, .375f, .555f) * 1.2f; + } + + + #endregion + + #region Shortcuts + + public static Rect lastRect => GUILayoutUtility.GetLastRect(); + + public static bool isDarkTheme => EditorGUIUtility.isProSkin; + + public static float GetLabelWidth(this string s) => GUI.skin.label.CalcSize(new GUIContent(s)).x; + public static float GetLabelWidth(this string s, int fotSize) + { + SetLabelFontSize(fotSize); + + var r = s.GetLabelWidth(); + + ResetLabelStyle(); + + return r; + + } + public static float GetLabelWidth(this string s, bool isBold) + { + if (isBold) + SetLabelBold(); + + var r = s.GetLabelWidth(); + + if (isBold) + ResetLabelStyle(); + + return r; + + } + + public static void SetGUIEnabled(bool enabled) { _prevGuiEnabled = GUI.enabled; GUI.enabled = enabled; } + public static void ResetGUIEnabled() => GUI.enabled = _prevGuiEnabled; + static bool _prevGuiEnabled = true; + + public static void SetLabelFontSize(int size) => GUI.skin.label.fontSize = size; + public static void SetLabelBold() => GUI.skin.label.fontStyle = FontStyle.Bold; + public static void SetLabelAlignmentCenter() => GUI.skin.label.alignment = TextAnchor.MiddleCenter; + public static void ResetLabelStyle() + { + GUI.skin.label.fontSize = 0; + GUI.skin.label.fontStyle = FontStyle.Normal; + GUI.skin.label.alignment = TextAnchor.MiddleLeft; + } + + + public static void SetGUIColor(Color c) + { + if (!_guiColorModified) + _defaultGuiColor = GUI.color; + + _guiColorModified = true; + + GUI.color = _defaultGuiColor * c; + + } + public static void ResetGUIColor() + { + GUI.color = _guiColorModified ? _defaultGuiColor : Color.white; + + _guiColorModified = false; + + } + static bool _guiColorModified; + static Color _defaultGuiColor; + + + + #endregion + + #region Events + + + public class WrappedEvent + { + public Event e; + + public bool isRepaint => e.type == EventType.Repaint; + public bool isLayout => e.type == EventType.Layout; + public bool isUsed => e.type == EventType.Used; + public bool isMouseLeaveWindow => e.type == EventType.MouseLeaveWindow; + public bool isMouseEnterWindow => e.type == EventType.MouseEnterWindow; + public bool isContextClick => e.type == EventType.ContextClick; + + public bool isKeyDown => e.type == EventType.KeyDown; + public bool isKeyUp => e.type == EventType.KeyUp; + public KeyCode keyCode => e.keyCode; + public char characted => e.character; + + public bool isExecuteCommand => e.type == EventType.ExecuteCommand; + public string commandName => e.commandName; + + public bool isMouse => e.isMouse; + public bool isMouseDown => e.type == EventType.MouseDown; + public bool isMouseUp => e.type == EventType.MouseUp; + public bool isMouseDrag => e.type == EventType.MouseDrag; + public bool isMouseMove => e.type == EventType.MouseMove; + public bool isScroll => e.type == EventType.ScrollWheel; + public int mouseButton => e.button; + public int clickCount => e.clickCount; + public Vector2 mousePosition => e.mousePosition; + public Vector2 mousePosition_screenSpace => GUIUtility.GUIToScreenPoint(e.mousePosition); + public Vector2 mouseDelta => e.delta; + + public bool isDragUpdate => e.type == EventType.DragUpdated; + public bool isDragPerform => e.type == EventType.DragPerform; + public bool isDragExit => e.type == EventType.DragExited; + + public EventModifiers modifiers => e.modifiers; + public bool holdingAnyModifierKey => modifiers != EventModifiers.None; + + public bool holdingAlt => e.alt; + public bool holdingShift => e.shift; + public bool holdingCtrl => e.control; + public bool holdingCmd => e.command; + public bool holdingCmdOrCtrl => e.command || e.control; + + public bool holdingAltOnly => e.modifiers == EventModifiers.Alt; // in some sessions FunctionKey is always pressed? + public bool holdingShiftOnly => e.modifiers == EventModifiers.Shift; // in some sessions FunctionKey is always pressed? + public bool holdingCtrlOnly => e.modifiers == EventModifiers.Control; + public bool holdingCmdOnly => e.modifiers == EventModifiers.Command; + public bool holdingCmdOrCtrlOnly => (e.modifiers == EventModifiers.Command || e.modifiers == EventModifiers.Control); + + public EventType type => e.type; + + public void Use() => e?.Use(); + + + public WrappedEvent(Event e) => this.e = e; + + public override string ToString() => e.ToString(); + + } + + public static WrappedEvent Wrap(this Event e) => new(e); + + public static WrappedEvent curEvent => _curEvent ??= typeof(Event).GetFieldValue("s_Current").Wrap(); + static WrappedEvent _curEvent; + + + + + + #endregion + + #region Layout + + + + public static void BeginPanel(string title, float height, System.Action onClose = null, System.Action onApply = null) + { + + void bg() + { + GUI.enabled = false; + SetGUIColor(Greyscale(.75f)); + var r = ExpandWidthLabelRect(0).SetHeight(height); + GUI.Button(r, ""); + GUI.Button(r, ""); + GUI.Button(r, ""); + GUI.Button(r, ""); + GUI.Button(r, ""); + GUI.Button(r, ""); + GUI.Button(r, ""); + GUI.Button(r, ""); + ResetGUIColor(); + GUI.enabled = true; + } + + void layout() + { + + GUILayout.BeginHorizontal(); + Space(7); + EditorGUIUtility.labelWidth -= 7; + GUILayout.BeginVertical(); + + } + + void title_() + { + Space(5); + EditorGUI.PrefixLabel(ExpandWidthLabelRect(), new GUIContent(title)); + // GUI.skin.label.fontStyle = FontStyle.Bold; + // GUILayout.Label(title); + + } + + void buttons() + { + var rClose = lastRect.SetWidthFromRight(16).MoveX(1).MoveY(-1); + + // if() + GUI.color = Greyscale(rClose.IsHovered() ? .9f : .6f); + GUI.Label(rClose, EditorGUIUtility.IconContent("CrossIcon")); + + GUI.color = Color.clear; + if (GUI.Button(rClose, "")) + onClose?.Invoke(); + + + var rApply = rClose.MoveX(-19).Resize(-1); + + GUI.color = Greyscale(rApply.IsHovered() ? .9f : .6f); + GUI.Label(rApply, EditorGUIUtility.IconContent("check")); + + GUI.color = Color.clear; + if (GUI.Button(rApply, "")) + onApply?.Invoke(); + + + GUI.color = Color.white; + + } + + + + bg(); + layout(); + title_(); + buttons(); + + Space(5); + + } + + public static void EndPanel() + { + GUILayout.EndVertical(); + Space(7); + EditorGUIUtility.labelWidth = 0; + GUILayout.EndHorizontal(); + } + + + + public static void BeginIndent(float f) + { + GUILayout.BeginHorizontal(); + GUILayout.Space(f); + GUILayout.BeginVertical(); + + _indentLabelWidthStack.Push(EditorGUIUtility.labelWidth); + + EditorGUIUtility.labelWidth -= f; + } + + public static void EndIndent(float f = 0) + { + GUILayout.EndVertical(); + GUILayout.Space(f); + GUILayout.EndHorizontal(); + + EditorGUIUtility.labelWidth = _indentLabelWidthStack.Pop(); + } + static Stack _indentLabelWidthStack = new Stack(); + + + + + #endregion + + #region Drawing + + public static Rect Draw(this Rect r) { EditorGUI.DrawRect(r, Color.black); return r; } + public static Rect Draw(this Rect r, Color c) { EditorGUI.DrawRect(r, c); return r; } + + + + public static Rect DrawWithRoundedCorners(this Rect rect, Color color, int cornerRadius) + { + if (!curEvent.isRepaint) return rect; + + cornerRadius = cornerRadius.Min((rect.height / 2).FloorToInt()).Min((rect.width / 2).FloorToInt()); + + GUIStyle style; + + void getStyle() + { + if (_roundedStylesByCornerRadius.TryGetValue(cornerRadius, out style)) return; + + var pixelsPerPoint = 2; + + var res = cornerRadius * 2 * pixelsPerPoint; + var pixels = new Color[res * res]; + + var white = Greyscale(1, 1); + var clear = Greyscale(1, 0); + var halfRes = res / 2; + + for (int x = 0; x < res; x++) + for (int y = 0; y < res; y++) + { + var sqrMagnitude = (new Vector2(x - halfRes + .5f, y - halfRes + .5f)).sqrMagnitude; + pixels[x + y * res] = sqrMagnitude <= halfRes * halfRes ? white : clear; + } + + var texture = new Texture2D(res, res); + texture.SetPropertyValue("pixelsPerPoint", pixelsPerPoint); + texture.hideFlags = HideFlags.DontSave; + texture.SetPixels(pixels); + texture.Apply(); + + + + style = new GUIStyle(); + style.normal.background = texture; + style.alignment = TextAnchor.MiddleCenter; + style.border = new RectOffset(cornerRadius, cornerRadius, cornerRadius, cornerRadius); + + + _roundedStylesByCornerRadius[cornerRadius] = style; + + } + void draw() + { + SetGUIColor(color); + + style.Draw(rect, false, false, false, false); + + ResetGUIColor(); + + } + + getStyle(); + draw(); + + return rect; + + } + public static Rect DrawWithRoundedCorners(this Rect rect, Color color, float cornerRadius) => rect.DrawWithRoundedCorners(color, cornerRadius.RoundToInt()); + static Dictionary _roundedStylesByCornerRadius = new Dictionary(); + + + public static Rect DrawBlurred(this Rect rect, Color color, int blurRadius) + { + if (!curEvent.isRepaint) return rect; + + var pixelsPerPoint = .5f; + // var pixelsPerPoint = 1f; + + var blurRadiusScaled = (blurRadius * pixelsPerPoint).RoundToInt().Max(1).Min(123); + + var croppedRectWidth = (rect.width * pixelsPerPoint).RoundToInt().Min(blurRadiusScaled * 2); + var croppedRectHeight = (rect.height * pixelsPerPoint).RoundToInt().Min(blurRadiusScaled * 2); + + var textureWidth = croppedRectWidth + blurRadiusScaled * 2; + var textureHeight = croppedRectHeight + blurRadiusScaled * 2; + + GUIStyle style; + + void getStyle() + { + if (_blurredStylesByTextureSize.TryGetValue((textureWidth, textureHeight), out style)) return; + + // VDebug.LogStart(blurRadius + ""); + + var pixels = new Color[textureWidth * textureHeight]; + var kernel = new GaussianKernel(false, blurRadiusScaled).Array2d(); + + for (int x = 0; x < textureWidth; x++) + for (int y = 0; y < textureHeight; y++) + { + var sum = 0f; + + for (int xSample = (x - blurRadiusScaled).Max(blurRadiusScaled); xSample <= (x + blurRadiusScaled).Min(textureWidth - 1 - blurRadiusScaled); xSample++) + for (int ySample = (y - blurRadiusScaled).Max(blurRadiusScaled); ySample <= (y + blurRadiusScaled).Min(textureHeight - 1 - blurRadiusScaled); ySample++) + sum += kernel[blurRadiusScaled + xSample - x, blurRadiusScaled + ySample - y]; + + pixels[x + y * textureWidth] = Greyscale(1, sum); + + } + + var texture = new Texture2D(textureWidth, textureHeight); + texture.SetPropertyValue("pixelsPerPoint", pixelsPerPoint); + texture.hideFlags = HideFlags.DontSave; + texture.SetPixels(pixels); + texture.Apply(); + + + style = new GUIStyle(); + style.normal.background = texture; + style.alignment = TextAnchor.MiddleCenter; + + var borderX = ((textureWidth / 2f - 1) / pixelsPerPoint).FloorToInt(); + var borderY = ((textureHeight / 2f - 1) / pixelsPerPoint).FloorToInt(); + style.border = new RectOffset(borderX, borderX, borderY, borderY); + + _blurredStylesByTextureSize[(textureWidth, textureHeight)] = style; + + // VDebug.LogFinish(); + + } + void draw() + { + SetGUIColor(color); + + style.Draw(rect.SetSizeFromMid(rect.width + blurRadius * 2, rect.height + blurRadius * 2), false, false, false, false); + + ResetGUIColor(); + + } + + getStyle(); + draw(); + + return rect; + + } + public static Rect DrawBlurred(this Rect rect, Color color, float blurRadius) => rect.DrawBlurred(color, blurRadius.RoundToInt()); + static Dictionary<(int, int), GUIStyle> _blurredStylesByTextureSize = new Dictionary<(int, int), GUIStyle>(); + + + static void DrawCurtain(this Rect rect, Color color, int dir) + { + void genTextures() + { + if (_gradientTextures != null) return; + + _gradientTextures = new Texture2D[4]; + + // var pixels = Enumerable.Range(0, 256).Select(r => Greyscale(1, r / 255f)); + var pixels = Enumerable.Range(0, 256).Select(r => Greyscale(1, (r / 255f).Smoothstep())); + + var up = new Texture2D(1, 256); + up.SetPixels(pixels.Reverse().ToArray()); + up.Apply(); + up.hideFlags = HideFlags.DontSave; + up.wrapMode = TextureWrapMode.Clamp; + _gradientTextures[0] = up; + + var down = new Texture2D(1, 256); + down.SetPixels(pixels.ToArray()); + down.Apply(); + down.hideFlags = HideFlags.DontSave; + down.wrapMode = TextureWrapMode.Clamp; + _gradientTextures[1] = down; + + var left = new Texture2D(256, 1); + left.SetPixels(pixels.ToArray()); + left.Apply(); + left.hideFlags = HideFlags.DontSave; + left.wrapMode = TextureWrapMode.Clamp; + _gradientTextures[2] = left; + + var right = new Texture2D(256, 1); + right.SetPixels(pixels.Reverse().ToArray()); + right.Apply(); + right.hideFlags = HideFlags.DontSave; + right.wrapMode = TextureWrapMode.Clamp; + _gradientTextures[3] = right; + + } + void draw() + { + SetGUIColor(color); + + GUI.DrawTexture(rect, _gradientTextures[dir]); + + ResetGUIColor(); + + } + + genTextures(); + draw(); + + } + public static void DrawCurtainUp(this Rect rect, Color color) => rect.DrawCurtain(color, 0); + public static void DrawCurtainDown(this Rect rect, Color color) => rect.DrawCurtain(color, 1); + public static void DrawCurtainLeft(this Rect rect, Color color) => rect.DrawCurtain(color, 2); + public static void DrawCurtainRight(this Rect rect, Color color) => rect.DrawCurtain(color, 3); + static Texture2D[] _gradientTextures; + + + + public static bool IsHovered(this Rect r) => r.Contains(curEvent.mousePosition); + + #endregion + + #region Spacing + + public static void Space(float px = 6) => GUILayout.Space(px); + + public static void Divider(float space = 15, float yOffset = 0) + { + GUILayout.Label("", GUILayout.Height(space), GUILayout.ExpandWidth(true)); + lastRect.SetHeightFromMid(1).SetWidthFromMid(lastRect.width - 16).MoveY(yOffset).Draw(isDarkTheme ? Color.white * .42f : Color.white * .72f); + } + + public static Rect ExpandSpace() { GUILayout.Label("", GUILayout.ExpandHeight(true), GUILayout.ExpandWidth(true)); return lastRect; } + + public static Rect ExpandWidthLabelRect() { GUILayout.Label(""/* , GUILayout.Height(0) */, GUILayout.ExpandWidth(true)); return lastRect; } + public static Rect ExpandWidthLabelRect(float height) { GUILayout.Label("", GUILayout.Height(height), GUILayout.ExpandWidth(true)); return lastRect; } + + + #endregion + + #region Icons + + public static class EditorIcons + { + public static Texture2D GetIcon(string iconNameOrPath) + { + if (icons_byName.TryGetValue(iconNameOrPath, out var cachedResult)) return cachedResult; + + var icon = typeof(EditorGUIUtility).InvokeMethod("LoadIcon", iconNameOrPath) as Texture2D; + + return icons_byName[iconNameOrPath] = icon; + + } + + static Dictionary icons_byName = new Dictionary(); + } + + + + + // toremove: + + public static void DrawIcon(Rect rect, string icon, Color? col = null) + { + if (icon == "") return; + Texture2D tex = (Texture2D)EditorGUIUtility.FindTexture(icon); + if (!tex) tex = (Texture2D)EditorGUIUtility.Load(icon); + if (!tex) tex = (Texture2D)Resources.Load(icon); + DrawIcon(rect, tex, col); + } + public static void DrawIcon(Rect rect, string icon, bool greyedOut) => DrawIcon(rect, icon, greyedOut ? GUIColors.greyedOutTint : (Color?)null); + + public static void DrawIcon(Rect rect, Texture2D tex, Color? col = null) + { + var color = Color.white; + if (col != null) color = col.GetValueOrDefault(); + + GUI.DrawTexture(rect, tex, ScaleMode.ScaleToFit, true, 0, color, 0, 0); + } + public static void DrawIcon(Rect rect, Texture2D tex, bool greyedOut) => DrawIcon(rect, tex, greyedOut ? GUIColors.greyedOutTint : (Color?)null); + + public static void DrawIconForGuid(Rect rect, string guid, bool greyedOut = false) + { + if (!guid.IsValidGuid()) return; + + var type = AssetDatabase.GetMainAssetTypeAtPath(guid.ToPath()); + + if (AssetDatabase.IsValidFolder(guid.ToPath())) + DrawIcon(rect, "Folder Icon", greyedOut); + + else if (guid.ToPath().GetExtension() == ".cs") + DrawIcon(rect, "cs Script Icon", greyedOut); + + else DrawIcon(rect, AssetPreview.GetMiniTypeThumbnail(type), greyedOut); + } + + // public static void DrawIcon(string icon, bool greyedOut = false, bool pressed = false) => DrawIcon(lastRect, icon, greyedOut, pressed); + // public static void DrawIcon(Rect rect, string icon, bool greyedOut = false, bool pressed = false) + // { + // if (icon == "") return; + // Texture2D tex = (Texture2D)EditorGUIUtility.FindTexture(icon); + // if (!tex) tex = (Texture2D)EditorGUIUtility.Load(icon); + // if (!tex) tex = (Texture2D)Resources.Load(icon); + // DrawIcon(rect, tex, greyedOut, pressed); + // } + // public static void DrawIcon(Rect rect, GUIContent tex, bool greyedOut = false) //some icons get fucked up if drawn differently + // { + // var r = GUI.enabled; + // if (greyedOut) GUI.enabled = false; + // var s = new GUIStyle(); + // GUI.Label(rect.Resize(0), tex, s); + // GUI.enabled = r; + // } + // public static void DrawIcon(Rect rect, Texture2D tex, bool greyedOut = false, bool pressed = false) + // { + // var col = Color.white; + // if (greyedOut) col *= greyedOutColor; + // if (pressed) col *= pressedCol; + // GUI.DrawTexture(rect, tex, ScaleMode.ScaleToFit, true, 0, col, 0, 0); + // } + // public static void DrawIcon(Rect rect, string icon, Color col) => DrawIcon(lastRect, EditorGUIUtility.FindTexture(icon), col); + // public static void DrawIcon(Rect rect, Texture2D tex, Color col) => GUI.DrawTexture(rect, tex, ScaleMode.ScaleToFit, true, 0, col, 0, 0); + + + + #endregion + + #region Other + + public static void MarkInteractive(this Rect rect) + { + if (!curEvent.isRepaint) return; + + var unclippedRect = (Rect)_mi_GUIClip_UnclipToWindow.Invoke(null, new object[] { rect }); + + var curGuiView = _pi_GUIView_current.GetValue(null); + + _mi_GUIView_MarkHotRegion.Invoke(curGuiView, new object[] { unclippedRect }); + + } + static PropertyInfo _pi_GUIView_current = typeof(Editor).Assembly.GetType("UnityEditor.GUIView").GetProperty("current", maxBindingFlags); + static MethodInfo _mi_GUIView_MarkHotRegion = typeof(Editor).Assembly.GetType("UnityEditor.GUIView").GetMethod("MarkHotRegion", maxBindingFlags); + static MethodInfo _mi_GUIClip_UnclipToWindow = typeof(GUI).Assembly.GetType("UnityEngine.GUIClip").GetMethod("UnclipToWindow", maxBindingFlags, null, new[] { typeof(Rect) }, null); + + + + #endregion + + + } +} +#endif \ No newline at end of file diff --git a/Assets/OtherAssets/DEV/PAID/vFavorites/VFavoritesLibs.cs.meta b/Assets/OtherAssets/DEV/PAID/vFavorites/VFavoritesLibs.cs.meta new file mode 100644 index 0000000..a0d7507 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFavorites/VFavoritesLibs.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 38092f82a4c054086826261d88277942 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vFavorites/VFavoritesMenu.cs b/Assets/OtherAssets/DEV/PAID/vFavorites/VFavoritesMenu.cs new file mode 100644 index 0000000..ba14769 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFavorites/VFavoritesMenu.cs @@ -0,0 +1,140 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEditor; +using static VFavorites.Libs.VUtils; +using static VFavorites.Libs.VGUI; + + + +namespace VFavorites +{ + public class VFavoritesMenu + { + + public static bool pageScrollEnabled { get => EditorPrefsCached.GetBool("vFavorites-pageScrollEnabled", true); set => EditorPrefsCached.SetBool("vFavorites-pageScrollEnabled", value); } + public static bool numberKeysEnabled { get => EditorPrefsCached.GetBool("vFavorites-numberKeysEnabled", true); set => EditorPrefsCached.SetBool("vFavorites-numberKeysEnabled", value); } + public static bool arrowKeysEnabled { get => EditorPrefsCached.GetBool("vFavorites-arrowKeysEnabled", true); set => EditorPrefsCached.SetBool("vFavorites-arrowKeysEnabled", value); } + + public static bool fadeAnimationsEnabled { get => EditorPrefsCached.GetBool("vFavorites-fadeAnimationsEnabled", true); set => EditorPrefsCached.SetBool("vFavorites-fadeAnimationsEnabled", value); } + public static bool pageScrollAnimationEnabled { get => EditorPrefsCached.GetBool("vFavorites-pageScrollAnimationEnabled", true); set => EditorPrefsCached.SetBool("vFavorites-pageScrollAnimationEnabled", value); } + + public static int activeOnKeyCombination { get => EditorPrefsCached.GetInt("vFavorites-activeOnKeyCombination", 0); set => EditorPrefsCached.SetInt("vFavorites-activeOnKeyCombination", value); } + public static bool activeOnAltEnabled { get => activeOnKeyCombination == 0; set => activeOnKeyCombination = 0; } + public static bool activeOnAltShiftEnabled { get => activeOnKeyCombination == 1; set => activeOnKeyCombination = 1; } + public static bool activeOnCtrlAltEnabled { get => activeOnKeyCombination == 2; set => activeOnKeyCombination = 2; } + + public static bool pluginDisabled { get => EditorPrefsCached.GetBool("vFavorites-pluginDisabled", false); set => EditorPrefsCached.SetBool("vFavorites-pluginDisabled", value); } + + + + + const string dir = "Tools/vFavorites/"; + + const string pageScroll = dir + "Scroll to change page"; + const string numberKeys = dir + "1-9 keys to change page"; + const string arrowKeys = dir + "Arrow keys to change page or selection "; + + const string fadeAnimations = dir + "Fade animations"; + const string pageScrollAnimation = dir + "Page scroll animation"; + + const string activeOnAlt = dir + "Holding Alt"; + const string activeOnAltShift = dir + "Holding Alt and Shift"; +#if UNITY_EDITOR_OSX + const string activeOnCtrlAlt = dir + "Holding Cmd and Alt"; +#else + const string activeOnCtrlAlt = dir + "Holding Ctrl and Alt"; + +#endif + + const string disablePlugin = dir + "Disable vFavorites"; + + + + + + + [MenuItem(dir + "Shortcuts", false, 1)] static void dadsas() { } + [MenuItem(dir + "Shortcuts", true, 1)] static bool dadsas123() => false; + + [MenuItem(pageScroll, false, 2)] static void dadsadasadsdadsas() => pageScrollEnabled = !pageScrollEnabled; + [MenuItem(pageScroll, true, 2)] static bool dadsadasdadsdasadsas() { Menu.SetChecked(pageScroll, pageScrollEnabled); return !pluginDisabled; } + + [MenuItem(numberKeys, false, 4)] static void dadsadadsas() => numberKeysEnabled = !numberKeysEnabled; + [MenuItem(numberKeys, true, 4)] static bool dadsaddasadsas() { Menu.SetChecked(numberKeys, numberKeysEnabled); return !pluginDisabled; } + + [MenuItem(arrowKeys, false, 5)] static void dadsadaddassas() => arrowKeysEnabled = !arrowKeysEnabled; + [MenuItem(arrowKeys, true, 5)] static bool dadadssaddasadsas() { Menu.SetChecked(arrowKeys, arrowKeysEnabled); return !pluginDisabled; } + + + + + + [MenuItem(dir + "Animations", false, 101)] static void dadsadsas() { } + [MenuItem(dir + "Animations", true, 101)] static bool dadadssas123() => false; + + [MenuItem(fadeAnimations, false, 102)] static void dadsdasadadsas() => fadeAnimationsEnabled = !fadeAnimationsEnabled; + [MenuItem(fadeAnimations, true, 102)] static bool dadsadadsadsdasadsas() { Menu.SetChecked(fadeAnimations, fadeAnimationsEnabled); return !pluginDisabled; } + + [MenuItem(pageScrollAnimation, false, 103)] static void dadsdasdasadadsas() => pageScrollAnimationEnabled = !pageScrollAnimationEnabled; + [MenuItem(pageScrollAnimation, true, 103)] static bool dadsadaddassadsdasadsas() { Menu.SetChecked(pageScrollAnimation, pageScrollAnimationEnabled); return !pluginDisabled; } + + + + + [MenuItem(dir + "Open when", false, 1001)] static void dadsaddssas() { } + [MenuItem(dir + "Open when", true, 1001)] static bool dadadsssas123() => false; + + [MenuItem(activeOnAlt, false, 1002)] static void dadsdasasdadsas() => activeOnAltEnabled = !activeOnAltEnabled; + [MenuItem(activeOnAlt, true, 1002)] static bool dadsadadssdadsdasadsas() { Menu.SetChecked(activeOnAlt, activeOnAltEnabled); return !pluginDisabled; } + + [MenuItem(activeOnAltShift, false, 1003)] static void dadsdasasdadsadsas() => activeOnAltShiftEnabled = !activeOnAltShiftEnabled; + [MenuItem(activeOnAltShift, true, 1003)] static bool dadsadadssdasdadsdasadsas() { Menu.SetChecked(activeOnAltShift, activeOnAltShiftEnabled); return !pluginDisabled; } + + [MenuItem(activeOnCtrlAlt, false, 1004)] static void dadsdasadasadssdadsas() => activeOnCtrlAltEnabled = !activeOnCtrlAltEnabled; + [MenuItem(activeOnCtrlAlt, true, 1004)] static bool dadsadadsadssdadsdasadsas() { Menu.SetChecked(activeOnCtrlAlt, activeOnCtrlAltEnabled); return !pluginDisabled; } + + + + + + [MenuItem(dir + "More", false, 10001)] static void daasadsddsas() { } + [MenuItem(dir + "More", true, 10001)] static bool dadsadsdasas123() => false; + + // [MenuItem(dir + "Open manual", false, 10002)] + // static void dadadssadsas() => AssetDatabase.OpenAsset(AssetDatabase.LoadAssetAtPath(GetScriptPath("VFavorites").GetParentPath().CombinePath("Manual.pdf"))); + + [MenuItem(dir + "Join our Discord", false, 10002)] + static void dadasdsas() => Application.OpenURL("https://discord.gg/pUektnZeJT"); + + + + + + [MenuItem(dir + "Deals ending soon/Get vHierarchy 2 at 60% off", false, 10003)] + static void dadadssadasdsas() => Application.OpenURL("https://assetstore.unity.com/packages/slug/253397?aid=1100lGLBn&pubref=deal60menuvfav"); + + [MenuItem(dir + "Deals ending soon/Get vFolders 2 at 60% off", false, 10004)] + static void dadadssasddsas() => Application.OpenURL("https://assetstore.unity.com/packages/slug/255470?aid=1100lGLBn&pubref=deal60menuvfav"); + + [MenuItem(dir + "Deals ending soon/Get vInspector 2 at 60% off", false, 10005)] + static void dadadadsssadsas() => Application.OpenURL("https://assetstore.unity.com/packages/slug/252297?aid=1100lGLBn&pubref=deal60menuvfav"); + + [MenuItem(dir + "Deals ending soon/Get vTabs 2 at 60% off", false, 10006)] + static void dadadadsssadsadsas() => Application.OpenURL("https://assetstore.unity.com/packages/slug/253396?aid=1100lGLBn&pubref=deal60menuvfav"); + + + + + + + + + [MenuItem(disablePlugin, false, 100001)] static void dadsadsdasadasdasdsadadsas() { pluginDisabled = !pluginDisabled; UnityEditor.Compilation.CompilationPipeline.RequestScriptCompilation(); } + [MenuItem(disablePlugin, true, 100001)] static bool dadsaddssdaasadsadadsdasadsas() { Menu.SetChecked(disablePlugin, pluginDisabled); return true; } + + } +} +#endif \ No newline at end of file diff --git a/Assets/OtherAssets/DEV/PAID/vFavorites/VFavoritesMenu.cs.meta b/Assets/OtherAssets/DEV/PAID/vFavorites/VFavoritesMenu.cs.meta new file mode 100644 index 0000000..c4ab387 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFavorites/VFavoritesMenu.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 65f8f6586ea4740238f667f8337cf6fb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vFavorites/VFavoritesMenuItems.cs b/Assets/OtherAssets/DEV/PAID/vFavorites/VFavoritesMenuItems.cs new file mode 100644 index 0000000..8fb6b18 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFavorites/VFavoritesMenuItems.cs @@ -0,0 +1,6 @@ + + +// this file was present in a previus version and is supposed to be deleted now +// but asset store update delivery system doesn't allow deleting files +// so instead this file is now emptied +// feel free to delete it if you want diff --git a/Assets/OtherAssets/DEV/PAID/vFavorites/VFavoritesMenuItems.cs.meta b/Assets/OtherAssets/DEV/PAID/vFavorites/VFavoritesMenuItems.cs.meta new file mode 100644 index 0000000..cef6260 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFavorites/VFavoritesMenuItems.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e1b1b30cd63e14da9a295742be768842 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vFavorites/VFavoritesState.cs b/Assets/OtherAssets/DEV/PAID/vFavorites/VFavoritesState.cs new file mode 100644 index 0000000..c4d1a52 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFavorites/VFavoritesState.cs @@ -0,0 +1,57 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Serialization; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.Linq; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using Type = System.Type; +using static VFavorites.Libs.VUtils; +using static VFavorites.Libs.VGUI; + + +namespace VFavorites +{ + [FilePath("Library/vFavorites State.asset", FilePathAttribute.Location.ProjectFolder)] + public class VFavoritesState : ScriptableSingleton + { + public int curPageIndex; + + public SerializableDictionary pageStates_byPageId = new SerializableDictionary(); + + public SerializableDictionary itemStates_byItemId = new SerializableDictionary(); + + + [System.Serializable] + public class PageState + { + public long lastItemSelectTime_ticks; + public long lastItemDragTime_ticks; + + public float scrollPos; + + } + + [System.Serializable] + public class ItemState + { + public string _name; + + public string sceneGameObjectIconName; + + public long lastSelectTime_ticks; + public bool isSelected; + + } + + + public static void Save() => instance.Save(true); + + } +} +#endif \ No newline at end of file diff --git a/Assets/OtherAssets/DEV/PAID/vFavorites/VFavoritesState.cs.meta b/Assets/OtherAssets/DEV/PAID/vFavorites/VFavoritesState.cs.meta new file mode 100644 index 0000000..a326150 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFavorites/VFavoritesState.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 362e220c378db4e97ae41fa35a6fb58f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vFavorites/vFavorites Data.asset b/Assets/OtherAssets/DEV/PAID/vFavorites/vFavorites Data.asset new file mode 100644 index 0000000..3f90086 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFavorites/vFavorites Data.asset @@ -0,0 +1,22 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 066cf82f8f80d408c856e48fc8f1127b, type: 3} + m_Name: vFavorites Data + m_EditorClassIdentifier: + pages: + - items: [] + name: Page 1 + _id: 1039256194 + - items: [] + name: Page 2 + _id: 0 + rowScale: 1 diff --git a/Assets/OtherAssets/DEV/PAID/vFavorites/vFavorites Data.asset.meta b/Assets/OtherAssets/DEV/PAID/vFavorites/vFavorites Data.asset.meta new file mode 100644 index 0000000..9a2b93b --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFavorites/vFavorites Data.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c421be6dfd25bbe458970dad85cc77f4 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vFolders.meta b/Assets/OtherAssets/DEV/PAID/vFolders.meta new file mode 100644 index 0000000..34b31e0 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFolders.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 109ccfb08b9fd4bf5a9d8deee2ace2ed +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vFolders/Read me.pdf b/Assets/OtherAssets/DEV/PAID/vFolders/Read me.pdf new file mode 100644 index 0000000000000000000000000000000000000000..48106d2d31242422bbb651291085e275178589e0 GIT binary patch literal 47718 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
gu+jAqv=X|_N?Sf((K&z(HYOh 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 O449COP5QnJ;r{}7lgwWL literal 0 HcmV?d00001 diff --git a/Assets/OtherAssets/DEV/PAID/vFolders/Read me.pdf.meta b/Assets/OtherAssets/DEV/PAID/vFolders/Read me.pdf.meta new file mode 100644 index 0000000..90fb259 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFolders/Read me.pdf.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 6c1da0cc833e64f4c96ba170387ea7a6 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vFolders/VFolders.asmdef b/Assets/OtherAssets/DEV/PAID/vFolders/VFolders.asmdef new file mode 100644 index 0000000..d3d34ab --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFolders/VFolders.asmdef @@ -0,0 +1,16 @@ +{ + "name": "VFolders", + "rootNamespace": "", + "references": [], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Assets/OtherAssets/DEV/PAID/vFolders/VFolders.asmdef.meta b/Assets/OtherAssets/DEV/PAID/vFolders/VFolders.asmdef.meta new file mode 100644 index 0000000..a4528fa --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFolders/VFolders.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 3331bc437f3a24586b7e86e7bdd62b5e +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vFolders/VFolders.cs b/Assets/OtherAssets/DEV/PAID/vFolders/VFolders.cs new file mode 100644 index 0000000..8b4cff2 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFolders/VFolders.cs @@ -0,0 +1,1677 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using UnityEditor.IMGUI.Controls; +using Type = System.Type; +using static VFolders.Libs.VUtils; +using static VFolders.Libs.VGUI; +// using static VTools.VDebug; +using static VFolders.VFoldersData; +using static VFolders.VFoldersCache; + + + +namespace VFolders +{ + public static class VFolders + { + + static void WrappedGUI(EditorWindow window) + { + var navbarHeight = 26; + + var isOneColumn = window.GetMemberValue("m_ViewMode") == 0; + + var prevSelection = Selection.objects; + + + void navbarGui() + { + if (!navbars_byWindow.ContainsKey(window)) + navbars_byWindow[window] = new VFoldersNavbar(window); + + var navbarRect = window.position.SetPos(0, 0).SetHeight(navbarHeight); + + navbars_byWindow[window].OnGUI(navbarRect); + + + } + void hideDefaultTopBar() + { + if (curEvent.isLayout || typeof(GUILayoutUtility).GetMemberValue("current")?.GetMemberValue("topLevel")?.GetMemberValue("entries").Count != 1) // prevents exception on focus by vTabs shift-scroll // doesnt always work. tbh not sure why it works at all + Space(-123); + + } + void defaultGuiWithOffset() + { + var defaultTopBarHeight = 20; + var topOffset = navbarHeight - defaultTopBarHeight; + + var m_Pos_original = window.GetFieldValue("m_Pos"); + + + + + GUI.BeginGroup(m_Pos_original.SetPos(0, 0).AddHeightFromBottom(-topOffset)); + + window.SetFieldValue("m_Pos", m_Pos_original.AddHeightFromBottom(-topOffset)); + + + try { window.InvokeMethod("OnGUI"); } + catch (System.Exception exception) + { + if (exception.InnerException is ExitGUIException) + throw exception.InnerException; + else + throw exception; + + // GUIUtility.ExitGUI() works by throwing ExitGUIException, which just exits imgui loop and doesn't appear in console + // but if ExitGUI is called from a reflected method (OnGUI in this case), the exception becomes TargetInvokationException + // which gets logged to console (only if debugger is attached, for some reason) + // so here in such cases we rethrow the original ExitGUIException + + } + + + window.SetFieldValue("m_Pos", m_Pos_original); + + GUI.EndGroup(); + + } + void treeViewShadow() + { + if (!curEvent.isRepaint) return; + + var shadowLength = 30; + var shadowPos = 21; + var shadowGreyscale = isDarkTheme ? .1f : .28f; + var shadowAlpha = .35f; + + var minScrollPos = 10; + var maxScrollPos = 20; + + + var scrollPos = window.GetMemberValue(isOneColumn ? "m_AssetTree" : "m_FolderTree").GetMemberValue("state").scrollPos.y; + + var opacity = ((scrollPos - minScrollPos) / (maxScrollPos - minScrollPos)).Clamp01(); + + + var rectWidth = isOneColumn ? window.position.width : window.GetMemberValue("m_TreeViewRect").width;// - 12; + + var rect = window.position.SetPos(0, 0).MoveY(shadowPos).SetHeight(shadowLength).SetWidth(rectWidth); + + + + var clipAtY = navbarHeight + 1; + + GUI.BeginClip(window.position.SetPos(0, clipAtY)); + + rect.MoveY(-clipAtY).DrawCurtainDown(Greyscale(shadowGreyscale, shadowAlpha * opacity)); + + GUI.EndClip(); + + + + + if (isOneColumn) return; + + var dividerRect = window.GetMemberValue("m_TreeViewRect").SetWidthFromRight(1).MoveX(1); + + var dividerColor = Greyscale(.16f); + + dividerRect.Draw(dividerColor); + + + } + void listAreaShadow() + { + if (isOneColumn) return; + if (!curEvent.isRepaint) return; + + + var shadowLength = 30; + var shadowPos = navbarHeight + 17; + var shadowGreyscale = isDarkTheme ? .1f : .28f; + var shadowAlpha = .35f; + + var minScrollPos = 10; + var maxScrollPos = 20; + + + var scrollPos = window.GetMemberValue("m_ListAreaState").GetMemberValue("m_ScrollPosition").y; + + var opacity = ((scrollPos - minScrollPos) / (maxScrollPos - minScrollPos)).Clamp01(); + + + var rectX = window.GetMemberValue("m_TreeViewRect").width + 1; + var rectWidth = window.position.width - rectX;// - 12; + + var rect = window.position.SetPos(rectX, 0).MoveY(shadowPos).SetHeight(shadowLength).SetWidth(rectWidth); + + + + var clipAtY = navbarHeight + 22; + + GUI.BeginClip(window.position.SetPos(0, clipAtY)); + + rect.MoveY(-clipAtY).DrawCurtainDown(Greyscale(shadowGreyscale, shadowAlpha * opacity)); + + GUI.EndClip(); + + + + if (isDarkTheme) + window.position.SetPos(rectX, clipAtY - 1).SetSize(12321, 1).Draw(Greyscale(.175f)); // line under breadcrumbs + + } + void preventSelectionChangeInOtherBrowsers() + { + if (Selection.objects.SequenceEqual(prevSelection)) return; + + + allBrowsers.ForEach(r => r?.SetMemberValue("m_InternalSelectionChange", true)); + + EditorApplication.delayCall += () => + allBrowsers.ForEach(r => r?.SetMemberValue("m_InternalSelectionChange", false)); + + } + + + + + + + var doNavbarFirst = GUI.GetNameOfFocusedControl() == "navbar search field" || curEvent.keyCode == KeyCode.Escape; + + if (doNavbarFirst) + navbarGui(); + + hideDefaultTopBar(); + defaultGuiWithOffset(); + treeViewShadow(); + listAreaShadow(); + preventSelectionChangeInOtherBrowsers(); + + if (!doNavbarFirst) + navbarGui(); + + } + + static Dictionary navbars_byWindow = new(); + + + + static void UpdateGUIWrapping(EditorWindow window) + { + if (!window.hasFocus) return; + + var isLocked = window.GetMemberValue("isLocked"); + var isVTabsActive = t_VTabs != null && !EditorPrefsCached.GetBool("vTabs-pluginDisabled", false); + + var curOnGUIMethod = window.GetMemberValue("m_Parent").GetMemberValue("m_OnGUI").Method; + + var isWrapped = curOnGUIMethod == mi_WrappedBrowserOnGUI; + var shouldBeWrapped = VFoldersMenu.navigationBarEnabled && !(isVTabsActive && isLocked) && curOnGUIMethod != mi_VFavorites_WrappedOnGUI; + + void wrap() + { + var hostView = window.GetMemberValue("m_Parent"); + + var newDelegate = typeof(VFolders).GetMethod(nameof(WrappedGUI), maxBindingFlags).CreateDelegate(t_EditorWindowDelegate, window); + + hostView.SetMemberValue("m_OnGUI", newDelegate); + + window.Repaint(); + + } + void unwrap() + { + var hostView = window.GetMemberValue("m_Parent"); + + var originalDelegate = hostView.InvokeMethod("CreateDelegate", "OnGUI"); + + hostView.SetMemberValue("m_OnGUI", originalDelegate); + + window.Repaint(); + + } + + + if (shouldBeWrapped && !isWrapped) + wrap(); + + if (!shouldBeWrapped && isWrapped) + unwrap(); + + } + static void UpdateGUIWrappingForAllBrowsers() => allBrowsers.ForEach(r => UpdateGUIWrapping(r)); + + static void OnDomainReloaded() => toCallInGUI += UpdateGUIWrappingForAllBrowsers; + + static void OnWindowUnmaximized() => UpdateGUIWrappingForAllBrowsers(); + + static void OnBrowserFocused() => UpdateGUIWrapping(EditorWindow.focusedWindow); + + static void OnDelayCall() => UpdateGUIWrappingForAllBrowsers(); + + + + + + static void CheckIfFocusedWindowChanged() + { + if (prevFocusedWindow != EditorWindow.focusedWindow) + if (EditorWindow.focusedWindow?.GetType() == t_ProjectBrowser) + OnBrowserFocused(); + + prevFocusedWindow = EditorWindow.focusedWindow; + + } + + static EditorWindow prevFocusedWindow; + + + + static void CheckIfWindowWasUnmaximized() + { + var isMaximized = EditorWindow.focusedWindow?.maximized == true; + + if (!isMaximized && wasMaximized) + OnWindowUnmaximized(); + + wasMaximized = isMaximized; + + } + + static bool wasMaximized; + + + + static void OnSomeGUI() + { + toCallInGUI?.Invoke(); + toCallInGUI = null; + + CheckIfFocusedWindowChanged(); + + } + + static void ProjectWindowItemOnGUI(string _, Rect __) => OnSomeGUI(); + static void HierarchyWindowItemOnGUI(int _, Rect __) => OnSomeGUI(); + + static System.Action toCallInGUI; + + + + static void DelayCallLoop() + { + OnDelayCall(); + + EditorApplication.delayCall -= DelayCallLoop; + EditorApplication.delayCall += DelayCallLoop; + + } + + + + + + + + + + + + + + + static void ItemGUI(Rect itemRect, string guid, int instanceId) + { + EditorWindow window; + + void findWindow() + { + if (allBrowsers.Count() == 1) { window = allBrowsers.First(); return; } + + + var pointInsideWindow = EditorGUIUtility.GUIToScreenPoint(itemRect.center); + + window = allBrowsers.FirstOrDefault(r => r.position.AddHeight(30).Contains(pointInsideWindow) && r.hasFocus); + + } + void updateWindow() + { + if (!window) return; // happens on half-visible rows during expand animation + + if (curEvent.isLayout && !lastEventWasLayout) + UpdateWindow_Layout(window); + + if (curEvent.isRepaint && !lastEventWasRepaint) + UpdateWindow_Repaint(window); + + + lastEventWasLayout = curEvent.isLayout; + lastEventWasRepaint = curEvent.isRepaint; + + } + void catchScrollInputForController() + { + if (!window) return; + if (!controllers_byWindow.ContainsKey(window)) return; + + if (curEvent.isScroll) + controllers_byWindow[window].animatingScroll = false; + + } + void callGUI() + { + if (!window) return; + if (!guis_byWindow.ContainsKey(window)) return; + + + var gui = guis_byWindow[window]; + + if (itemRect.height == 16) + gui.RowGUI(itemRect, guid, instanceId); + else + gui.CellGUI(itemRect, guid, instanceId); + + } + + findWindow(); + updateWindow(); + catchScrollInputForController(); + callGUI(); + + } + + static void ItemGUI_2021_3_and_older(string guid, Rect itemRect) + { + var instanceId = typeof(AssetDatabase).InvokeMethod("GetMainAssetOrInProgressProxyInstanceID", guid.ToPath()); + + ItemGUI(itemRect, guid, instanceId); + + } + static void ItemGUI_2022_1_and_newer(int instanceId, Rect itemRect) + { + var guid = AssetDatabase.GetAssetPath(instanceId).ToGuid(); + + ItemGUI(itemRect, guid, instanceId); + + } + + static bool lastEventWasLayout; + static bool lastEventWasRepaint; + + + + static void UpdateWindow_Layout(EditorWindow window) + { + if (!guis_byWindow.TryGetValue(window, out var gui)) + gui = guis_byWindow[window] = new(window); + + if (!controllers_byWindow.TryGetValue(window, out var controller)) + controller = controllers_byWindow[window] = new(window); + + if (!histories_byWindow.TryGetValue(window, out var history)) + history = histories_byWindow[window] = new(window); + + + gui.UpdateState_Layout(); + gui.UpdateFoldersFirst(); + + controller.UpdateState(); + controller.UpdateExpandQueue(); + controller.UpdateScrollAnimation(); + controller.UpdateHighlightAnimation(); + + history.UpdateState(); + history.CheckTreeStateChange(); + history.CheckFolderPathChange(); + + } + static void UpdateWindow_Repaint(EditorWindow window) + { + if (guis_byWindow.ContainsKey(window)) + guis_byWindow[window].UpdateState_Repaint(); + } + + public static Dictionary guis_byWindow = new(); + public static Dictionary controllers_byWindow = new(); + public static Dictionary histories_byWindow => VFoldersHistorySingleton.instance.histories_byWindow; + + + + + + + + + public static Texture2D GetSmallFolderIcon(FolderInfo folderInfo, bool removeColor = false) + { + var hasColor = folderInfo.hasColor && !removeColor; + var hasIcon = folderInfo.hasIcon; + + var color = hasColor ? folderInfo.color : default; + var iconNameOrPath = hasIcon ? folderInfo.iconNameOrPath : ""; + + var isEmpty = folderInfo.folderState.isEmpty; + + var key = new object[] { iconNameOrPath, color, isEmpty, isDarkTheme }.Aggregate(0, (hash, r) => (hash * 2) ^ r.GetHashCode()); + + + Texture2D icon = null; + + void getCached() + { + if (!cache.HasIcon(key)) return; + + icon = cache.GetIcon(key); + + } + void generateAndCache() + { + if (icon != null) return; + if (Event.current != null) return; // interactions with gpu in OnGUI may interfere with gui rendering + + var iconSizeX = hasIcon ? 36 : 32; + var iconSizeY = 32; + + var assetIconSize = 20; // 20 21 + var assetIconOffsetX = 16; + var assetIconOffsetY = -2; // -2 -3 + + var folderIconSize = iconSizeY; + var folderIconOffsetY_ifHasAssetIcon = 1; + + Color[] iconPixels; + + Texture2D folderIcon; + Color[] folderIconPixels; + + + void createIcon() + { + icon = new Texture2D(iconSizeX, iconSizeY, TextureFormat.RGBA32, 1, false); + icon.hideFlags = HideFlags.DontSave; + icon.SetPropertyValue("pixelsPerPoint", 2); + + iconPixels = new Color[iconSizeX * iconSizeY]; + + } + void createFolderIcon() + { + var folderIconName = hasColor ? (isEmpty ? "FolderEmpty On Icon" : "Folder On Icon") : + (isEmpty ? "FolderEmpty Icon" : "Folder Icon"); + + + folderIcon = EditorGUIUtility.FindTexture(folderIconName); + + if (folderIcon.width != folderIconSize) + folderIcon = folderIcon.CreateResizedCopy(folderIconSize, folderIconSize); + else + folderIcon = folderIcon.CreateCopy(); + + folderIconPixels = folderIcon.GetPixels(0); + + } + void copyFolderIcon() + { + if (!hasIcon) { iconPixels = folderIconPixels; return; } + + for (int x = 0; x < folderIcon.width; x++) + for (int y = 0; y < folderIcon.height - folderIconOffsetY_ifHasAssetIcon; y++) + iconPixels[x + (y + folderIconOffsetY_ifHasAssetIcon) * icon.width] = folderIconPixels[x + y * folderIcon.width]; + + } + void applyColor() + { + if (!hasColor) return; + + for (int i = 0; i < iconPixels.Length; i++) + iconPixels[i] *= (color * 1.06f).SetAlpha(1); + + } + void insertAssetIcon() + { + if (!hasIcon) return; + + + var assetIconOriginal = EditorIcons.GetIcon(iconNameOrPath); + + if (!assetIconOriginal) return; + + + var prevFilter = assetIconOriginal.filterMode; + + assetIconOriginal.filterMode = FilterMode.Bilinear; + var assetIconPixels_bilinear = assetIconOriginal.CreateResizedCopy(assetIconSize, assetIconSize).GetPixels(); + + assetIconOriginal.filterMode = FilterMode.Point; + var assetIconPixels_point = assetIconOriginal.CreateResizedCopy(assetIconSize, assetIconSize).GetPixels(); + + + assetIconOriginal.filterMode = prevFilter; + + + for (int x = 0; x < iconSizeX; x++) + for (int y = 0; y < iconSizeY; y++) + { + var xAssetIcon = x - assetIconOffsetX; + var yAssetIcon = y - assetIconOffsetY - folderIconOffsetY_ifHasAssetIcon; + + if (!xAssetIcon.IsInRange(0, assetIconSize - 1)) continue; + if (!yAssetIcon.IsInRange(0, assetIconSize - 1)) continue; + + + var innerRadius = (iconNameOrPath == "AudioClip Icon" ? .2f : .4f); + var isInnerPixel = (new Vector2(xAssetIcon, yAssetIcon) / (assetIconSize - 1) - Vector2.one * .5f).magnitude < innerRadius; + + var isOutlinePixel = false; + var outlineRadius = isInnerPixel ? 2 : 1; + for (int xx = xAssetIcon - outlineRadius; xx <= xAssetIcon + outlineRadius; xx++) + if (!isOutlinePixel) + for (int yy = yAssetIcon - outlineRadius; yy <= yAssetIcon + outlineRadius; yy++) + if (!isOutlinePixel) + if (xx.IsInRange(0, assetIconSize - 1) && yy.IsInRange(0, assetIconSize - 1)) + if (assetIconPixels_bilinear[xx + yy * assetIconSize].a > .2f) + isOutlinePixel = true; + + + var pxBilinear = assetIconPixels_bilinear[xAssetIcon + yAssetIcon * assetIconSize]; + var pxPoint = assetIconPixels_point[xAssetIcon + yAssetIcon * assetIconSize]; + var pxCombined = new Color(pxPoint.r, pxPoint.g, pxPoint.b, pxBilinear.a); + + if (pxCombined.a == 0 && !isOutlinePixel) continue; + + iconPixels[x + y * iconSizeX] = pxCombined; + + } + + } + + + createIcon(); + createFolderIcon(); + copyFolderIcon(); + applyColor(); + insertAssetIcon(); + + icon.SetPixels(iconPixels); + icon.Apply(); + + cache.AddIcon(key, icon); + + } + void queueGeneration() + { + if (icon != null) return; + + toGenerateInUpdate.Add(generateAndCache); + + } + + + getCached(); + generateAndCache(); + queueGeneration(); + + return icon ?? EditorGUIUtility.FindTexture(isEmpty ? "FolderEmpty Icon" : "Project@2x"); + + } + + static void GenerateIconsInUpdate() + { + foreach (var r in toGenerateInUpdate) + r.Invoke(); + + toGenerateInUpdate.Clear(); + + } + + static List toGenerateInUpdate = new(); + + + + public static void DrawBigFolderIcon(Rect rect, FolderInfo folderInfo) + { + Rect folderIconRect; + Rect assetIconRect; + + + void calcRects() + { + folderIconRect = rect.SetHeight(rect.width); + +#if !UNITY_2022_3_OR_NEWER + if (Application.platform == RuntimePlatform.OSXEditor) + if (folderIconRect.width > 64) + folderIconRect = folderIconRect.SetSizeFromMid(64, 64); +#endif + + + var assetIconOffsetMin = new Vector2(4.5f, 3.5f); + var assetIconSizeMin = 10; + + var assetIconOffsetMax = new Vector2(19, 15); + var assetIconSizeMax = 24.5f; // 25 + + var t = ((folderIconRect.width - 16) / (64 - 16)); + +#if !UNITY_2022_3_OR_NEWER + if (Application.platform == RuntimePlatform.OSXEditor) + t = t.Clamp01(); +#endif + + var assetIconOffset = MathUtil.Lerp(assetIconOffsetMin, assetIconOffsetMax, t); + var assetIconSize = MathUtil.Lerp(assetIconSizeMin, assetIconSizeMax, t); + + assetIconRect = folderIconRect.Move(assetIconOffset).SetSizeFromMid(assetIconSize, assetIconSize).AlignToPixelGrid(); + + } + + void color() + { + if (!folderInfo.hasColor) return; + + + SetGUIColor(folderInfo.color.SetAlpha(1)); + + GUI.DrawTexture(folderIconRect, EditorGUIUtility.FindTexture(folderInfo.folderState.isEmpty ? "FolderEmpty On Icon" : "Folder On Icon")); + + ResetGUIColor(); + + } + void assetIcon() + { + if (!folderInfo.hasIcon) return; + + + var texture = EditorIcons.GetIcon(folderInfo.iconNameOrPath); + + if (!texture) return; + + if (texture.width < texture.height) assetIconRect = assetIconRect.SetWidthFromMid(assetIconRect.height * texture.width / texture.height); + if (texture.height < texture.width) assetIconRect = assetIconRect.SetHeightFromMid(assetIconRect.width * texture.height / texture.width); + + + + void material() + { + if (!outlineMaterial) + outlineMaterial = new Material(Shader.Find("Hidden/Internal-GUITextureClipText")); + + outlineMaterial.color = isDarkTheme ? Greyscale(.2f, .6f) : Greyscale(.75f); + + // .color needs to be updated continiously because it gets reset on some editor events + // only happens when internal shader is used + + } + + void shadow() + { + var contrast = isDarkTheme ? .6f : .2f; // was .65 then .6 + + assetIconRect.SetSizeFromMid(assetIconRect.width * .8f).DrawBlurred(Greyscale(.2f, contrast), assetIconRect.width * .55f); + + } + void outline() + { + var outlineRect = assetIconRect.Resize(rect.height >= 70 && EditorGUIUtility.pixelsPerPoint >= 2 ? -1f / EditorGUIUtility.pixelsPerPoint : 0).AlignToPixelGrid(); + + EditorGUI.DrawPreviewTexture(outlineRect.Move(-1, -1), texture, outlineMaterial); + EditorGUI.DrawPreviewTexture(outlineRect.Move(-1, 1), texture, outlineMaterial); + EditorGUI.DrawPreviewTexture(outlineRect.Move(1, 1), texture, outlineMaterial); + EditorGUI.DrawPreviewTexture(outlineRect.Move(1, -1), texture, outlineMaterial); + + } + void background() + { + for (int i = 0; i < assetIconRect.size.x; i++) + EditorGUI.DrawPreviewTexture(assetIconRect.Resize(i * .5f + 1), texture, outlineMaterial); + + } + void icon() + { + GUI.DrawTexture(assetIconRect, texture); + } + + material(); + + shadow(); + outline(); + background(); + icon(); + + } + + + calcRects(); + + color(); + assetIcon(); + + } + + static Material outlineMaterial; + + + + + + + + + + + + + + + + + + public static Texture2D GetSmallFolderIcon_forVTabs(string folderGuid) + { + var folderInfo = GetFolderInfo(folderGuid); + + if (folderInfo.hasColor || folderInfo.hasIcon) + return GetSmallFolderIcon(folderInfo); + + return null; + + } + + public static void DrawBigFolderIcon_forVFavorites(Rect rect, string folderGuid) + { + DrawBigFolderIcon(rect, GetFolderInfo(folderGuid)); + } + + public static void SetIcon(string folderPath, string iconName, bool recursive = false) + { + var folderData = GetFolderData(folderPath.ToGuid(), createDataIfDoesntExist: true); + + folderData.iconNameOrGuid = iconName ?? ""; + folderData.isIconRecursive = recursive; + + + folderInfoCache.Clear(); + + EditorApplication.RepaintProjectWindow(); + + } + public static void SetColor(string folderPath, int colorIndex, bool recursive = false) + { + var folderData = GetFolderData(folderPath.ToGuid(), createDataIfDoesntExist: true); + + folderData.colorIndex = colorIndex; + folderData.isColorRecursive = recursive; + + + folderInfoCache.Clear(); + + EditorApplication.RepaintProjectWindow(); + + } + + + + + + + + + + + + + + + + + + static void Shortcuts() // globalEventHandler + { + if (EditorWindow.mouseOverWindow is not EditorWindow hoveredWindow) return; + if (hoveredWindow.GetType() != t_ProjectBrowser) return; + + void toggleExpanded() + { + if (!curEvent.isKeyDown) return; + if (curEvent.keyCode != KeyCode.E) return; + if (curEvent.holdingAnyModifierKey) return; + if (!VFoldersMenu.toggleExpandedEnabled) return; + + if (lastHoveredTreeItem == null) return; + if (!lastHoveredRowRect_screenSpace.Contains(lastKnownMousePosition_screenSpace)) return; + + curEvent.Use(); + + if (lastHoveredTreeItem.children == null) return; + if (lastHoveredTreeItem.children.Count == 0) return; + + + controllers_byWindow[hoveredWindow].ToggleExpanded(lastHoveredTreeItem); + + } + void collapseEverything() + { + if (!curEvent.isKeyDown) return; + if (curEvent.keyCode != KeyCode.E) return; + if (curEvent.modifiers != (EventModifiers.Shift | EventModifiers.Command) && curEvent.modifiers != (EventModifiers.Shift | EventModifiers.Control)) return; + if (!VFoldersMenu.collapseEverythingEnabled) return; + + curEvent.Use(); + + + controllers_byWindow[hoveredWindow].CollapseAll(); + + } + void collapseEverythingElse() + { + if (!curEvent.isKeyDown) return; + if (curEvent.keyCode != KeyCode.E) return; + if (curEvent.modifiers != EventModifiers.Shift) return; + if (!VFoldersMenu.collapseEverythingElseEnabled) return; + + if (lastHoveredTreeItem == null) return; + if (!lastHoveredRowRect_screenSpace.Contains(lastKnownMousePosition_screenSpace)) return; + + curEvent.Use(); + + if (lastHoveredTreeItem.children == null) return; + if (lastHoveredTreeItem.children.Count == 0) return; + + + controllers_byWindow[hoveredWindow].Isolate(lastHoveredTreeItem); + + } + void moveBack() + { + if (!curEvent.isMouseDown) return; + if (curEvent.mouseButton != 3) return; + + + var isOneColumn = hoveredWindow.GetMemberValue("m_ViewMode") == 0; + + if (isOneColumn) return; + + + + if (!histories_byWindow.ContainsKey(hoveredWindow)) + histories_byWindow[hoveredWindow] = new(hoveredWindow); + + var history = histories_byWindow[hoveredWindow]; + + if (!history.prevFolderPaths.Any()) return; + + + history.MoveBack_TwoColumns(); + + } + void moveForward() + { + if (!curEvent.isMouseDown) return; + if (curEvent.mouseButton != 4) return; + + + var isOneColumn = hoveredWindow.GetMemberValue("m_ViewMode") == 0; + + if (isOneColumn) return; + + + + if (!histories_byWindow.ContainsKey(hoveredWindow)) + histories_byWindow[hoveredWindow] = new(hoveredWindow); + + var history = histories_byWindow[hoveredWindow]; + + if (!history.nextFolderPaths.Any()) return; + + + history.MoveForward_TwoColumns(); + + } + + toggleExpanded(); + collapseEverything(); + collapseEverythingElse(); + moveBack(); + moveForward(); + + } + + public static TreeViewItem lastHoveredTreeItem; + + public static Rect lastHoveredRowRect_screenSpace; + + public static Vector2 lastKnownMousePosition_screenSpace; + + + + + + + + + + + + + + + + + + public static FolderInfo GetFolderInfo(string guid) + { + if (folderInfoCache.TryGetValue(guid, out var cachedFolderInfo)) return cachedFolderInfo; + + + var folderInfo = new FolderInfo(); + + var folderData = folderInfo.folderData = GetFolderData(guid, createDataIfDoesntExist: false); + var folderState = folderInfo.folderState = GetFolderState(guid); + + + var recursiveIconNameOrGuid = ""; + var recursiveColorIndex = 0; + + var ruledIconNameOrGuid = ""; + var ruledColorIndex = 0; + + void checkRules() + { + if (rules == null) + rules = TypeCache.GetMethodsWithAttribute() + .Where(r => r.IsStatic + && r.GetParameters().Count() == 1 + && r.GetParameters().First().ParameterType == typeof(Folder)).ToList(); + + if (!rules.Any()) return; + + + + var folder = new Folder(guid); + + foreach (var rule in rules) + rule.Invoke(null, new[] { folder }); + + + ruledIconNameOrGuid = folder.icon; + ruledColorIndex = folder.color; + + + } + void checkRecursion(string path, int depth) + { + if (!path.HasParentPath()) return; + + var parentFolderData = GetFolderData(path.GetParentPath().ToGuid(), createDataIfDoesntExist: false); + + if (parentFolderData != null) + { + + if (parentFolderData.isIconRecursive && parentFolderData.iconNameOrGuid != "") + if (recursiveIconNameOrGuid == "") + recursiveIconNameOrGuid = parentFolderData.iconNameOrGuid; + + if (parentFolderData.isColorRecursive && parentFolderData.colorIndex != 0) + if (recursiveColorIndex == 0) + recursiveColorIndex = parentFolderData.colorIndex; + + + if (parentFolderData.isColorRecursive && parentFolderData.colorIndex != 0) + folderInfo.maxColorRecursionDepth = depth + 1; + + } + + + + checkRecursion(path.GetParentPath(), depth + 1); + + } + void setIcon() + { + var iconNameOrGuid = ""; + + if (folderData != null && folderData.iconNameOrGuid != "") + iconNameOrGuid = folderData.iconNameOrGuid; + + else if (recursiveIconNameOrGuid != "") + iconNameOrGuid = recursiveIconNameOrGuid; + + else if (ruledIconNameOrGuid != "") + iconNameOrGuid = ruledIconNameOrGuid; + + else if (VFoldersMenu.autoIconsEnabled && folderState.autoIconName != "" && folderData?.isIconRecursive != true) + iconNameOrGuid = folderState.autoIconName; + + + + if (iconNameOrGuid == "" || iconNameOrGuid == "none") { folderInfo.hasIcon = false; return; } + + folderInfo.hasIcon = true; + folderInfo.hasIconByRecursion = recursiveIconNameOrGuid != ""; + + folderInfo.iconNameOrPath = iconNameOrGuid.Length == 32 ? iconNameOrGuid.ToPath() + : iconNameOrGuid; + + } + void setColor() + { + var colorIndex = 0; + + if (folderData != null && folderData.colorIndex > 0) + colorIndex = folderData.colorIndex; + + else if (recursiveColorIndex != 0) + colorIndex = recursiveColorIndex; + + else if (ruledColorIndex != 0) + colorIndex = ruledColorIndex; + + + + if (colorIndex == 0) { folderInfo.hasColor = false; return; } + + folderInfo.hasColor = true; + folderInfo.hasColorByRecursion = recursiveColorIndex != 0; + + + + + var brightness = palette?.colorBrightness ?? 1; + var saturation = palette?.colorSaturation ?? 1; + + + var rawColor = palette ? palette.colors[colorIndex - 1] : VFoldersPalette.GetDefaultColor(colorIndex - 1); + + var brightenedColor = MathUtil.Lerp(Greyscale(.2f), rawColor, brightness); + + Color.RGBToHSV(brightenedColor, out float h, out float s, out float v); + var saturatedColor = Color.HSVToRGB(h, s * saturation, v); + + + folderInfo.color = saturatedColor; + + } + + checkRules(); + checkRecursion(guid.ToPath(), 0); + setIcon(); + setColor(); + + + return folderInfoCache[guid] = folderInfo; + + } + + public class FolderInfo + { + public string iconNameOrPath = ""; + public bool hasIcon; + public bool hasIconByRecursion; + + public Color color; + public bool hasColor; + public bool hasColorByRecursion; + public int maxColorRecursionDepth; + + + public FolderData folderData; + public FolderState folderState; + + } + + public static Dictionary folderInfoCache = new(); + + public static List rules = null; + + + + public static FolderData GetFolderData(string guid, bool createDataIfDoesntExist) + { + if (!data) return null; + + FolderData folderData = null; + + void fromScripableObject() + { + if (VFoldersData.storeDataInMetaFiles) return; + + data.folderDatas_byGuid.TryGetValue(guid, out folderData); + + + if (folderData != null || !createDataIfDoesntExist) return; + + folderData = new FolderData(); + + data.folderDatas_byGuid[guid] = folderData; + + } + void fromMetaFile() + { + if (!VFoldersData.storeDataInMetaFiles) return; + + folderDatasFromMetaFiles_byGuid.TryGetValue(guid, out folderData); + + + + if (folderData != null) return; + + var importer = AssetImporter.GetAtPath(guid.ToPath()); + + try { folderData = JsonUtility.FromJson(importer.userData); } catch { } + + folderDatasFromMetaFiles_byGuid[guid] = folderData; + + + + if (folderData != null || !createDataIfDoesntExist) return; + + folderData = new FolderData(); + + folderDatasFromMetaFiles_byGuid[guid] = folderData; + + } + + fromScripableObject(); + fromMetaFile(); + + return folderData; + + } + + public static Dictionary folderDatasFromMetaFiles_byGuid = new(); + + + + public static FolderState GetFolderState(string guid) + { + FolderState folderState = null; + + void getCached() + { + cache.folderStates_byGuid.TryGetValue(guid, out folderState); + } + void create() + { + if (folderState != null) return; + + folderState = new FolderState(); + + folderState.needsUpdate = true; + + cache.folderStates_byGuid[guid] = folderState; + + } + void update() + { + if (!folderState.needsUpdate) return; + if (!Directory.Exists(guid.ToPath())) { folderState.needsUpdate = false; return; } + + + var typesInFolder = Directory.GetFiles(guid.ToPath(), "*.*").Select(r => AssetDatabase.GetMainAssetTypeAtPath(r)).Where(r => r != null); + + void isEmpty() + { + folderState.isEmpty = !Directory.EnumerateFileSystemEntries(guid.ToPath()).Any(); + } + void contentMinimap() + { + var iconNames = new List(); + + void fill() + { + foreach (var type in typesInFolder) + + if (type == typeof(Texture2D)) + iconNames.Add("Texture Icon"); + + else if (type == typeof(GameObject)) + iconNames.Add("Prefab Icon"); + + else if (type.BaseType == typeof(ScriptableObject) || type.BaseType?.BaseType == typeof(ScriptableObject)) + iconNames.Add("ScriptableObject Icon"); + + else if (type == typeof(MonoScript)) + iconNames.Add("cs Script Icon"); + + else if (AssetPreview.GetMiniTypeThumbnail(type)?.name is string iconName) + iconNames.Add(iconName); + + } + void filter() + { + iconNames = iconNames.Distinct().ToList(); + + + for (int i = 0; i < iconNames.Count; i++) + if (iconNames[i].StartsWith("d_")) + iconNames[i] = iconNames[i].Substring(2); + + + + iconNames.Remove("DefaultAsset Icon"); + iconNames.Remove("TextAsset Icon"); + + + + if (iconNames.Contains("cs Script Icon")) + iconNames.Remove("AssemblyDefinitionAsset Icon"); + + if (iconNames.Contains("Shader Icon")) + iconNames.Remove("ShaderInclude Icon"); + + } + void order() + { + var order = new List + { + + "SceneAsset Icon", + + + "Prefab Icon", + "Mesh Icon", + "Material Icon", + "Texture Icon", + + + "cs Script Icon", + "Shader Icon", + "ComputeShader Icon", + "ShaderInclude Icon", + + + "ScriptableObject Icon", + + }; + + iconNames = iconNames.OrderBy(r => order.IndexOf(r) is int i && i != -1 ? i : 1232) + .ThenBy(r => r) + .ToList(); + } + + fill(); + filter(); + order(); + + folderState.contentMinimapIconNames = iconNames; + + } + void autoIcon() + { + folderState.autoIconName = ""; + + + if (!typesInFolder.Any()) return; + if (!typesInFolder.All(r => r == typesInFolder.First()) && !typesInFolder.All(r => typeof(ScriptableObject).IsAssignableFrom(r))) return; + + var type = typesInFolder.First(); + + + if (type == typeof(SceneAsset)) + folderState.autoIconName = "SceneAsset Icon"; + + else if (type == typeof(GameObject)) + folderState.autoIconName = "Prefab Icon"; + + else if (type == typeof(Material)) + folderState.autoIconName = "Material Icon"; + + else if (type == typeof(Texture)) + folderState.autoIconName = "Texture Icon"; + + else if (type == typeof(TerrainData)) + folderState.autoIconName = "TerrainData Icon"; + + else if (type == typeof(AudioClip)) + folderState.autoIconName = "AudioClip Icon"; + + else if (type == typeof(Shader)) + folderState.autoIconName = "Shader Icon"; + + else if (type == typeof(ComputeShader)) + folderState.autoIconName = "ComputeShader Icon"; + + else if (type == typeof(MonoScript) || type == typeof(UnityEditorInternal.AssemblyDefinitionAsset) || type == typeof(UnityEditorInternal.AssemblyDefinitionReferenceAsset)) + folderState.autoIconName = "cs Script Icon"; + + else if (typeof(ScriptableObject).IsAssignableFrom(type)) + folderState.autoIconName = "ScriptableObject Icon"; + + } + + + isEmpty(); + contentMinimap(); + autoIcon(); + + folderState.needsUpdate = false; + + } + + getCached(); + create(); + update(); + + return folderState; + + } + + class FolderStateChangeDetector : AssetPostprocessor + { +#if UNITY_2021_1_OR_NEWER + static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths, bool didDomainReload) +#else + static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) +#endif + { + foreach (var path in importedAssets.Concat(deletedAssets).Concat(movedAssets).Concat(movedFromAssetPaths)) + if (path.HasParentPath()) + if (cache.folderStates_byGuid.TryGetValue(path.GetParentPath().ToGuid(), out var folderState)) + folderState.needsUpdate = true; // todo only clear folderinfo cache here? + } + } + + public static VFoldersCache cache => VFoldersCache.instance; + + + + public static void OnProjectChanged() => folderInfoCache.Clear(); + public static void OnDataSerialization() => folderInfoCache.Clear(); + + + + + + + + + + + + + + + + + [InitializeOnLoadMethod] + static void Init() + { + if (VFoldersMenu.pluginDisabled) return; + + void subscribe() + { + + // gui + +#if UNITY_2022_1_OR_NEWER + EditorApplication.projectWindowItemInstanceOnGUI -= ItemGUI_2022_1_and_newer; + EditorApplication.projectWindowItemInstanceOnGUI = ItemGUI_2022_1_and_newer + EditorApplication.projectWindowItemInstanceOnGUI; +#else + EditorApplication.projectWindowItemOnGUI -= ItemGUI_2021_3_and_older; + EditorApplication.projectWindowItemOnGUI = ItemGUI_2021_3_and_older + EditorApplication.projectWindowItemOnGUI; +#endif + + + + // wrapping updaters + + EditorApplication.projectWindowItemOnGUI -= ProjectWindowItemOnGUI; + EditorApplication.projectWindowItemOnGUI += ProjectWindowItemOnGUI; + + EditorApplication.hierarchyWindowItemOnGUI -= HierarchyWindowItemOnGUI; + EditorApplication.hierarchyWindowItemOnGUI += HierarchyWindowItemOnGUI; + + EditorApplication.delayCall -= DelayCallLoop; + EditorApplication.delayCall += DelayCallLoop; + + EditorApplication.update -= CheckIfFocusedWindowChanged; + EditorApplication.update += CheckIfFocusedWindowChanged; + + + + // shortcuts + + var globalEventHandler = typeof(EditorApplication).GetFieldValue("globalEventHandler"); + typeof(EditorApplication).SetFieldValue("globalEventHandler", Shortcuts + (globalEventHandler - Shortcuts)); + + + + // other + + EditorApplication.update -= GenerateIconsInUpdate; + EditorApplication.update += GenerateIconsInUpdate; + + EditorApplication.projectChanged -= OnProjectChanged; + EditorApplication.projectChanged += OnProjectChanged; + + + } + void loadData() + { + data = AssetDatabase.LoadAssetAtPath(ProjectPrefs.GetString("vFolders-lastKnownDataPath")); + + + if (data) return; + + data = AssetDatabase.FindAssets("t:VFoldersData").Select(guid => AssetDatabase.LoadAssetAtPath(guid.ToPath())).FirstOrDefault(); + + + if (!data) return; + + ProjectPrefs.SetString("vFolders-lastKnownDataPath", data.GetPath()); + + } + void loadPalette() + { + palette = AssetDatabase.LoadAssetAtPath(ProjectPrefs.GetString("vFolders-lastKnownPalettePath")); + + + if (palette) return; + + palette = AssetDatabase.FindAssets("t:VFoldersPalette").Select(guid => AssetDatabase.LoadAssetAtPath(guid.ToPath())).FirstOrDefault(); + + + if (!palette) return; + + ProjectPrefs.SetString("vFolders-lastKnownPalettePath", palette.GetPath()); + + } + void loadDataAndPaletteDelayed() + { + if (!data) + EditorApplication.delayCall += () => EditorApplication.delayCall += loadData; + + if (!palette) + EditorApplication.delayCall += () => EditorApplication.delayCall += loadPalette; + + // AssetDatabase isn't up to date at this point (it gets updated after InitializeOnLoadMethod) + // and if current AssetDatabase state doesn't contain the data - it won't be loaded during Init() + // so here we schedule an additional, delayed attempt to load the data + // this addresses reports of data loss when trying to load it on a new machine + + } + void migrateDataFromV1() + { + if (!data) return; + if (ProjectPrefs.GetBool("vFolders-dataMigrationFromV1Attempted", false)) return; + + ProjectPrefs.SetBool("vFolders-dataMigrationFromV1Attempted", true); + + var lines = System.IO.File.ReadAllLines(data.GetPath()); + + if (lines.Length < 15 || !lines[14].Contains("folderDatasByGuid")) return; + + var guids = new List(); + var icons = new List(); + var colors = new List(); + + void parseGudis() + { + for (int i = 16; i < lines.Length; i++) + { + if (lines[i].Contains("values:")) break; + + var startIndex = lines[i].IndexOf("- ") + 2; + + if (startIndex < lines[i].Length) + guids.Add(lines[i].Substring(startIndex)); + else + guids.Add(""); + + } + + } + void parseIcons() + { + for (int i = 0; i < guids.Count; i++) + if (lines[29 + i * 5 + 3] is string line) + if (line.Length > line.IndexOf(": ") + 2) + icons.Add(line.Substring(line.IndexOf(": ") + 2)); + else + icons.Add(""); + + } + void parseColors() + { + for (int i = 0; i < guids.Count; i++) + if (lines[29 + i * 5 + 1] is string line) + if (line.Length > line.IndexOf(": ") + 2) + colors.Add(int.Parse(line.Substring(line.IndexOf(": ") + 2))); + else + colors.Add(0); + + } + + void remapColors() + { + for (int i = 0; i < colors.Count; i++) + if (colors[i] == 10) + colors[i] = 1; + else if (colors[i] != 0) + colors[i]++; + + } + void fillData() + { + for (int i = 0; i < guids.Count; i++) + if (icons[i] != "" || colors[i] != 0) + data.folderDatas_byGuid[guids[i]] = new FolderData { iconNameOrGuid = icons[i], colorIndex = colors[i] }; + + data.Dirty(); + data.Save(); + + } + + + try + { + parseGudis(); + parseIcons(); + parseColors(); + + remapColors(); + fillData(); + + } + catch { } + + } + void fixIconNamesForUnity6() + { + if (!Application.unityVersion.Contains("6000")) return; + if (ProjectPrefs.GetBool("vFolders-iconNamesForUnity6Fixed", false)) return; + if (!palette) return; + if (!data) return; + + foreach (var iconRow in palette.iconRows) + if (iconRow.builtinIcons.Contains("PhysicMaterial Icon")) + iconRow.builtinIcons[iconRow.builtinIcons.IndexOf("PhysicMaterial Icon")] = "PhysicsMaterial Icon"; + + foreach (var folderData in data.folderDatas_byGuid.Values) + if (folderData.iconNameOrGuid == "PhysicMaterial Icon") + folderData.iconNameOrGuid = "PhysicsMaterial Icon"; + + ProjectPrefs.SetBool("vFolders-iconNamesForUnity6Fixed", true); + + } + + subscribe(); + loadData(); + loadPalette(); + loadDataAndPaletteDelayed(); + migrateDataFromV1(); + fixIconNamesForUnity6(); + + OnDomainReloaded(); + + } + + public static VFoldersData data; + public static VFoldersPalette palette; + + + + + + static IEnumerable allBrowsers => _allBrowsers ??= t_ProjectBrowser.GetFieldValue("s_ProjectBrowsers").Cast(); + static IEnumerable _allBrowsers; + + static Type t_ProjectBrowser = typeof(Editor).Assembly.GetType("UnityEditor.ProjectBrowser"); + static Type t_HostView = typeof(Editor).Assembly.GetType("UnityEditor.HostView"); + static Type t_EditorWindowDelegate = t_HostView.GetNestedType("EditorWindowDelegate", maxBindingFlags); + + static Type t_VTabs = Type.GetType("VTabs.VTabs") ?? Type.GetType("VTabs.VTabs, VTabs, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"); + static Type t_VFavorites = Type.GetType("VFavorites.VFavorites") ?? Type.GetType("VFavorites.VFavorites, VFavorites, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"); + + static MethodInfo mi_WrappedBrowserOnGUI = typeof(VFolders).GetMethod(nameof(WrappedGUI), maxBindingFlags); + static MethodInfo mi_VFavorites_WrappedOnGUI = t_VFavorites?.GetMethod("WrappedOnGUI", maxBindingFlags); + + + + + + const string version = "2.1.6"; + + } + + #region Rules + + public class RuleAttribute : System.Attribute { } + + public class Folder + { + public string path => guid.ToPath(); + public string name => path.Split('/').Last(); + + public int color = 0; + public string icon = ""; + + + + public Folder(string guid) => this.guid = guid; + + string guid; + + + } + + + + #endregion + +} +#endif diff --git a/Assets/OtherAssets/DEV/PAID/vFolders/VFolders.cs.meta b/Assets/OtherAssets/DEV/PAID/vFolders/VFolders.cs.meta new file mode 100644 index 0000000..726886f --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFolders/VFolders.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cb4ae53db31ef4fdfbfc47484afb8192 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersCache.cs b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersCache.cs new file mode 100644 index 0000000..87086b4 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersCache.cs @@ -0,0 +1,102 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.Linq; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using static VFolders.Libs.VUtils; +using static VFolders.Libs.VGUI; +// using static VTools.VDebug; + +namespace VFolders +{ + [FilePath("Library/vFolders Cache.asset", FilePathAttribute.Location.ProjectFolder)] + public class VFoldersCache : ScriptableSingleton + { + + public Texture2D GetIcon(int key) + { + if (instance.iconTextures_byKey.ContainsKey(key)) + return instance.iconTextures_byKey[key]; + + if (instance.iconTextureDatas_byKey.ContainsKey(key)) + return instance.iconTextures_byKey[key] = instance.iconTextureDatas_byKey[key].GetTexture(); + + return null; + + } + + public void AddIcon(int key, Texture2D icon) + { + instance.iconTextures_byKey[key] = icon; + instance.iconTextureDatas_byKey[key] = new TextureData(icon); + + instance.Save(true); + + } + public bool HasIcon(int key) => instance.iconTextureDatas_byKey.ContainsKey(key); + + public Dictionary iconTextures_byKey = new(); + public SerializableDictionary iconTextureDatas_byKey = new(); + + [System.Serializable] + public class TextureData + { + public byte[] rawData; + public int width; + public int height; + public TextureFormat format; + public int mipCount; + public float pixelsPerPoint; + + public TextureData(Texture2D t) + { + rawData = t.GetRawTextureData(); + width = t.width; + height = t.height; + format = t.format; + mipCount = t.mipmapCount; + pixelsPerPoint = t.GetPropertyValue("pixelsPerPoint"); + + } + + public Texture2D GetTexture() + { + var t = new Texture2D(width, height, format, mipCount, false); + t.LoadRawTextureData(rawData); + t.SetPropertyValue("pixelsPerPoint", pixelsPerPoint); + t.hideFlags = HideFlags.DontSave; + t.Apply(); + + return t; + + } + + } + + + + + public SerializableDictionary folderStates_byGuid = new(); + + [System.Serializable] + public class FolderState + { + public bool isEmpty; + + public List contentMinimapIconNames = new(); + + public string autoIconName = ""; + + public bool needsUpdate; + + } + + } +} +#endif diff --git a/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersCache.cs.meta b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersCache.cs.meta new file mode 100644 index 0000000..8cdb893 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersCache.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 69c14c9fceb5b4c968f55fe5786a0bee +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersController.cs b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersController.cs new file mode 100644 index 0000000..d15ecb3 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersController.cs @@ -0,0 +1,521 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using UnityEditor.IMGUI.Controls; +using Type = System.Type; +using static VFolders.Libs.VUtils; +using static VFolders.Libs.VGUI; +// using static VTools.VDebug; +using static VFolders.VFolders; +using static VFolders.VFoldersData; +using static VFolders.VFoldersCache; + + + +namespace VFolders +{ + public class VFoldersController + { + + public void UpdateExpandQueue() + { + if (treeViewAnimatesExpansion) return; + + if (!expandQueue_toAnimate.Any()) + { + if (!expandQueue_toCollapseAfterAnimation.Any()) return; + + foreach (var id in expandQueue_toCollapseAfterAnimation) + SetExpanded_withoutAnimation(id, false); + + expandQueue_toCollapseAfterAnimation.Clear(); + + return; + } + + + var iid = expandQueue_toAnimate.First().id; + var expand = expandQueue_toAnimate.First().expand; + + + if (expandedIds.Contains(iid) != expand) + SetExpanded_withAnimation(iid, expand); + + expandQueue_toAnimate.RemoveAt(0); + + + window.Repaint(); + + } + + public List expandQueue_toAnimate = new(); + public List expandQueue_toCollapseAfterAnimation = new(); + + public struct ExpandQueueEntry { public int id; public bool expand; } + + public bool animatingExpansion => expandQueue_toAnimate.Any() || expandQueue_toCollapseAfterAnimation.Any(); + + + + + + + public void UpdateScrollAnimation() + { + if (!animatingScroll) return; + + + var lerpSpeed = 10; + + var lerpedScrollPos = MathUtil.SmoothDamp(currentScrollPos, targetScrollPos, lerpSpeed, ref scrollPosDerivative, editorDeltaTime); + + SetScrollPos(lerpedScrollPos); + + window.Repaint(); + + + + if (lerpedScrollPos.DistanceTo(targetScrollPos) > .4f) return; + + SetScrollPos(targetScrollPos); + + animatingScroll = false; + + + } + + public float targetScrollPos; + public float scrollPosDerivative; + + public bool animatingScroll; + + + + + + + + public void UpdateHighlightAnimation() + { + if (!animatingHighlight) return; + + + var lerpSpeed = 1.2f; + + MathUtil.SmoothDamp(ref highlightAmount, 0, lerpSpeed, ref highlightDerivative, editorDeltaTime); + + window.Repaint(); + + + + if (highlightAmount > .05f) return; + + highlightAmount = 0; + + animatingHighlight = false; + + + } + + public float highlightAmount; + public float highlightDerivative; + + public bool animatingHighlight; + + public string folderToHighlight; + + + + + + + + public void UpdateState() + { + isOneColumn = window.GetFieldValue("m_ViewMode") == 0; + + treeViewController = window.GetFieldValue(isOneColumn ? "m_AssetTree" : "m_FolderTree"); + treeViewControllerData = treeViewController?.GetPropertyValue("data"); + + + + var treeViewState = treeViewController?.GetPropertyValue("state"); + + currentScrollPos = treeViewState?.scrollPos.y ?? 0; + + expandedIds = treeViewState?.expandedIDs ?? new List(); + + + + var treeViewAnimator = treeViewController?.GetMemberValue("m_ExpansionAnimator"); + var treeViewAnimatorSetup = treeViewAnimator?.GetMemberValue("m_Setup"); + + treeViewAnimatesScroll = treeViewController?.GetMemberValue("m_FramingAnimFloat").isAnimating ?? false; + + treeViewAnimatesExpansion = treeViewAnimator?.GetMemberValue("isAnimating") ?? false; + + } + + bool isOneColumn; + + object treeViewController; + object treeViewControllerData; + + public float currentScrollPos; + + public List expandedIds = new(); + + public bool treeViewAnimatesScroll; + public bool treeViewAnimatesExpansion; + + public int GetRowIndex(int instanceId) + { + return treeViewControllerData.InvokeMethod("GetRow", instanceId); + } + + + + + + + + + + + + + + + + + public void ToggleExpanded(TreeViewItem item) + { + SetExpanded_withAnimation(item.id, !expandedIds.Contains(item.id)); + + window.Repaint(); + + } + + public void CollapseAll() + { + + var idsToCollapse_roots = expandedIds.Where(id => EditorUtility.InstanceIDToObject(id).GetPath() is string path && + path.HasParentPath() && + (path.GetParentPath() == "Assets" || path.GetParentPath() == "Packages")); + + + var idsToCollapse_children = expandedIds.Where(id => EditorUtility.InstanceIDToObject(id).GetPath() is string path && + !path.IsNullOrEmpty() && + path != "Assets" && + path != "Packages" && + !idsToCollapse_roots.Contains(id)); + + + expandQueue_toCollapseAfterAnimation = idsToCollapse_children.ToList(); + + expandQueue_toAnimate = idsToCollapse_roots.Select(id => new ExpandQueueEntry { id = id, expand = false }) + .OrderBy(row => GetRowIndex(row.id)).ToList(); + + + StartScrollAnimation(targetScrollPos: 0); + + + window.Repaint(); + + } + + public void Isolate(TreeViewItem targetItem) + { + + List getParents(TreeViewItem item) + { + var parents = new List(); + + while (item.parent != null) + parents.Add(item = item.parent); + + return parents; + + } + + var targetItemParents = getParents(targetItem); + + + + var expandedVisibleItems = new List(); + + foreach (var expandedId in expandedIds) + if (GetRowIndex(expandedId) is int rowIndex && rowIndex != -1) + expandedVisibleItems.Add(treeViewControllerData.InvokeMethod("GetItem", rowIndex)); + + + + var itemsToCollapse = expandedVisibleItems.ToList(); + + itemsToCollapse.Remove(targetItem); + itemsToCollapse.RemoveAll(r => targetItemParents.Contains(r)); + itemsToCollapse.RemoveAll(r => itemsToCollapse.Intersect(getParents(r)).Any()); + + + + expandQueue_toAnimate = itemsToCollapse.Select(item => new ExpandQueueEntry { id = item.id, expand = false }) + .Append(new ExpandQueueEntry { id = targetItem.id, expand = true }) + .OrderBy(r => GetRowIndex(r.id)).ToList(); + + + window.Repaint(); + + } + + + + + + public void StartExpandAnimation(List targetExpandedIds) + { + + var toExpand = targetExpandedIds.Except(expandedIds).ToHashSet(); + var toCollapse = expandedIds.Except(targetExpandedIds).ToHashSet(); + + + + // hanlde non-animated expansions/collapses + + bool hasParentToCollapse(int id) + { + var o = Resources.InstanceIDToObject(id); + + if (!o) return false; + + + var assetPath = AssetDatabase.GetAssetPath(o); + + if (!assetPath.HasParentPath()) return false; + if (assetPath == "Assets") return false; + + + var parentAsset = AssetDatabase.LoadAssetAtPath(assetPath.GetParentPath()); + + if (!parentAsset) return false; // packages item + + + var parentId = parentAsset.GetInstanceID(); + + return toCollapse.Contains(parentId) + || hasParentToCollapse(parentId); + + } + bool areAllParentsExpanded(int id) + { + var o = Resources.InstanceIDToObject(id); + + if (!o) return true; + + + var assetPath = AssetDatabase.GetAssetPath(o); + + if (!assetPath.HasParentPath()) return true; + if (assetPath == "Assets") return true; + + + var parentAsset = AssetDatabase.LoadAssetAtPath(assetPath.GetParentPath()); + + if (!parentAsset) return true; // packages item + + + var parentId = parentAsset.GetInstanceID(); + + return expandedIds.Contains(parentId) + && areAllParentsExpanded(parentId); + + } + + var toExpand_beforeAnimation = toExpand.Where(id => !areAllParentsExpanded(id)).ToHashSet(); + var toCollapse_afterAnimation = toCollapse.Where(id => hasParentToCollapse(id)).ToHashSet(); + + + foreach (var id in toExpand_beforeAnimation) + SetExpanded_withoutAnimation(id, true); + + foreach (var id in toCollapse_afterAnimation) + expandQueue_toCollapseAfterAnimation.Add(id); + + + toExpand.ExceptWith(toExpand_beforeAnimation); + toCollapse.ExceptWith(toCollapse_afterAnimation); + + + + + // setup animation + + expandQueue_toAnimate = toCollapse.Select(id => new ExpandQueueEntry { id = id, expand = false }) + .Concat(toExpand.Select(id => new ExpandQueueEntry { id = id, expand = true })) + .OrderBy(r => GetRowIndex(r.id)).ToList(); + + } + + public void SetExpandedIds(List targetExpandedIds) + { + treeViewControllerData.InvokeMethod("SetExpandedIDs", targetExpandedIds.ToArray()); + } + public void SetExpanded_withAnimation(int instanceId, bool expanded) + { + treeViewController.InvokeMethod("ChangeFoldingForSingleItem", instanceId, expanded); + } + public void SetExpanded_withoutAnimation(int instanceId, bool expanded) + { + treeViewControllerData.InvokeMethod("SetExpanded", instanceId, expanded); + } + + + + public void StartScrollAnimation(float targetScrollPos) + { + if (targetScrollPos.DistanceTo(currentScrollPos) < .05f) return; + + this.targetScrollPos = targetScrollPos; + + animatingScroll = true; + + } + + public void SetScrollPos(float targetScrollPos) + { + treeViewController.GetPropertyValue("state").scrollPos = Vector2.up * targetScrollPos; + } + + + + + public void RevealFolder(string path, bool expand, bool highlight, bool snapToTopMargin) + { + + int getId(string path) => AssetDatabase.LoadAssetAtPath(path).GetInstanceID(); + + + var idsToExpand = new List(); + + if (expand) + idsToExpand.Add(getId(path)); + + var cur = path; + while (!(cur = cur.GetParentPath()).IsNullOrEmpty()) + idsToExpand.Add(getId(cur)); + + idsToExpand.RemoveAll(r => expandedIds.Contains(r)); + + + + + foreach (var id in idsToExpand.SkipLast(1)) + SetExpanded_withoutAnimation(id, true); + + if (idsToExpand.Any()) + SetExpanded_withAnimation(idsToExpand.Last(), true); + + + + + var rowCount = treeViewControllerData.GetMemberValue("m_Rows").Count; + var maxScrollPos = rowCount * 16 - window.position.height + (isOneColumn ? 49.9f : 45.9f); + + var rowIndex = treeViewControllerData.InvokeMethod("GetRow", getId(path)); + var rowPos = rowIndex * 16f + (isOneColumn ? 11 : 23); + + var scrollAreaHeight = window.GetMemberValue("m_TreeViewRect").height; + + + + + var margin = 48; + + var targetScrollPos = 0f; + + if (snapToTopMargin) + targetScrollPos = (rowPos - margin).Min(maxScrollPos) + .Max(0); + else + targetScrollPos = currentScrollPos.Min(rowPos - margin) + .Max(rowPos - scrollAreaHeight + margin) + .Min(maxScrollPos) + .Max(0); + if (targetScrollPos < 25) + targetScrollPos = 0; + + StartScrollAnimation(targetScrollPos); + + + + + + if (!highlight) return; + + highlightAmount = 2.2f; + + animatingHighlight = true; + + folderToHighlight = path; + + + } + + public void OpenFolder(string path) + { + // update search + window.GetMemberValue("m_SearchFilter").InvokeMethod("ClearSearch"); + window.GetMemberValue("m_SearchFilter").SetMemberValue("folders", new[] { path }); + + + // update folder tree + window.GetMemberValue("m_FolderTree").InvokeMethod("SetSelection", new[] { AssetDatabase.LoadAssetAtPath(path).GetInstanceID() }, false); + + + // update list area + var listAreaRect = window.GetMemberValue("m_ListAreaRect"); + var searchFilter = window.GetMemberValue("m_SearchFilter"); + var checkThumbnails = false; + var assetToInstanceId = (System.Func)((s) => typeof(AssetDatabase).InvokeMethod("GetMainAssetInstanceID", s)); + + window.GetMemberValue("m_ListArea")?.InvokeMethod("InitForSearch", listAreaRect, HierarchyType.Assets, searchFilter, checkThumbnails, assetToInstanceId); + + + // updat breadcrumbs + window.GetMemberValue("m_BreadCrumbs").Clear(); + + + + // pretty much the same as ProjectBrowser.ShowFolderContents() + // but without m_FolderTree.SetSelection() + + } + + + + + + + + + + + + + public VFoldersController(EditorWindow window) => this.window = window; + + public EditorWindow window; + + public VFoldersGUI gui => VFolders.guis_byWindow[window]; + + } +} +#endif \ No newline at end of file diff --git a/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersController.cs.meta b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersController.cs.meta new file mode 100644 index 0000000..290180f --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersController.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 380edf2cfb553464d9f7ec73602e93d1 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersData.cs b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersData.cs new file mode 100644 index 0000000..630bf5c --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersData.cs @@ -0,0 +1,145 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Serialization; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.Linq; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using static VFolders.Libs.VUtils; +using static VFolders.Libs.VGUI; +// using static VTools.VDebug; + + +namespace VFolders +{ + public class VFoldersData : ScriptableObject, ISerializationCallbackReceiver + { + + public SerializableDictionary folderDatas_byGuid = new(); + + [System.Serializable] + public class FolderData + { + public string iconNameOrGuid = ""; + public int colorIndex = 0; + + public bool isIconRecursive; + public bool isColorRecursive; + + } + + public void OnBeforeSerialize() => VFolders.OnDataSerialization(); + public void OnAfterDeserialize() { } + + + + + public List bookmarks = new(); + + [System.Serializable] + public class Bookmark + { + + public string name => isDeleted ? "Deleted" : guid.ToPath().GetFilename(); + + + public bool isDeleted => !AssetDatabase.IsValidFolder(guid.ToPath()); + + + + public Bookmark(Object o) => guid = o.GetGuid(); + + public string guid; + + } + + + + + + + + + [CustomEditor(typeof(VFoldersData))] + class Editor : UnityEditor.Editor + { + public override void OnInspectorGUI() + { + var style = new GUIStyle(EditorStyles.label) { wordWrap = true }; + + void normal() + { + if (storeDataInMetaFiles) return; + + SetGUIEnabled(false); + BeginIndent(0); + + Space(10); + EditorGUILayout.LabelField("This file stores data about which icons and colors are assigned to folders, along with bookmarks from navigation bar.", style); + + Space(6); + GUILayout.Label("If there are multiple people working on the project, it's better to store icon and color data in .meta files of folders to avoid merge conflicts. To do that, click the â‹® button at the top right corner and enable Team Mode.", style); + + EndIndent(10); + ResetGUIEnabled(); + } + void meta() + { + if (!storeDataInMetaFiles) return; + + SetGUIEnabled(false); + BeginIndent(0); + + Space(10); + EditorGUILayout.LabelField("Icon and color data is currently stored in folders .meta files of folders, and this file only contains bookmarks from navigation bar.", style); + + Space(6); + GUILayout.Label("If you want all data to be stored in this file, click the â‹® button at the top right corner and disable Team Mode.", style); + + EndIndent(10); + ResetGUIEnabled(); + } + + normal(); + meta(); + + } + } + + public static bool storeDataInMetaFiles { get => EditorPrefsCached.GetBool("vFolders-teamModeEnabled", false); set => EditorPrefsCached.SetBool("vFolders-teamModeEnabled", value); } + + + + [ContextMenu("Enable Team Mode", isValidateFunction: false, priority: 1)] + public void EnableTeamMode() + { + var option = EditorUtility.DisplayDialogComplex("Licensing notice", + "To use vFolders 2 within a team, licenses must be purchased for each individual user as per the Asset Store EULA.\n\n Sharing one license across the team is illegal and considered piracy.", + "Acknowledge", + "Cancel", + "Purchase more seats"); + if (option == 0) + storeDataInMetaFiles = true; + + if (option == 2) + Application.OpenURL("https://prf.hn/click/camref:1100lGLBn/pubref:teammode/destination:https://assetstore.unity.com/packages/tools/utilities/vfolders-2-255470"); + // Application.OpenURL("https://assetstore.unity.com/packages/slug/255470"); + + } + + [ContextMenu("Disable Team Mode", isValidateFunction: false, priority: 2)] + public void DisableTeamMode() => storeDataInMetaFiles = false; + + [ContextMenu("Enable Team Mode", isValidateFunction: true, priority: 1)] bool asd() => !storeDataInMetaFiles; + [ContextMenu("Disable Team Mode", isValidateFunction: true, priority: 2)] bool ads() => storeDataInMetaFiles; + + + + } +} +#endif \ No newline at end of file diff --git a/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersData.cs.meta b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersData.cs.meta new file mode 100644 index 0000000..30bf11c --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: adb9c12a8c6c949f7af83a3d238359d0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersGUI.cs b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersGUI.cs new file mode 100644 index 0000000..b483950 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersGUI.cs @@ -0,0 +1,1325 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using UnityEditor.IMGUI.Controls; +using Type = System.Type; +using static VFolders.Libs.VUtils; +using static VFolders.Libs.VGUI; +// using static VTools.VDebug; +using static VFolders.VFolders; +using static VFolders.VFoldersData; +using static VFolders.VFoldersCache; + + + +namespace VFolders +{ + public class VFoldersGUI + { + + public void RowGUI(Rect rowRect, string guid, int instanceId) + { + var fullRowRect = rowRect.AddWidthFromRight(rowRect.x); + + var isRowHovered = fullRowRect.IsHovered(); + + var isFolder = AssetDatabase.IsValidFolder(guid.ToPath()); + var isAsset = !isFolder && !guid.IsNullOrEmpty(); + var isFavorite = !isFolder && !isAsset && rowRect.x != 16; + + var isFavoritesRoot = rowRect.x == 16 && !isFolder && rowRect.y == 0; + var isAssetsRoot = rowRect.x == 16 && isFolder && guid.ToPath() == "Assets"; + var isPackagesRoot = rowRect.x == 16 && !isFavoritesRoot && !isAssetsRoot && guid.IsNullOrEmpty(); + + var isListArea = rowRect.x == 14 || !isFolder; + + var useMinimalMode = VFoldersMenu.minimalModeEnabled && !isListArea; + var useBackgroundColors = (VFoldersMenu.backgroundColorsEnabled || useMinimalMode) && !isListArea; + var useHierarchyLines = VFoldersMenu.hierarchyLinesEnabled && !isListArea; + var useContentMinimap = VFoldersMenu.contentMinimapEnabled; + var useZebraStriping = VFoldersMenu.zebraStripingEnabled; + + + + TreeViewItem treeItem = null; + + var isRowSelected = false; + var isRowBeingRenamed = false; + + void setObjects() + { + if (isListArea) return; + + if (!curEvent.isRepaint) // only needed for drawing, + if (!curEvent.isMouseUp) // altClick, + if (!isRowHovered) // and setting last hovered tree item + return; + + + void set_treeItem_byRect() + { + if (treeViewAnimatesExpansion) return; + + var offest = isTwoColumns ? -15 : -4; + + + if ((rowRect.y + offest) % 16 != 0) return; + + var rowIndex = ((rowRect.y + offest) / 16).ToInt(); + + + if (rowIndex < 0 || rowIndex >= rows.Count) return; + if (rows == null) return; + + treeItem = rows[rowIndex]; + + } + void set_treeItem_byInstanceId() + { + if (treeItem != null) return; + if (isFavorite || isFavoritesRoot) return; + + treeItem = treeViewController?.InvokeMethod("FindItem", instanceId); + + } + + set_treeItem_byRect(); + set_treeItem_byInstanceId(); + + } + void setState() + { + void set_isRowSelected_oneColumn() + { + if (!curEvent.isRepaint) return; + if (isListArea) return; + if (treeItem == null) return; + if (isTwoColumns) return; + + + var dragging = treeView_dragSelectionList?.Any() == true; + + isRowSelected = dragging ? (treeView_dragSelectionList.Contains(treeItem.id)) : Selection.Contains(treeItem.id); + + } + void set_isRowSelected_twoColumns() + { + if (!curEvent.isRepaint) return; + if (isListArea) return; + if (treeItem == null) return; + if (!isTwoColumns) return; + + + var dragging = treeView_dragSelectionList != null + && treeView_dragSelectionList.Any(); + + isRowSelected = dragging ? (treeView_dragSelectionList.Contains(treeItem.id)) : treeView_normalSelectionList != null && treeView_normalSelectionList.Contains(treeItem.id); + + } + void set_lastHovered() + { + if (isListArea) return; + if (!isRowHovered) return; + + lastHoveredRowRect_screenSpace = EditorGUIUtility.GUIToScreenRect(fullRowRect); + lastHoveredTreeItem = treeItem; + + } + void set_lastKnownMousePosition() + { + if (!curEvent.isRepaint) return; + + lastKnownMousePosition_screenSpace = curEvent.mousePosition_screenSpace; + + } + + set_isRowSelected_oneColumn(); + set_isRowSelected_twoColumns(); + set_lastHovered(); + set_lastKnownMousePosition(); + + isRowBeingRenamed = renamingRow && isRowSelected; + + } + + void drawing() + { + if (!curEvent.isRepaint) { hierarchyLines_isFirstRowDrawn = false; return; } + + + + var folderInfo = GetFolderInfo(guid); + + var rowHasColor = useBackgroundColors && folderInfo.hasColor; + + var rowHasCustomIcon = !isFolder ? false + : useBackgroundColors ? folderInfo.hasIcon + : folderInfo.hasIcon || folderInfo.hasColor; + var rowHasIcon = !useMinimalMode ? true + : rowHasCustomIcon || isAsset || isFavorite || isRowBeingRenamed; + + + var hideName = rowHasColor || !rowHasIcon; + var hideDefaultIcon = rowHasCustomIcon || !rowHasIcon; + + var drawTriangle = rowHasColor && treeItem?.hasChildren == true; + var drawName = rowHasColor || !rowHasIcon; + var drawDefaultIcon = (rowHasColor && !rowHasCustomIcon) && rowHasIcon; + + var makeTriangleBrighter = rowHasColor && isDarkTheme; + var makeNameBrighter = rowHasColor && isDarkTheme; + var makeIconBrighter = rowHasColor && isFolder; + var makeExtraSpaceForVCSIcons = isVersionControlEnabled; + + + var defaultBackground = isListArea ? Greyscale(isDarkTheme ? .2f : .75f) + : GUIColors.windowBackground; + if (isRowSelected) + if (!isRowBeingRenamed) + defaultBackground = isTreeFocused ? GUIColors.selectedBackground + : Greyscale(isDarkTheme ? .3f : .68f); + + + + void hideName_() + { + if (!hideName) return; + if (isFavorite) return; + + var name = isListArea ? guid.ToPath().GetFilename() : treeItem != null ? treeItem.displayName : "Favorites"; + + var nameRect = rowRect.SetWidth(name.GetLabelWidth(isBold: isFavorite || isAssetsRoot || isPackagesRoot) + 6).MoveX(16).MoveX(isListArea ? 3 : 0); + + nameRect.Draw(defaultBackground); + + } + void hideDefaultIcon_() + { + if (!hideDefaultIcon) return; + + var iconRect = rowRect.SetWidth(16).MoveX(isListArea ? 3 : 0); + + if (makeExtraSpaceForVCSIcons) + iconRect = iconRect.MoveX(8); + + + if (isListArea) + { + SetGUIColor(defaultBackground); + + GUI.DrawTexture(iconRect, EditorIcons.GetIcon("Folder On Icon")); + + ResetGUIColor(); + + } + else + iconRect.Draw(defaultBackground); + + } + + void color() + { + if (!rowHasColor) return; + + + + var color = (isDarkTheme ? Color.Lerp(folderInfo.color, Greyscale(.05f), .42f) + : Color.Lerp(folderInfo.color, Greyscale(.8f), .5f)).SetAlpha(1); + if (isRowHovered) + color *= isDarkTheme ? 1.1f : .92f; + + if (isRowSelected) + color *= isDarkTheme ? 1.2f : .8f; + + if (palette?.colorGradientsEnabled == false) + color = MathUtil.Lerp(color, Greyscale(.2f), isDarkTheme ? .25f : .03f); + + if (folderInfo.hasColorByRecursion) + color = MathUtil.Lerp(color, Greyscale(isDarkTheme ? .24f : .8f), .45f); + + + + + + + var colorRect = rowRect.AddWidthFromRight(30).AddWidth(16); + + if (folderInfo.hasColorByRecursion) + colorRect = colorRect.AddWidthFromRight(folderInfo.maxColorRecursionDepth * 14); + + if (!isRowSelected && !folderInfo.hasColorByRecursion) + colorRect = colorRect.AddHeightFromMid(EditorGUIUtility.pixelsPerPoint >= 2 ? -.5f : -1); + + if (folderInfo.hasColorByRecursion) + colorRect = colorRect.MoveY(EditorGUIUtility.pixelsPerPoint >= 2 ? -.25f : -.5f); + + if (palette?.colorGradientsEnabled == false) { colorRect.Draw(color); return; } + + + var hasLeftGradient = colorRect.x > 4; + + if (hasLeftGradient) + colorRect = colorRect.AddWidthFromRight(3); + + + + var leftGradientWith = hasLeftGradient ? 22 : 0; + var rightGradientWidth = (fullRowRect.width * .77f).Min(colorRect.width - leftGradientWith); + + var leftGradientRect = colorRect.SetWidth(leftGradientWith); + var rightGradientRect = colorRect.SetWidthFromRight(rightGradientWidth); + + var flatColorRect = colorRect.SetX(leftGradientRect.xMax).SetXMax(rightGradientRect.x); + + + + + + + leftGradientRect.AddWidth(1).DrawCurtainLeft(color); + + flatColorRect.AddWidth(1).Draw(color); + + rightGradientRect.Draw(color.MultiplyAlpha(.1f)); + rightGradientRect.DrawCurtainRight(color); + + + } + void triangle() + { + if (!drawTriangle) return; + + + var triangleRect = rowRect.MoveX(-15).SetWidth(16).Resize(-1); + + GUI.Label(triangleRect, EditorGUIUtility.IconContent(expandedIds.Contains(treeItem.id) ? "IN_foldout_on" : "IN_foldout")); + + + if (!makeTriangleBrighter) return; + + GUI.Label(triangleRect, EditorGUIUtility.IconContent(expandedIds.Contains(treeItem.id) ? "IN_foldout_on" : "IN_foldout")); + + } + void name() + { + if (!drawName) return; + if (isRowBeingRenamed) return; + + + var nameRect = rowRect.MoveX(18).AddHeight(1); ; + + if (isListArea) + nameRect = nameRect.MoveX(3); + + if (!rowHasIcon) + nameRect = nameRect.MoveX(-17); + + if (makeNameBrighter) + nameRect = nameRect.MoveX(-2).MoveY(-.5f); + + if (makeExtraSpaceForVCSIcons) + nameRect = nameRect.MoveX(16); + + + + var styleName = makeNameBrighter ? "WhiteLabel" : "TV Line"; + + if (isFavoritesRoot || isAssetsRoot || isPackagesRoot) + styleName = "BoldLabel"; + + + + var name = isFavoritesRoot ? "Favorites" : + isPackagesRoot ? "Packages" : + isListArea || treeItem == null ? guid.ToPath().GetFilename() : + treeItem.displayName; + + + + if (makeNameBrighter) + SetGUIColor(Greyscale(isRowSelected ? 1 : .93f)); + + GUI.skin.GetStyle(styleName).Draw(nameRect, name, false, false, isRowSelected, isTreeFocused && styleName != "BoldLabel"); + + if (makeNameBrighter) + ResetGUIColor(); + + } + void defaultIcon() + { + if (!drawDefaultIcon) return; + + + var iconRect = rowRect.SetWidth(16).MoveX(isListArea ? 3 : 0); + + if (makeExtraSpaceForVCSIcons) + iconRect = iconRect.MoveX(8); + + + var icon = isAsset ? AssetDatabase.GetCachedIcon(guid.ToPath()) + : makeIconBrighter ? EditorIcons.GetIcon(folderInfo.folderState.isEmpty ? "FolderEmpty On Icon" : "Folder On Icon") + : EditorIcons.GetIcon(folderInfo.folderState.isEmpty ? "FolderEmpty Icon" : "Folder Icon"); + + + SetLabelAlignmentCenter(); + + if (makeIconBrighter) + SetGUIColor(Greyscale(.88f)); + + GUI.DrawTexture(iconRect, icon); + + if (makeIconBrighter) + ResetGUIColor(); + + ResetLabelStyle(); + + } + void customIcon() + { + if (!rowHasCustomIcon) return; + + + var icon = GetSmallFolderIcon(folderInfo, removeColor: useBackgroundColors); + + if (useMinimalMode && folderInfo.hasIcon) + icon = EditorIcons.GetIcon(folderInfo.iconNameOrPath); + + if (!icon) return; + + + var iconRect = rowRect.SetWidth(16).MoveX(isListArea ? 3 : 0); + + iconRect = iconRect.SetWidth(iconRect.height / icon.height * icon.width); + + if (makeExtraSpaceForVCSIcons) + iconRect = iconRect.MoveX(7); + + + GUI.DrawTexture(iconRect, icon); + + } + void versionControlOverlay() + { + if (!isFolder) return; + if (!hideDefaultIcon && !rowHasColor) return; + + if (!isVersionControlEnabled) return; + + typeof(Editor).Assembly.GetType("UnityEditorInternal.VersionControl.ProjectHooks").InvokeMethod("HandleVersionControlOverlays", guid, rowRect.MoveX(3)); + + } + + void hierarchyLines() + { + if (!useHierarchyLines) return; + if (isListArea) return; + if (treeItem == null) return; + + + var lineThickness = 1f; + var lineColor = isDarkTheme ? Greyscale(1, .165f) : Greyscale(0, .23f); + + var depth = ((rowRect.x - 16) / 14).RoundToInt(); + + bool isLastChild(TreeViewItem item) => item.parent?.children?.LastOrDefault() == item; + bool hasChilren(TreeViewItem item) => item.children != null && item.children.Count > 0; + + void calcVerticalGaps_beforeFirstRowDrawn() + { + if (hierarchyLines_isFirstRowDrawn) return; + + hierarchyLines_verticalGaps.Clear(); + + var curItem = treeItem.parent; + var curDepth = depth - 1; + + while (curItem != null && curItem.parent != null) + { + if (isLastChild(curItem)) + hierarchyLines_verticalGaps.Add(curDepth - 1); + + curItem = curItem.parent; + curDepth--; + } + + } + void updateVerticalGaps_beforeNextRowDrawn() + { + if (isLastChild(treeItem)) + hierarchyLines_verticalGaps.Add(depth - 1); + + if (depth < hierarchyLines_prevRowDepth) + hierarchyLines_verticalGaps.RemoveAll(r => r >= depth); + + } + + void drawVerticals() + { + for (int i = 1; i < depth; i++) + if (!hierarchyLines_verticalGaps.Contains(i)) + rowRect.SetX(9 + i * 14 - lineThickness / 2) + .SetWidth(lineThickness) + .SetHeight(isLastChild(treeItem) && i == depth - 1 ? 8 + lineThickness / 2 : 16) + .Draw(lineColor); + + } + void drawHorizontals() + { + if (depth == 0) return; + if (depth == 1) return; + + rowRect.MoveX(-21) + .SetHeightFromMid(lineThickness) + .SetWidth(hasChilren(treeItem) ? 7 : 17) + .AddWidthFromRight(-lineThickness / 2f) + .Draw(lineColor); + + } + + + + calcVerticalGaps_beforeFirstRowDrawn(); + + drawVerticals(); + drawHorizontals(); + + updateVerticalGaps_beforeNextRowDrawn(); + + hierarchyLines_prevRowDepth = depth; + hierarchyLines_isFirstRowDrawn = true; + + } + void zebraStriping_() + { + if (!useZebraStriping) return; + + + var contrast = isDarkTheme ? .033f : .05f; + + + var firstRowY = isOneColumn ? 4 : -1; + + var t = 1 - (rowRect.y - firstRowY).PingPong(16f) / 16f; + + if (isRowHovered || isRowSelected) + if (!VFoldersPaletteWindow.instance || VFoldersPaletteWindow.instance.guids.Contains(guid)) + t = 1; + + if (t.Approx(0)) return; + + + + fullRowRect.Draw(Greyscale(isDarkTheme ? 1 : 0, contrast * t)); + + + } + void highlight_() + { + if (!controller.animatingHighlight) return; + if (guid.ToPath() != controller.folderToHighlight) return; + + + var highlightBrightness = isDarkTheme ? .16f : .35f; + + + var highlightAmount = controller.highlightAmount.Clamp01(); + + highlightAmount = highlightAmount * highlightAmount * (3 - 2 * highlightAmount); + + + fullRowRect.AddWidthFromRight(123).Draw(Greyscale(1, highlightBrightness * highlightAmount)); + + } + void hoverHighlight() + { + if (!VFoldersPaletteWindow.instance) + if (!rowRect.IsHovered()) return; + + if (VFoldersPaletteWindow.instance) + if (VFoldersPaletteWindow.instance.guids.Count > 1 || VFoldersPaletteWindow.instance.guids.First() != guid || !paletteOpenedOnRow) return; + + + fullRowRect.Draw(Greyscale(isDarkTheme ? 1 : 0, .06f)); + + } + + void contentMinimap() + { + if (!isFolder) return; + if (!useContentMinimap) return; + if (guid.IsNullOrEmpty()) return; + + void icon(Rect rect, string name) + { + var icon = EditorIcons.GetIcon(name); + + if (!icon) return; + + + SetGUIColor(Greyscale(1, isDarkTheme ? .5f : .7f)); + + GUI.DrawTexture(rect, icon); + + ResetGUIColor(); + + } + + + var iconDistance = 13; + var minButtonX = rowRect.x + guid.ToPath().GetFilename().GetLabelWidth() + iconDistance + 2; + var iconRect = fullRowRect.SetWidthFromRight(iconDistance).SetSizeFromMid(12, 12).MoveX(-1.5f); + + foreach (var iconName in folderInfo.folderState.contentMinimapIconNames) + { + if (iconRect.x < minButtonX) continue; + + icon(iconRect, iconName); + + iconRect = iconRect.MoveX(-iconDistance); + + } + + } + + + + fullRowRect.MarkInteractive(); + + hideName_(); + hideDefaultIcon_(); + + color(); + triangle(); + defaultIcon(); + customIcon(); + versionControlOverlay(); + name(); + + hierarchyLines(); + zebraStriping_(); + highlight_(); + hoverHighlight(); + + contentMinimap(); + + } + + void altDrag() + { + if (!curEvent.holdingAlt) return; + + void mouseDown() + { + if (!curEvent.isMouseDown) return; + if (!isRowHovered) return; + + mouseDownPos = curEvent.mousePosition; + + } + void mouseDrag() + { + if (!curEvent.isMouseDrag) return; + if ((curEvent.mousePosition - mouseDownPos).magnitude < 5) return; + if (!rowRect.Contains(mouseDownPos)) return; + if (!rowRect.Contains(curEvent.mousePosition - curEvent.mouseDelta)) return; + if (DragAndDrop.objectReferences.Any()) return; + + DragAndDrop.PrepareStartDrag(); + DragAndDrop.objectReferences = new[] { AssetDatabase.LoadAssetAtPath(guid.ToPath()) }; + DragAndDrop.StartDrag(guid.ToPath().GetFilename()); + + } + + mouseDown(); + mouseDrag(); + + // altdrag has to be set up manually before altClick + // because altClick will use() mouseDown event to prevent selection change + + } + void altClick() + { + if (!isRowHovered) return; + if (!curEvent.holdingAlt) return; + if (!isFolder) return; + + void mouseDown() + { + if (!curEvent.isMouseDown) return; + + curEvent.Use(); + + } + void mouseUp() + { + if (!curEvent.isMouseUp) return; + + var selectedGuids = isListArea + ? + Selection.objects.Where(r => r is DefaultAsset).Select(r => r.GetPath().ToGuid()) + : +#if UNITY_2021_1_OR_NEWER + treeViewController.GetFieldValue("m_CachedSelection").GetFieldValue>("m_List") +#else + treeViewController?.GetMemberValue("state").GetMemberValue>("selectedIDs") +#endif + .Select(id => treeViewController.InvokeMethod("FindItem", id)) + .Where(r => r?.GetType().Name == "FolderTreeItem") + .Select(r => r.GetPropertyValue("Guid")) + .Where(r => r != null); + + + var editMultiSelection = selectedGuids.Count() > 1 && selectedGuids.Contains(guid); + + var guidsToEdit = (editMultiSelection ? selectedGuids.Where(r => AssetDatabase.IsValidFolder(r.ToPath())) : new[] { guid }).ToList(); + + + if (VFoldersPaletteWindow.instance && VFoldersPaletteWindow.instance.guids.SequenceEqual(guidsToEdit)) { VFoldersPaletteWindow.instance.Close(); return; } + + var openNearRect = rowRect; + var position = EditorGUIUtility.GUIToScreenPoint(new Vector2(curEvent.mousePosition.x + 20, openNearRect.y - 13)); + + if (!VFoldersPaletteWindow.instance) + VFoldersPaletteWindow.CreateInstance(position); + + VFoldersPaletteWindow.instance.Init(guidsToEdit); + VFoldersPaletteWindow.instance.Focus(); + + VFoldersPaletteWindow.instance.targetPosition = position; + + paletteOpenedOnRow = true; + paletteOpenedOnCell = false; + + if (editMultiSelection) + Selection.objects = null; + } + + mouseDown(); + mouseUp(); + + } + + + + setObjects(); + setState(); + + drawing(); + + altDrag(); + altClick(); + + } + + List hierarchyLines_verticalGaps = new(); + bool hierarchyLines_isFirstRowDrawn; + int hierarchyLines_prevRowDepth; + + Vector2 mouseDownPos; + + bool paletteOpenedOnRow; + + + + + public void CellGUI(Rect cellRect, string guid, int instanceId) + { + var isFolder = AssetDatabase.IsValidFolder(guid.ToPath()); + + void setLastVisibleSelectedForAltClick() + { + if (!isFolder) return; + if (!curEvent.isRepaint) return; + if (!Selection.objects.Contains(AssetDatabase.LoadAssetAtPath(guid.ToPath()))) return; + + lastVisibleSelectedCellRect = cellRect; + + } + + void drawing() + { + if (!curEvent.isRepaint) { namesDrawnForGuids.Clear(); return; } + + var folderInfo = isFolder ? GetFolderInfo(guid) : null; + var showingSecondNameLine = false; + + void hideIcon() + { + if (!isFolder) return; + if (!folderInfo.hasColor) return; + + var maskRect = cellRect.SetHeight(cellRect.width).Resize(4); + + // if (isVersionControlEnabled) + // maskRect = maskRect.SetHeightFromBottom(maskRect.height * 2 / 3); + + + maskRect.Draw(EditorGUIUtility.isProSkin ? Greyscale(.2f) : Greyscale(.75f)); + + } + void icon() + { + if (!isFolder) return; + if (!folderInfo.hasColor && !folderInfo.hasIcon) return; + + DrawBigFolderIcon(cellRect, folderInfo); + + } + void versionControlOverlay() + { + if (!isFolder) return; + if (!folderInfo.hasColor) return; + + if (!isVersionControlEnabled) return; + + typeof(Editor).Assembly.GetType("UnityEditorInternal.VersionControl.ProjectHooks").InvokeMethod("HandleVersionControlOverlays", guid, cellRect); + + } + void twoLineName() + { + if (namesDrawnForGuids.Contains(guid)) return; // disables two-line names on subassets + if (!VFoldersMenu.twoLineNamesEnabled) return; + + + var isSelected = listArea_dragSelectionList.Any() ? listArea_dragSelectionList.Contains(instanceId) : Selection.instanceIDs.Contains(instanceId); + + var isCellBeingRenamed = isSelected && renamingCell; + + if (isCellBeingRenamed) return; + + + + var maxLineWidth = cellRect.width + 14; + + var name = guid.ToPath().GetFilename(withExtension: false); // Resources.InstanceIDToObject(instanceId).name; + + + string firstLine = null; + string secondLine = null; + + Rect firstLineRect = default; + Rect secondLineRect = default; + + + void getCachedLines() + { + if (twoLineNamesCachedForCellWidth != cellRect.width) + twoLineNamesCache.Clear(); + + if (!twoLineNamesCache.ContainsKey(name)) return; + + + firstLine = twoLineNamesCache[name].Item1; + secondLine = twoLineNamesCache[name].Item2; + + } + void calcLines() + { + if (twoLineNamesCache.ContainsKey(name)) return; + if (name.GetLabelWidth() < maxLineWidth) + { + firstLine = name; + + twoLineNamesCache[name] = (firstLine, secondLine); + twoLineNamesCachedForCellWidth = cellRect.width; + + return; + + } + + + var separators = new[] { ' ', '_', '-', '.' }; + + var splitIndexes_separators = Enumerable.Range(1, name.Length - 2).Where(i => separators.Contains(name[i])).Select(i => i + 1); + var splitIndexes_camelcase = Enumerable.Range(2, name.Length - 2).Where(i => name[i].IsUpper() && name[i - 1].IsLower() && !separators.Contains(name[i - 1])); + var splitIndexes_all = Enumerable.Range(1, name.Length - 1); + + bool splitSucceeded = false; + + + string fit(string s, IEnumerable splitIndexes, bool splitToRight, bool addEllipsis = false) + { + foreach (var i in splitToRight ? splitIndexes : splitIndexes.Reverse()) + { + var substring = splitToRight ? s[i..] : s[..i]; + + substring = substring.Trim(); + + if (addEllipsis) + substring += "…"; + + + + if (substring.GetLabelWidth() < maxLineWidth) + return substring; + + } + + return null; + + } + + void truncate() + { + if (splitIndexes_separators.Any()) return; + if (splitIndexes_camelcase.Any()) return; + + firstLine = fit(name, splitIndexes_all, splitToRight: false, addEllipsis: true); + + splitSucceeded = true; + + } + void split_clean() + { + if (splitSucceeded) return; + + + firstLine = null; + firstLine ??= fit(name, splitIndexes_separators, splitToRight: false); + firstLine ??= fit(name, splitIndexes_camelcase, splitToRight: false); + + if (firstLine == null) return; + + + + secondLine = name.Remove(firstLine); + + splitSucceeded = secondLine.GetLabelWidth() < maxLineWidth; + + } + void split_withEllipsis() + { + if (splitSucceeded) return; + + + secondLine = null; + secondLine ??= fit(name, splitIndexes_separators, splitToRight: true); + secondLine ??= fit(name, splitIndexes_camelcase, splitToRight: true); + secondLine ??= fit(name, splitIndexes_all, splitToRight: true); + + + + firstLine = name.Remove(secondLine).Trim(); + + if (firstLine.GetLabelWidth() > maxLineWidth) + firstLine = fit(firstLine, Enumerable.Range(0, firstLine.Length), splitToRight: false, addEllipsis: true); + + } + + + truncate(); + split_clean(); + split_withEllipsis(); + + firstLine = firstLine.Trim(); + secondLine = secondLine?.Trim(); + + twoLineNamesCache[name] = (firstLine, secondLine); + twoLineNamesCachedForCellWidth = cellRect.width; + + } + void calcLineRects() + { + firstLineRect = cellRect.SetHeightFromBottom(12).AddHeight(2).SetWidthFromMid(maxLineWidth).MoveY(-1); + + if (secondLine.IsNullOrEmpty()) return; + + if (isFolder) + firstLineRect = firstLineRect.MoveY(-4); + else + firstLineRect = firstLineRect.MoveY(-1); + + + + secondLineRect = firstLineRect.MoveY(12); + + } + + void hideDefaultName() + { + if (!curEvent.isRepaint) return; + + var defaultNameRect = cellRect.SetHeightFromBottom(16).AddHeight(2).AddWidthFromMid(12); + var maskColor = isDarkTheme ? Greyscale(.2f) : Greyscale(.75f); + + defaultNameRect.Draw(maskColor); + + } + void selectedbackground() + { + if (!isSelected) return; + + + var backgroundColor = isListAreaFocused ? GUIColors.selectedBackground : Greyscale(isDarkTheme ? .3f : .68f); + + + + var longestLine = secondLine?.Length > firstLine.Length ? secondLine : firstLine; + + var backgroundRect = firstLineRect.SetWidthFromMid(longestLine.GetLabelWidth(fontSize: 10) + 2).SetHeightFromMid(14); + + if (!secondLine.IsNullOrEmpty()) + backgroundRect = backgroundRect.SetYMax(secondLineRect.SetHeightFromMid(14).yMax); + + + + backgroundRect.DrawRounded(backgroundColor, 4); + + } + void drawName() + { + SetLabelAlignmentCenter(); + SetLabelFontSize(10); + SetGUIColor(isSelected && isListAreaFocused ? Greyscale(123, 123) : Greyscale(1)); + + GUI.Label(firstLineRect, firstLine); + + if (secondLine != null) + GUI.Label(secondLineRect, secondLine); + + ResetGUIColor(); + ResetLabelStyle(); + + } + + + getCachedLines(); + calcLines(); + calcLineRects(); + + hideDefaultName(); + selectedbackground(); + drawName(); + + showingSecondNameLine = secondLine != null; + + namesDrawnForGuids.Add(guid); + + } + void hoverHighlight() + { + + var highlightColor = isDarkTheme ? Greyscale(1, .058f) : Greyscale(0, .07f); + + + var highlightRect = cellRect.Resize(-2).AddHeight(4).AddWidthFromMid(4); + + if (showingSecondNameLine) + highlightRect = highlightRect.AddHeight(8); + + + + var hoverRect = cellRect.AddWidthFromMid(gridHorizontalSpacing).AddHeight(gridVerticalSpacing); + + hoverRect.MarkInteractive(); + + + + + if (!VFoldersPaletteWindow.instance) + if (!hoverRect.IsHovered()) return; + + if (VFoldersPaletteWindow.instance) + if (!VFoldersPaletteWindow.instance.guids.Contains(guid) || !paletteOpenedOnCell) return; + + + highlightRect.DrawRounded(highlightColor, 5); + + } + + + hideIcon(); + icon(); + versionControlOverlay(); + twoLineName(); + hoverHighlight(); + + } + + void altDrag() + { + if (!isFolder) return; + if (!curEvent.holdingAlt) return; + + void mouseDown() + { + if (!curEvent.isMouseDown) return; + if (!cellRect.IsHovered()) return; + + mouseDownPos = curEvent.mousePosition; + + } + void mouseDrag() + { + if (!curEvent.isMouseDrag) return; + if ((curEvent.mousePosition - mouseDownPos).magnitude < 5) return; + if (!cellRect.Contains(mouseDownPos)) return; + if (!cellRect.Contains(curEvent.mousePosition - curEvent.mouseDelta)) return; + if (DragAndDrop.objectReferences.Any()) return; + + DragAndDrop.PrepareStartDrag(); + DragAndDrop.objectReferences = new[] { AssetDatabase.LoadAssetAtPath(guid.ToPath()) }; + DragAndDrop.StartDrag(guid.ToPath().GetFilename()); + + } + + mouseDown(); + mouseDrag(); + + } + void altClick() + { + if (!isFolder) return; + if (!cellRect.IsHovered()) return; + if (!curEvent.holdingAlt) return; + + void mouseDown() + { + if (!curEvent.isMouseDown) return; + + curEvent.Use(); + + } + void mouseUp() + { + if (!curEvent.isMouseUp) return; + + var selectedFoldersGuids = Selection.objects.Where(r => r is DefaultAsset).Select(r => r.GetPath().ToGuid()); + + var editMultiSelection = selectedFoldersGuids.Count() > 1 && selectedFoldersGuids.Contains(guid); + + var guidsToEdit = (editMultiSelection ? selectedFoldersGuids : new[] { guid }).ToList(); + + + if (VFoldersPaletteWindow.instance && VFoldersPaletteWindow.instance.guids.SequenceEqual(guidsToEdit)) { VFoldersPaletteWindow.instance.Close(); return; } + + var openNearRect = editMultiSelection ? lastVisibleSelectedCellRect : cellRect; + var position = EditorGUIUtility.GUIToScreenPoint(new Vector2(openNearRect.xMax + 10, openNearRect.y - 5)); + + if (!VFoldersPaletteWindow.instance) + VFoldersPaletteWindow.CreateInstance(position); + + VFoldersPaletteWindow.instance.Init(guidsToEdit); + VFoldersPaletteWindow.instance.Focus(); + + VFoldersPaletteWindow.instance.targetPosition = position; + + paletteOpenedOnCell = true; + paletteOpenedOnRow = false; + + if (editMultiSelection) + Selection.objects = null; + } + + mouseDown(); + mouseUp(); + + } + + + setLastVisibleSelectedForAltClick(); + + drawing(); + + altDrag(); + altClick(); + + } + + Rect lastVisibleSelectedCellRect; + + HashSet namesDrawnForGuids = new(); + + Dictionary twoLineNamesCache = new(); + + float twoLineNamesCachedForCellWidth; + + bool paletteOpenedOnCell; + + + + + + + + + + + + public void UpdateState_Layout() + { + isOneColumn = window.GetFieldValue("m_ViewMode") == 0; + + + listArea = isTwoColumns ? window.GetFieldValue("m_ListArea") : null; + + treeViewController = window.GetFieldValue(isTwoColumns ? "m_FolderTree" : "m_AssetTree"); + treeViewControllerData = treeViewController?.GetPropertyValue("data"); + + rows = treeViewControllerData?.InvokeMethod>("GetRows"); + + + var treeViewState = treeViewController?.GetPropertyValue("state"); + + expandedIds = treeViewState?.expandedIDs ?? new List(); + + + + var treeViewAnimator = treeViewController?.GetMemberValue("m_ExpansionAnimator"); + + treeViewAnimatesExpansion = treeViewAnimator?.GetMemberValue("isAnimating") ?? false; + + } + public void UpdateState_Repaint() + { + var treeViewControlID = window.GetFieldValue("m_TreeViewKeyboardControlID"); + var listAreaControlID = window.GetFieldValue("m_ListKeyboardControlID"); + + isTreeFocused = EditorWindow.focusedWindow == window && (GUIUtility.keyboardControl == treeViewControlID || treeViewControlID == 0 || treeViewControlID == -1); + isListAreaFocused = EditorWindow.focusedWindow == window && (GUIUtility.keyboardControl == listAreaControlID || listAreaControlID == 0 || listAreaControlID == -1); + + renamingRow = EditorGUIUtility.editingTextField && treeViewController?.GetMemberValue("state")?.GetMemberValue("renameOverlay")?.InvokeMethod("IsRenaming") == true; + renamingCell = EditorGUIUtility.editingTextField && listArea?.InvokeMethod("GetRenameOverlay")?.InvokeMethod("IsRenaming") == true; + + + + var grid = listArea?.GetMemberValue("m_LocalAssets")?.GetMemberValue("m_Grid"); + + gridHorizontalSpacing = grid?.GetMemberValue("horizontalSpacing") ?? 0; + gridVerticalSpacing = grid?.GetMemberValue("verticalSpacing") ?? 0; + + + + listArea_dragSelectionList = listArea?.GetMemberValue("m_LocalAssets")?.GetMemberValue>("m_DragSelection") ?? new(); + treeView_dragSelectionList = treeViewController?.GetFieldValue("m_DragSelection")?.GetFieldValue>("m_List") ?? new(); + treeView_normalSelectionList = isTwoColumns ? treeViewController?.GetFieldValue("m_CachedSelection")?.GetFieldValue>("m_List") ?? new() : null; + + + + // isVersionControlEnabled = typeof(Editor).Assembly.GetType("UnityEditor.AssetsTreeViewGUI").GetMemberValue("s_VCEnabled"); + + + // only treeView_normalSelectionList must be updated in repaint, the rest can be moved to UpdateState_Layout + // but they are all grouped here since they are only used for drawing + + } + + bool isOneColumn; + bool isTwoColumns => !isOneColumn; + + bool treeViewAnimatesExpansion; + + public object listArea; + + public object treeViewController; + public object treeViewControllerData; + + public IList rows; + + public List expandedIds = new(); + + bool isTreeFocused; + bool isListAreaFocused; + + bool renamingRow; + bool renamingCell; + + float gridVerticalSpacing; + float gridHorizontalSpacing; + + List listArea_dragSelectionList = new(); + List treeView_dragSelectionList = new(); + List treeView_normalSelectionList = new(); + + bool isVersionControlEnabled; + + + + + + + + + + + + + public void UpdateFoldersFirst() + { + if (!VFoldersMenu.foldersFirstEnabled) return; + if (Application.platform != RuntimePlatform.OSXEditor) return; + + void oneColumn() + { + if (isTwoColumns) return; + if (foldersFirst_initedForOneColumn) return; + + + var m_AssetTree = window.GetFieldValue("m_AssetTree"); + + if (m_AssetTree == null) return; + + m_AssetTree.GetPropertyValue("data").SetPropertyValue("foldersFirst", true); + m_AssetTree.InvokeMethod("ReloadData"); + + + foldersFirst_initedForOneColumn = true; + foldersFirst_initedForTwoColumns = false; + + } + void twoColumns() + { + if (!isTwoColumns) return; + if (foldersFirst_initedForTwoColumns) return; + + + var m_ListArea = window.GetFieldValue("m_ListArea"); + + if (m_ListArea == null) return; + + m_ListArea.SetPropertyValue("foldersFirst", true); + window.InvokeMethod("InitListArea"); + + + foldersFirst_initedForOneColumn = false; + foldersFirst_initedForTwoColumns = true; + + } + + oneColumn(); + twoColumns(); + + } + + bool foldersFirst_initedForOneColumn; + bool foldersFirst_initedForTwoColumns; + + + + + + + + + + + + + public VFoldersGUI(EditorWindow window) => this.window = window; + + public EditorWindow window; + + public VFoldersController controller => VFolders.controllers_byWindow[window]; + + } +} +#endif diff --git a/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersGUI.cs.meta b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersGUI.cs.meta new file mode 100644 index 0000000..f66fff0 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersGUI.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0743ead56bea942af9c21d27c5c45b62 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersHistory.cs b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersHistory.cs new file mode 100644 index 0000000..2f928fc --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersHistory.cs @@ -0,0 +1,403 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using UnityEditor.IMGUI.Controls; +using Type = System.Type; +using static VFolders.Libs.VUtils; +using static VFolders.Libs.VGUI; +// using static VTools.VDebug; +using static VFolders.VFolders; +using static VFolders.VFoldersData; +using static VFolders.VFoldersCache; + + + +namespace VFolders +{ + [System.Serializable] + public class VFoldersHistory + { + + public void CheckTreeStateChange() + { + if (!VFoldersMenu.navigationBarEnabled) return; + if (!isOneColumn) return; + + if (lastTreeState == null) { lastTreeState = new() { scrollPos = currentScrollPos, expandedIds = expandedIds.ToList() }; return; } + + + var curTreeState = new TreeState(); + + var targetScrollPosChanged = false; + var targetExpandedIdsChanged = false; + + + + void set_curState_scrollPos() + { + if (controller.animatingScroll) + curTreeState.scrollPos = controller.targetScrollPos; + else + curTreeState.scrollPos = currentScrollPos; + + } + void set_curState_expandedIds() + { + var expandedIdsHashset = expandedIds.ToHashSet(); + + + + if (treeViewAnimatesExpansion) + if (animatingItemTragetExpanded_fromTreeViewExpandAnimator == true) + expandedIdsHashset.UnionWith(new HashSet() { animatingItemId_fromTreeViewExpandAnimator }); + else + expandedIdsHashset.ExceptWith(new HashSet() { animatingItemId_fromTreeViewExpandAnimator }); + + + + if (controller.animatingExpansion) + { + foreach (var r in controller.expandQueue_toAnimate) + if (r.expand) + expandedIdsHashset.UnionWith(new HashSet() { r.id }); + else + expandedIdsHashset.ExceptWith(new HashSet() { r.id }); + + expandedIdsHashset.ExceptWith(controller.expandQueue_toCollapseAfterAnimation.ToHashSet()); + } + + + curTreeState.expandedIds = expandedIdsHashset.ToList(); + + } + + void checkScrollPosChange() + { + if (framesSinceLastExpansionAnimation < 2) return; + if (framesSinceLastScrollAnimation < 2) return; + + if (curTreeState.scrollPos == lastTreeState.scrollPos) return; + + + + if (EditorApplication.timeSinceStartup - lastScrollTime > 2 || lastTargetStateChangeWasExpandedIds) + targetScrollPosChanged = true; + + lastScrollTime = EditorApplication.timeSinceStartup; + + } + void checkExpandedIdsChange() + { + if (lastTreeState.expandedIds.ToHashSet().SetEquals(curTreeState.expandedIds.ToHashSet())) return; + + targetExpandedIdsChanged = true; + + // lastTargetState.expandedIds.LogAll("lastTargetState"); + // curTargetState.expandedIds.LogAll("curTargetState"); + + } + + void registerStateChange() + { + if (!targetScrollPosChanged && !targetExpandedIdsChanged) return; + + + prevTreeStates.Add(lastTreeState); + nextTreeStates.Clear(); + + lastTargetStateChangeWasExpandedIds = targetExpandedIdsChanged; + + + if (prevTreeStates.Count > 50) + prevTreeStates.RemoveAt(0); + + + // if (targetScrollPosChanged && targetExpandedIdsChanged) + // "expand and scroll".Log(); + // else if (targetScrollPosChanged) + // "scroll".Log(); + // else if (targetExpandedIdsChanged) + // "expand".Log(); + + } + + void updateCountersSinceAnimation() + { + if (controller.animatingExpansion || treeViewAnimatesExpansion) + framesSinceLastExpansionAnimation = 0; + else + framesSinceLastExpansionAnimation++; + + if (controller.animatingScroll || treeViewAnimatesScroll) + framesSinceLastScrollAnimation = 0; + else + framesSinceLastScrollAnimation++; + + } + + + + set_curState_scrollPos(); + set_curState_expandedIds(); + + checkScrollPosChange(); + checkExpandedIdsChange(); + + registerStateChange(); + + updateCountersSinceAnimation(); + + + lastTreeState = curTreeState; + + } + + int framesSinceLastExpansionAnimation; + int framesSinceLastScrollAnimation; + + public double lastScrollTime = 0; + + bool lastTargetStateChangeWasExpandedIds; + + [System.NonSerialized] TreeState lastTreeState = null; + + + + public void CheckFolderPathChange() + { + if (!VFoldersMenu.navigationBarEnabled) return; + if (isOneColumn) return; + + var curFolderPath = window.GetMemberValue("m_SearchFilter").GetMemberValue("folders").FirstOrDefault(); + + if (curFolderPath.IsNullOrEmpty()) return; + + if (lastFolderPath.IsNullOrEmpty()) lastFolderPath = curFolderPath; + if (curFolderPath == lastFolderPath) return; + + + prevFolderPaths.Add(lastFolderPath); + nextFolderPaths.Clear(); + + if (prevFolderPaths.Count > 50) + prevFolderPaths.RemoveAt(0); + + + lastFolderPath = curFolderPath; + + } + + string lastFolderPath = ""; + + + + + + + + public void UpdateState() + { + if (!VFoldersMenu.navigationBarEnabled) return; + + + isOneColumn = window.GetFieldValue("m_ViewMode") == 0; + + var treeViewController = window.GetFieldValue(isOneColumn ? "m_AssetTree" : "m_FolderTree"); + var treeViewControllerData = treeViewController?.GetPropertyValue("data"); + + + + var treeViewControllerState = treeViewController?.GetPropertyValue("state"); + + currentScrollPos = treeViewControllerState?.scrollPos.y ?? 0; + + expandedIds = treeViewControllerState?.expandedIDs ?? new List(); + + + + var treeViewAnimator = treeViewController?.GetMemberValue("m_ExpansionAnimator"); + var treeViewAnimatorSetup = treeViewAnimator?.GetMemberValue("m_Setup"); + + treeViewAnimatesScroll = treeViewController?.GetMemberValue("m_FramingAnimFloat").isAnimating ?? false; + + treeViewAnimatesExpansion = treeViewAnimator?.GetMemberValue("isAnimating") ?? false; + animatingItemTragetExpanded_fromTreeViewExpandAnimator = treeViewAnimatorSetup?.GetMemberValue("expanding") ?? false; + animatingItemId_fromTreeViewExpandAnimator = treeViewAnimatorSetup?.GetMemberValue("item").GetMemberValue("id") ?? 0; + + } + + public bool isOneColumn; + public bool isTwoColumns => !isOneColumn; + + public bool isSearchActive; + + public float currentScrollPos; + + public List expandedIds = new(); + + public bool treeViewAnimatesScroll; + + public bool treeViewAnimatesExpansion; + public bool animatingItemTragetExpanded_fromTreeViewExpandAnimator; + public int animatingItemId_fromTreeViewExpandAnimator; + + + + + + + + + + + + + + + public void MoveBack_OneColumn(bool withAnimation = true) + { + var prevState = prevTreeStates.Last(); + + prevTreeStates.Remove(prevState); + nextTreeStates.Add(lastTreeState); + lastTreeState = prevState; + + + if (withAnimation) + { + controller.StartScrollAnimation(prevState.scrollPos); + controller.StartExpandAnimation(prevState.expandedIds); + } + else + { + controller.SetScrollPos(prevState.scrollPos); + controller.SetExpandedIds(prevState.expandedIds); + } + + } + public void MoveForward_OneColumn(bool withAnimation = true) + { + var nextState = nextTreeStates.Last(); + + nextTreeStates.Remove(nextState); + prevTreeStates.Add(lastTreeState); + lastTreeState = nextState; + + + if (withAnimation) + { + controller.StartScrollAnimation(nextState.scrollPos); + controller.StartExpandAnimation(nextState.expandedIds); + } + else + { + controller.SetScrollPos(nextState.scrollPos); + controller.SetExpandedIds(nextState.expandedIds); + } + + } + + public List prevTreeStates = new(); + public List nextTreeStates = new(); + + + + public void MoveBack_TwoColumns(bool withAnimation = true) + { + var prevPath = prevFolderPaths.Last(); + + prevFolderPaths.RemoveLast(); + nextFolderPaths.Add(lastFolderPath); + lastFolderPath = prevPath; + + + if (withAnimation) + { + controller.RevealFolder(prevPath, expand: false, highlight: false, snapToTopMargin: false); + controller.OpenFolder(prevPath); + } + else + { + window.InvokeMethod("ShowFolderContents", AssetDatabase.LoadAssetAtPath(prevPath).GetInstanceID(), true); + } + + } + public void MoveForward_TwoColumns(bool withAnimation = true) + { + var nextPath = nextFolderPaths.Last(); + + nextFolderPaths.RemoveLast(); + prevFolderPaths.Add(lastFolderPath); + lastFolderPath = nextPath; + + + if (withAnimation) + { + controller.RevealFolder(nextPath, expand: false, highlight: false, snapToTopMargin: false); + controller.OpenFolder(nextPath); + } + else + { + window.InvokeMethod("ShowFolderContents", AssetDatabase.LoadAssetAtPath(nextPath).GetInstanceID(), true); + } + + } + + public List prevFolderPaths = new(); + public List nextFolderPaths = new(); + + + + + + + + + + + + + + [System.Serializable] + public class TreeState + { + public List expandedIds = new(); + + public float scrollPos; + + } + + + + + + + + + public VFoldersHistory(EditorWindow window) => this.window = window; + + EditorWindow window; + + VFoldersGUI gui => VFolders.guis_byWindow[window]; + VFoldersController controller => VFolders.controllers_byWindow[window]; + + } + + + public class VFoldersHistorySingleton : ScriptableSingleton + { + public SerializableDictionary histories_byWindow = new(); + } + + +} +#endif \ No newline at end of file diff --git a/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersHistory.cs.meta b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersHistory.cs.meta new file mode 100644 index 0000000..a0fc8da --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersHistory.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2d3e0e15bb2714894bdfb696f7509428 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersIconEditor.cs b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersIconEditor.cs new file mode 100644 index 0000000..028ed42 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersIconEditor.cs @@ -0,0 +1,5 @@ + +// this file was present in a previus version and is supposed to be deleted now +// but asset store update delivery system doesn't allow deleting files +// so instead this file is now emptied +// feel free to delete it if you want diff --git a/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersIconEditor.cs.meta b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersIconEditor.cs.meta new file mode 100644 index 0000000..759e212 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersIconEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: fc2d205dd3ec1446d843e566b05c2eda +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersLibs.cs b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersLibs.cs new file mode 100644 index 0000000..7a44a3b --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersLibs.cs @@ -0,0 +1,2667 @@ + +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; +using System.Reflection; +using System.Linq; +using UnityEngine; +using UnityEngine.Rendering; +using UnityEngine.Experimental.Rendering; +using UnityEditor; +using Type = System.Type; +using static VFolders.Libs.VUtils; +using static VFolders.Libs.VGUI; + + + +namespace VFolders.Libs +{ + public static class VUtils + { + + #region Reflection + + + public static object GetFieldValue(this object o, string fieldName) + { + var type = o as Type ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetFieldInfo(fieldName) is FieldInfo fieldInfo) + return fieldInfo.GetValue(target); + + + throw new System.Exception($"Field '{fieldName}' not found in type '{type.Name}' and its parent types"); + + } + public static object GetPropertyValue(this object o, string propertyName) + { + var type = o as Type ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetPropertyInfo(propertyName) is PropertyInfo propertyInfo) + return propertyInfo.GetValue(target); + + + throw new System.Exception($"Property '{propertyName}' not found in type '{type.Name}' and its parent types"); + + } + public static object GetMemberValue(this object o, string memberName) + { + var type = o as Type ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetFieldInfo(memberName) is FieldInfo fieldInfo) + return fieldInfo.GetValue(target); + + if (type.GetPropertyInfo(memberName) is PropertyInfo propertyInfo) + return propertyInfo.GetValue(target); + + + throw new System.Exception($"Member '{memberName}' not found in type '{type.Name}' and its parent types"); + + } + + public static void SetFieldValue(this object o, string fieldName, object value) + { + var type = o as Type ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetFieldInfo(fieldName) is FieldInfo fieldInfo) + fieldInfo.SetValue(target, value); + + + else throw new System.Exception($"Field '{fieldName}' not found in type '{type.Name}' and its parent types"); + + } + public static void SetPropertyValue(this object o, string propertyName, object value) + { + var type = o as Type ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetPropertyInfo(propertyName) is PropertyInfo propertyInfo) + propertyInfo.SetValue(target, value); + + + else throw new System.Exception($"Property '{propertyName}' not found in type '{type.Name}' and its parent types"); + + } + public static void SetMemberValue(this object o, string memberName, object value) + { + var type = o as Type ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetFieldInfo(memberName) is FieldInfo fieldInfo) + fieldInfo.SetValue(target, value); + + else if (type.GetPropertyInfo(memberName) is PropertyInfo propertyInfo) + propertyInfo.SetValue(target, value); + + + else throw new System.Exception($"Member '{memberName}' not found in type '{type.Name}' and its parent types"); + + } + + public static object InvokeMethod(this object o, string methodName, params object[] parameters) // todo handle null params (can't get their type) + { + var type = o as Type ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetMethodInfo(methodName, parameters.Select(r => r.GetType()).ToArray()) is MethodInfo methodInfo) + return methodInfo.Invoke(target, parameters); + + + throw new System.Exception($"Method '{methodName}' not found in type '{type.Name}', its parent types and interfaces"); + + } + + + public static T GetFieldValue(this object o, string fieldName) => (T)o.GetFieldValue(fieldName); + public static T GetPropertyValue(this object o, string propertyName) => (T)o.GetPropertyValue(propertyName); + public static T GetMemberValue(this object o, string memberName) => (T)o.GetMemberValue(memberName); + public static T InvokeMethod(this object o, string methodName, params object[] parameters) => (T)o.InvokeMethod(methodName, parameters); + + + + + public static FieldInfo GetFieldInfo(this Type type, string fieldName) + { + if (fieldInfoCache.TryGetValue(type, out var fieldInfosByNames)) + if (fieldInfosByNames.TryGetValue(fieldName, out var fieldInfo)) + return fieldInfo; + + + if (!fieldInfoCache.ContainsKey(type)) + fieldInfoCache[type] = new Dictionary(); + + for (var curType = type; curType != null; curType = curType.BaseType) + if (curType.GetField(fieldName, maxBindingFlags) is FieldInfo fieldInfo) + return fieldInfoCache[type][fieldName] = fieldInfo; + + + return fieldInfoCache[type][fieldName] = null; + + } + public static PropertyInfo GetPropertyInfo(this Type type, string propertyName) + { + if (propertyInfoCache.TryGetValue(type, out var propertyInfosByNames)) + if (propertyInfosByNames.TryGetValue(propertyName, out var propertyInfo)) + return propertyInfo; + + + if (!propertyInfoCache.ContainsKey(type)) + propertyInfoCache[type] = new Dictionary(); + + for (var curType = type; curType != null; curType = curType.BaseType) + if (curType.GetProperty(propertyName, maxBindingFlags) is PropertyInfo propertyInfo) + return propertyInfoCache[type][propertyName] = propertyInfo; + + + return propertyInfoCache[type][propertyName] = null; + + } + public static MethodInfo GetMethodInfo(this Type type, string methodName, params Type[] argumentTypes) + { + var methodHash = methodName.GetHashCode() ^ argumentTypes.Aggregate(0, (hash, r) => hash ^= r.GetHashCode()); + + + if (methodInfoCache.TryGetValue(type, out var methodInfosByHashes)) + if (methodInfosByHashes.TryGetValue(methodHash, out var methodInfo)) + return methodInfo; + + + + if (!methodInfoCache.ContainsKey(type)) + methodInfoCache[type] = new Dictionary(); + + for (var curType = type; curType != null; curType = curType.BaseType) + if (curType.GetMethod(methodName, maxBindingFlags, null, argumentTypes, null) is MethodInfo methodInfo) + return methodInfoCache[type][methodHash] = methodInfo; + + foreach (var interfaceType in type.GetInterfaces()) + if (interfaceType.GetMethod(methodName, maxBindingFlags, null, argumentTypes, null) is MethodInfo methodInfo) + return methodInfoCache[type][methodHash] = methodInfo; + + + + return methodInfoCache[type][methodHash] = null; + + } + + static Dictionary> fieldInfoCache = new(); + static Dictionary> propertyInfoCache = new(); + static Dictionary> methodInfoCache = new(); + + + + + public const BindingFlags maxBindingFlags = (BindingFlags)62; + + + + + + + + + #endregion + + #region Linq + + + public static T NextTo(this IEnumerable e, T to) => e.SkipWhile(r => !r.Equals(to)).Skip(1).FirstOrDefault(); + public static T PreviousTo(this IEnumerable e, T to) => e.Reverse().SkipWhile(r => !r.Equals(to)).Skip(1).FirstOrDefault(); + public static T NextToOtFirst(this IEnumerable e, T to) => e.NextTo(to) ?? e.First(); + public static T PreviousToOrLast(this IEnumerable e, T to) => e.PreviousTo(to) ?? e.Last(); + + public static Dictionary MergeDictionaries(IEnumerable> dicts) + { + if (dicts.Count() == 0) return null; + if (dicts.Count() == 1) return dicts.First(); + + var mergedDict = new Dictionary(dicts.First()); + + foreach (var dict in dicts.Skip(1)) + foreach (var r in dict) + if (!mergedDict.ContainsKey(r.Key)) + mergedDict.Add(r.Key, r.Value); + + return mergedDict; + } + + public static IEnumerable InsertFirst(this IEnumerable ie, T t) => new[] { t }.Concat(ie); + + public static int IndexOfFirst(this List list, System.Func f) => list.FirstOrDefault(f) is T t ? list.IndexOf(t) : -1; + public static int IndexOfLast(this List list, System.Func f) => list.LastOrDefault(f) is T t ? list.IndexOf(t) : -1; + + public static void SortBy(this List list, System.Func keySelector) where T2 : System.IComparable => list.Sort((q, w) => keySelector(q).CompareTo(keySelector(w))); + + public static void RemoveValue(this IDictionary dictionary, TValue value) + { + if (dictionary.FirstOrDefault(r => r.Value.Equals(value)) is var kvp) + dictionary.Remove(kvp); + } + + public static void ForEach(this IEnumerable sequence, System.Action action) { foreach (T item in sequence) action(item); } + + + + public static T AddAt(this List l, T r, int i) + { + if (i < 0) i = 0; + if (i >= l.Count) + l.Add(r); + else + l.Insert(i, r); + return r; + } + public static T RemoveLast(this List l) + { + if (!l.Any()) return default; + + var r = l.Last(); + + l.RemoveAt(l.Count - 1); + + return r; + } + + public static void Add(this List list, params T[] items) + { + foreach (var r in items) + list.Add(r); + } + + + + + + + #endregion + + #region Math + + + public static class MathUtil // MathUtils name is taken by UnityEditor.MathUtils + { + + public static float TriangleArea(Vector2 A, Vector2 B, Vector2 C) => Vector3.Cross(A - B, A - C).z.Abs() / 2; + + public static Vector2 LineIntersection(Vector2 A, Vector2 B, Vector2 C, Vector2 D) + { + var a1 = B.y - A.y; + var b1 = A.x - B.x; + var c1 = a1 * A.x + b1 * A.y; + + var a2 = D.y - C.y; + var b2 = C.x - D.x; + var c2 = a2 * C.x + b2 * C.y; + + var d = a1 * b2 - a2 * b1; + + var x = (b2 * c1 - b1 * c2) / d; + var y = (a1 * c2 - a2 * c1) / d; + + return new Vector2(x, y); + + } + + + + + public static float Lerp(float f1, float f2, float t) => Mathf.LerpUnclamped(f1, f2, t); + public static float Lerp(ref float f1, float f2, float t) + { + return f1 = Lerp(f1, f2, t); + } + + public static Vector2 Lerp(Vector2 f1, Vector2 f2, float t) => Vector2.LerpUnclamped(f1, f2, t); + public static Vector2 Lerp(ref Vector2 f1, Vector2 f2, float t) + { + return f1 = Lerp(f1, f2, t); + } + + public static Vector3 Lerp(Vector3 f1, Vector3 f2, float t) => Vector3.LerpUnclamped(f1, f2, t); + public static Vector3 Lerp(ref Vector3 f1, Vector3 f2, float t) + { + return f1 = Lerp(f1, f2, t); + } + + public static Color Lerp(Color f1, Color f2, float t) => Color.LerpUnclamped(f1, f2, t); + public static Color Lerp(ref Color f1, Color f2, float t) + { + return f1 = Lerp(f1, f2, t); + } + + + public static float Lerp(float current, float target, float speed, float deltaTime) => Mathf.Lerp(current, target, GetLerpT(speed, deltaTime)); + public static float Lerp(ref float current, float target, float speed, float deltaTime) + { + return current = Lerp(current, target, speed, deltaTime); + } + + public static Vector2 Lerp(Vector2 current, Vector2 target, float speed, float deltaTime) => Vector2.Lerp(current, target, GetLerpT(speed, deltaTime)); + public static Vector2 Lerp(ref Vector2 current, Vector2 target, float speed, float deltaTime) + { + return current = Lerp(current, target, speed, deltaTime); + } + + public static Vector3 Lerp(Vector3 current, Vector3 target, float speed, float deltaTime) => Vector3.Lerp(current, target, GetLerpT(speed, deltaTime)); + public static Vector3 Lerp(ref Vector3 current, Vector3 target, float speed, float deltaTime) + { + return current = Lerp(current, target, speed, deltaTime); + } + + public static float SmoothDamp(float current, float target, float speed, ref float derivative, float deltaTime, float maxSpeed) => Mathf.SmoothDamp(current, target, ref derivative, .5f / speed, maxSpeed, deltaTime); + public static float SmoothDamp(float current, float target, float speed, ref float derivative, float deltaTime) + { + return Mathf.SmoothDamp(current, target, ref derivative, .5f / speed, Mathf.Infinity, deltaTime); + } + public static float SmoothDamp(float current, float target, float speed, ref float derivative) + { + return SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + } + public static float SmoothDamp(ref float current, float target, float speed, ref float derivative, float deltaTime, float maxSpeed) + { + return current = SmoothDamp(current, target, speed, ref derivative, deltaTime, maxSpeed); + } + public static float SmoothDamp(ref float current, float target, float speed, ref float derivative, float deltaTime) + { + return current = SmoothDamp(current, target, speed, ref derivative, deltaTime); + } + public static float SmoothDamp(ref float current, float target, float speed, ref float derivative) + { + return current = SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + } + + public static Vector2 SmoothDamp(Vector2 current, Vector2 target, float speed, ref Vector2 derivative, float deltaTime) => Vector2.SmoothDamp(current, target, ref derivative, .5f / speed, Mathf.Infinity, deltaTime); + public static Vector2 SmoothDamp(Vector2 current, Vector2 target, float speed, ref Vector2 derivative) + { + return SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + } + public static Vector2 SmoothDamp(ref Vector2 current, Vector2 target, float speed, ref Vector2 derivative, float deltaTime) + { + return current = SmoothDamp(current, target, speed, ref derivative, deltaTime); + } + public static Vector2 SmoothDamp(ref Vector2 current, Vector2 target, float speed, ref Vector2 derivative) + { + return current = SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + } + + public static Vector3 SmoothDamp(Vector3 current, Vector3 target, float speed, ref Vector3 derivative, float deltaTime) => Vector3.SmoothDamp(current, target, ref derivative, .5f / speed, Mathf.Infinity, deltaTime); + public static Vector3 SmoothDamp(Vector3 current, Vector3 target, float speed, ref Vector3 derivative) + { + return SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + } + public static Vector3 SmoothDamp(ref Vector3 current, Vector3 target, float speed, ref Vector3 derivative, float deltaTime) + { + return current = SmoothDamp(current, target, speed, ref derivative, deltaTime); + } + public static Vector3 SmoothDamp(ref Vector3 current, Vector3 target, float speed, ref Vector3 derivative) + { + return current = SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + } + + + public static float GetLerpT(float lerpSpeed, float deltaTime) => 1 - Mathf.Exp(-lerpSpeed * 2f * deltaTime); + public static float GetLerpT(float lerpSpeed) + { + return GetLerpT(lerpSpeed, Time.deltaTime); + } + + + + } + + + public static float DistanceTo(this float f1, float f2) => Mathf.Abs(f1 - f2); + public static float DistanceTo(this Vector2 f1, Vector2 f2) => (f1 - f2).magnitude; + public static float DistanceTo(this Vector3 f1, Vector3 f2) => (f1 - f2).magnitude; + + public static float Sign(this float f) => f == 0 ? 0 : Mathf.Sign(f); + + public static int Abs(this int f) => Mathf.Abs(f); + public static float Abs(this float f) => Mathf.Abs(f); + + public static int Clamp(this int f, int f0, int f1) => Mathf.Clamp(f, f0, f1); + public static float Clamp(this float f, float f0, float f1) => Mathf.Clamp(f, f0, f1); + + + public static float Clamp01(this float f) => Mathf.Clamp(f, 0, 1); + public static Vector2 Clamp01(this Vector2 f) => new(f.x.Clamp01(), f.y.Clamp01()); + public static Vector3 Clamp01(this Vector3 f) => new(f.x.Clamp01(), f.y.Clamp01(), f.z.Clamp01()); + + + public static int Pow(this int f, int pow) => (int)Mathf.Pow(f, pow); + public static float Pow(this float f, float pow) => Mathf.Pow(f, pow); + + public static float Round(this float f) => Mathf.Round(f); + public static float Ceil(this float f) => Mathf.Ceil(f); + public static float Floor(this float f) => Mathf.Floor(f); + + public static int RoundToInt(this float f) => Mathf.RoundToInt(f); + public static int CeilToInt(this float f) => Mathf.CeilToInt(f); + public static int FloorToInt(this float f) => Mathf.FloorToInt(f); + + public static int ToInt(this float f) => (int)f; + public static float ToFloat(this int f) => (float)f; + public static float ToFloat(this double f) => (float)f; + + + + public static float Sqrt(this float f) => Mathf.Sqrt(f); + + public static int Max(this int f, int ff) => Mathf.Max(f, ff); + public static int Min(this int f, int ff) => Mathf.Min(f, ff); + public static float Max(this float f, float ff) => Mathf.Max(f, ff); + public static float Min(this float f, float ff) => Mathf.Min(f, ff); + + public static float ClampMin(this float f, float limitMin) => Mathf.Max(f, limitMin); + public static float ClampMax(this float f, float limitMax) => Mathf.Min(f, limitMax); + + + public static float Loop(this float f, float boundMin, float boundMax) + { + while (f < boundMin) f += boundMax - boundMin; + while (f > boundMax) f -= boundMax - boundMin; + return f; + } + public static float Loop(this float f, float boundMax) => f.Loop(0, boundMax); + + public static float PingPong(this float f, float boundMin, float boundMax) => boundMin + Mathf.PingPong(f - boundMin, boundMax - boundMin); + public static float PingPong(this float f, float boundMax) => f.PingPong(0, boundMax); + + + public static float ProjectOn(this Vector2 v, Vector2 on) => Vector3.Project(v, on).magnitude; + public static float ProjectOn(this Vector3 v, Vector3 on) => Vector3.Project(v, on).magnitude; + + public static float AngleTo(this Vector2 v, Vector2 to) => Vector2.Angle(v, to); + + public static Vector2 Rotate(this Vector2 v, float deg) => Quaternion.AngleAxis(deg, Vector3.forward) * v; + + public static float Smoothstep(this float f) { f = f.Clamp01(); return f * f * (3 - 2 * f); } + + public static float InverseLerp(this Vector2 v, Vector2 a, Vector2 b) + { + var ab = b - a; + var av = v - a; + return Vector2.Dot(av, ab) / Vector2.Dot(ab, ab); + } + + + public static bool IsOdd(this int i) => i % 2 == 1; + public static bool IsEven(this int i) => i % 2 == 0; + + public static bool IsInRange(this int i, int a, int b) => i >= a && i <= b; + public static bool IsInRange(this float i, float a, float b) => i >= a && i <= b; + + public static bool IsInRangeOf(this int i, IList list) => i.IsInRange(0, list.Count - 1); + public static bool IsInRangeOf(this int i, T[] array) => i.IsInRange(0, array.Length - 1); + + public static bool Approx(this float f1, float f2) => Mathf.Approximately(f1, f2); + + + + [System.Serializable] + public class GaussianKernel + { + public static float[,] GenerateArray(int size, float sharpness = .5f) + { + float[,] kr = new float[size, size]; + + if (size == 1) { kr[0, 0] = 1; return kr; } + + + var sigma = 1f - Mathf.Pow(sharpness, .1f) * .99999f; + var radius = (size / 2f).FloorToInt(); + + + var a = -2f * radius * radius / Mathf.Log(sigma); + var sum = 0f; + + for (int y = 0; y < size; y++) + for (int x = 0; x < size; x++) + { + var rX = size % 2 == 1 ? (x - radius) : (x - radius) + .5f; + var rY = size % 2 == 1 ? (y - radius) : (y - radius) + .5f; + var dist = Mathf.Sqrt(rX * rX + rY * rY); + kr[x, y] = Mathf.Exp(-dist * dist / a); + sum += kr[x, y]; + } + + for (int y = 0; y < size; y++) + for (int x = 0; x < size; x++) + kr[x, y] /= sum; + + return kr; + } + + + + public GaussianKernel(bool isEvenSize = false, int radius = 7, float sharpness = .5f) + { + this.isEvenSize = isEvenSize; + this.radius = radius; + this.sharpness = sharpness; + } + + public bool isEvenSize = false; + public int radius = 7; + public float sharpness = .5f; + + public int size => radius * 2 + (isEvenSize ? 0 : 1); + public float sigma => 1 - Mathf.Pow(sharpness, .1f) * .99999f; + + public float[,] Array2d() // todo test and use GenerateArray + { + float[,] kr = new float[size, size]; + + if (size == 1) { kr[0, 0] = 1; return kr; } + + var a = -2f * radius * radius / Mathf.Log(sigma); + var sum = 0f; + + for (int y = 0; y < size; y++) + for (int x = 0; x < size; x++) + { + var rX = size % 2 == 1 ? (x - radius) : (x - radius) + .5f; + var rY = size % 2 == 1 ? (y - radius) : (y - radius) + .5f; + var dist = Mathf.Sqrt(rX * rX + rY * rY); + kr[x, y] = Mathf.Exp(-dist * dist / a); + sum += kr[x, y]; + } + + for (int y = 0; y < size; y++) + for (int x = 0; x < size; x++) + kr[x, y] /= sum; + + return kr; + } + public float[] ArrayFlat() + { + var gk = Array2d(); + float[] flat = new float[size * size]; + + for (int i = 0; i < size; i++) + for (int j = 0; j < size; j++) + flat[(i * size + j)] = gk[i, j]; + + return flat; + } + + } + + + + + + + + #endregion + + #region Lerping + + + public static float LerpT(float lerpSpeed, float deltaTime) => 1 - Mathf.Exp(-lerpSpeed * 2f * deltaTime); + public static float LerpT(float lerpSpeed) => LerpT(lerpSpeed, Time.deltaTime); + + public static float Lerp(float f1, float f2, float t) => Mathf.LerpUnclamped(f1, f2, t); + public static float Lerp(ref float f1, float f2, float t) => f1 = Lerp(f1, f2, t); + + public static Vector2 Lerp(Vector2 f1, Vector2 f2, float t) => Vector2.LerpUnclamped(f1, f2, t); + public static Vector2 Lerp(ref Vector2 f1, Vector2 f2, float t) => f1 = Lerp(f1, f2, t); + + public static Vector3 Lerp(Vector3 f1, Vector3 f2, float t) => Vector3.LerpUnclamped(f1, f2, t); + public static Vector3 Lerp(ref Vector3 f1, Vector3 f2, float t) => f1 = Lerp(f1, f2, t); + + public static Color Lerp(Color f1, Color f2, float t) => Color.LerpUnclamped(f1, f2, t); + public static Color Lerp(ref Color f1, Color f2, float t) => f1 = Lerp(f1, f2, t); + + + public static float Lerp(float current, float target, float speed, float deltaTime) => Mathf.Lerp(current, target, LerpT(speed, deltaTime)); + public static float Lerp(ref float current, float target, float speed, float deltaTime) => current = Lerp(current, target, speed, deltaTime); + + public static Vector2 Lerp(Vector2 current, Vector2 target, float speed, float deltaTime) => Vector2.Lerp(current, target, LerpT(speed, deltaTime)); + public static Vector2 Lerp(ref Vector2 current, Vector2 target, float speed, float deltaTime) => current = Lerp(current, target, speed, deltaTime); + + public static Vector3 Lerp(Vector3 current, Vector3 target, float speed, float deltaTime) => Vector3.Lerp(current, target, LerpT(speed, deltaTime)); + public static Vector3 Lerp(ref Vector3 current, Vector3 target, float speed, float deltaTime) => current = Lerp(current, target, speed, deltaTime); + + public static float SmoothDamp(float current, float target, float speed, ref float derivative, float deltaTime, float maxSpeed) => Mathf.SmoothDamp(current, target, ref derivative, .5f / speed, maxSpeed, deltaTime); + public static float SmoothDamp(ref float current, float target, float speed, ref float derivative, float deltaTime, float maxSpeed) => current = SmoothDamp(current, target, speed, ref derivative, deltaTime, maxSpeed); + public static float SmoothDamp(float current, float target, float speed, ref float derivative, float deltaTime) => Mathf.SmoothDamp(current, target, ref derivative, .5f / speed, Mathf.Infinity, deltaTime); + public static float SmoothDamp(ref float current, float target, float speed, ref float derivative, float deltaTime) => current = SmoothDamp(current, target, speed, ref derivative, deltaTime); + public static float SmoothDamp(float current, float target, float speed, ref float derivative) => SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + public static float SmoothDamp(ref float current, float target, float speed, ref float derivative) => current = SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + + public static Vector2 SmoothDamp(Vector2 current, Vector2 target, float speed, ref Vector2 derivative, float deltaTime) => Vector2.SmoothDamp(current, target, ref derivative, .5f / speed, Mathf.Infinity, deltaTime); + public static Vector2 SmoothDamp(ref Vector2 current, Vector2 target, float speed, ref Vector2 derivative, float deltaTime) => current = SmoothDamp(current, target, speed, ref derivative, deltaTime); + public static Vector2 SmoothDamp(Vector2 current, Vector2 target, float speed, ref Vector2 derivative) => SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + public static Vector2 SmoothDamp(ref Vector2 current, Vector2 target, float speed, ref Vector2 derivative) => current = SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + + public static Vector3 SmoothDamp(Vector3 current, Vector3 target, float speed, ref Vector3 derivative, float deltaTime) => Vector3.SmoothDamp(current, target, ref derivative, .5f / speed, Mathf.Infinity, deltaTime); + public static Vector3 SmoothDamp(ref Vector3 current, Vector3 target, float speed, ref Vector3 derivative, float deltaTime) => current = SmoothDamp(current, target, speed, ref derivative, deltaTime); + public static Vector3 SmoothDamp(Vector3 current, Vector3 target, float speed, ref Vector3 derivative) => SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + public static Vector3 SmoothDamp(ref Vector3 current, Vector3 target, float speed, ref Vector3 derivative) => current = SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + + + + + + + #endregion + + #region Colors + + + public class ColorUtils + { + public static Color HSLToRGB(float h, float s, float l) + { + float hue2Rgb(float v1, float v2, float vH) + { + if (vH < 0f) + vH += 1f; + + if (vH > 1f) + vH -= 1f; + + if (6f * vH < 1f) + return v1 + (v2 - v1) * 6f * vH; + + if (2f * vH < 1f) + return v2; + + if (3f * vH < 2f) + return v1 + (v2 - v1) * (2f / 3f - vH) * 6f; + + return v1; + } + + if (s.Approx(0)) return new Color(l, l, l); + + float k1; + + if (l < .5f) + k1 = l * (1f + s); + else + k1 = l + s - s * l; + + + var k2 = 2f * l - k1; + + float r, g, b; + r = hue2Rgb(k2, k1, h + 1f / 3); + g = hue2Rgb(k2, k1, h); + b = hue2Rgb(k2, k1, h - 1f / 3); + + return new Color(r, g, b); + } + public static Color LCHtoRGB(float l, float c, float h) + { + l *= 100; + c *= 100; + h *= 360; + + double xw = 0.948110; + double yw = 1.00000; + double zw = 1.07304; + + float a = c * Mathf.Cos(Mathf.Deg2Rad * h); + float b = c * Mathf.Sin(Mathf.Deg2Rad * h); + + float fy = (l + 16) / 116; + float fx = fy + (a / 500); + float fz = fy - (b / 200); + + float x = (float)System.Math.Round(xw * ((System.Math.Pow(fx, 3) > 0.008856) ? System.Math.Pow(fx, 3) : ((fx - 16 / 116) / 7.787)), 5); + float y = (float)System.Math.Round(yw * ((System.Math.Pow(fy, 3) > 0.008856) ? System.Math.Pow(fy, 3) : ((fy - 16 / 116) / 7.787)), 5); + float z = (float)System.Math.Round(zw * ((System.Math.Pow(fz, 3) > 0.008856) ? System.Math.Pow(fz, 3) : ((fz - 16 / 116) / 7.787)), 5); + + float r = x * 3.2406f - y * 1.5372f - z * 0.4986f; + float g = -x * 0.9689f + y * 1.8758f + z * 0.0415f; + float bValue = x * 0.0557f - y * 0.2040f + z * 1.0570f; + + r = r > 0.0031308f ? 1.055f * (float)System.Math.Pow(r, 1 / 2.4) - 0.055f : r * 12.92f; + g = g > 0.0031308f ? 1.055f * (float)System.Math.Pow(g, 1 / 2.4) - 0.055f : g * 12.92f; + bValue = bValue > 0.0031308f ? 1.055f * (float)System.Math.Pow(bValue, 1 / 2.4) - 0.055f : bValue * 12.92f; + + // r = (float)System.Math.Round(System.Math.Max(0, System.Math.Min(1, r))); + // g = (float)System.Math.Round(System.Math.Max(0, System.Math.Min(1, g))); + // bValue = (float)System.Math.Round(System.Math.Max(0, System.Math.Min(1, bValue))); + + return new Color(r, g, bValue); + + } + + } + + + public static Color Greyscale(float brightness, float alpha = 1) => new(brightness, brightness, brightness, alpha); + + public static Color SetAlpha(this Color color, float alpha) { color.a = alpha; return color; } + public static Color MultiplyAlpha(this Color color, float k) { color.a *= k; return color; } + + + + + + #endregion + + #region Rects + + + public static Rect Resize(this Rect rect, float px) { rect.x += px; rect.y += px; rect.width -= px * 2; rect.height -= px * 2; return rect; } + + public static Rect SetPos(this Rect rect, Vector2 v) => rect.SetPos(v.x, v.y); + public static Rect SetPos(this Rect rect, float x, float y) { rect.x = x; rect.y = y; return rect; } + + public static Rect SetX(this Rect rect, float x) => rect.SetPos(x, rect.y); + public static Rect SetY(this Rect rect, float y) => rect.SetPos(rect.x, y); + public static Rect SetXMax(this Rect rect, float xMax) { rect.xMax = xMax; return rect; } + public static Rect SetYMax(this Rect rect, float yMax) { rect.yMax = yMax; return rect; } + + public static Rect SetMidPos(this Rect r, Vector2 v) => r.SetPos(v).MoveX(-r.width / 2).MoveY(-r.height / 2); + public static Rect SetMidPos(this Rect r, float x, float y) => r.SetMidPos(new Vector2(x, y)); + + public static Rect Move(this Rect rect, Vector2 v) { rect.position += v; return rect; } + public static Rect Move(this Rect rect, float x, float y) { rect.x += x; rect.y += y; return rect; } + public static Rect MoveX(this Rect rect, float px) { rect.x += px; return rect; } + public static Rect MoveY(this Rect rect, float px) { rect.y += px; return rect; } + + public static Rect SetWidth(this Rect rect, float f) { rect.width = f; return rect; } + public static Rect SetWidthFromMid(this Rect rect, float px) { rect.x += rect.width / 2; rect.width = px; rect.x -= rect.width / 2; return rect; } + public static Rect SetWidthFromRight(this Rect rect, float px) { rect.x += rect.width; rect.width = px; rect.x -= rect.width; return rect; } + + public static Rect SetHeight(this Rect rect, float f) { rect.height = f; return rect; } + public static Rect SetHeightFromMid(this Rect rect, float px) { rect.y += rect.height / 2; rect.height = px; rect.y -= rect.height / 2; return rect; } + public static Rect SetHeightFromBottom(this Rect rect, float px) { rect.y += rect.height; rect.height = px; rect.y -= rect.height; return rect; } + + public static Rect AddWidth(this Rect rect, float f) => rect.SetWidth(rect.width + f); + public static Rect AddWidthFromMid(this Rect rect, float f) => rect.SetWidthFromMid(rect.width + f); + public static Rect AddWidthFromRight(this Rect rect, float f) => rect.SetWidthFromRight(rect.width + f); + + public static Rect AddHeight(this Rect rect, float f) => rect.SetHeight(rect.height + f); + public static Rect AddHeightFromMid(this Rect rect, float f) => rect.SetHeightFromMid(rect.height + f); + public static Rect AddHeightFromBottom(this Rect rect, float f) => rect.SetHeightFromBottom(rect.height + f); + + public static Rect SetSize(this Rect rect, Vector2 v) => rect.SetWidth(v.x).SetHeight(v.y); + public static Rect SetSize(this Rect rect, float w, float h) => rect.SetWidth(w).SetHeight(h); + public static Rect SetSize(this Rect rect, float f) { rect.height = rect.width = f; return rect; } + + public static Rect SetSizeFromMid(this Rect r, Vector2 v) => r.Move(r.size / 2).SetSize(v).Move(-v / 2); + public static Rect SetSizeFromMid(this Rect r, float x, float y) => r.SetSizeFromMid(new Vector2(x, y)); + public static Rect SetSizeFromMid(this Rect r, float f) => r.SetSizeFromMid(new Vector2(f, f)); + + public static Rect AlignToPixelGrid(this Rect r) => GUIUtility.AlignRectToDevice(r); + + + + + + #endregion + + #region Textures + + + public static Texture2D CreateTexture2D(int width, int height, GraphicsFormat graphicsFormat = GraphicsFormat.R8G8B8A8_SRGB, bool useMips = false) + { + return new Texture2D(width, height, graphicsFormat, useMips ? TextureCreationFlags.MipChain : TextureCreationFlags.None); + } + + public static RenderTexture CreateRT(int width, int height, GraphicsFormat graphicsFormat = GraphicsFormat.R8G8B8A8_SRGB, bool useMips = false, bool autoGenerateMips = true, bool useDepth = false) + { + var rt = new RenderTexture(width, height, useDepth ? 24 : 0, graphicsFormat); + + rt.useMipMap = useMips; + rt.autoGenerateMips = autoGenerateMips; + + rt.enableRandomWrite = true; + + return rt; + + } + public static RenderTexture GetTemporaryRT(int width, int height, GraphicsFormat graphicsFormat = GraphicsFormat.R8G8B8A8_SRGB, bool useMips = false, bool autoGenerateMips = true, bool useDepth = false) + { + var rt = RenderTexture.GetTemporary(width, height, useDepth ? 24 : 0, graphicsFormat); + + rt.useMipMap = useMips; + rt.autoGenerateMips = autoGenerateMips; + + rt.enableRandomWrite = true; + + return rt; + + } + + public static RenderTexture CreateRT(this RenderTextureDescriptor descriptor) => new(descriptor); + public static RenderTexture CreateRT(this RenderTextureDescriptor descriptor, int resolution) + { + descriptor.width = descriptor.height = resolution; + + return descriptor.CreateRT(); + + } + public static RenderTexture CreateRT(this RenderTextureDescriptor descriptor, int width, int height) + { + descriptor.width = width; + descriptor.height = height; + + return descriptor.CreateRT(); + + } + public static RenderTexture CreateRT(this RenderTextureDescriptor descriptor, float resolution) => descriptor.GetTemporaryRT(Mathf.RoundToInt(resolution)); + public static RenderTexture CreateRT(this RenderTextureDescriptor descriptor, float width, float height) => descriptor.CreateRT(Mathf.RoundToInt(width), Mathf.RoundToInt(height)); + public static RenderTexture GetTemporaryRT(this RenderTextureDescriptor descriptor) => RenderTexture.GetTemporary(descriptor); + public static RenderTexture GetTemporaryRT(this RenderTextureDescriptor descriptor, int resolution) + { + descriptor.width = descriptor.height = resolution; + + return descriptor.GetTemporaryRT(); + + } + public static RenderTexture GetTemporaryRT(this RenderTextureDescriptor descriptor, int width, int height) + { + descriptor.width = width; + descriptor.height = height; + + return descriptor.GetTemporaryRT(); + + } + public static RenderTexture GetTemporaryRT(this RenderTextureDescriptor descriptor, float resolution) => descriptor.GetTemporaryRT(Mathf.RoundToInt(resolution)); + public static RenderTexture GetTemporaryRT(this RenderTextureDescriptor descriptor, float width, float height) => descriptor.GetTemporaryRT(Mathf.RoundToInt(width), Mathf.RoundToInt(height)); + + public static void ReleaseTemporary(this RenderTexture rt) { if (rt) RenderTexture.ReleaseTemporary(rt); } + + + + public static Texture2D ToTexture2D(this RenderTexture rt) + { + var texture2D = CreateTexture2D(rt.width, rt.height, rt.graphicsFormat, rt.useMipMap); + + texture2D.ReadPixelsFrom(rt); + texture2D.Apply(); + + return texture2D; + + } + public static RenderTexture ToRenderTexture(this Texture2D texture2d) + { + var rt = CreateRT(texture2d.width, texture2d.height, texture2d.graphicsFormat, texture2d.mipmapCount > 1); + + Graphics.CopyTexture(texture2d, rt); + + return rt; + + } + + + public static void ReadPixelsFrom(this Texture2D texture2D, RenderTexture renderTexture) + { + var prevActive = RenderTexture.active; + + RenderTexture.active = renderTexture; + + texture2D.ReadPixels(new Rect(0, 0, renderTexture.width, renderTexture.height), 0, 0); + + RenderTexture.active = prevActive; + + } + // public static void CopyTo(this RenderTexture source, Texture2D target) // todo to readpixels overload + // { + // var prevActive = RenderTexture.active; + + // RenderTexture.active = source; + + // target.ReadPixels(new Rect(0, 0, source.width, source.height), 0, 0); + // target.Apply(); + + // RenderTexture.active = prevActive; + + + // // somewhere in unity source code reading is done like this, but it throws out of bounds read exception on win: + + // // if (!SystemInfo.graphicsUVStartsAtTop || SystemInfo.graphicsDeviceType == GraphicsDeviceType.Metal) + // // texture2d.ReadPixels(new Rect(0, 0, texture2d.width, texture2d.height), 0, 0); + // // else + // // texture2d.ReadPixels(new Rect(0, texture2d.height, texture2d.width, texture2d.height), 0, 0); + + // // this was used in the legacy rt.CopyToTexture2D extension method + + // } + + public static Texture2D CreateCopy(this Texture2D texture2d) + { + var copy = CreateTexture2D(texture2d.width, texture2d.height, texture2d.graphicsFormat, texture2d.mipmapCount > 1); + + Graphics.CopyTexture(texture2d, copy); + + return copy; + + } + public static Texture2D CreateResizedCopy(this Texture2D texture2d, int w, int h) + { + var rt = GetTemporaryRT(w, h, texture2d.graphicsFormat.GetCompatibleForRendering(), false); + + Graphics.Blit(texture2d, rt); + + + var resizedCopy = CreateTexture2D(w, h, texture2d.graphicsFormat.GetCompatibleForRendering(), texture2d.mipmapCount > 1); + + resizedCopy.ReadPixelsFrom(rt); + resizedCopy.Apply(); + + if (RenderTexture.active == rt) + RenderTexture.active = null; + + rt.ReleaseTemporary(); + + + return resizedCopy; + + } + + + public static void FillWithColor(this Texture2D texture2d, Color color) + { + var pixels = new Color32[texture2d.width * texture2d.height]; + + var color32 = (Color32)color; + + for (int i = 0; i < pixels.Length; i++) + pixels[i] = color32; + + texture2d.SetPixels32(pixels); + texture2d.Apply(); + + } + public static RenderTexture FillWithColor(this RenderTexture rt, Color color) // todo builtin shader or GL.clear + { + var mat = new Material(Shader.Find("Hidden/VBlitColor")); + + mat.SetColor("_color", color); + + Graphics.Blit(null, rt, mat); + + mat.Destroy(); + + return rt; + + } + + + + public static GraphicsFormat GetCompatibleForRendering(this GraphicsFormat graphicsFormat) + { + if ((int)graphicsFormat == 88) // unsupported on dx11 but GetCompatibleFormat() doesn't think so + if (PlayerSettings.colorSpace == ColorSpace.Linear) + return GraphicsFormat.R8G8B8A8_SRGB; + else + return GraphicsFormat.R8G8B8A8_UNorm; + +#if UNITY_2023_2_OR_NEWER + return SystemInfo.GetCompatibleFormat(graphicsFormat, GraphicsFormatUsage.Render); +#else + return SystemInfo.GetCompatibleFormat(graphicsFormat, FormatUsage.Render); +#endif + + } + + + +#if UNITY_EDITOR + + public static void SavePNG(this Texture2D texture2d, string path) => File.WriteAllBytes(path, texture2d.EncodeToPNG()); + + public static void SetImportSettings(this Texture2D texture2d, int? maxSize = null, bool? useMips = null, bool? sRGB = null, bool? isReadable = null, bool? useCompression = null) + { + var importer = texture2d.GetImporter(); + + if (useCompression != null) + importer.textureCompression = useCompression.GetValueOrDefault() ? TextureImporterCompression.Compressed : TextureImporterCompression.Uncompressed; + + if (sRGB != null) + importer.sRGBTexture = sRGB.GetValueOrDefault(); + + if (maxSize != null) + importer.maxTextureSize = maxSize.GetValueOrDefault(); + + if (useMips != null) + importer.mipmapEnabled = useMips.GetValueOrDefault(); + + + // if (texture2d.format == TextureFormat.R16 || texture2d.format == TextureFormat.RG32) + if (texture2d.format == TextureFormat.R16) + { + var platformSettings = importer.GetDefaultPlatformTextureSettings(); + + platformSettings.format = TextureImporterFormat.R16; + + if (maxSize != null) + platformSettings.maxTextureSize = maxSize.GetValueOrDefault(); + + importer.SetPlatformTextureSettings(platformSettings); + + } + + } + + public static TextureImporter GetImporter(this Texture2D t) => (TextureImporter)AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(t)); + +#endif + + + + + + + + #endregion + + #region Objects + + + public static Object[] FindObjects(Type type) + { +#if UNITY_2023_1_OR_NEWER + return Object.FindObjectsByType(type, FindObjectsSortMode.None); +#else + return Object.FindObjectsOfType(type); +#endif + } + public static T[] FindObjects() where T : Object + { +#if UNITY_2023_1_OR_NEWER + return Object.FindObjectsByType(FindObjectsSortMode.None); +#else + return Object.FindObjectsOfType(); +#endif + } + + public static void Destroy(this Object r) + { + if (Application.isPlaying) + Object.Destroy(r); + else + Object.DestroyImmediate(r); + + } + + public static void DestroyImmediate(this Object o) => Object.DestroyImmediate(o); + + + + + + #endregion + + #region Text + + + public static class TextUtils + { + public static string FormatDistance(float meters) + { + int m = (int)meters; + + if (m < 1000) + return m + " m"; + else + return (m / 1000) + "." + (m / 100) % 10 + " km"; + + } + public static string FormatLong(long l) => System.String.Format("{0:n0}", l); + public static string FormatInt(int l) => FormatLong((long)l); + public static string FormatTime(long ms, bool includeMs = false) + { + System.TimeSpan t = System.TimeSpan.FromMilliseconds(ms); + var s = ""; + if (t.Hours != 0) s += " " + t.Hours + " hour" + GetCountSuffix(t.Hours); + if (t.Minutes != 0) s += " " + t.Minutes + " minute" + GetCountSuffix(t.Minutes); + if (t.Seconds != 0) s += " " + t.Seconds + " second" + GetCountSuffix(t.Seconds); + if (t.Milliseconds != 0 && includeMs) s += " " + t.Milliseconds + " millisecond" + GetCountSuffix(t.Milliseconds); + + if (s == "") + if (includeMs) s = "0 milliseconds"; + else s = "0 seconds"; + + return s.Trim(); + } + public static string FormatFileSize(long bytes, bool sizeUnknownIfNotMoreThanZero = false) + { + if (sizeUnknownIfNotMoreThanZero && bytes == 0) return "Size unknown"; + + var ss = new[] { "B", "KB", "MB", "GB", "TB" }; + var bprev = bytes; + int i = 0; + while (bytes >= 1024 && i++ < ss.Length - 1) bytes = (bprev = bytes) / 1024; + + if (bytes < 0) return "? B"; + if (i < 3) return string.Format("{0:0.#} ", bytes) + ss[i]; + return string.Format("{0:0.##} ", bytes) + ss[i]; + } + + static string GetCountSuffix(long c) => c % 10 != 1 ? "s" : ""; + + public static string GetLoremIpsum(int words = 2) + { + var s = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur Excepteur sint occaecat cupidatat non proident sunt in culpa qui officia deserunt mollit anim id est laborum"; + var ws = s.Split(' ').Select(r => r.ToLower().Trim(new[] { ',', '.' })); + ws = ws.OrderBy(r => UnityEngine.Random.Range(0, 1232)).Take(words); + var ss = string.Join(" ", ws); + return char.ToUpper(ss[0]) + ss.Substring(1); + } + + } + + + public static bool IsEmpty(this string s) => s == ""; + public static bool IsNullOrEmpty(this string s) => string.IsNullOrEmpty(s); + + public static bool IsLower(this char c) => System.Char.IsLower(c); + public static bool IsUpper(this char c) => System.Char.IsUpper(c); + public static bool IsDigit(this char c) => System.Char.IsDigit(c); + public static bool IsLetter(this char c) => System.Char.IsLetter(c); + public static bool IsWhitespace(this char c) => System.Char.IsWhiteSpace(c); + + public static char ToLower(this char c) => System.Char.ToLower(c); + public static char ToUpper(this char c) => System.Char.ToUpper(c); + + + + public static string Decamelcase(this string s) + { + return Regex.Replace(Regex.Replace(s, @"(\P{Ll})(\P{Ll}\p{Ll})", "$1 $2"), @"(\p{Ll})(\P{Ll})", "$1 $2"); + } + public static string FormatVariableName(this string s, bool lowercaseFollowingWords = true) + { + return string.Join(" ", s.Decamelcase() + .Split(' ') + .Select(r => new[] { "", "and", "or", "with", "without", "by", "from" }.Contains(r.ToLower()) || (lowercaseFollowingWords && !s.Trim().StartsWith(r)) ? r.ToLower() + : r.Substring(0, 1).ToUpper() + r.Substring(1))).Trim(' '); + } + + public static string Remove(this string s, string toRemove) + { + if (toRemove == "") return s; + return s.Replace(toRemove, ""); + } + + + + + + + #endregion + + #region Paths + + + public static bool HasParentPath(this string path) => path.LastIndexOf('/') > 0; + public static string GetParentPath(this string path) => path.HasParentPath() ? path.Substring(0, path.LastIndexOf('/')) : ""; + + public static string ToGlobalPath(this string localPath) => Application.dataPath + "/" + localPath.Substring(0, localPath.Length - 1); + public static string ToLocalPath(this string globalPath) => "Assets" + globalPath.Remove(Application.dataPath); + + + + public static string CombinePath(this string p, string p2) => Path.Combine(p, p2); + + public static bool IsSubpathOf(this string path, string of) => path.StartsWith(of + "/") || of == ""; + + public static string GetDirectory(this string pathOrDirectory) + { + var directory = pathOrDirectory.Contains('.') ? pathOrDirectory.Substring(0, pathOrDirectory.LastIndexOf('/')) : pathOrDirectory; + + if (directory.Contains('.')) + directory = directory.Substring(0, directory.LastIndexOf('/')); + + return directory; + + } + + public static bool DirectoryExists(this string pathOrDirectory) => Directory.Exists(pathOrDirectory.GetDirectory()); + + public static string EnsureDirExists(this string pathOrDirectory) // todo to EnsureDirectoryExists + { + var directory = pathOrDirectory.GetDirectory(); + + if (directory.HasParentPath() && !Directory.Exists(directory.GetParentPath())) + EnsureDirExists(directory.GetParentPath()); + + if (!Directory.Exists(directory)) + Directory.CreateDirectory(directory); + + return pathOrDirectory; + + } + + + + public static string ClearDir(this string dir) + { + if (!Directory.Exists(dir)) return dir; + + var diri = new DirectoryInfo(dir); + foreach (var r in diri.EnumerateFiles()) r.Delete(); + foreach (var r in diri.EnumerateDirectories()) r.Delete(true); + + return dir; + } + + + + + + +#if UNITY_EDITOR + + public static string EnsurePathIsUnique(this string path) + { + if (!path.DirectoryExists()) return path; + + var s = AssetDatabase.GenerateUniqueAssetPath(path); // returns empty if parent dir doesnt exist + + return s == "" ? path : s; + + } + + public static void EnsureDirExistsAndRevealInFinder(string dir) + { + EnsureDirExists(dir); + UnityEditor.EditorUtility.OpenWithDefaultApp(dir); + } + +#endif + + + + #endregion + + #region AssetDatabase + +#if UNITY_EDITOR + + public static AssetImporter GetImporter(this Object t) => AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(t)); + + public static string ToPath(this string guid) => AssetDatabase.GUIDToAssetPath(guid); // returns empty string if not found + public static List ToPaths(this IEnumerable guids) => guids.Select(r => r.ToPath()).ToList(); + + public static string GetFilename(this string path, bool withExtension = false) => withExtension ? Path.GetFileName(path) : Path.GetFileNameWithoutExtension(path); // prev GetName + public static string GetExtension(this string path) => Path.GetExtension(path); + + + public static string ToGuid(this string pathInProject) => AssetDatabase.AssetPathToGUID(pathInProject); + public static List ToGuids(this IEnumerable pathsInProject) => pathsInProject.Select(r => r.ToGuid()).ToList(); + + public static string GetPath(this Object o) => AssetDatabase.GetAssetPath(o); + public static string GetGuid(this Object o) => AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(o)); + + public static string GetScriptPath(string scriptName) => AssetDatabase.FindAssets("t: script " + scriptName, null).FirstOrDefault()?.ToPath() ?? "scirpt not found"; + + + public static bool IsValidGuid(this string guid) => AssetDatabase.AssetPathToGUID(AssetDatabase.GUIDToAssetPath(guid), AssetPathToGUIDOptions.OnlyExistingAssets) != ""; + + + + // toremove + public static Object LoadGuid(this string guid) => AssetDatabase.LoadAssetAtPath(guid.ToPath(), typeof(Object)); + public static T LoadGuid(this string guid) where T : Object => AssetDatabase.LoadAssetAtPath(guid.ToPath()); + + + + + public static List FindAllAssetsOfType_guids(Type type) => AssetDatabase.FindAssets("t:" + type.Name).ToList(); + public static List FindAllAssetsOfType_guids(Type type, string path) => AssetDatabase.FindAssets("t:" + type.Name, new[] { path }).ToList(); + public static List FindAllAssetsOfType() where T : Object => FindAllAssetsOfType_guids(typeof(T)).Select(r => (T)r.LoadGuid()).ToList(); + public static List FindAllAssetsOfType(string path) where T : Object => FindAllAssetsOfType_guids(typeof(T), path).Select(r => (T)r.LoadGuid()).ToList(); + + public static T Reimport(this T t) where T : Object { AssetDatabase.ImportAsset(t.GetPath(), ImportAssetOptions.ForceUpdate); return t; } + +#endif + + + + + + #endregion + + #region Serialization + + + [System.Serializable] + public class SerializableDictionary : Dictionary, ISerializationCallbackReceiver + { + [SerializeField] List keys = new(); + [SerializeField] List values = new(); + + public void OnBeforeSerialize() + { + keys.Clear(); + values.Clear(); + + foreach (KeyValuePair kvp in this) + { + keys.Add(kvp.Key); + values.Add(kvp.Value); + } + + } + public void OnAfterDeserialize() + { + this.Clear(); + + for (int i = 0; i < keys.Count; i++) + this[keys[i]] = values[i]; + + } + + } + + + + + + #endregion + + #region GlobalID + +#if UNITY_EDITOR + + [System.Serializable] + public struct GlobalID : System.IEquatable + { + public Object GetObject() => GlobalObjectId.GlobalObjectIdentifierToObjectSlow(globalObjectId); + public int GetObjectInstanceId() => GlobalObjectId.GlobalObjectIdentifierToInstanceIDSlow(globalObjectId); + + + public string guid => globalObjectId.assetGUID.ToString(); + public ulong fileId => globalObjectId.targetObjectId; + + public bool isNull => globalObjectId.identifierType == 0; + public bool isAsset => globalObjectId.identifierType == 1; + public bool isSceneObject => globalObjectId.identifierType == 2; + + public GlobalObjectId globalObjectId => _globalObjectId.Equals(default) && globalObjectIdString != null && GlobalObjectId.TryParse(globalObjectIdString, out var r) ? _globalObjectId = r : _globalObjectId; + public GlobalObjectId _globalObjectId; + + public GlobalID(Object o) => globalObjectIdString = (_globalObjectId = GlobalObjectId.GetGlobalObjectIdSlow(o)).ToString(); + public GlobalID(string s) => globalObjectIdString = GlobalObjectId.TryParse(s, out _globalObjectId) ? s : s; + + public string globalObjectIdString; + + + + public bool Equals(GlobalID other) => this.globalObjectIdString.Equals(other.globalObjectIdString); + + public static bool operator ==(GlobalID a, GlobalID b) => a.Equals(b); + public static bool operator !=(GlobalID a, GlobalID b) => !a.Equals(b); + + public override bool Equals(object other) => other is GlobalID otherglobalID && this.Equals(otherglobalID); + public override int GetHashCode() => globalObjectIdString == null ? 0 : globalObjectIdString.GetHashCode(); + + + public override string ToString() => globalObjectIdString; + + } + + public static GlobalID GetGlobalID(this Object o) => new(o); + public static GlobalID[] GetGlobalIDs(this IEnumerable instanceIds) + { + var unityGlobalIds = new GlobalObjectId[instanceIds.Count()]; + + GlobalObjectId.GetGlobalObjectIdsSlow(instanceIds.ToArray(), unityGlobalIds); + + var globalIds = unityGlobalIds.Select(r => new GlobalID(r.ToString())); + + return globalIds.ToArray(); + + } + + public static Object[] GetObjects(this IEnumerable globalIDs) + { + var goids = globalIDs.Select(r => r.globalObjectId).ToArray(); + + var objects = new Object[goids.Length]; + + GlobalObjectId.GlobalObjectIdentifiersToObjectsSlow(goids, objects); + + return objects; + + } + public static int[] GetObjectInstanceIds(this IEnumerable globalIDs) + { + var goids = globalIDs.Select(r => r.globalObjectId).ToArray(); + + var iids = new int[goids.Length]; + + GlobalObjectId.GlobalObjectIdentifiersToInstanceIDsSlow(goids, iids); + + return iids; + + } + + +#endif + + + + + #endregion + + #region Editor + +#if UNITY_EDITOR + + + public static class EditorUtils + { + + public static void OpenFolder(string path) + { + var folder = AssetDatabase.LoadAssetAtPath(path, typeof(Object)); + + var t = typeof(Editor).Assembly.GetType("UnityEditor.ProjectBrowser"); + var w = (EditorWindow)t.GetField("s_LastInteractedProjectBrowser").GetValue(null); + + var m_ListAreaState = t.GetField("m_ListAreaState", maxBindingFlags).GetValue(w); + + m_ListAreaState.GetType().GetField("m_SelectedInstanceIDs").SetValue(m_ListAreaState, new List { folder.GetInstanceID() }); + + t.GetMethod("OpenSelectedFolders", maxBindingFlags).Invoke(null, null); + + } + + public static void PingObject(Object o, bool select = false, bool focusProjectWindow = true) + { + if (select) + { + Selection.activeObject = null; + Selection.activeObject = o; + } + if (focusProjectWindow) EditorUtility.FocusProjectWindow(); + EditorGUIUtility.PingObject(o); + + } + public static void PingObject(string guid, bool select = false, bool focusProjectWindow = true) => PingObject(AssetDatabase.LoadAssetAtPath(guid.ToPath())); + + public static EditorWindow OpenObjectPicker(Object obj = null, bool allowSceneObjects = false, string searchFilter = "", int controlID = 0) where T : Object + { + EditorGUIUtility.ShowObjectPicker(obj, allowSceneObjects, searchFilter, controlID); + + return Resources.FindObjectsOfTypeAll(typeof(Editor).Assembly.GetType("UnityEditor.ObjectSelector")).FirstOrDefault() as EditorWindow; + + } + public static EditorWindow OpenColorPicker(System.Action colorChangedCallback, Color color, bool showAlpha = true, bool hdr = false) + { + typeof(Editor).Assembly.GetType("UnityEditor.ColorPicker").InvokeMethod("Show", colorChangedCallback, color, showAlpha, hdr); + + return typeof(Editor).Assembly.GetType("UnityEditor.ColorPicker").GetPropertyValue("instance"); + + } + + + + public static bool CheckUnityVersion(string versionQuery) + { + if (versionQueryCache.TryGetValue(versionQuery, out var cachedResult)) return cachedResult; + + if (versionQuery.Any(r => r.IsLetter() && !versionQuery.EndsWith(" or older") && !versionQuery.EndsWith(" or newer"))) throw new System.ArgumentException("Invalid unity version query"); + + + + + var curVersion = new string(Application.unityVersion.TakeWhile(r => !r.IsLetter()).ToArray()); + + var curMajor = int.Parse(curVersion.Split('.')[0]); + var curMinor = int.Parse(curVersion.Split('.')[1]); + var curPatch = int.Parse(curVersion.Split('.')[2]); + + + + + + var givenVersion = new string(versionQuery.TakeWhile(r => !r.IsWhitespace()).ToArray()); + + var isMinorGiven = givenVersion.Count(r => r == '.') >= 1; + var isPatchGiven = givenVersion.Count(r => r == '.') >= 2; + + var givenMajor = int.Parse(givenVersion.Split('.')[0]); + var givenMinor = isMinorGiven ? int.Parse(givenVersion.Split('.')[1]) : 0; + var givenPatch = isPatchGiven ? int.Parse(givenVersion.Split('.')[2]) : 0; + + + + + + + var curVersionCanBeNewer = versionQuery.Contains("or newer"); + var curVersionCanBeOlder = versionQuery.Contains("or older"); + + + if (curMajor > givenMajor) return versionQueryCache[versionQuery] = curVersionCanBeNewer; + if (curMajor < givenMajor) return versionQueryCache[versionQuery] = curVersionCanBeOlder; + + if (!isMinorGiven) return versionQueryCache[versionQuery] = true; + + + if (curMinor > givenMinor) return versionQueryCache[versionQuery] = curVersionCanBeNewer; + if (curMinor < givenMinor) return versionQueryCache[versionQuery] = curVersionCanBeOlder; + + if (!isPatchGiven) return versionQueryCache[versionQuery] = true; + + + if (curPatch > givenPatch) return versionQueryCache[versionQuery] = curVersionCanBeNewer; + if (curPatch < givenPatch) return versionQueryCache[versionQuery] = curVersionCanBeOlder; + + return versionQueryCache[versionQuery] = true; + + + + + // query examples: + // + // "2022.3.5 or newer" + // "2022.3.5 or older" + // "2022.3 or older" + // "2022.3" + // "2022" + + } + + static Dictionary versionQueryCache = new(); + + + + public static void SetSymbolDefinedInAsmdef(string asmdefName, string symbol, bool defined) + { + var isDefined = IsSymbolDefinedInAsmdef(asmdefName, symbol); + var shouldBeDefined = defined; + + if (shouldBeDefined && !isDefined) + DefineSymbolInAsmdef(asmdefName, symbol); + + if (!shouldBeDefined && isDefined) + UndefineSymbolInAsmdef(asmdefName, symbol); + + } + public static bool IsSymbolDefinedInAsmdef(string asmdefName, string symbol) + { + var path = AssetDatabase.FindAssets("t: asmdef " + asmdefName, null).First().ToPath(); + var importer = AssetImporter.GetAtPath(path); + + var editorType = typeof(Editor).Assembly.GetType("UnityEditor.AssemblyDefinitionImporterInspector"); + var editor = Editor.CreateEditor(importer, editorType); + + var state = editor.GetFieldValue("m_ExtraDataTargets").First(); + + + var definesList = state.GetFieldValue("versionDefines"); + var isSymbolDefined = Enumerable.Range(0, definesList.Count).Any(i => definesList[i].GetFieldValue("define") == symbol); + + + Object.DestroyImmediate(editor); + + return isSymbolDefined; + + } + + static void DefineSymbolInAsmdef(string asmdefName, string symbol) + { + var path = AssetDatabase.FindAssets("t: asmdef " + asmdefName, null).First().ToPath(); + var importer = AssetImporter.GetAtPath(path); + + var editorType = typeof(Editor).Assembly.GetType("UnityEditor.AssemblyDefinitionImporterInspector"); + var editor = Editor.CreateEditor(importer, editorType); + + var state = editor.GetFieldValue("m_ExtraDataTargets").First(); + + + var definesList = state.GetFieldValue("versionDefines"); + + var defineType = definesList.GetType().GenericTypeArguments[0]; + var newDefine = System.Activator.CreateInstance(defineType); + + newDefine.SetFieldValue("name", "Unity"); + newDefine.SetFieldValue("define", symbol); + + definesList.Add(newDefine); + + + editor.InvokeMethod("Apply"); + + Object.DestroyImmediate(editor); + + } + static void UndefineSymbolInAsmdef(string asmdefName, string symbol) + { + var path = AssetDatabase.FindAssets("t: asmdef " + asmdefName, null).First().ToPath(); + var importer = AssetImporter.GetAtPath(path); + + var editorType = typeof(Editor).Assembly.GetType("UnityEditor.AssemblyDefinitionImporterInspector"); + var editor = Editor.CreateEditor(importer, editorType); + + var state = editor.GetFieldValue("m_ExtraDataTargets").First(); + + + var definesList = state.GetFieldValue("versionDefines"); + + var defineIndex = Enumerable.Range(0, definesList.Count).First(i => definesList[i].GetFieldValue("define") == symbol); + + definesList.RemoveAt(defineIndex); + + + editor.InvokeMethod("Apply"); + + Object.DestroyImmediate(editor); + + } + + + + + public static int GetCurrendUndoGroupIndex() + { + var args = new object[] { _dummyList, 0 }; + + typeof(Undo).GetMethodInfo("GetRecords", typeof(List), typeof(int).MakeByRefType()) + .Invoke(null, args); + + + return (int)args[1]; + + } + + static List _dummyList = new(); + + + + + + public static void Hide(string path) + { + if (IsHidden(path)) return; + + if (File.Exists(path)) + File.Move(path, path + "~"); + + + path += ".meta"; + if (File.Exists(path)) + File.Move(path, path + "~"); + } + public static void Unhide(string path) + { + if (!IsHidden(path)) return; + if (path.EndsWith("~")) path = path.Substring(0, path.Length - 1); + + if (File.Exists(path + "~")) + File.Move(path + "~", path); + + path += ".meta"; + if (File.Exists(path + "~")) + File.Move(path + "~", path); + } + public static bool IsHidden(string path) => path.EndsWith("~") || File.Exists(path + "~"); + + + public static void CopyDirectoryDeep(string sourcePath, string destinationPath) + { + CopyDirectoryRecursively(sourcePath, destinationPath); + + var metas = GetFilesRecursively(destinationPath, (f) => f.EndsWith(".meta")); + var guidTable = new List<(string originalGuid, string newGuid)>(); + + foreach (string meta in metas) + { + StreamReader file = new(meta); + file.ReadLine(); + string guidLine = file.ReadLine(); + file.Close(); + string originalGuid = guidLine.Substring(6, guidLine.Length - 6); + string newGuid = GUID.Generate().ToString().Replace("-", ""); + guidTable.Add((originalGuid, newGuid)); + } + + var allFiles = GetFilesRecursively(destinationPath); + + foreach (string fileToModify in allFiles) + { + string content = File.ReadAllText(fileToModify); + + foreach (var guidPair in guidTable) + { + content = content.Replace(guidPair.originalGuid, guidPair.newGuid); + } + + File.WriteAllText(fileToModify, content); + } + + AssetDatabase.Refresh(); + } + + private static void CopyDirectoryRecursively(string sourceDirName, string destDirName) + { + DirectoryInfo dir = new(sourceDirName); + + DirectoryInfo[] dirs = dir.GetDirectories(); + + if (!Directory.Exists(destDirName)) + { + Directory.CreateDirectory(destDirName); + } + + FileInfo[] files = dir.GetFiles(); + foreach (FileInfo file in files) + { + string temppath = Path.Combine(destDirName, file.Name); + file.CopyTo(temppath, false); + } + + foreach (DirectoryInfo subdir in dirs) + { + string temppath = Path.Combine(destDirName, subdir.Name); + CopyDirectoryRecursively(subdir.FullName, temppath); + } + } + + private static List GetFilesRecursively(string path, System.Func criteria = null, List files = null) + { + if (files == null) + { + files = new List(); + } + + files.AddRange(Directory.GetFiles(path).Where(f => criteria == null || criteria(f))); + + foreach (string directory in Directory.GetDirectories(path)) + { + GetFilesRecursively(directory, criteria, files); + } + + return files; + } + + + + + + // for non-extension methods + + } + + + public static class EditorPrefsCached + { + public static int GetInt(string key, int defaultValue = 0) + { + if (ints_byKey.ContainsKey(key)) + return ints_byKey[key]; + else + return ints_byKey[key] = EditorPrefs.GetInt(key, defaultValue); + + } + public static bool GetBool(string key, bool defaultValue = false) + { + if (bools_byKey.ContainsKey(key)) + return bools_byKey[key]; + else + return bools_byKey[key] = EditorPrefs.GetBool(key, defaultValue); + + } + public static float GetFloat(string key, float defaultValue = 0) + { + if (floats_byKey.ContainsKey(key)) + return floats_byKey[key]; + else + return floats_byKey[key] = EditorPrefs.GetFloat(key, defaultValue); + + } + public static string GetString(string key, string defaultValue = "") + { + if (strings_byKey.ContainsKey(key)) + return strings_byKey[key]; + else + return strings_byKey[key] = EditorPrefs.GetString(key, defaultValue); + + } + + public static void SetInt(string key, int value) + { + ints_byKey[key] = value; + + EditorPrefs.SetInt(key, value); + + } + public static void SetBool(string key, bool value) + { + bools_byKey[key] = value; + + EditorPrefs.SetBool(key, value); + + } + public static void SetFloat(string key, float value) + { + floats_byKey[key] = value; + + EditorPrefs.SetFloat(key, value); + + } + public static void SetString(string key, string value) + { + strings_byKey[key] = value; + + EditorPrefs.SetString(key, value); + + } + + + static Dictionary ints_byKey = new(); + static Dictionary bools_byKey = new(); + static Dictionary floats_byKey = new(); + static Dictionary strings_byKey = new(); + + } + + public static class ProjectPrefs + { + public static int GetInt(string key, int defaultValue = 0) => EditorPrefsCached.GetInt(key + projectId, defaultValue); + public static bool GetBool(string key, bool defaultValue = false) => EditorPrefsCached.GetBool(key + projectId, defaultValue); + public static float GetFloat(string key, float defaultValue = 0) => EditorPrefsCached.GetFloat(key + projectId, defaultValue); + public static string GetString(string key, string defaultValue = "") => EditorPrefsCached.GetString(key + projectId, defaultValue); + + public static void SetInt(string key, int value) => EditorPrefsCached.SetInt(key + projectId, value); + public static void SetBool(string key, bool value) => EditorPrefsCached.SetBool(key + projectId, value); + public static void SetFloat(string key, float value) => EditorPrefsCached.SetFloat(key + projectId, value); + public static void SetString(string key, string value) => EditorPrefsCached.SetString(key + projectId, value); + + + + public static bool HasKey(string key) => EditorPrefs.HasKey(key + projectId); + public static void DeleteKey(string key) => EditorPrefs.DeleteKey(key + projectId); + + + + public static int projectId => PlayerSettings.productGUID.GetHashCode(); + + } + + + + public static void RecordUndo(this Object o, string operationName = "") => Undo.RecordObject(o, operationName); + public static void Dirty(this Object o) => UnityEditor.EditorUtility.SetDirty(o); + public static void Save(this Object o) => AssetDatabase.SaveAssetIfDirty(o); + + + + public static void SelectInInspector(this Object[] objects, bool frameInHierarchy = false, bool frameInProject = false) + { + void setHierarchyLocked(bool isLocked) => allHierarchies.ForEach(r => r?.GetMemberValue("m_SceneHierarchy")?.SetMemberValue("m_RectSelectInProgress", true)); + void setProjectLocked(bool isLocked) => allProjectBrowsers.ForEach(r => r?.SetMemberValue("m_InternalSelectionChange", isLocked)); + + + if (!frameInHierarchy) setHierarchyLocked(true); + if (!frameInProject) setProjectLocked(true); + + Selection.objects = objects?.ToArray(); + + if (!frameInHierarchy) EditorApplication.delayCall += () => setHierarchyLocked(false); + if (!frameInProject) EditorApplication.delayCall += () => setProjectLocked(false); + + } + public static void SelectInInspector(this Object obj, bool frameInHierarchy = false, bool frameInProject = false) => new[] { obj }.SelectInInspector(frameInHierarchy, frameInProject); + + static IEnumerable allHierarchies => _allHierarchies ??= typeof(Editor).Assembly.GetType("UnityEditor.SceneHierarchyWindow").GetFieldValue("s_SceneHierarchyWindows").Cast(); + static IEnumerable _allHierarchies; + + static IEnumerable allProjectBrowsers => _allProjectBrowsers ??= typeof(Editor).Assembly.GetType("UnityEditor.ProjectBrowser").GetFieldValue("s_ProjectBrowsers").Cast(); + static IEnumerable _allProjectBrowsers; + + + + public static void MoveTo(this EditorWindow window, Vector2 position, bool ensureFitsOnScreen = true) + { + if (!ensureFitsOnScreen) { window.position = window.position.SetPos(position); return; } + + var windowRect = window.position; + var unityWindowRect = EditorGUIUtility.GetMainWindowPosition(); + + position.x = position.x.Max(unityWindowRect.position.x); + position.y = position.y.Max(unityWindowRect.position.y); + + position.x = position.x.Min(unityWindowRect.xMax - windowRect.width); + position.y = position.y.Min(unityWindowRect.yMax - windowRect.height); + + window.position = windowRect.SetPos(position); + + } + + + +#endif + + #endregion + + } + + public static class VGUI + { + + #region Drawing + + + public static Rect Draw(this Rect rect, Color color) + { + EditorGUI.DrawRect(rect, color); + + return rect; + + } + public static Rect Draw(this Rect rect) => rect.Draw(Color.black); + + public static Rect DrawOutline(this Rect rect, Color color, float thickness = 1) + { + + rect.SetWidth(thickness).Draw(color); + rect.SetWidthFromRight(thickness).Draw(color); + + rect.SetHeight(thickness).Draw(color); + rect.SetHeightFromBottom(thickness).Draw(color); + + + return rect; + + } + public static Rect DrawOutline(this Rect rect, float thickness = 1) => rect.DrawOutline(Color.black, thickness); + + + + + public static Rect DrawRounded(this Rect rect, Color color, int cornerRadius) + { + if (!curEvent.isRepaint) return rect; + + cornerRadius = cornerRadius.Min((rect.height / 2).FloorToInt()).Min((rect.width / 2).FloorToInt()); + + if (cornerRadius < 0) return rect; + + GUIStyle style; + + void getStyle() + { + if (_roundedStylesByCornerRadius.TryGetValue(cornerRadius, out style)) return; + + var pixelsPerPoint = 2; + + var res = cornerRadius * 2 * pixelsPerPoint; + var pixels = new Color[res * res]; + + var white = Greyscale(1, 1); + var clear = Greyscale(1, 0); + var halfRes = res / 2; + + for (int x = 0; x < res; x++) + for (int y = 0; y < res; y++) + { + var sqrMagnitude = (new Vector2(x - halfRes + .5f, y - halfRes + .5f)).sqrMagnitude; + pixels[x + y * res] = sqrMagnitude <= halfRes * halfRes ? white : clear; + } + + var texture = new Texture2D(res, res); + texture.SetPropertyValue("pixelsPerPoint", pixelsPerPoint); + texture.hideFlags = HideFlags.DontSave; + texture.SetPixels(pixels); + texture.Apply(); + + + + style = new GUIStyle(); + style.normal.background = texture; + style.alignment = TextAnchor.MiddleCenter; + style.border = new RectOffset(cornerRadius, cornerRadius, cornerRadius, cornerRadius); + + + _roundedStylesByCornerRadius[cornerRadius] = style; + + } + void draw() + { + SetGUIColor(color); + + style.Draw(rect, false, false, false, false); + + ResetGUIColor(); + + } + + getStyle(); + draw(); + + return rect; + + } + public static Rect DrawRounded(this Rect rect, Color color, float cornerRadius) => rect.DrawRounded(color, cornerRadius.RoundToInt()); + + static Dictionary _roundedStylesByCornerRadius = new(); + + + + + public static Rect DrawBlurred(this Rect rect, Color color, int blurRadius) + { + if (!curEvent.isRepaint) return rect; + + var pixelsPerPoint = .5f; + // var pixelsPerPoint = 1f; + + var blurRadiusScaled = (blurRadius * pixelsPerPoint).RoundToInt().Max(1).Min(123); + + var croppedRectWidth = (rect.width * pixelsPerPoint).RoundToInt().Min(blurRadiusScaled * 2); + var croppedRectHeight = (rect.height * pixelsPerPoint).RoundToInt().Min(blurRadiusScaled * 2); + + var textureWidth = croppedRectWidth + blurRadiusScaled * 2; + var textureHeight = croppedRectHeight + blurRadiusScaled * 2; + + if (textureWidth <= 0 || textureWidth > 1232) return rect; + if (textureHeight <= 0 || textureHeight > 1232) return rect; + + + GUIStyle style; + + void getStyle() + { + if (_blurredStylesByTextureSize.TryGetValue((textureWidth, textureHeight), out style)) return; + + // VDebug.LogStart(blurRadius + ""); + + var pixels = new Color[textureWidth * textureHeight]; + var kernel = GaussianKernel.GenerateArray(blurRadiusScaled * 2 + 1); + + for (int x = 0; x < textureWidth; x++) + for (int y = 0; y < textureHeight; y++) + { + var sum = 0f; + + for (int xSample = (x - blurRadiusScaled).Max(blurRadiusScaled); xSample <= (x + blurRadiusScaled).Min(textureWidth - 1 - blurRadiusScaled); xSample++) + for (int ySample = (y - blurRadiusScaled).Max(blurRadiusScaled); ySample <= (y + blurRadiusScaled).Min(textureHeight - 1 - blurRadiusScaled); ySample++) + sum += kernel[blurRadiusScaled + xSample - x, blurRadiusScaled + ySample - y]; + + pixels[x + y * textureWidth] = Greyscale(1, sum); + + } + + var texture = new Texture2D(textureWidth, textureHeight); + texture.SetPropertyValue("pixelsPerPoint", pixelsPerPoint); + texture.hideFlags = HideFlags.DontSave; + texture.SetPixels(pixels); + texture.Apply(); + + + style = new GUIStyle(); + style.normal.background = texture; + style.alignment = TextAnchor.MiddleCenter; + + var borderX = ((textureWidth / 2f - 1) / pixelsPerPoint).FloorToInt(); + var borderY = ((textureHeight / 2f - 1) / pixelsPerPoint).FloorToInt(); + style.border = new RectOffset(borderX, borderX, borderY, borderY); + + _blurredStylesByTextureSize[(textureWidth, textureHeight)] = style; + + // VDebug.LogFinish(); + + } + void draw() + { + SetGUIColor(color); + + style.Draw(rect.SetSizeFromMid(rect.width + blurRadius * 2, rect.height + blurRadius * 2), false, false, false, false); + + ResetGUIColor(); + + } + + getStyle(); + draw(); + + return rect; + + } + public static Rect DrawBlurred(this Rect rect, Color color, float blurRadius) => rect.DrawBlurred(color, blurRadius.RoundToInt()); + + static Dictionary<(int, int), GUIStyle> _blurredStylesByTextureSize = new(); + + + + + static void DrawCurtain(this Rect rect, Color color, int dir) + { + void genTextures() + { + if (_gradientTextures != null) return; + + _gradientTextures = new Texture2D[4]; + + // var pixels = Enumerable.Range(0, 256).Select(r => Greyscale(1, r / 255f)); + var pixels = Enumerable.Range(0, 256).Select(r => Greyscale(1, (r / 255f).Smoothstep())); + + var up = new Texture2D(1, 256); + up.SetPixels(pixels.Reverse().ToArray()); + up.Apply(); + up.hideFlags = HideFlags.DontSave; + up.wrapMode = TextureWrapMode.Clamp; + _gradientTextures[0] = up; + + var down = new Texture2D(1, 256); + down.SetPixels(pixels.ToArray()); + down.Apply(); + down.hideFlags = HideFlags.DontSave; + down.wrapMode = TextureWrapMode.Clamp; + _gradientTextures[1] = down; + + var left = new Texture2D(256, 1); + left.SetPixels(pixels.ToArray()); + left.Apply(); + left.hideFlags = HideFlags.DontSave; + left.wrapMode = TextureWrapMode.Clamp; + _gradientTextures[2] = left; + + var right = new Texture2D(256, 1); + right.SetPixels(pixels.Reverse().ToArray()); + right.Apply(); + right.hideFlags = HideFlags.DontSave; + right.wrapMode = TextureWrapMode.Clamp; + _gradientTextures[3] = right; + + } + void draw() + { + SetGUIColor(color); + + GUI.DrawTexture(rect, _gradientTextures[dir]); + + ResetGUIColor(); + + } + + genTextures(); + draw(); + + } + + static Texture2D[] _gradientTextures; + + public static void DrawCurtainUp(this Rect rect, Color color) => rect.DrawCurtain(color, 0); + public static void DrawCurtainDown(this Rect rect, Color color) => rect.DrawCurtain(color, 1); + public static void DrawCurtainLeft(this Rect rect, Color color) => rect.DrawCurtain(color, 2); + public static void DrawCurtainRight(this Rect rect, Color color) => rect.DrawCurtain(color, 3); + + + + + + + #endregion + + #region Events + + + public class WrappedEvent + { + public Event e; + + public bool isRepaint => e.type == EventType.Repaint; + public bool isLayout => e.type == EventType.Layout; + public bool isUsed => e.type == EventType.Used; + public bool isMouseLeaveWindow => e.type == EventType.MouseLeaveWindow; + public bool isMouseEnterWindow => e.type == EventType.MouseEnterWindow; + public bool isContextClick => e.type == EventType.ContextClick; + + public bool isKeyDown => e.type == EventType.KeyDown; + public bool isKeyUp => e.type == EventType.KeyUp; + public KeyCode keyCode => e.keyCode; + public char characted => e.character; + + public bool isExecuteCommand => e.type == EventType.ExecuteCommand; + public string commandName => e.commandName; + + public bool isMouse => e.isMouse; + public bool isMouseDown => e.type == EventType.MouseDown; + public bool isMouseUp => e.type == EventType.MouseUp; + public bool isMouseDrag => e.type == EventType.MouseDrag; + public bool isMouseMove => e.type == EventType.MouseMove; + public bool isScroll => e.type == EventType.ScrollWheel; + public int mouseButton => e.button; + public int clickCount => e.clickCount; + public Vector2 mousePosition => e.mousePosition; + public Vector2 mousePosition_screenSpace => GUIUtility.GUIToScreenPoint(e.mousePosition); + public Vector2 mouseDelta => e.delta; + + public bool isDragUpdate => e.type == EventType.DragUpdated; + public bool isDragPerform => e.type == EventType.DragPerform; + public bool isDragExit => e.type == EventType.DragExited; + + public EventModifiers modifiers => e.modifiers; + public bool holdingAnyModifierKey => modifiers != EventModifiers.None; + + public bool holdingAlt => e.alt; + public bool holdingShift => e.shift; + public bool holdingCtrl => e.control; + public bool holdingCmd => e.command; + public bool holdingCmdOrCtrl => e.command || e.control; + + public bool holdingAltOnly => e.modifiers == EventModifiers.Alt; // in some sessions FunctionKey is always pressed? + public bool holdingShiftOnly => e.modifiers == EventModifiers.Shift; // in some sessions FunctionKey is always pressed? + public bool holdingCtrlOnly => e.modifiers == EventModifiers.Control; + public bool holdingCmdOnly => e.modifiers == EventModifiers.Command; + public bool holdingCmdOrCtrlOnly => (e.modifiers == EventModifiers.Command || e.modifiers == EventModifiers.Control); + + public EventType type => e.type; + + public void Use() => e?.Use(); + + + public WrappedEvent(Event e) => this.e = e; + + public override string ToString() => e.ToString(); + + } + + public static WrappedEvent Wrap(this Event e) => new(e); + + public static WrappedEvent curEvent => _curEvent ??= typeof(Event).GetFieldValue("s_Current").Wrap(); + static WrappedEvent _curEvent; + + + + + + #endregion + + #region Shortcuts + + + public static Rect lastRect => GUILayoutUtility.GetLastRect(); + + public static bool isDarkTheme => EditorGUIUtility.isProSkin; + + public static bool IsHovered(this Rect r) => r.Contains(curEvent.mousePosition); + + public static float GetLabelWidth(this string s) => GUI.skin.label.CalcSize(new GUIContent(s)).x; + public static float GetLabelWidth(this string s, int fontSize) + { + SetLabelFontSize(fontSize); + + var r = s.GetLabelWidth(); + + ResetLabelStyle(); + + return r; + + } + public static float GetLabelWidth(this string s, bool isBold) + { + if (isBold) + SetLabelBold(); + + var r = s.GetLabelWidth(); + + if (isBold) + ResetLabelStyle(); + + return r; + + } + public static float GetLabelWidth(this string s, int fontSize, bool isBold) + { + if (isBold) + SetLabelBold(); + + SetLabelFontSize(fontSize); + + var r = s.GetLabelWidth(); + + ResetLabelStyle(); + + return r; + + } + + public static void SetGUIEnabled(bool enabled) { _prevGuiEnabled = GUI.enabled; GUI.enabled = enabled; } + public static void ResetGUIEnabled() => GUI.enabled = _prevGuiEnabled; + static bool _prevGuiEnabled = true; + + public static void SetLabelFontSize(int size) => GUI.skin.label.fontSize = size; + public static void SetLabelBold() => GUI.skin.label.fontStyle = FontStyle.Bold; + public static void SetLabelAlignmentCenter() => GUI.skin.label.alignment = TextAnchor.MiddleCenter; + public static void ResetLabelStyle() + { + GUI.skin.label.fontSize = 0; + GUI.skin.label.fontStyle = FontStyle.Normal; + GUI.skin.label.alignment = TextAnchor.MiddleLeft; + GUI.skin.label.wordWrap = false; + } + + + public static void SetGUIColor(Color c) + { + _guiColorStack.Push(GUI.color); + + GUI.color *= c; + + } + public static void ResetGUIColor() + { + GUI.color = _guiColorStack.Pop(); + } + + static Stack _guiColorStack = new(); + + + + public static float editorDeltaTime = .0166f; + + static void EditorDeltaTime_Update() + { + editorDeltaTime = (float)(EditorApplication.timeSinceStartup - _lastUpdateTime); + + _lastUpdateTime = EditorApplication.timeSinceStartup; + + } + static double _lastUpdateTime; + + [InitializeOnLoadMethod] + static void EditorDeltaTime_Subscribe() + { + EditorApplication.update -= EditorDeltaTime_Update; + EditorApplication.update += EditorDeltaTime_Update; + } + + + + + #endregion + + #region Controls + + + public static bool IconButton(Rect rect, string iconName, float iconSize = default, Color color = default, Color colorHovered = default, Color colorPressed = default) + { + var id = EditorGUIUtility.GUIToScreenRect(rect).GetHashCode();// GUIUtility.GetControlID(FocusType.Passive, rect); + var isPressed = id == _pressedIconButtonId; + + var wasActivated = false; + + void icon() + { + if (!curEvent.isRepaint) return; + + + if (color == default) + color = Color.white; + + if (colorHovered == default) + colorHovered = Color.white; + + if (colorPressed == default) + colorPressed = Color.white.SetAlpha(.6f); + + + if (rect.IsHovered()) + color = colorHovered; + + if (isPressed) + color = colorPressed; + + + if (iconSize == default) + iconSize = rect.width.Min(rect.height); + + var iconRect = rect.SetSizeFromMid(iconSize); + + + + SetGUIColor(color); + + GUI.DrawTexture(iconRect, EditorIcons.GetIcon(iconName)); + + ResetGUIColor(); + + + } + void mouseDown() + { + if (!curEvent.isMouseDown) return; + if (!rect.IsHovered()) return; + + _pressedIconButtonId = id; + + curEvent.Use(); + + } + void mouseUp() + { + if (!curEvent.isMouseUp) return; + if (!isPressed) return; + + _pressedIconButtonId = 0; + + if (rect.IsHovered()) + wasActivated = true; + + curEvent.Use(); + + } + void mouseDrag() + { + if (!curEvent.isMouseDrag) return; + if (!isPressed) return; + + curEvent.Use(); + + } + + rect.MarkInteractive(); + + icon(); + mouseDown(); + mouseUp(); + mouseDrag(); + + return wasActivated; + + } + + static int _pressedIconButtonId; + + + + + + #endregion + + #region Layout + + + public static void Space(float px = 6) => GUILayout.Space(px); + + public static Rect ExpandWidthLabelRect() { GUILayout.Label(""/* , GUILayout.Height(0) */, GUILayout.ExpandWidth(true)); return lastRect; } + public static Rect ExpandWidthLabelRect(float height) { GUILayout.Label("", GUILayout.Height(height), GUILayout.ExpandWidth(true)); return lastRect; } + + + + + public static void BeginIndent(float f) + { + GUILayout.BeginHorizontal(); + GUILayout.Space(f); + GUILayout.BeginVertical(); + + _indentLabelWidthStack.Push(EditorGUIUtility.labelWidth); + + EditorGUIUtility.labelWidth -= f; + } + + public static void EndIndent(float f = 0) + { + GUILayout.EndVertical(); + GUILayout.Space(f); + GUILayout.EndHorizontal(); + + EditorGUIUtility.labelWidth = _indentLabelWidthStack.Pop(); + } + static Stack _indentLabelWidthStack = new(); + + + + + + public static void Horizontal() { if (__hor) GUILayout.EndHorizontal(); else GUILayout.BeginHorizontal(); __hor = !__hor; } + public static void Vertical() { if (__v) GUILayout.EndVertical(); else GUILayout.BeginVertical(); __v = !__v; } + public static void Area(Rect r) { if (__a) GUILayout.EndArea(); else GUILayout.BeginArea(r); __a = !__a; } + public static void Area() { if (__a) GUILayout.EndArea(); __a = !__a; } + public static void ResetUIBools() { __a = __hor = __v = false; _prevGuiEnabled = true; } + static bool __hor, __a, __v; + + + + + + #endregion + + #region GUIColors + + + public static class GUIColors + { + public static Color windowBackground => isDarkTheme ? Greyscale(.22f) : Greyscale(.78f); // prev backgroundCol + public static Color pressedButtonBackground => isDarkTheme ? new Color(.48f, .76f, 1f, 1f) * 1.4f : new Color(.48f, .7f, 1f, 1f) * 1.2f; // prev pressedButtonCol + public static Color greyedOutTint => Greyscale(.7f); + public static Color selectedBackground => isDarkTheme ? new Color(.17f, .365f, .535f) : new Color(.2f, .375f, .555f) * 1.2f; + } + + + + + #endregion + + #region EditorIcons + + + public static partial class EditorIcons + { + public static Texture2D GetIcon(string iconNameOrPath) + { + if (icons_byName.TryGetValue(iconNameOrPath, out var cachedResult) && cachedResult != null) return cachedResult; + + Texture2D icon = null; + + void getCustom() + { + if (icon) return; + if (!customIcons.ContainsKey(iconNameOrPath)) return; + + var pngBytesString = customIcons[iconNameOrPath]; + var pngBytes = pngBytesString.Split("-").Select(r => System.Convert.ToByte(r, 16)).ToArray(); + + icon = new Texture2D(1, 1); + + icon.LoadImage(pngBytes); + + } + void getBuiltin() + { + if (icon) return; + + icon = typeof(EditorGUIUtility).InvokeMethod("LoadIcon", iconNameOrPath) as Texture2D; + + } + void getEmpty() + { + if (icon) return; + + icon = new Texture2D(1, 1); + + } + + getCustom(); + getBuiltin(); + getEmpty(); + + return icons_byName[iconNameOrPath] = icon; + + } + + static Dictionary icons_byName = new(); + + + static Dictionary customIcons = new() + { + ["Chevron Left"] = "89-50-4E-47-0D-0A-1A-0A-00-00-00-0D-49-48-44-52-00-00-00-40-00-00-00-40-08-06-00-00-00-AA-69-71-DE-00-00-00-09-70-48-59-73-00-00-0B-13-00-00-0B-13-01-00-9A-9C-18-00-00-00-01-73-52-47-42-00-AE-CE-1C-E9-00-00-00-04-67-41-4D-41-00-00-B1-8F-0B-FC-61-05-00-00-01-29-49-44-41-54-78-01-ED-9B-CD-71-83-30-14-06-3F-A5-02-4A-71-09-A4-A3-B4-94-0A-28-01-77-10-77-90-16-D2-81-A2-A7-21-A7-FC-8C-A4-60-23-9E-76-67-34-1C-2C-1F-76-2D-30-C3-08-09-00-00-60-58-82-3A-25-C6-38-A7-C3-25-8D-29-8D-6B-08-E1-AA-11-48-E2-53-1A-6B-FC-CE-62-9F-C9-33-9B-FC-5B-FC-9D-55-5E-29-90-FF-62-D6-8E-3C-A9-03-B6-A5-6D-BF-EE-A5-60-7A-C9-9C-62-0E-0F-50-29-6F-F8-B9-0E-54-2C-FB-BB-9D-02-87-D1-28-BF-C8-03-8D-F2-36-FF-FC-CB-1F-79-E4-91-47-1E-79-E4-91-47-1E-79-E4-91-F7-21-6F-C4-C1-E5-5F-62-1D-7E-E4-8D-24-F3-3E-B2-FC-74-76-F9-2E-1E-8A-9E-9A-B3-9F-02-7B-AC-80-D7-8A-B9-F6-E4-77-ED-2D-C2-BF-88-A3-DF-03-18-44-10-11-32-44-10-11-32-44-10-11-32-44-10-11-32-44-10-11-32-44-10-11-32-44-50-73-04-5F-FB-04-1B-23-CC-DA-91-43-1F-89-85-10-3E-D2-E1-39-8D-5B-C5-D7-7C-ED-13-6C-88-E0-73-BF-70-C5-E9-30-CB-2B-05-11-7C-EC-13-FC-8B-2D-C2-F2-83-FC-1A-EF-F0-37-D8-FB-0B-13-36-EC-1A-71-1B-E6-85-09-00-00-80-C7-F1-09-79-C0-DD-81-D6-B5-69-91-00-00-00-00-49-45-4E-44-AE-42-60-82", + ["Chevron Right"] = "89-50-4E-47-0D-0A-1A-0A-00-00-00-0D-49-48-44-52-00-00-00-40-00-00-00-40-08-06-00-00-00-AA-69-71-DE-00-00-00-09-70-48-59-73-00-00-0B-13-00-00-0B-13-01-00-9A-9C-18-00-00-00-01-73-52-47-42-00-AE-CE-1C-E9-00-00-00-04-67-41-4D-41-00-00-B1-8F-0B-FC-61-05-00-00-01-1A-49-44-41-54-78-01-ED-9B-D1-0D-82-30-10-86-AF-4E-C0-28-8E-80-1B-39-82-1B-39-02-6C-00-1B-B8-82-1B-9C-6D-D0-27-05-69-4B-48-B9-7E-5F-D2-F0-40-5F-FE-2F-B4-47-9A-AB-08-00-00-40-B5-38-D9-08-55-6D-FD-E3-EC-47-E3-47-EF-9C-EB-A5-06-7C-F0-C6-8F-4E-BF-B9-87-77-62-9D-77-D0-39-06-D3-12-C2-67-AF-FF-29-5A-C2-49-F2-68-57-CC-09-FB-42-57-AA-84-5C-01-CF-95-F3-8A-95-90-2B-60-8C-98-5B-F4-97-90-CC-4C-05-38-EC-9E-10-8D-4E-65-70-88-73-80-04-24-20-01-09-48-40-02-12-90-80-04-24-20-01-09-48-40-42-04-37-B1-44-82-84-87-58-23-41-C2-A6-CB-20-F7-48-0C-72-A8-7A-09-68-DA-26-78-15-0B-24-86-1F-C4-02-5A-F3-3F-00-E1-09-4F-78-C2-13-9E-F0-84-27-3C-E1-09-4F-78-3B-E7-7B-BA-DC-27-68-3E-FC-9A-3E-C1-A2-C3-EF-D1-27-F8-21-74-94-5D-9C-73-6B-5B-EB-76-61-AF-3E-C1-22-C3-67-A3-06-5A-65-B3-D1-E5-3E-41-DB-E1-03-3A-95-C1-5F-95-A0-3B-42-F8-AD-2F-4C-84-11-D6-F9-58-CD-85-09-00-00-80-E3-F2-02-5D-3B-DF-D0-96-78-5C-6E-00-00-00-00-49-45-4E-44-AE-42-60-82", + ["Search_"] = "89-50-4E-47-0D-0A-1A-0A-00-00-00-0D-49-48-44-52-00-00-00-20-00-00-00-20-08-06-00-00-00-73-7A-7A-F4-00-00-00-09-70-48-59-73-00-00-0B-13-00-00-0B-13-01-00-9A-9C-18-00-00-00-01-73-52-47-42-00-AE-CE-1C-E9-00-00-00-04-67-41-4D-41-00-00-B1-8F-0B-FC-61-05-00-00-02-00-49-44-41-54-78-01-ED-56-3D-4F-02-41-10-1D-3E-22-C6-C4-CA-D0-9A-58-6B-87-8D-12-6B-0B-1B-7F-07-85-89-95-F4-34-D6-F2-07-2C-FD-23-D0-41-A2-8D-FF-00-8A-0B-0D-7A-70-7C-1C-EB-7B-B8-47-16-C4-70-7B-77-1C-0D-2F-B9-DC-DE-DD-CE-CE-DB-99-D9-37-27-B2-C7-8E-91-B1-99-EC-38-CE-71-A1-50-38-E0-78-34-1A-8D-8B-C5-E2-97-6C-1B-4A-A9-43-38-AB-8E-C7-E3-CF-D9-6C-E6-29-0D-DF-F7-5D-BE-C3-B7-1A-E7-C8-36-E0-BA-EE-1D-1C-39-6A-03-40-CC-E5-5C-49-12-9E-E7-55-B0-B0-AF-1D-F8-93-C9-A4-81-77-0F-B8-97-79-E9-71-DB-20-E1-83-44-45-92-00-77-13-38-47-04-BA-83-C1-E0-FA-BF-B9-20-71-85-39-3D-83-44-BC-48-30-9F-0C-A9-5E-B0-D7-E9-74-8E-36-D9-70-0E-89-6A-C2-4E-AC-9A-D0-45-35-07-43-1D-D6-0E-51-2A-07-51-63-D1-4A-54-B0-B2-B5-F3-B6-58-02-36-4D-DA-72-8D-B0-36-59-F3-81-E7-3C-9B-CD-9E-72-8C-75-5E-C5-12-B0-79-E3-3D-9F-CF-9F-71-AD-30-36-4B-04-28-32-B9-5C-6E-9E-F3-4C-26-D3-12-4B-4C-A7-D3-96-B6-2D-04-82-65-45-60-17-58-22-40-79-65-0D-71-8C-7B-49-2C-81-D0-97-B4-AD-CB-B5-24-0A-8C-22-6C-8A-25-70-04-DB-B6-45-F8-07-3C-42-86-FA-59-1D-43-E3-F8-D6-24-2A-28-22-81-FE-53-5C-C2-0A-11-45-2B-E8-0B-2A-6E-73-32-A5-98-0B-87-90-E2-6E-62-52-6C-90-58-34-A3-40-98-90-DB-D5-66-D4-80-F3-45-C3-62-03-A3-ED-70-38-BC-C1-AB-0B-89-0B-1D-09-57-6D-00-53-16-EC-1C-24-6E-F1-3C-82-DD-37-08-5F-4A-5C-A8-DF-1F-92-1A-2B-1B-42-E3-1A-ED-D7-D3-3F-24-D5-20-E7-DC-39-9D-1B-73-26-F8-7E-2F-49-81-F2-DA-EF-F7-4F-78-AD-93-5A-38-3B-E7-CE-D5-F2-CF-CA-14-A9-8A-DE-9C-6C-C1-B0-73-E7-AB-69-42-6A-5E-24-2D-30-EC-DC-F9-1A-12-8F-92-16-50-0B-4F-6B-8A-F5-5D-D2-04-76-5C-37-09-20-3D-75-49-1B-28-C0-67-5C-1F-74-0E-0E-79-D9-63-8F-15-FC-00-17-02-EB-AB-1A-4B-B3-E7-00-00-00-00-49-45-4E-44-AE-42-60-82", + ["Cross"] = "89-50-4E-47-0D-0A-1A-0A-00-00-00-0D-49-48-44-52-00-00-00-20-00-00-00-20-08-06-00-00-00-73-7A-7A-F4-00-00-00-09-70-48-59-73-00-00-0B-13-00-00-0B-13-01-00-9A-9C-18-00-00-00-01-73-52-47-42-00-AE-CE-1C-E9-00-00-00-04-67-41-4D-41-00-00-B1-8F-0B-FC-61-05-00-00-00-C5-49-44-41-54-78-01-ED-96-D1-0D-83-30-0C-44-9D-4E-D0-51-BA-02-13-B5-23-A4-1B-A4-13-31-42-3B-4A-37-70-8D-6A-04-42-E0-D8-88-E0-1F-3F-29-8A-50-1C-DF-05-48-62-80-20-08-9C-49-D2-20-22-5E-A9-BB-53-1B-FA-67-4A-E9-0B-0A-66-F3-06-5E-DA-79-6B-89-32-4E-BC-39-71-55-9C-63-47-B2-14-7F-01-3D-37-6A-BD-64-82-C7-7A-8E-1D-A9-9A-06-29-E1-62-35-9B-6F-C2-12-7B-B8-89-66-E2-1A-81-E6-E2-0A-13-ED-C5-2B-26-CE-11-57-98-D8-25-6E-D9-86-FE-B8-7E-02-D7-9F-10-3D-B7-21-7A-1E-44-96-C4-4D-4C-D0-E4-62-49-B8-61-22-4B-1A-B5-6D-38-BF-C7-3F-D4-3A-E9-6E-E7-B1-8E-63-55-68-0A-92-07-3F-16-63-41-92-E1-BF-80-B2-BB-20-09-82-E0-0C-7E-54-36-6A-69-F6-3F-13-EF-00-00-00-00-49-45-4E-44-AE-42-60-82", + ["Plus"] = "89-50-4E-47-0D-0A-1A-0A-00-00-00-0D-49-48-44-52-00-00-00-20-00-00-00-20-08-06-00-00-00-73-7A-7A-F4-00-00-00-09-70-48-59-73-00-00-0B-13-00-00-0B-13-01-00-9A-9C-18-00-00-00-01-73-52-47-42-00-AE-CE-1C-E9-00-00-00-04-67-41-4D-41-00-00-B1-8F-0B-FC-61-05-00-00-00-A7-49-44-41-54-78-01-ED-D5-01-09-84-30-14-06-E0-7F-C7-05-B8-06-77-0D-CE-08-46-31-C2-1A-68-04-4D-A0-51-8C-A0-0D-B4-81-0D-E6-13-14-C6-10-C5-4D-11-E1-FF-60-F8-78-0C-F7-B3-E1-04-88-02-18-63-B4-8C-04-77-98-17-5F-64-F0-F4-82-BF-C8-AA-BF-F0-14-12-E0-14-0C-C0-00-0C-A0-D6-9A-72-B1-A4-F2-F8-61-5B-6C-CD-E9-64-D4-3B-F3-1B-A5-54-81-3D-D3-D5-6A-AE-A3-DD-F5-D6-8E-E0-83-EB-0C-6E-E3-ED-36-64-9B-72-49-3A-95-7F-6C-8B-71-EC-08-7A-79-77-85-B3-48-C8-CA-DA-DA-12-9E-F8-19-32-00-03-3C-3A-40-67-D5-2D-EE-30-FF-37-34-88-02-8C-19-03-9B-84-46-97-A8-ED-00-00-00-00-49-45-4E-44-AE-42-60-82", + ["Plus Thicker"] = "89-50-4E-47-0D-0A-1A-0A-00-00-00-0D-49-48-44-52-00-00-00-20-00-00-00-20-08-06-00-00-00-73-7A-7A-F4-00-00-00-09-70-48-59-73-00-00-0B-13-00-00-0B-13-01-00-9A-9C-18-00-00-00-01-73-52-47-42-00-AE-CE-1C-E9-00-00-00-04-67-41-4D-41-00-00-B1-8F-0B-FC-61-05-00-00-00-A1-49-44-41-54-78-01-ED-96-D1-09-84-30-10-44-27-C7-15-70-D7-81-25-D9-C1-9D-95-A8-1D-D9-89-25-C4-0E-4C-07-71-85-80-12-22-91-B8-E8-CF-3C-98-8F-2C-21-79-10-D8-0D-40-48-21-DE-FB-5A-32-4B-AC-E4-87-BB-91-4B-47-BF-61-51-C8-0B-E5-7C-A0-C0-15-01-15-28-40-01-0A-98-B8-10-BA-5A-87-3C-55-B4-9E-32-FB-9D-A4-37-C6-0C-39-01-9B-38-5C-0B-27-02-DF-7D-21-F5-04-2A-1D-EE-48-20-2E-BC-13-9B-1A-49-7B-42-A4-8A-D6-13-F2-F4-D0-22-4C-C1-47-87-91-0A-14-A0-00-05-B4-04-1C-EE-46-9A-CF-3F-34-A3-F5-6B-5E-83-90-42-16-B4-42-4C-CD-3F-8F-0E-C4-00-00-00-00-49-45-4E-44-AE-42-60-82", + ["Collapse"] = "89-50-4E-47-0D-0A-1A-0A-00-00-00-0D-49-48-44-52-00-00-00-20-00-00-00-20-08-06-00-00-00-73-7A-7A-F4-00-00-00-09-70-48-59-73-00-00-0B-13-00-00-0B-13-01-00-9A-9C-18-00-00-00-01-73-52-47-42-00-AE-CE-1C-E9-00-00-00-04-67-41-4D-41-00-00-B1-8F-0B-FC-61-05-00-00-00-CE-49-44-41-54-78-01-ED-95-01-0D-C2-30-10-45-7F-A7-60-12-2A-01-09-93-80-04-1C-80-03-EA-60-16-70-30-1C-20-01-09-93-C0-1C-1C-BF-61-24-CD-18-0C-9A-DE-1A-92-BE-A4-49-D7-25-F7-7F-72-FF-5A-A0-90-19-33-3D-10-91-1D-F4-18-8C-31-E7-B7-7F-29-7E-10-7D-5C-A8-59-4D-3C-D4-D0-67-08-3F-E6-5A-B0-D5-34-C2-16-9C-50-48-05-DB-B5-F7-C1-45-0E-28-BC-19-53-7D-F3-7B-AC-09-05-2D-57-1F-8C-96-DF-5B-AC-05-C5-AE-33-F3-ED-CF-F4-C7-98-22-ED-87-4B-A6-85-26-14-38-CA-32-3A-A1-64-E1-46-BE-A7-41-4A-E4-35-74-4B-F8-C9-B0-48-01-0B-D5-3F-8A-3F-E9-25-45-28-59-A4-93-78-3A-68-C1-E2-2E-10-72-88-A4-42-66-8A-81-62-A0-18-C8-6E-20-1A-79-BC-0F-97-71-59-14-FE-95-3B-1B-9A-F9-A3-61-43-3F-A9-00-00-00-00-49-45-4E-44-AE-42-60-82", + }; + + } + + + + #endregion + + #region Other + + + public static void MarkInteractive(this Rect rect) + { + if (!curEvent.isRepaint) return; + + var unclippedRect = (Rect)_mi_GUIClip_UnclipToWindow.Invoke(null, new object[] { rect }); + + var curGuiView = _pi_GUIView_current.GetValue(null); + + _mi_GUIView_MarkHotRegion.Invoke(curGuiView, new object[] { unclippedRect }); + + } + + static PropertyInfo _pi_GUIView_current = typeof(Editor).Assembly.GetType("UnityEditor.GUIView").GetProperty("current", maxBindingFlags); + static MethodInfo _mi_GUIView_MarkHotRegion = typeof(Editor).Assembly.GetType("UnityEditor.GUIView").GetMethod("MarkHotRegion", maxBindingFlags); + static MethodInfo _mi_GUIClip_UnclipToWindow = typeof(GUI).Assembly.GetType("UnityEngine.GUIClip").GetMethod("UnclipToWindow", maxBindingFlags, null, new[] { typeof(Rect) }, null); + + + + + public static float GetCurrentInspectorWidth() => typeof(EditorGUIUtility).GetPropertyValue("contextWidth"); + + public static void CheckScrollbarVisibility(ref bool isScrollbarVisible) + { + GUILayout.Label("", GUILayout.Height(0), GUILayout.ExpandWidth(true)); + + if (Event.current.type == EventType.Repaint) + isScrollbarVisible = GetCurrentInspectorWidth() - 33 > lastRect.width; + + } + + + + + + + #endregion + + } + +} +#endif \ No newline at end of file diff --git a/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersLibs.cs.meta b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersLibs.cs.meta new file mode 100644 index 0000000..0c41ce9 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersLibs.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d2528d4639fe84d44bfdfde62c65dc86 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersMenu.cs b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersMenu.cs new file mode 100644 index 0000000..2f48e12 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersMenu.cs @@ -0,0 +1,128 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using static VFolders.Libs.VUtils; +using static VFolders.Libs.VGUI; +// using static VTools.VDebug; + + +namespace VFolders +{ + class VFoldersMenu + { + + public static bool navigationBarEnabled { get => EditorPrefsCached.GetBool("vFolders-navigationBarEnabled", false); set => EditorPrefsCached.SetBool("vFolders-navigationBarEnabled", value); } + public static bool twoLineNamesEnabled { get => EditorPrefsCached.GetBool("vFolders-twoLineNamesEnabled", false); set => EditorPrefsCached.SetBool("vFolders-twoLineNamesEnabled", value); } + + public static bool autoIconsEnabled { get => EditorPrefsCached.GetBool("vFolders-autoIconsEnabled", false); set => EditorPrefsCached.SetBool("vFolders-autoIconsEnabled", value); } + public static bool hierarchyLinesEnabled { get => EditorPrefsCached.GetBool("vFolders-hierarchyLinesEnabled", false); set => EditorPrefsCached.SetBool("vFolders-hierarchyLinesEnabled", value); } + public static bool zebraStripingEnabled { get => EditorPrefsCached.GetBool("vFolders-zebraStripingEnabled", false); set => EditorPrefsCached.SetBool("vFolders-zebraStripingEnabled", value); } + public static bool contentMinimapEnabled { get => EditorPrefsCached.GetBool("vFolders-contentMinimapEnabled", false); set => EditorPrefsCached.SetBool("vFolders-contentMinimapEnabled", value); } + public static bool backgroundColorsEnabled { get => EditorPrefsCached.GetBool("vFolders-backgroundColorsEnabled", false); set => EditorPrefsCached.SetBool("vFolders-backgroundColorsEnabled", value); } + public static bool minimalModeEnabled { get => EditorPrefsCached.GetBool("vFolders-minimalModeEnabled", false); set => EditorPrefsCached.SetBool("vFolders-minimalModeEnabled", value); } + public static bool foldersFirstEnabled { get => EditorPrefsCached.GetBool("vFolders-foldersFirstEnabled", false); set => EditorPrefsCached.SetBool("vFolders-foldersFirstEnabled", value); } + public static bool toggleExpandedEnabled { get => EditorPrefsCached.GetBool("vFolders-toggleExpandedEnabled", true); set => EditorPrefsCached.SetBool("vFolders-toggleExpandedEnabled", value); } + public static bool collapseEverythingElseEnabled { get => EditorPrefsCached.GetBool("vFolders-collapseEverythingElseEnabled", true); set => EditorPrefsCached.SetBool("vFolders-collapseEverythingElseEnabled", value); } + public static bool collapseEverythingEnabled { get => EditorPrefsCached.GetBool("vFolders-collapseEverythingEnabled", true); set => EditorPrefsCached.SetBool("vFolders-collapseEverythingEnabled", value); } + + public static bool pluginDisabled { get => EditorPrefsCached.GetBool("vFolders-pluginDisabled", false); set => EditorPrefsCached.SetBool("vFolders-pluginDisabled", value); } + + + + + const string dir = "Tools/vFolders/"; + + const string navigationBar = dir + "Navigation bar"; + const string autoIcons = dir + "Automatic icons"; + const string twoLineNames = dir + "Two-line names"; + const string hierarchyLines = dir + "Hierarchy lines"; + const string backgroundColors = dir + "Background colors"; + const string minimalMode = dir + "Minimal mode"; + const string zebraStriping = dir + "Zebra striping"; + const string contentMinimap = dir + "Content minimap"; + const string foldersFirst = dir + "Sort folders first"; + + const string toggleExpanded = dir + "E to expand \u2215 collapse folder"; + const string collapseEverythingElse = dir + "Shift-E to isolate folder"; + const string collapseEverything = dir + "Ctrl-Shift-E to collapse all folders"; + + const string disablePlugin = dir + "Disable vFolders"; + + + + + + + [MenuItem(dir + "Features", false, 1)] static void daasddsas() { } + [MenuItem(dir + "Features", true, 1)] static bool dadsdasas123() => false; + + [MenuItem(navigationBar, false, 2)] static void dadsaadsdsadasdsadadsas() { navigationBarEnabled = !navigationBarEnabled; EditorApplication.RepaintProjectWindow(); } + [MenuItem(navigationBar, true, 2)] static bool dadsaddasdsasadadsdasadsas() { Menu.SetChecked(navigationBar, navigationBarEnabled); return !pluginDisabled; } + + [MenuItem(twoLineNames, false, 3)] static void dadsadaddssadass() { twoLineNamesEnabled = !twoLineNamesEnabled; EditorApplication.RepaintProjectWindow(); } + [MenuItem(twoLineNames, true, 3)] static bool dadsaddasdsaasddsas() { Menu.SetChecked(twoLineNames, twoLineNamesEnabled); return !pluginDisabled; } + + [MenuItem(autoIcons, false, 4)] static void dadsadadsas() { autoIconsEnabled = !autoIconsEnabled; VFolders.folderInfoCache.Clear(); EditorApplication.RepaintProjectWindow(); } + [MenuItem(autoIcons, true, 4)] static bool dadsaddasadsas() { Menu.SetChecked(autoIcons, autoIconsEnabled); return !pluginDisabled; } + + [MenuItem(hierarchyLines, false, 5)] static void dadsadadsadass() { hierarchyLinesEnabled = !hierarchyLinesEnabled; EditorApplication.RepaintProjectWindow(); } + [MenuItem(hierarchyLines, true, 5)] static bool dadsaddasaasddsas() { Menu.SetChecked(hierarchyLines, hierarchyLinesEnabled); return !pluginDisabled; } + + [MenuItem(zebraStriping, false, 6)] static void dadsadaddsasadsadass() { zebraStripingEnabled = !zebraStripingEnabled; EditorApplication.RepaintProjectWindow(); } + [MenuItem(zebraStriping, true, 6)] static bool dadsaddadassadsaasddsas() { Menu.SetChecked(zebraStriping, zebraStripingEnabled); return !pluginDisabled; } + + [MenuItem(contentMinimap, false, 7)] static void dadsadadasdsadass() { contentMinimapEnabled = !contentMinimapEnabled; EditorApplication.RepaintProjectWindow(); } + [MenuItem(contentMinimap, true, 7)] static bool dadsadddasasaasddsas() { Menu.SetChecked(contentMinimap, contentMinimapEnabled); return !pluginDisabled; } + + [MenuItem(backgroundColors, false, 8)] static void dadsadadsadsadass() { backgroundColorsEnabled = !backgroundColorsEnabled; EditorApplication.RepaintProjectWindow(); } + [MenuItem(backgroundColors, true, 8)] static bool dadsaddasadsaasddsas() { Menu.SetChecked(backgroundColors, backgroundColorsEnabled); return !pluginDisabled; } + + [MenuItem(minimalMode, false, 9)] static void dadsadadsaddsasadass() { minimalModeEnabled = !minimalModeEnabled; EditorApplication.RepaintProjectWindow(); } + [MenuItem(minimalMode, true, 9)] static bool dadsaddasadsadsaasddsas() { Menu.SetChecked(minimalMode, minimalModeEnabled); return !pluginDisabled; } +#if UNITY_EDITOR_OSX + [MenuItem(foldersFirst, false, 10)] static void dadsdsfaadsdadsas() { foldersFirstEnabled = !foldersFirstEnabled; EditorApplication.RepaintProjectWindow(); if (!foldersFirstEnabled) UnityEditor.Compilation.CompilationPipeline.RequestScriptCompilation(); } + [MenuItem(foldersFirst, true, 10)] static bool dadsasdfdadsdasadsas() { Menu.SetChecked(foldersFirst, foldersFirstEnabled); return !pluginDisabled; } +#endif + + + + [MenuItem(dir + "Shortcuts", false, 101)] static void dadsas() { } + [MenuItem(dir + "Shortcuts", true, 101)] static bool dadsas123() => false; + + [MenuItem(toggleExpanded, false, 102)] static void dadsadsadasdsadadsas() => toggleExpandedEnabled = !toggleExpandedEnabled; + [MenuItem(toggleExpanded, true, 102)] static bool dadsaddsasadadsdasadsas() { Menu.SetChecked(toggleExpanded, toggleExpandedEnabled); return !pluginDisabled; } + + [MenuItem(collapseEverythingElse, false, 103)] static void dadsadsasdadasdsadadsas() => collapseEverythingElseEnabled = !collapseEverythingElseEnabled; + [MenuItem(collapseEverythingElse, true, 103)] static bool dadsaddsdasasadadsdasadsas() { Menu.SetChecked(collapseEverythingElse, collapseEverythingElseEnabled); return !pluginDisabled; } + + [MenuItem(collapseEverything, false, 104)] static void dadsadsdasadasdsadadsas() => collapseEverythingEnabled = !collapseEverythingEnabled; + [MenuItem(collapseEverything, true, 104)] static bool dadsaddssdaasadadsdasadsas() { Menu.SetChecked(collapseEverything, collapseEverythingEnabled); return !pluginDisabled; } + + + + + [MenuItem(dir + "More", false, 1001)] static void daasadsddsas() { } + [MenuItem(dir + "More", true, 1001)] static bool dadsadsdasas123() => false; + + [MenuItem(dir + "Open manual", false, 1002)] + static void dadadssadsas() => Application.OpenURL("https://kubacho-lab.gitbook.io/vfolders-2"); + + [MenuItem(dir + "Join our Discord", false, 1003)] + static void dadasdsas() => Application.OpenURL("https://discord.gg/pUektnZeJT"); + + + + + + + [MenuItem(disablePlugin, false, 10001)] static void dadsadsdasadasdasdsadadsas() { pluginDisabled = !pluginDisabled; UnityEditor.Compilation.CompilationPipeline.RequestScriptCompilation(); } + [MenuItem(disablePlugin, true, 10001)] static bool dadsaddssdaasadsadadsdasadsas() { Menu.SetChecked(disablePlugin, pluginDisabled); return true; } + + + + + } +} +#endif \ No newline at end of file diff --git a/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersMenu.cs.meta b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersMenu.cs.meta new file mode 100644 index 0000000..19b54b3 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersMenu.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: f118e09f35fa241fda19f1f3b1e7c5a2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersMenuItems.cs b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersMenuItems.cs new file mode 100644 index 0000000..028ed42 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersMenuItems.cs @@ -0,0 +1,5 @@ + +// this file was present in a previus version and is supposed to be deleted now +// but asset store update delivery system doesn't allow deleting files +// so instead this file is now emptied +// feel free to delete it if you want diff --git a/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersMenuItems.cs.meta b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersMenuItems.cs.meta new file mode 100644 index 0000000..4bdc039 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersMenuItems.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: ec6d8bc24e3ec4cb6b055630a6425686 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersNavbar.cs b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersNavbar.cs new file mode 100644 index 0000000..47068ff --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersNavbar.cs @@ -0,0 +1,1411 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using UnityEditor.IMGUI.Controls; +using Type = System.Type; +using static VFolders.Libs.VUtils; +using static VFolders.Libs.VGUI; +// using static VTools.VDebug; +using static VFolders.VFoldersData; +using static VFolders.VFolders; + + + +namespace VFolders +{ + public class VFoldersNavbar + { + + public void OnGUI(Rect navbarRect) + { + void updateState() + { + if (!curEvent.isLayout) return; + + + openedFolderPath = window.GetMemberValue("m_SearchFilter")?.GetMemberValue("folders")?.FirstOrDefault() ?? "Assets"; + + isOneColumn = window.GetMemberValue("m_ViewMode") == 0; + + isSearchActive = window.GetMemberValue("m_FocusSearchField") + || !window.GetMemberValue("m_SearchFieldText").IsNullOrEmpty() + || GUI.GetNameOfFocusedControl() == "navbar search field"; + + } + + void background() + { + var backgroundColor = Greyscale(isDarkTheme ? .235f : .8f); + var lineColor = Greyscale(isDarkTheme ? .13f : .58f); + + navbarRect.Draw(backgroundColor); + + navbarRect.SetHeightFromBottom(1).MoveY(1).Draw(lineColor); + + } + void hiddenMenu() + { + if (!curEvent.holdingAlt) return; + if (!curEvent.isMouseUp) return; + if (curEvent.mouseButton != 1) return; + if (!navbarRect.IsHovered()) return; + + + void selectData() + { + Selection.activeObject = data; + } + void selectPalette() + { + Selection.activeObject = palette; + } + void clearCache() + { + VFoldersCache.instance.iconTextures_byKey.Clear(); + VFoldersCache.instance.iconTextureDatas_byKey.Clear(); + VFoldersCache.instance.folderStates_byGuid.Clear(); + + VFolders.folderInfoCache.Clear(); + + bookmarkWidthsCache.Clear(); + + } + + + + GenericMenu menu = new(); + + menu.AddDisabledItem(new GUIContent("vFolders hidden menu")); + + if (isOneColumn) + { + var backForwardButtonsEnabled = EditorPrefsCached.GetBool("vFolders-showBackForwardButtonsInOneColumn", defaultValue: false); + + menu.AddSeparator(""); + menu.AddItem(new GUIContent("Show + button"), false, backForwardButtonsEnabled ? () => EditorPrefsCached.SetBool("vFolders-showBackForwardButtonsInOneColumn", false) : null); + menu.AddItem(new GUIContent("Show < > buttons"), false, backForwardButtonsEnabled ? null : () => EditorPrefsCached.SetBool("vFolders-showBackForwardButtonsInOneColumn", true)); + } + + + if (isTwoColumns) + { + var forceCompactMode = EditorPrefsCached.GetBool("vFolders-forceCompactMode", defaultValue: false); + + menu.AddSeparator(""); + menu.AddItem(new GUIContent("Enable compact bookmarks"), forceCompactMode, () => EditorPrefsCached.SetBool("vFolders-forceCompactMode", !forceCompactMode)); + } + + + + menu.AddSeparator(""); + menu.AddItem(new GUIContent("Select data"), false, selectData); + menu.AddItem(new GUIContent("Select palette"), false, selectPalette); + + menu.AddSeparator(""); + menu.AddItem(new GUIContent("Clear cache"), false, clearCache); + + menu.ShowAsContext(); + + } + + void backButton() + { + if (isOneColumn && !EditorPrefsCached.GetBool("vFolders-showBackForwardButtonsInOneColumn", defaultValue: false)) return; + + + var buttonRect = navbarRect.SetWidth(30).MoveX(4); + + if (Application.unityVersion.StartsWith("6000")) + buttonRect = buttonRect.MoveY(-.49f); + + + var iconName = "Chevron Left"; + var iconSize = 14; + var colorNormal = Greyscale(isDarkTheme ? .75f : .2f); + var colorHovered = Greyscale(isDarkTheme ? 1f : .2f); + var colorPressed = Greyscale(isDarkTheme ? .75f : .5f); + var colorDisabled = Greyscale(isDarkTheme ? .53f : .55f); + + + var disabled = isOneColumn ? !history.prevTreeStates.Any() : !history.prevFolderPaths.Any(); + + if (disabled) { IconButton(buttonRect, iconName, iconSize, colorDisabled, colorDisabled, colorDisabled); return; } + + + if (!IconButton(buttonRect, iconName, iconSize, colorNormal, colorHovered, colorPressed)) return; + + if (isOneColumn) + history.MoveBack_OneColumn(); + else + history.MoveBack_TwoColumns(); + + } + void forwardButton() + { + if (isOneColumn && !EditorPrefsCached.GetBool("vFolders-showBackForwardButtonsInOneColumn", defaultValue: false)) return; + + + var buttonRect = navbarRect.SetWidth(30).MoveX(30).MoveX(1).AddWidthFromMid(-6); + + if (Application.unityVersion.StartsWith("6000")) + buttonRect = buttonRect.MoveY(-.49f); + + + var iconName = "Chevron Right"; + var iconSize = 14; + var colorNormal = Greyscale(isDarkTheme ? .75f : .2f); + var colorHovered = Greyscale(isDarkTheme ? 1f : .2f); + var colorPressed = Greyscale(isDarkTheme ? .75f : .5f); + var colorDisabled = Greyscale(isDarkTheme ? .53f : .55f); + + + var disabled = isOneColumn ? !history.nextTreeStates.Any() : !history.nextFolderPaths.Any(); + + if (disabled) { IconButton(buttonRect, iconName, iconSize, colorDisabled, colorDisabled, colorDisabled); return; } + + + if (!IconButton(buttonRect, iconName, iconSize, colorNormal, colorHovered, colorPressed)) return; + + if (isOneColumn) + history.MoveForward_OneColumn(); + else + history.MoveForward_TwoColumns(); + + } + void plusButton_oneColumn() + { + if (!isOneColumn) return; + if (EditorPrefsCached.GetBool("vFolders-showBackForwardButtonsInOneColumn", defaultValue: false)) return; + + + var buttonRect = navbarRect.SetWidth(28).MoveX(4.5f); + + if (Application.unityVersion.StartsWith("6000")) + buttonRect = buttonRect.MoveY(-.49f); + + + var iconName = "Plus Thicker"; + var iconSize = 16; + var colorNormal = Greyscale(isDarkTheme ? .7f : .44f); + var colorHovered = Greyscale(isDarkTheme ? 1f : .42f); + var colorPressed = Greyscale(isDarkTheme ? .75f : .6f); + + + if (!IconButton(buttonRect, iconName, iconSize, colorNormal, colorHovered, colorPressed)) return; + + EditorUtility.DisplayPopupMenu(buttonRect.MoveX(24), "Assets/Create", null); + + } + + + void searchButton() + { + if (searchAnimationT == 1) return; + + + var buttonRect = navbarRect.SetWidthFromRight(28).MoveX(-5); + + var iconName = "Search_"; + var iconSize = 16; + var colorNormal = Greyscale(isDarkTheme ? .75f : .2f); + var colorHovered = Greyscale(isDarkTheme ? 1f : .2f); + var colorPressed = Greyscale(isDarkTheme ? .75f : .5f); + + + if (!IconButton(buttonRect, iconName, iconSize, colorNormal, colorHovered, colorPressed)) return; + + window.SetMemberValue("m_FocusSearchField", true); + + } + void plusButton_twoColumns() + { + if (isOneColumn) return; + if (searchAnimationT == 1) return; + + + var buttonRect = navbarRect.SetWidthFromRight(28).MoveX(-33); + + var iconName = "Plus"; + var iconSize = 16; + var colorNormal = Greyscale(isDarkTheme ? .735f : .44f); + var colorHovered = Greyscale(isDarkTheme ? 1f : .42f); + var colorPressed = Greyscale(isDarkTheme ? .75f : .6f); + + + if (!IconButton(buttonRect, iconName, iconSize, colorNormal, colorHovered, colorPressed)) return; + + if (curEvent.holdingAlt) + { + history.prevTreeStates.Clear(); + history.nextTreeStates.Clear(); + + history.prevFolderPaths.Clear(); + history.nextFolderPaths.Clear(); + + history.lastScrollTime = 0; + + + window.Repaint(); + + return; + + + // for debug + + } + + EditorUtility.DisplayPopupMenu(buttonRect.MoveX(24), "Assets/Create", null); + + } + void collapseAllButton_oneColumn() + { + if (!isOneColumn) return; + if (searchAnimationT == 1) return; + + + var buttonRect = navbarRect.SetWidthFromRight(28).MoveX(-33); + + var iconName = "Collapse"; + var iconSize = 16; + var colorNormal = Greyscale(isDarkTheme ? .71f : .44f); + var colorHovered = Greyscale(isDarkTheme ? 1f : .42f); + var colorPressed = Greyscale(isDarkTheme ? .75f : .6f); + + + if (!IconButton(buttonRect, iconName, iconSize, colorNormal, colorHovered, colorPressed)) return; + + controller.CollapseAll(); + + } + void bookmarks() + { + if (searchAnimationT == 1) return; + if (isSearchActive && !curEvent.isRepaint) return; + + void createData() + { + if (data) return; + if (!navbarRect.IsHovered()) return; + if (!DragAndDrop.objectReferences.Any()) return; + + data = ScriptableObject.CreateInstance(); + + AssetDatabase.CreateAsset(data, GetScriptPath("VFolders").GetParentPath().CombinePath("vFolders Data.asset")); + + } + void handleUndoRedo() + { + if (!data) return; + if (curEvent.commandName != "UndoRedoPerformed") return; + + + if (repaintNeededAfterUndoRedo) + window.Repaint(); + + repaintNeededAfterUndoRedo = false; + + + bookmarkWidthsCache.Clear(); + bookmarkCenterXCache.Clear(); + + } + void divider() + { + if (!data) return; + if (!data.bookmarks.Any()) return; + + + var dividerRect = navbarRect.SetWidthFromRight(1).SetHeightFromMid(16).MoveX(-65).MoveX(isCompactMode ? 1.5f : .5f); + + var dividerColor = Greyscale(isDarkTheme ? .33f : .64f); + + + dividerRect.Draw(dividerColor); + + } + void gui() + { + if (!data) return; + + this.navbarRect = navbarRect; + this.bookmarksRect = navbarRect.AddWidth(-69).AddWidthFromRight(-60).MoveX(2).MoveX(isCompactMode ? -3 : 0); + + BookmarksGUI(); + + } + + createData(); + handleUndoRedo(); + divider(); + gui(); + + } + + void searchField() + { + if (searchAnimationT == 0) return; + + var searchFieldRect = navbarRect.SetHeightFromMid(20).AddWidth(-33).SetWidthFromRight(240f.Min(window.position.width - (isOneColumn ? 195 : 223))).Move(-1, 2); + + + GUILayout.BeginArea(searchFieldRect); + GUILayout.BeginHorizontal(); + + + GUI.SetNextControlName("navbar search field"); + + Space(2); + window.InvokeMethod("SearchField"); + + + GUILayout.EndHorizontal(); + GUILayout.EndArea(); + + } + void searchOptions() + { + if (searchAnimationT == 0) return; + + var searchFieldRect = navbarRect.SetHeightFromMid(20).AddWidth(-33).SetWidthFromRight(240f.Min(window.position.width - (isOneColumn ? 195 : 223))).Move(-1, 2); + var searchOptionsRect = navbarRect.SetHeightFromMid(20).SetXMax(searchFieldRect.x).SetWidthFromRight(123); + + + + + GUILayout.BeginArea(searchOptionsRect); + GUILayout.BeginHorizontal(); + + + + + GUILayout.FlexibleSpace(); + + Space(3); + window.InvokeMethod("TypeDropDown"); + + var masksStartX = lastRect.x; + + + window.InvokeMethod("AssetLabelsDropDown"); + + if (!isOneColumn) + window.InvokeMethod("ButtonSaveFilter"); + + + window.InvokeMethod("ToggleHiddenPackagesVisibility"); + + Space(4); + + + + + + var maskRect = searchOptionsRect.SetWidth(1).SetX(masksStartX - 1).MoveY(-3); + var maskColor = Greyscale(isDarkTheme ? .235f : .8f); + + for (int i = 0; i < (isOneColumn ? 3 : 4); i++) + { + maskRect.Draw(maskColor); + maskRect = maskRect.MoveX(26); + } + + + + + GUILayout.EndHorizontal(); + GUILayout.EndArea(); + + + } + void closeSearchButton() + { + if (searchAnimationT == 0) return; + + + var buttonRect = navbarRect.SetWidthFromRight(30).MoveX(-4); + + var iconName = "Cross"; + var iconSize = 16; + var colorNormal = Greyscale(isDarkTheme ? .72f : .2f); + var colorHovered = Greyscale(isDarkTheme ? .9f : .2f); + var colorPressed = Greyscale(isDarkTheme ? .75f : .5f); + + + if (!IconButton(buttonRect, iconName, iconSize, colorNormal, colorHovered, colorPressed)) return; + + window.SetMemberValue("m_SearchFieldText", ""); + window.GetMemberValue("m_SearchFilter").SetMemberValue("m_NameFilter", ""); + + window.InvokeMethod("UpdateSearchDelayed"); + + GUIUtility.keyboardControl = 0; + + } + void closeSearchOnEsc() + { + if (!isSearchActive) return; + if (curEvent.keyCode != KeyCode.Escape) return; + + window.SetMemberValue("m_SearchFieldText", ""); + window.GetMemberValue("m_SearchFilter").SetMemberValue("m_NameFilter", ""); + + window.InvokeMethod("UpdateSearchDelayed"); + + GUIUtility.keyboardControl = 0; + + } + + + void searchAnimation() + { + if (!curEvent.isLayout) return; + + + var lerpSpeed = 8f; + + if (isSearchActive) + MathUtil.SmoothDamp(ref searchAnimationT, 1, lerpSpeed, ref searchAnimationDerivative, editorDeltaTime); + else + MathUtil.SmoothDamp(ref searchAnimationT, 0, lerpSpeed, ref searchAnimationDerivative, editorDeltaTime); + + + if (isSearchActive && searchAnimationT > .99f) + searchAnimationT = 1; + + if (!isSearchActive && searchAnimationT < .01f) + searchAnimationT = 0; + + + animatingSearch = searchAnimationT != 0 && searchAnimationT != 1; + + } + + void buttonsAndBookmarks() + { + SetGUIColor(Greyscale(1, (1 - searchAnimationT).Pow(2))); + GUI.BeginGroup(window.position.SetPos(0, 0).MoveX(-searchAnimationDistance * searchAnimationT)); + + searchButton(); + plusButton_twoColumns(); + collapseAllButton_oneColumn(); + bookmarks(); + + GUI.EndGroup(); + ResetGUIColor(); + + } + void search() + { + SetGUIColor(Greyscale(1, searchAnimationT.Pow(2))); + GUI.BeginGroup(window.position.SetPos(0, 0).MoveX(searchAnimationDistance * (1 - searchAnimationT))); + + searchField(); + searchOptions(); + closeSearchButton(); + closeSearchOnEsc(); + + GUI.EndGroup(); + ResetGUIColor(); + + } + + + + updateState(); + + background(); + hiddenMenu(); + + backButton(); + forwardButton(); + plusButton_oneColumn(); + + searchAnimation(); + buttonsAndBookmarks(); + search(); + + + + if (draggingBookmark || animatingDroppedBookmark || animatingGaps || animatingTooltip || animatingSearch) + window.Repaint(); + + } + + bool animatingSearch; + + float searchAnimationDistance = 90; + float searchAnimationT; + float searchAnimationDerivative; + + string openedFolderPath; + + bool isOneColumn; + bool isTwoColumns => !isOneColumn; + bool isSearchActive; + + bool isCompactMode + { + get + { + return isOneColumn || EditorPrefsCached.GetBool("vFolders-forceCompactMode", defaultValue: false); + } + } + + Rect navbarRect; + Rect bookmarksRect; + + + + + + + + + + + + + public void BookmarksGUI() + { + void bookmark(Vector2 centerPosition, Bookmark bookmark) + { + if (bookmark == null) return; + if (!curEvent.isRepaint && !curEvent.isMouseUp) return; + + + var bookmarkRect = Rect.zero.SetSize(GetBookmarkWidth(bookmark), bookmarksRect.height).SetMidPos(centerPosition); + + var IsHovered = bookmarkRect.IsHovered(); + var isSelected = openedFolderPath == bookmark.guid.ToPath() && !isCompactMode; + var isPressed = bookmark == pressedBookmark; + var isDragged = draggingBookmark && draggedBookmark == bookmark; + + + void shadow() + { + if (!draggingBookmark) return; + if (draggedBookmark != bookmark) return; + + bookmarkRect.SetSizeFromMid(GetBookmarkWidth(bookmark) - 4, bookmarkRect.height - 4).DrawBlurred(Greyscale(0, .3f), 15); + + } + void background() + { + if (isCompactMode) return; + if (!isSelected && !IsHovered && !isDragged) return; + + if (IsHovered && draggingBookmark && draggedBookmark != bookmark) return; + + + var backgroundRect = bookmarkRect.SetSizeFromMid(bookmarkRect.width - 7, bookmarkRect.height - 8).AddWidthFromRight(1); + + + var backgroundColor = isSelected ? Greyscale(isDarkTheme ? .35f : .7f) + : Greyscale(isDarkTheme ? .31f : .75f); + var outlineColor = isSelected ? Greyscale(isDarkTheme ? .21f : .65f) + : Greyscale(isDarkTheme ? .21f : .65f, .5f); + + + backgroundRect.Resize(-1).DrawRounded(outlineColor, 6); + backgroundRect.DrawRounded(backgroundColor, 5); + + } + void backgroundCompact() + { + if (!isCompactMode) return; + + if (!IsHovered) return; + if (draggingBookmark && draggedBookmark != bookmark) return; + + + var backgroundColor = Greyscale(isDarkTheme ? .35f : .7f); + + var backgroundRect = bookmarkRect.SetSizeFromMid(bookmarkRect.width - 2, bookmarkRect.width - 4); + + backgroundRect.DrawRounded(backgroundColor, 4); + + } + void icon() + { + var folderInfo = VFolders.GetFolderInfo(bookmark.guid); + + Texture iconTexture = folderInfo.hasIcon || folderInfo.hasColor ? VFolders.GetSmallFolderIcon(folderInfo) + : EditorIcons.GetIcon("Folder Icon"); + + var iconRect = isCompactMode ? bookmarkRect.SetSizeFromMid(iconSize) : bookmarkRect.SetWidth(iconSize).SetHeightFromMid(iconSize).MoveX(bookmarkSpacing / 2); + + iconRect = iconRect.SetWidthFromMid(iconRect.height * iconTexture.width / iconTexture.height); + + + + var opacity = folderInfo.hasColor ? .96f : .92f; + + if (isSelected) + opacity = 1; + + if (isPressed && !isDragged) + opacity = .73f; + + if (bookmark.isDeleted) + opacity = .4f; + + + + SetGUIColor(Greyscale(1, opacity)); + + GUI.DrawTexture(iconRect, iconTexture); + + ResetGUIColor(); + + } + void name() + { + if (isCompactMode) return; + if (!curEvent.isRepaint) return; + + + var nameRect = bookmarkRect.MoveX(bookmarkSpacing / 2 + iconSize + iconSpacing); + + var makeExtraBright = isSelected && isDarkTheme; + + + if (!makeExtraBright) + { + var opacity = .95f; + + if (isSelected) + opacity = 1; + + if (isPressed && !isDragged) + opacity = .82f; + + if (bookmark.isDeleted) + opacity = .4f; + + + + SetGUIColor(Greyscale(1, opacity)); + + GUI.Label(nameRect, bookmark.name); + + ResetGUIColor(); + + } + + if (makeExtraBright) + { + var opacity = .85f; + + nameRect = nameRect.SetHeightFromMid(16).AddHeight(4); + + + SetGUIColor(Greyscale(1, opacity)); + + GUI.skin.GetStyle("WhiteLabel").Draw(nameRect, bookmark.name, false, false, false, false); + + ResetGUIColor(); + + } + + } + void tooltip() + { + if (!isCompactMode) return; + + if (bookmark != (draggingBookmark ? (draggedBookmark) : (lastHoveredBookmark))) return; + if (tooltipOpacity == 0) return; + + var fontSize = 11; // ,maybe 12 + var tooltipText = bookmark.isDeleted ? "Deleted" : bookmark.name; + + Rect tooltipRect; + + void set_tooltipRect() + { + var width = tooltipText.GetLabelWidth(fontSize) + 6; + var height = 16 + (fontSize - 12) * 2; + + var yOffset = 28; + var rightMargin = -1; + + + tooltipRect = Rect.zero.SetMidPos(centerPosition.x, centerPosition.y + yOffset).SetSizeFromMid(width, height); + + + var maxXMax = navbarRect.xMax - rightMargin; + + if (tooltipRect.xMax > maxXMax) + tooltipRect = tooltipRect.MoveX(maxXMax - tooltipRect.xMax); + + } + void shadow() + { + var shadowAmount = .33f; + var shadowRadius = 10; + + tooltipRect.DrawBlurred(Greyscale(0, shadowAmount).MultiplyAlpha(tooltipOpacity), shadowRadius); + + } + void background() + { + var cornerRadius = 5; + + var backgroundColor = Greyscale(isDarkTheme ? .13f : .9f); + var outerEdgeColor = Greyscale(isDarkTheme ? .25f : .6f); + var innerEdgeColor = Greyscale(isDarkTheme ? .0f : .95f); + + tooltipRect.Resize(-1).DrawRounded(outerEdgeColor.SetAlpha(tooltipOpacity.Pow(2)), cornerRadius + 1); + tooltipRect.Resize(0).DrawRounded(innerEdgeColor.SetAlpha(tooltipOpacity.Pow(2)), cornerRadius + 0); + tooltipRect.Resize(1).DrawRounded(backgroundColor.SetAlpha(tooltipOpacity), cornerRadius - 1); + + } + void text() + { + var textRect = tooltipRect.MoveY(-.5f); + + var textColor = Greyscale(1f); + + SetLabelAlignmentCenter(); + SetLabelFontSize(fontSize); + SetGUIColor(textColor.SetAlpha(tooltipOpacity)); + + GUI.Label(textRect, tooltipText); + + ResetLabelStyle(); + ResetGUIColor(); + + } + + set_tooltipRect(); + shadow(); + background(); + text(); + + } + void click() + { + if (!IsHovered) return; + if (!curEvent.isMouseUp) return; + + + curEvent.Use(); + + if (draggingBookmark) return; + if ((curEvent.mousePosition - mouseDownPosiion).magnitude > 2) return; + if (bookmark.isDeleted) return; + + + if (isOneColumn) + controller.RevealFolder(bookmark.guid.ToPath(), expand: true, highlight: true, snapToTopMargin: true); + + if (isTwoColumns) + { + controller.RevealFolder(bookmark.guid.ToPath(), expand: false, highlight: false, snapToTopMargin: false); + controller.OpenFolder(bookmark.guid.ToPath()); + } + + + lastClickedBookmark = bookmark; + + hideTooltip = true; + + } + + + bookmarkRect.MarkInteractive(); + + shadow(); + background(); + backgroundCompact(); + icon(); + name(); + tooltip(); + click(); + + } + + void normalBookmark(int i) + { + if (data.bookmarks[i] == droppedBookmark && animatingDroppedBookmark) return; + + var centerX = GetBookmarkCenterX(i); + var centerY = bookmarksRect.height / 2; + + + var minX = centerX - GetBookmarkWidth(data.bookmarks[i]) / 2; + + if (minX < bookmarksRect.x) return; + + lastBookmarkX = minX; + + + bookmark(new Vector2(centerX, centerY), data.bookmarks[i]); + + } + void normalBookmarks() + { + for (int i = 0; i < data.bookmarks.Count; i++) + normalBookmark(i); + + } + void draggedBookmark_() + { + if (!draggingBookmark) return; + + var centerX = curEvent.mousePosition.x + draggedBookmarkHoldOffset.x; + var centerY = bookmarksRect.IsHovered() ? bookmarksRect.height / 2 : curEvent.mousePosition.y; + + bookmark(new Vector2(centerX, centerY), draggedBookmark); + + } + void droppedBookmark_() + { + if (!animatingDroppedBookmark) return; + + var centerX = droppedBookmarkX; + var centerY = bookmarksRect.height / 2; + + bookmark(new Vector2(centerX, centerY), droppedBookmark); + + } + + + ClearCacheIfNeeded(); + + BookmarksMouseState(); + BookmarksDragging(); + BookmarksAnimations(); + + normalBookmarks(); + draggedBookmark_(); + droppedBookmark_(); + + } + + float iconSize = 16; + float iconSpacing = 1; + float bookmarkSpacing = 16; + + float bookmarkWidth_compactMode = 24; + + bool repaintNeededAfterUndoRedo; + + public float lastBookmarkX; + + + + + int GetBookmarkIndex(float mouseX) + { + var curBookmarkWidthSum = 0f; + + for (int i = 0; i < data.bookmarks.Count; i++) + { + curBookmarkWidthSum += GetBookmarkWidth(data.bookmarks[i]); + + if (bookmarksRect.xMax - curBookmarkWidthSum < mouseX + .5f) + return i; + } + + return data.bookmarks.Count; + + } + + float GetBookmarkWidth(Bookmark bookmark) + { + if (!animatingBookmarks) + if (bookmarkWidthsCache.TryGetValue(bookmark, out var cachedWidth)) return cachedWidth; + + + + var width = isCompactMode ? bookmarkWidth_compactMode + : bookmark.name.GetLabelWidth() + iconSize + iconSpacing + bookmarkSpacing; + + + if (!animatingBookmarks) + bookmarkWidthsCache[bookmark] = width; + else + bookmarkWidthsCache.Clear(); + + + return width; + + } + float GetBookmarkCenterX(int i, bool includeGaps = true) + { + if (!animatingBookmarks) + if (bookmarkCenterXCache.TryGetValue(i, out var cachedCenterX)) return cachedCenterX; + + + + var centerX = bookmarksRect.xMax + - GetBookmarkWidth(data.bookmarks[i.Clamp(0, data.bookmarks.Count - 1)]) / 2 + - data.bookmarks.Take(i).Sum(r => GetBookmarkWidth(r)) + - (includeGaps ? gaps.Take(i + 1).Sum() : 0); + + + if (!animatingBookmarks) + bookmarkCenterXCache[i] = centerX; + else + bookmarkCenterXCache.Clear(); + + + return centerX; + + } + + Dictionary bookmarkWidthsCache = new(); + Dictionary bookmarkCenterXCache = new(); + + + + + void ClearCacheIfNeeded() + { + var modeChanged = wasCompactMode != isCompactMode; + var windowResized = prevWindowWidth != window.position.width; + var undoPerformed = curEvent.commandName == "UndoRedoPerformed"; + var renameHappened = curEvent.commandName == "NewKeyboardFocus"; + + wasCompactMode = isCompactMode; + prevWindowWidth = window.position.width; + + + + if (!modeChanged && !windowResized && !undoPerformed && !renameHappened) return; + + bookmarkWidthsCache.Clear(); + bookmarkCenterXCache.Clear(); + + } + + bool wasCompactMode; + float prevWindowWidth; + + + + + + + + + + + + + void BookmarksMouseState() + { + void down() + { + if (!curEvent.isMouseDown) return; + if (!bookmarksRect.IsHovered()) return; + + mousePressed = true; + + mouseDownPosiion = curEvent.mousePosition; + + var pressedBookmarkIndex = GetBookmarkIndex(mouseDownPosiion.x); + + if (pressedBookmarkIndex.IsInRangeOf(data.bookmarks)) + pressedBookmark = data.bookmarks[pressedBookmarkIndex]; + + doubleclickUnhandled = curEvent.clickCount == 2; + + curEvent.Use(); + + } + void up() + { + if (!curEvent.isMouseUp) return; + + mousePressed = false; + pressedBookmark = null; + + } + void hover() + { + var hoveredBookmarkIndex = GetBookmarkIndex(curEvent.mousePosition.x); + + mouseHoversBookmark = bookmarksRect.IsHovered() && hoveredBookmarkIndex.IsInRangeOf(data.bookmarks); + + if (mouseHoversBookmark) + lastHoveredBookmark = data.bookmarks[hoveredBookmarkIndex]; + + + } + + down(); + up(); + hover(); + + } + + bool mouseHoversBookmark; + bool mousePressed; + bool doubleclickUnhandled; + + Vector2 mouseDownPosiion; + + Bookmark pressedBookmark; + Bookmark lastHoveredBookmark; + + + + + + + void BookmarksDragging() + { + void initFromOutside() + { + if (draggingBookmark) return; + if (!bookmarksRect.IsHovered()) return; + if (!curEvent.isDragUpdate) return; + if (DragAndDrop.objectReferences.FirstOrDefault() is not Object draggedObject) return; + if (draggedObject is not DefaultAsset) return; + + animatingDroppedBookmark = false; + + draggingBookmark = true; + draggingBookmarkFromInside = false; + + draggedBookmark = new Bookmark(draggedObject); + draggedBookmarkHoldOffset = Vector2.zero; + + } + void initFromInside() + { + if (draggingBookmark) return; + if (!mousePressed) return; + if ((curEvent.mousePosition - mouseDownPosiion).magnitude <= 2) return; + if (pressedBookmark == null) return; + + var i = GetBookmarkIndex(mouseDownPosiion.x); + + if (i >= data.bookmarks.Count) return; + if (i < 0) return; + + + animatingDroppedBookmark = false; + + draggingBookmark = true; + draggingBookmarkFromInside = true; + + draggedBookmark = data.bookmarks[i]; + draggedBookmarkHoldOffset = new Vector2(GetBookmarkCenterX(i) - mouseDownPosiion.x, bookmarksRect.center.y - mouseDownPosiion.y); + + gaps[i] = GetBookmarkWidth(draggedBookmark); + + + data.RecordUndo(); + + data.bookmarks.Remove(draggedBookmark); + + } + + void acceptFromOutside() + { + if (!draggingBookmark) return; + if (!curEvent.isDragPerform) return; + if (!bookmarksRect.IsHovered()) return; + + DragAndDrop.AcceptDrag(); + curEvent.Use(); + + data.RecordUndo(); + + accept(); + + data.Dirty(); + + } + void acceptFromInside() + { + if (!draggingBookmark) return; + if (!curEvent.isMouseUp) return; + if (!bookmarksRect.IsHovered()) return; + + curEvent.Use(); + EditorGUIUtility.hotControl = 0; + + DragAndDrop.PrepareStartDrag(); // fixes phantom dragged component indicator after reordering bookmarks + + data.RecordUndo(); + data.Dirty(); + + accept(); + + } + void accept() + { + draggingBookmark = false; + draggingBookmarkFromInside = false; + mousePressed = false; + + data.bookmarks.AddAt(draggedBookmark, insertDraggedBookmarkAtIndex); + + gaps[insertDraggedBookmarkAtIndex] -= GetBookmarkWidth(draggedBookmark); + gaps.AddAt(0, insertDraggedBookmarkAtIndex); + + droppedBookmark = draggedBookmark; + + droppedBookmarkX = curEvent.mousePosition.x + draggedBookmarkHoldOffset.x; + droppedBookmarkXDerivative = 0; + animatingDroppedBookmark = true; + + draggedBookmark = null; + + EditorGUIUtility.hotControl = 0; + + repaintNeededAfterUndoRedo = true; + + } + + void cancelFromOutside() + { + if (!draggingBookmark) return; + if (draggingBookmarkFromInside) return; + if (bookmarksRect.IsHovered()) return; + + draggingBookmark = false; + mousePressed = false; + + } + void cancelFromInsideAndDelete() + { + if (!draggingBookmark) return; + if (!curEvent.isMouseUp) return; + if (bookmarksRect.IsHovered()) return; + + draggingBookmark = false; + + DragAndDrop.PrepareStartDrag(); // fixes phantom dragged component indicator after reordering bookmarks + + data.Dirty(); + + repaintNeededAfterUndoRedo = true; + + } + + void update() + { + if (!draggingBookmark) return; + + DragAndDrop.visualMode = DragAndDropVisualMode.Generic; + + if (draggingBookmarkFromInside) // otherwise it breaks vTabs dragndrop + EditorGUIUtility.hotControl = EditorGUIUtility.GetControlID(FocusType.Passive); + + + + insertDraggedBookmarkAtIndex = GetBookmarkIndex(curEvent.mousePosition.x + draggedBookmarkHoldOffset.x); + + } + + void dropIntoBookmark() + { + if (draggingBookmark) return; + if (!bookmarksRect.IsHovered()) return; + if (!DragAndDrop.objectReferences.Any()) return; + if (!DragAndDrop.objectReferences.Any(r => r is not DefaultAsset)) return; + if (!DragAndDrop.objectReferences.All(r => AssetDatabase.Contains(r))) return; + + if (!mouseHoversBookmark) return; + if (lastHoveredBookmark.isDeleted) return; + + + if (curEvent.isDragUpdate) + DragAndDrop.visualMode = DragAndDropVisualMode.Move; + + + if (!curEvent.isDragPerform) return; + + DragAndDrop.AcceptDrag(); + + curEvent.Use(); + + + var oldPaths = DragAndDrop.objectReferences.Select(r => r.GetPath()).ToList(); + + var newPaths = oldPaths.Select(r => lastHoveredBookmark.guid.ToPath().CombinePath(r.GetFilename(withExtension: true))) + .Select(r => AssetDatabase.GenerateUniqueAssetPath(r)).ToList(); + + + typeof(Undo).GetMethod("RegisterAssetsMoveUndo", maxBindingFlags).Invoke(null, new object[] { oldPaths.ToArray() }); + + for (int i = 0; i < newPaths.Count; i++) + AssetDatabase.MoveAsset(oldPaths[i], newPaths[i]); + + } + + + initFromOutside(); + initFromInside(); + + acceptFromOutside(); + acceptFromInside(); + + cancelFromOutside(); + cancelFromInsideAndDelete(); + + update(); + + dropIntoBookmark(); + + } + + bool draggingBookmark; + bool draggingBookmarkFromInside; + + int insertDraggedBookmarkAtIndex; + + Vector2 draggedBookmarkHoldOffset; + + Bookmark draggedBookmark; + Bookmark droppedBookmark; + + + + + + + void BookmarksAnimations() + { + if (!curEvent.isLayout) return; + + void gaps_() + { + var makeSpaceForDraggedBookmark = draggingBookmark && bookmarksRect.IsHovered(); + + var lerpSpeed = 12; + + for (int i = 0; i < gaps.Count; i++) + if (makeSpaceForDraggedBookmark && i == insertDraggedBookmarkAtIndex) + gaps[i] = MathUtil.Lerp(gaps[i], GetBookmarkWidth(draggedBookmark), lerpSpeed, editorDeltaTime); + else + gaps[i] = MathUtil.Lerp(gaps[i], 0, lerpSpeed, editorDeltaTime); + + + + for (int i = 0; i < gaps.Count; i++) + if (gaps[i].Approx(0)) + gaps[i] = 0; + + + + animatingGaps = gaps.Any(r => r > .1f); + + + } + void droppedBookmark_() + { + if (!animatingDroppedBookmark) return; + + var lerpSpeed = 8; + + var targX = GetBookmarkCenterX(data.bookmarks.IndexOf(droppedBookmark), includeGaps: false); + + MathUtil.SmoothDamp(ref droppedBookmarkX, targX, lerpSpeed, ref droppedBookmarkXDerivative, editorDeltaTime); + + if ((droppedBookmarkX - targX).Abs() < .5f) + animatingDroppedBookmark = false; + + } + void tooltip() + { + if (!mouseHoversBookmark || lastHoveredBookmark != lastClickedBookmark) + hideTooltip = false; + + + var lerpSpeed = UnityEditorInternal.InternalEditorUtility.isApplicationActive ? 15 : 12321; + + if (mouseHoversBookmark && !draggingBookmark && !hideTooltip) + MathUtil.SmoothDamp(ref tooltipOpacity, 1, lerpSpeed, ref tooltipOpacityDerivative, editorDeltaTime); + else + MathUtil.SmoothDamp(ref tooltipOpacity, 0, lerpSpeed, ref tooltipOpacityDerivative, editorDeltaTime); + + + if (tooltipOpacity > .99f) + tooltipOpacity = 1; + + if (tooltipOpacity < .01f) + tooltipOpacity = 0; + + + animatingTooltip = tooltipOpacity != 0 && tooltipOpacity != 1; + + } + + gaps_(); + droppedBookmark_(); + tooltip(); + + } + + float droppedBookmarkX; + float droppedBookmarkXDerivative; + + float tooltipOpacity; + float tooltipOpacityDerivative; + + bool animatingDroppedBookmark; + bool animatingGaps; + bool animatingTooltip; + bool animatingBookmarks => animatingDroppedBookmark || animatingGaps; + + bool hideTooltip; + + List gaps + { + get + { + while (_gaps.Count < data.bookmarks.Count + 1) _gaps.Add(0); + while (_gaps.Count > data.bookmarks.Count + 1) _gaps.RemoveLast(); + + return _gaps; + + } + } + List _gaps = new(); + + Bookmark lastClickedBookmark; + + + + + + + + + + + + + + public VFoldersNavbar(EditorWindow window) + { + this.window = window; + + if (!VFoldersHistorySingleton.instance.histories_byWindow.TryGetValue(window, out history)) + VFoldersHistorySingleton.instance.histories_byWindow[window] = history = new VFoldersHistory(window); + + } + + public EditorWindow window; + public VFoldersHistory history; + + public VFoldersController controller => VFolders.controllers_byWindow[window]; + + + } +} +#endif \ No newline at end of file diff --git a/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersNavbar.cs.meta b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersNavbar.cs.meta new file mode 100644 index 0000000..25cfaa7 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersNavbar.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 30acd0784e948403f8f15047ab22692c +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersPalette.cs b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersPalette.cs new file mode 100644 index 0000000..ab6eb4b --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersPalette.cs @@ -0,0 +1,268 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.Linq; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using UnityEditorInternal; +using static VFolders.Libs.VUtils; +using static VFolders.Libs.VGUI; +// using static VTools.VDebug; + + +namespace VFolders +{ + public class VFoldersPalette : ScriptableObject + { + public List colors = new(); + + public bool colorsEnabled; + + public float colorSaturation = 1; + public float colorBrightness = 1; + public bool colorGradientsEnabled = true; + + public void ResetColors() + { + colors.Clear(); + + for (int i = 0; i < colorsCount; i++) + colors.Add(GetDefaultColor(i)); + + colorsEnabled = true; + + this.Dirty(); + + } + + + public static Color GetDefaultColor(int colorIndex) + { + Color color = default; + + void grey() + { + if (colorIndex >= greyColorsCount) return; + +#if UNITY_2022_1_OR_NEWER + color = Greyscale(EditorGUIUtility.isProSkin ? .16f : .9f); +#else + color = Greyscale(EditorGUIUtility.isProSkin ? .315f : .9f); +#endif + + } + void rainbowDarkTheme() + { + if (colorIndex < greyColorsCount) return; + if (!isDarkTheme) return; + + + var t = (colorIndex - greyColorsCount.ToFloat()) / rainbowColorsCount; + + if (colorIndex == 0) + t += .01f; + + if (colorIndex == 1) + t -= .02f; + + if (colorIndex == 2) + t -= .015f; + + if (colorIndex == 3) + t -= .01f; + + if (colorIndex == 4) + t += .02f; + + if (colorIndex == 5) + t += .01f; + + + if (colorIndex == 8) + t -= .01f; + + + // color = HSLToRGB(t, .61f, .57f); + color = ColorUtils.HSLToRGB(t, .61f, .57f); + + + if (colorIndex == 0) + color *= 1.16f; + + if (colorIndex == 1) + color *= 1.17f; + + if (colorIndex == 2) + color *= 1.03f; + + if (colorIndex == 6) + color *= 1.2f; + + if (colorIndex == 7) + color *= 1.55f; + + if (colorIndex == 8) + color *= 1.2f; + + if (colorIndex == 9) + color *= 1.08f; + + + color.a = .1f; + + } + void rainbowLightTheme() + { + if (colorIndex < greyColorsCount) return; + if (isDarkTheme) return; + + color = ColorUtils.HSLToRGB((colorIndex - greyColorsCount.ToFloat()) / rainbowColorsCount, .99f, .75f); + + if (colorIndex == 0) + color *= 1.1f; + + if (colorIndex == 1) + color *= 1.05f; + + color.a = .1f; + + } + + grey(); + rainbowDarkTheme(); + rainbowLightTheme(); + + return color; + + } + + public static int greyColorsCount = 0; + public static int rainbowColorsCount = 10; + public static int colorsCount => greyColorsCount + rainbowColorsCount; + + + + + public List iconRows = new(); + + [System.Serializable] + public class IconRow + { + public List builtinIcons = new(); // names + public List customIcons = new(); // guids + + public bool enabled = true; + + public bool isCustom => !builtinIcons.Any() || customIcons.Any(); + public bool isEmpty => !builtinIcons.Any() && !customIcons.Any(); + public int iconCount => builtinIcons.Count + customIcons.Count; + + public IconRow(string[] builtinIcons) => this.builtinIcons = builtinIcons.ToList(); + public IconRow() { } + + } + + public void ResetIcons() + { + iconRows.Clear(); + + iconRows.Add(new IconRow(new[] + { + "SceneAsset Icon", + "Prefab Icon", + "PrefabModel Icon", + "Material Icon", + "Texture Icon", + "Mesh Icon", + "cs Script Icon", + "Shader Icon", + "ComputeShader Icon", + "ScriptableObject Icon", + + })); + iconRows.Add(new IconRow(new[] + { + "Light Icon", + "LightProbes Icon", + "LightmapParameters Icon", + "LightingDataAsset Icon", + "Cubemap Icon" + + })); + iconRows.Add(new IconRow(new[] + { + #if UNITY_6000_0_OR_NEWER + "PhysicsMaterial Icon", + #else + "PhysicMaterial Icon", + #endif + "BoxCollider Icon", + "TerrainCollider Icon", + "MeshCollider Icon", + "WheelCollider Icon", + "Rigidbody Icon", + + })); + iconRows.Add(new IconRow(new[] + { + "AudioClip Icon", + "AudioMixerController Icon", + "AudioMixerGroup Icon", + "AudioEchoFilter Icon", + "AudioSource Icon", + + })); + iconRows.Add(new IconRow(new[] + { + "TextAsset Icon", + "AssemblyDefinitionAsset Icon", + "TerrainData Icon", + "Terrain Icon", + "AnimatorController Icon", + "AnimationClip Icon", + "Font Icon", + "RawImage Icon", + "Settings Icon", + + })); + + this.Dirty(); + + } + + + + + [ContextMenu("Export palette")] + public void Export() + { + var packagePath = EditorUtility.SaveFilePanel("Export vFolders Palette", "", this.GetPath().GetFilename(withExtension: false), "unitypackage"); + + var iconPaths = iconRows.SelectMany(r => r.customIcons).Select(r => r.ToPath()).Where(r => !r.IsNullOrEmpty()); + + AssetDatabase.ExportPackage(iconPaths.Append(this.GetPath()).ToArray(), packagePath); + + EditorUtility.RevealInFinder(packagePath); + + } + + + + + void Reset() + { + ResetColors(); + ResetIcons(); + + VFolders.folderInfoCache.Clear(); + + } + + } +} +#endif \ No newline at end of file diff --git a/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersPalette.cs.meta b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersPalette.cs.meta new file mode 100644 index 0000000..318740f --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersPalette.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 618f730fcd14941fab38169a7dde8c64 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersPaletteEditor.cs b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersPaletteEditor.cs new file mode 100644 index 0000000..081c9aa --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersPaletteEditor.cs @@ -0,0 +1,1375 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.Linq; +using UnityEditorInternal; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using UnityEditor.IMGUI.Controls; +using static VFolders.Libs.VUtils; +using static VFolders.Libs.VGUI; +// using static VTools.VDebug; +using static VFolders.VFoldersPalette; + + +namespace VFolders +{ + [CustomEditor(typeof(VFoldersPalette))] + class VFoldersPaletteEditor : Editor + { + + public override void OnInspectorGUI() + { + void colors() + { + var rowRect = ExpandWidthLabelRect(cellSize).SetX(rowsOffsetX).SetWidth(rowWidth + 16); + + void backgroundHovered() + { + if (!rowRect.IsHovered()) return; + if (pickingColor) return; + if (draggingRow) return; + + rowRect.Draw(hoveredRowBackground); + + } + void toggle() + { + var toggleRect = rowRect.SetWidth(16).MoveX(5); + + var prevEnabled = palette.colorsEnabled; + var newEnabled = EditorGUI.Toggle(toggleRect, palette.colorsEnabled); + + if (prevEnabled != newEnabled) + palette.RecordUndo(); + + palette.colorsEnabled = newEnabled; + + if (prevEnabled != newEnabled) + palette.Dirty(); + + } + void crossIcon() + { + var crossIconRect = rowRect.SetX(rowsOffsetX + iconsOffsetX + iconSpacing / 2).SetWidth(iconSize).SetHeightFromMid(iconSize); + + SetGUIColor(palette.colorsEnabled ? Color.white : disabledRowTint); + + GUI.DrawTexture(crossIconRect, EditorIcons.GetIcon("CrossIcon")); + + ResetGUIColor(); + + } + void color(int i) + { + var cellRect = rowRect.MoveX(iconsOffsetX + (i + 1) * cellSize).SetWidth(cellSize).SetHeightFromMid(cellSize); + + void backgroundPicking() + { + if (!pickingColor) return; + if (i != pickingColorAtIndex) return; + + cellRect.DrawRounded(pickingBackground, 2); + + } + void backgroundHovered_andStartPickingColor() + { + if (pickingColor) return; + if (!cellRect.IsHovered()) return; + + + SetGUIColor(Color.clear); + + var clicked = GUI.Button(cellRect.Resize(1), ""); + + ResetGUIColor(); + + + + + var isPressed = GUIUtility.hotControl == typeof(EditorGUIUtility).GetFieldValue("s_LastControlID"); + + cellRect.DrawRounded(Greyscale(isPressed ? .39f : .43f), 2); + + + + + if (!clicked) return; + + colorPickerWindow = EditorUtils.OpenColorPicker((c) => { palette.RecordUndo(); palette.Dirty(); palette.colors[i] = c; }, palette.colors[i], true, false); + + colorPickerWindow.MoveTo(EditorGUIUtility.GUIToScreenPoint(cellRect.Move(-3, 50).position)); + + pickingColor = true; + pickingColorAtIndex = i; + + } + void colorOutline() + { + var outlineColor = i < VFoldersPalette.greyColorsCount ? Greyscale(.0f, .4f) : Greyscale(.15f, .2f); + + if (!palette.colorsEnabled) + outlineColor *= disabledRowTint; + + + cellRect.Resize(3).DrawRounded(outlineColor, 4); + + } + void color() + { + var brightness = palette.colorBrightness; + var saturation = palette.colorSaturation; + var drawGradients = palette.colorGradientsEnabled; + + if (!palette.colorGradientsEnabled) + brightness *= isDarkTheme ? .75f : .97f; + + if (i < VFoldersPalette.greyColorsCount) + { + saturation = brightness = 1; + drawGradients = false; + } + + + var colorRaw = palette.colors[i]; + + var color = MathUtil.Lerp(Greyscale(.2f), colorRaw, brightness); + + Color.RGBToHSV(color, out float h, out float s, out float v); + color = Color.HSVToRGB(h, s * saturation, v); + + color = MathUtil.Lerp(color, colorRaw, .5f).SetAlpha(1); + + if (!palette.colorsEnabled) + color *= disabledRowTint; + + + + + cellRect.Resize(4).DrawRounded(color, 3); + + if (drawGradients) + cellRect.Resize(4).AddWidthFromRight(-2).DrawCurtainLeft(GUIColors.windowBackground.SetAlpha(.45f)); + + } + void updatePickingColor() + { + if (!pickingColor) return; + + EditorApplication.RepaintProjectWindow(); + + } + void stopPickingColor() + { + if (!pickingColor) return; + if (colorPickerWindow) return; + + pickingColor = false; + + } + + + cellRect.MarkInteractive(); + + + backgroundPicking(); + backgroundHovered_andStartPickingColor(); + + colorOutline(); + color(); + + updatePickingColor(); + stopPickingColor(); + + } + void adjustColorsButton() + { + var cellRect = rowRect.MoveX(iconsOffsetX + (palette.colors.Count + 1) * cellSize).SetWidth(cellSize).SetHeightFromMid(cellSize).MoveX(-1f); + + + var iconSize = 16; + var iconName = "Preset.Context"; + var iconColor = Greyscale(.75f, palette.colorsEnabled ? (isDarkTheme ? 1 : .8f) : .5f); + + if (!IconButton(cellRect, iconName, iconSize, iconColor)) return; + + + if (adjustColorsWindow) { adjustColorsWindow.Close(); return; } + + var windowX = 107f.Min(this.GetMemberValue("propertyViewer").position.width - 310); + var windowY = cellRect.y + 25; + var windowWidth = 270; + var windowHeight = VFoldersMenu.backgroundColorsEnabled ? 92 : 73; + + adjustColorsWindow = ScriptableObject.CreateInstance(); + adjustColorsWindow.palette = palette; + adjustColorsWindow.paletteEditor = this; + + adjustColorsWindow.ShowPopup(); + adjustColorsWindow.Focus(); + + adjustColorsWindow.position = EditorGUIUtility.GUIToScreenRect(new Rect(windowX, windowY, windowWidth, windowHeight)); + + } + + + backgroundHovered(); + toggle(); + crossIcon(); + + for (int i = 0; i < palette.colors.Count; i++) + color(i); + + adjustColorsButton(); + + Space(rowSpacing - 2); + + } + void icons() + { + void row(Rect rowRect, IconRow row) + { + var isLastRow = row == palette.iconRows.Last(); + var isDraggedRow = row == draggedRow; + var spaceForCrossIcon = 0f; + + + void updatePickingIcon() + { + if (!pickingIcon) return; + if (pickingIconAtRow != row) return; + if (pickingIconAtIndex >= row.customIcons.Count) return; // somehow happens if RecordUndo is used + + palette.RecordUndo(); + palette.Dirty(); + + row.customIcons[pickingIconAtIndex] = addIconWindow.hoveredIconName; + + } + void stopPickingIcon() + { + if (!pickingIcon) return; + if (pickingIconAtRow != row) return; + if (addIconWindow) return; + + if (pickingIconAtIndex < row.customIcons.Count) + if (row.customIcons[pickingIconAtIndex] == null) + row.customIcons.RemoveAt(pickingIconAtIndex); + + pickingIcon = false; + + } + void dragndrop() + { + if (!rowRect.IsHovered()) return; + if (!row.isCustom) return; + + if (curEvent.isDragUpdate && DragAndDrop.objectReferences.First() is Texture2D) + DragAndDrop.visualMode = DragAndDropVisualMode.Copy; + + if (!curEvent.isDragPerform) return; + if (!(DragAndDrop.objectReferences.Any(r => r is Texture2D))) return; + + DragAndDrop.AcceptDrag(); + + palette.RecordUndo(); + palette.Dirty(); + + foreach (var icon in DragAndDrop.objectReferences.Where(r => r is Texture2D)) + row.customIcons.Add(icon.GetPath().ToGuid()); + + } + + void calcSpaceForCrossIcon() + { + if (row == curFirstEnabledRow) + spaceForCrossIcon = crossIconAnimationT * cellSize; + + if (row == crossIconAnimationSourceRow) + spaceForCrossIcon = (1 - crossIconAnimationT) * cellSize; + + } + + void backgroundHovered() + { + if (!rowRect.IsHovered()) return; + if (pickingColor) return; + if (pickingIcon) return; + if (draggingRow) return; + if (DragAndDrop.objectReferences.Any() && !row.isCustom) return; + + + rowRect.Draw(hoveredRowBackground); + + } + void backgroundDragged() + { + if (!isDraggedRow) return; + + rowRect.DrawBlurred(Greyscale(0, .3f), 12); + rowRect.Draw(draggedRowBackground); + + } + void toggle() + { + var prevEnabled = row.enabled; + var newEnabled = EditorGUI.Toggle(rowRect.SetWidth(16).MoveX(5), row.enabled); + + if (prevEnabled != newEnabled) + palette.RecordUndo(); + + row.enabled = newEnabled; + + if (prevEnabled != newEnabled) + palette.Dirty(); + + } + void addIconButton() + { + if (!row.isCustom) return; + if (pickingIcon && pickingIconAtRow == row) return; + + + var cellRect = rowRect.MoveX(iconsOffsetX + row.customIcons.Count * cellSize + spaceForCrossIcon).SetWidth(cellSize).SetHeightFromMid(cellSize); + + var iconSize = 16; + var iconName = "Toolbar Plus"; + var iconColor = Greyscale(.73f, row.enabled ? (isDarkTheme ? 1 : .65f) : .5f); + + if (!IconButton(cellRect, iconName, iconSize, iconColor)) return; + + + + palette.RecordUndo(); + + row.customIcons.Add(null); + + + + var windowX = 15; + var windowY = cellRect.y + 23; + var windowWidth = (this.GetMemberValue("propertyViewer").position.width - 26).Min(679); + var windowHeight = windowWidth * 1.2f; + + windowWidth = (windowWidth / AddIconWindow.cellSize).FloorToInt() * AddIconWindow.cellSize - 3; + + + addIconWindow = ScriptableObject.CreateInstance(); + addIconWindow.palette = palette; + addIconWindow.paletteEditor = this; + + addIconWindow.ShowPopup(); + addIconWindow.Focus(); + + addIconWindow.position = EditorGUIUtility.GUIToScreenRect(new Rect(windowX, windowY, windowWidth, windowHeight)); + + addIconWindow.Init(); + + + pickingIcon = true; + pickingIconAtIndex = row.customIcons.Count - 1; + pickingIconAtRow = row; + + + } + void icon(int i) + { + var cellRect = rowRect.MoveX(iconsOffsetX + spaceForCrossIcon + i * cellSize).SetWidth(cellSize).SetHeightFromMid(cellSize); + + + void backgroundPicking() + { + if (!pickingIcon) return; + if (pickingIconAtRow != row) return; + if (pickingIconAtIndex != i) return; + + cellRect.DrawRounded(pickingBackground, 2); + + } + void backgroundHovered_andEditIconButton() + { + if (!row.isCustom) return; + if (pickingIcon) return; + if (!cellRect.IsHovered()) return; + if (DragAndDrop.objectReferences.Any()) return; + + + SetGUIColor(Color.clear); + + var clicked = GUI.Button(cellRect.Resize(1), ""); + + ResetGUIColor(); + + + + + var isPressed = GUIUtility.hotControl == typeof(EditorGUIUtility).GetFieldValue("s_LastControlID"); + + cellRect.DrawRounded(Greyscale(isPressed ? .39f : .45f), 2); + + + + + if (!clicked) return; + + GenericMenu menu = new(); + + menu.AddItem(new GUIContent("Move left"), false, i == 0 ? null : () => + { + palette.RecordUndo(); + palette.Dirty(); + + var icon = row.customIcons[i]; + + row.customIcons.RemoveAt(i); + row.customIcons.AddAt(icon, i - 1); + + }); + menu.AddItem(new GUIContent("Move right"), false, i == row.customIcons.Count - 1 ? null : () => + { + palette.RecordUndo(); + palette.Dirty(); + + var icon = row.customIcons[i]; + + row.customIcons.RemoveAt(i); + row.customIcons.AddAt(icon, i + 1); + + }); + + menu.AddSeparator(""); + + menu.AddItem(new GUIContent("Remove icon"), false, () => { palette.RecordUndo(); row.customIcons.RemoveAt(i); palette.Dirty(); }); + + + menu.ShowAsContext(); + + } + + void drawIcon() + { + var iconNameOrGuid = row.isCustom ? row.customIcons[i] : row.builtinIcons[i]; + + if (iconNameOrGuid == null) return; + + var iconNameOrPath = iconNameOrGuid.Length == 32 ? iconNameOrGuid.ToPath() : iconNameOrGuid; + var icon = EditorIcons.GetIcon(iconNameOrPath) ?? Texture2D.blackTexture; + + + var cellRect = rowRect.MoveX(iconsOffsetX + spaceForCrossIcon + i * cellSize).SetWidth(cellSize).SetHeightFromMid(cellSize); + var iconRect = cellRect.SetSizeFromMid(iconSize); + + if (icon.width < icon.height) iconRect = iconRect.SetWidthFromMid(iconRect.height * icon.width / icon.height); + if (icon.height < icon.width) iconRect = iconRect.SetHeightFromMid(iconRect.width * icon.height / icon.width); + + + + SetGUIColor(row.enabled ? Color.white : disabledRowTint); + + GUI.DrawTexture(iconRect, icon); + + ResetGUIColor(); + + } + + + cellRect.MarkInteractive(); + + backgroundPicking(); + backgroundHovered_andEditIconButton(); + + drawIcon(); + + } + + + rowRect.MarkInteractive(); + + updatePickingIcon(); + stopPickingIcon(); + dragndrop(); + + calcSpaceForCrossIcon(); + backgroundHovered(); + backgroundDragged(); + toggle(); + addIconButton(); + + for (int i = 0; i < row.iconCount; i++) + icon(i); + + } + + void updateRowsCount() + { + palette.iconRows.RemoveAll(r => r.isEmpty && r != palette.iconRows.Last()); + + if (!palette.iconRows.Last().isEmpty) + palette.iconRows.Add(new IconRow()); + + } + void updateRowGapsCount() + { + while (rowGaps.Count < palette.iconRows.Count) + rowGaps.Add(0); + + while (rowGaps.Count > palette.iconRows.Count) + rowGaps.RemoveLast(); + + } + + void normalRow(int i) + { + Space(rowGaps[i] * (cellSize + rowSpacing)); + + if (i == 0 && lastRect.y != 0) + firstRowY = lastRect.y; + + Space(cellSize + rowSpacing); + + var rowRect = Rect.zero.SetPos(rowsOffsetX, lastRect.y).SetSize(rowWidth, cellSize); + + if (curEvent.isRepaint) + if (rowRect.IsHovered()) + hoveredRow = palette.iconRows[i]; + + + row(rowRect, palette.iconRows[i]); + + } + void draggedRow_() + { + if (!draggingRow) return; + + draggedRowY = (curEvent.mousePosition.y + draggedRowHoldOffset).Clamp(firstRowY, firstRowY + (palette.iconRows.Count - 1) * (cellSize + rowSpacing)); + + var rowRect = Rect.zero.SetPos(rowsOffsetX, draggedRowY).SetSize(rowWidth, cellSize); + + row(rowRect, draggedRow); + + } + void crossIcon() + { + if (!palette.iconRows.Any(r => r.enabled)) return; + + var rect = Rect.zero.SetPos(rowsOffsetX + iconsOffsetX, crossIconY).SetSize(cellSize, cellSize).Resize(iconSpacing / 2); + + GUI.DrawTexture(rect, EditorIcons.GetIcon("CrossIcon")); + + } + + + updateRowsCount(); + updateRowGapsCount(); + + + if (curEvent.isRepaint) + hoveredRow = null; + + for (int i = 0; i < palette.iconRows.Count; i++) + normalRow(i); + + crossIcon(); + + draggedRow_(); + + + } + void tutor() + { + SetGUIEnabled(false); + + + Space(4); + GUILayout.Label("Add icons with drag-and-drop or by clicking '+'"); + + Space(4); + GUILayout.Label("Click added icon to move or remove it"); + + Space(4); + GUILayout.Label("Drag rows to reorder them"); + + + ResetGUIEnabled(); + + } + + + Space(15); + colors(); + + Space(15); + icons(); + + Space(22); + tutor(); + + UpdateAnimations(); + + UpdateDragging(); + + palette.Dirty(); + + if (draggingRow || animatingCrossIcon) + Repaint(); + + } + + float iconSize => 14; + float iconSpacing => 6; + float cellSize => iconSize + iconSpacing; + float rowSpacing = 1; + float rowsOffsetX => 14; + float iconsOffsetX => 27; + + Color hoveredRowBackground => Greyscale(isDarkTheme ? 1 : 0, .05f); + Color draggedRowBackground => Greyscale(isDarkTheme ? .3f : .9f); + Color pickingBackground => Greyscale(1, .17f); + Color disabledRowTint => Greyscale(1, .45f); + + float rowWidth => cellSize * Mathf.Max(palette.colors.Count, palette.iconRows.Max(r => r.iconCount + 1)) + 55; + + bool pickingColor; + int pickingColorAtIndex; + EditorWindow colorPickerWindow; + + bool pickingIcon; + int pickingIconAtIndex; + IconRow pickingIconAtRow; + AddIconWindow addIconWindow; + + IconRow hoveredRow; + + float firstRowY = 51; + + static AdjustColorsWindow adjustColorsWindow; + + + + + + void UpdateAnimations() + { + void lerpRowGaps() + { + if (!curEvent.isLayout) return; + + var lerpSpeed = draggingRow ? 12 : 12321; + + for (int i = 0; i < rowGaps.Count; i++) + rowGaps[i] = MathUtil.Lerp(rowGaps[i], draggingRow && i == insertDraggedRowAtIndex ? 1 : 0, lerpSpeed, editorDeltaTime); + + for (int i = 0; i < rowGaps.Count; i++) + if (rowGaps[i].Approx(0)) + rowGaps[i] = 0; + else if (rowGaps[i].Approx(1)) + rowGaps[i] = 1; + + + } + + void lerpCrossIconAnimationT() + { + if (!curEvent.isLayout) return; + + var lerpSpeed = 12; + + MathUtil.Lerp(ref crossIconAnimationT, 1, lerpSpeed, editorDeltaTime); + + } + void startCrossIconAnimation() + { + if (prevFirstEnabledRow == null) { prevFirstEnabledRow = curFirstEnabledRow; return; } + if (prevFirstEnabledRow == curFirstEnabledRow) return; + + crossIconAnimationT = 0; + crossIconAnimationSourceRow = prevFirstEnabledRow; + + prevFirstEnabledRow = curFirstEnabledRow; + + } + void stopCrossIconAnimation() + { + if (!crossIconAnimationT.Approx(1)) return; + + crossIconAnimationT = 1; + crossIconAnimationSourceRow = null; + + } + void calcCrossIconY() + { + var indexOfFirstEnabled = palette.iconRows.IndexOfFirst(r => r.enabled); + var yOfFirstEnabled = firstRowY + indexOfFirstEnabled * (cellSize + rowSpacing); + for (int i = 0; i < indexOfFirstEnabled + 1; i++) + yOfFirstEnabled += rowGaps[i] * (cellSize + rowSpacing); + + + var indexOfSourceRow = palette.iconRows.IndexOf(crossIconAnimationSourceRow); + var yOfSourceRow = firstRowY + indexOfSourceRow * (cellSize + rowSpacing); + for (int i = 0; i < indexOfSourceRow + 1; i++) + yOfSourceRow += rowGaps[i] * (cellSize + rowSpacing); + + if (crossIconAnimationSourceRow == draggedRow) + yOfSourceRow = draggedRowY; + + + crossIconY = MathUtil.Lerp(yOfSourceRow, yOfFirstEnabled, crossIconAnimationT); + + if (indexOfFirstEnabled == indexOfSourceRow) + crossIconAnimationT = 1; + + } + + + lerpRowGaps(); + + lerpCrossIconAnimationT(); + startCrossIconAnimation(); + stopCrossIconAnimation(); + calcCrossIconY(); + + } + + List rowGaps = new(); + + float crossIconY = 51; + float crossIconAnimationT = 1; + IconRow crossIconAnimationSourceRow; + bool animatingCrossIcon => crossIconAnimationT != 1; + + [System.NonSerialized] IconRow prevFirstEnabledRow; + IconRow curFirstEnabledRow => palette.iconRows.FirstOrDefault(r => r.enabled); + + + + + + + void UpdateDragging() + { + void startDragging() + { + if (draggingRow) return; + if (!curEvent.isMouseDrag) return; + if (hoveredRow == null) return; + if (hoveredRow == palette.iconRows.Last()) return; + + palette.RecordUndo(); + + draggingRow = true; + draggedRow = hoveredRow; + draggingRowFromIndex = palette.iconRows.IndexOf(hoveredRow); + draggedRowHoldOffset = firstRowY + draggingRowFromIndex * (cellSize + rowSpacing) - curEvent.mousePosition.y; + + palette.iconRows.Remove(hoveredRow); + rowGaps[draggingRowFromIndex] = 1; + + } + void updateDragging() + { + if (!draggingRow) return; + + insertDraggedRowAtIndex = ((curEvent.mousePosition.y - firstRowY) / (cellSize + rowSpacing)).FloorToInt().Clamp(0, palette.iconRows.Count - 1); + + EditorGUIUtility.hotControl = EditorGUIUtility.GetControlID(FocusType.Passive); + + } + void stopDragging() + { + if (!draggingRow) return; + if (!curEvent.isMouseUp) return; + + palette.RecordUndo(); + palette.Dirty(); + + palette.iconRows.AddAt(draggedRow, insertDraggedRowAtIndex); + + rowGaps[insertDraggedRowAtIndex] = 0; + + draggingRow = false; + draggedRow = null; + + EditorGUIUtility.hotControl = 0; + + } + + + startDragging(); + updateDragging(); + stopDragging(); + + } + + IconRow draggedRow; + bool draggingRow; + int draggingRowFromIndex; + float draggedRowHoldOffset; + float draggedRowY; + int insertDraggedRowAtIndex; + + + + + + + VFoldersPalette palette => target as VFoldersPalette; + + } + + + class AddIconWindow : EditorWindow + { + + void OnGUI() + { + void header() + { + var headerRect = Rect.zero.SetHeight(20).SetWidth(position.width); + var closeButtonRect = headerRect.SetWidthFromRight(16).SetHeightFromMid(16).Move(-3, -.5f); + + void background() + { + headerRect.Draw(EditorGUIUtility.isProSkin ? Greyscale(.18f) : Greyscale(.7f)); + } + void title() + { + SetGUIColor(Greyscale(.8f)); + SetLabelAlignmentCenter(); + + GUI.Label(headerRect.MoveY(-1), "Add icon"); + + ResetLabelStyle(); + ResetGUIColor(); + + } + void closeButton() + { + var colorNormal = isDarkTheme ? Greyscale(.55f) : Greyscale(.35f); + var colorHovered = isDarkTheme ? Greyscale(.9f) : colorNormal; + + var iconSize = 14; + + if (IconButton(closeButtonRect, "CrossIcon", iconSize, colorNormal, colorHovered)) + Close(); + + } + void escHint() + { + if (!closeButtonRect.IsHovered()) return; + + var textRect = headerRect.SetWidthFromRight(42).MoveY(-.5f).MoveX(1); + var fontSize = 11; + var color = Greyscale(.65f); + + + SetLabelFontSize(fontSize); + SetGUIColor(color); + + GUI.Label(textRect, "Esc"); + + ResetGUIColor(); + ResetLabelStyle(); + + } + + background(); + title(); + closeButton(); + escHint(); + + Space(headerRect.height); + + } + void search() + { + var backgroundRect = ExpandWidthLabelRect(height: 21).SetWidthFromMid(position.width); + var backgroundColor = isDarkTheme ? Greyscale(.25f) : Greyscale(.8f); + + backgroundRect.Draw(backgroundColor); + + + var lineRect = backgroundRect.SetHeightFromBottom(1).MoveY(.5f); + var lineColor = isDarkTheme ? Greyscale(.15f) : Greyscale(.7f); + + lineRect.Draw(lineColor); + + + var searchRect = backgroundRect.Resize(2); + + EditorGUI.BeginChangeCheck(); + + searchString = searchField.OnGUI(searchRect, searchString); + + if (EditorGUI.EndChangeCheck()) + { + GenerateRows(); + FilterIconsBySearch(); + GenerateRows(); + } + + } + void icons() + { + void row(int i) + { + var rowRect = position.SetPos(0, 0).SetHeight(cellSize).MoveY(i * rowHeight); + var iconNames = rows[i]; + + void icon(int i) + { + var iconName = iconNames[i]; + var icon = EditorIcons.GetIcon(iconName); + + var cellRect = rowRect.SetWidth(cellSize).MoveX(i * cellSize + paddingLeft); + var iconRect = cellRect.SetSizeFromMid(iconSize); + + if (icon.width < icon.height) iconRect = iconRect.SetWidthFromMid(iconRect.height * icon.width / icon.height); + if (icon.height < icon.width) iconRect = iconRect.SetHeightFromMid(iconRect.width * icon.height / icon.width); + + + + var hoverRect = cellRect.AddHeightFromMid(rowSpacing); + + hoverRect.MarkInteractive(); + + if (hoverRect.IsHovered()) + cellRect.Draw(Greyscale(isDarkTheme ? .42f : .69f)); + + + + GUI.DrawTexture(iconRect, EditorIcons.GetIcon(iconName)); + + + + if (curEvent.isRepaint) + if (hoverRect.IsHovered()) + hoveredIconName = iconName; + + if (hoverRect.IsHovered() && curEvent.isMouseDown) + Close(); + + } + + for (int ii = 0; ii < iconNames.Count; ii++) + icon(ii); + } + + + if (curEvent.isRepaint) + hoveredIconName = null; + + + scrollPos = GUILayout.BeginScrollView(new Vector2(0, scrollPos)).y; + + GUILayout.Space(rows.Count * rowHeight + 23); + + + var i0 = (scrollPos / rowHeight).FloorToInt(); + var i1 = (i0 + ((position.height - 30) / rowHeight).CeilToInt()).Min(rows.Count); + + for (int ii = i0; ii < i1; ii++) + row(ii); + + + GUILayout.EndScrollView(); + + } + void hoveredIconLabel() + { + if (hoveredIconName == null) return; + + + var nameRect = position.SetPos(0, 0).SetHeightFromBottom(18).SetWidth(hoveredIconName.GetLabelWidth() + 6).Move(1, -1); + + var shadowRect = nameRect.AddWidthFromRight(10).AddHeight(10); + + + shadowRect.DrawBlurred(GUIColors.windowBackground, 12); + shadowRect.DrawBlurred(GUIColors.windowBackground.SetAlpha(.4f), 8); + + + SetLabelAlignmentCenter(); + + GUI.Label(nameRect, hoveredIconName); + + ResetLabelStyle(); + + } + void closeOnEsc() + { + if (!curEvent.isKeyDown) return; + if (curEvent.keyCode != KeyCode.Escape) return; + + hoveredIconName = null; + + Close(); + + } + void outline() + { + if (Application.platform == RuntimePlatform.OSXEditor) return; + + position.SetPos(0, 0).DrawOutline(Greyscale(.1f)); + + } + + header(); + search(); + icons(); + hoveredIconLabel(); + closeOnEsc(); + outline(); + + paletteEditor.Repaint(); + + if (EditorWindow.focusedWindow != this) + Close(); + + } + + public static float iconSize => 16; + public static float iconSpacing => 6; + public static float cellSize => iconSize + iconSpacing; + public static float rowSpacing = 1; + + public static float rowHeight => cellSize + rowSpacing; + + public static float paddingLeft => 3; + public static float paddingRight => 3; + + public string hoveredIconName; + + static string searchString = ""; + static float scrollPos; + + + + + + void LoadAllIcons() + { + if (allIconNames != null) return; + + + (float h, float s, float v, float a) GetAverageColor(Texture2D texture) + { + + var readableTexture = new Texture2D(texture.width, texture.height, texture.format, texture.mipmapCount > 1); + + Graphics.CopyTexture(texture, readableTexture); + + var pixels = readableTexture.GetPixels32(); + + readableTexture.DestroyImmediate(); + + + float hSum = 0; + float sSum = 0; + float vSum = 0; + + int nonTransparentPxCount = pixels.Length; + int coloredPxCount = pixels.Length; + + for (var i = 0; i < pixels.Length; i++) + { + if (pixels[i].a <= .1f) { nonTransparentPxCount--; coloredPxCount--; continue; } + + Color.RGBToHSV(pixels[i], out float h, out float s, out float v); + + if (s > .1f) + hSum += h; + else + coloredPxCount--; + + sSum += s; + + vSum += v; + + } + + var hAvg = hSum / coloredPxCount; + var sAvg = sSum / nonTransparentPxCount; + var vAvg = vSum / nonTransparentPxCount; + var aAvg = nonTransparentPxCount / pixels.Length.ToFloat(); + + if (coloredPxCount == 0) + hAvg = -1; + + + return (hAvg, sAvg, vAvg, aAvg); + + } + + + + var editorAssetBundle = typeof(EditorGUIUtility).InvokeMethod("GetEditorAssetBundle"); + + allIconNames = ( + + from path in editorAssetBundle.GetAllAssetNames() + + + let icon = editorAssetBundle.LoadAsset(path) + + where icon + + + where path.StartsWith("icons/") + where !path.Contains("avatarinspector") + + where !icon.name.ToLower().StartsWith("d_") + + where !icon.name.ToLower().EndsWith(".small") + where !icon.name.ToLower().EndsWith("_sml") + + where !icon.name.Contains("@") + where !icon.name.Contains("TreeEditor") + where !icon.name.Contains("scene-template") + where !icon.name.Contains("StateMachineEditor.Background") + where !icon.name.Contains("SpeedTree") + where !icon.name.Contains("TextMesh") + where !icon.name.Contains("Profiler.Instrumentation") + where !icon.name.Contains("Profiler.Record") + where !icon.name.Contains("SocialNetworks") + where !icon.name.Contains("Groove") + + + + let avgColor = GetAverageColor(icon) + + where avgColor.a > .1f + + + + orderby avgColor.h * -3f + + avgColor.s * .09f + + avgColor.v * 0f, icon.name + + + + select icon.name + + ).ToHashSet() + .ToList(); + + } + + static List allIconNames; + + + + void FilterIconsBySearch() + { + filteredIcons = ( + + from iconName in allIconNames + + where iconName.ToLower().Contains(searchString.ToLower()) + + orderby iconName.ToLower().IndexOf(searchString.ToLower(), System.StringComparison.Ordinal) + + select iconName + + ).ToList(); + } + + static List filteredIcons; + + + + void GenerateRows() + { + + var iconsPerRow = ((position.width - paddingLeft - paddingRight) / cellSize).FloorToInt(); + + rows = new(); + + var curRow = new List(); + + foreach (var icon in filteredIcons) + { + curRow.Add(icon); + + if (curRow.Count == iconsPerRow) + { + rows.Add(curRow); + curRow = new(); + } + } + + if (curRow.Any()) + rows.Add(curRow); + + } + + static List> rows; + + + + + + + public void Init() + { + LoadAllIcons(); + FilterIconsBySearch(); + GenerateRows(); + + searchField = new(); + + } + + SearchField searchField; + + + + + + public VFoldersPalette palette; + public VFoldersPaletteEditor paletteEditor; + + } + + class AdjustColorsWindow : EditorWindow + { + void OnGUI() + { + void header() + { + var headerRect = Rect.zero.SetHeight(20).SetWidth(position.width); + var closeButtonRect = headerRect.SetWidthFromRight(16).SetHeightFromMid(16).Move(-3, -.5f); + + void background() + { + headerRect.Draw(EditorGUIUtility.isProSkin ? Greyscale(.18f) : Greyscale(.7f)); + } + void title() + { + SetGUIColor(Greyscale(.8f)); + SetLabelAlignmentCenter(); + + GUI.Label(headerRect.MoveY(-1), "Adjust colors"); + + ResetLabelStyle(); + ResetGUIColor(); + + } + void closeButton() + { + var colorNormal = isDarkTheme ? Greyscale(.55f) : Greyscale(.35f); + var colorHovered = isDarkTheme ? Greyscale(.9f) : colorNormal; + + var iconSize = 14; + + if (IconButton(closeButtonRect, "CrossIcon", iconSize, colorNormal, colorHovered)) + Close(); + + } + void escHint() + { + if (!closeButtonRect.IsHovered()) return; + + var textRect = headerRect.SetWidthFromRight(42).MoveY(-.5f).MoveX(1); + var fontSize = 11; + var color = Greyscale(.65f); + + + SetLabelFontSize(fontSize); + SetGUIColor(color); + + GUI.Label(textRect, "Esc"); + + ResetGUIColor(); + ResetLabelStyle(); + + } + void outline() + { + if (Application.platform == RuntimePlatform.OSXEditor) return; + + position.SetPos(0, 0).DrawOutline(Greyscale(.1f)); + + } + + background(); + title(); + closeButton(); + escHint(); + outline(); + + Space(headerRect.height); + + } + void body() + { + EditorGUIUtility.labelWidth = 85; + EditorGUIUtility.keyboardControl = 0; + + palette.RecordUndo(); + + + EditorGUI.BeginChangeCheck(); + + palette.colorBrightness = (EditorGUILayout.Slider("Brightness", palette.colorBrightness, 0, 2) / .1f).RoundToInt() * .1f; + palette.colorSaturation = (EditorGUILayout.Slider("Saturation", palette.colorSaturation, 0, 2) / .1f).RoundToInt() * .1f; + + if (VFoldersMenu.backgroundColorsEnabled) + palette.colorGradientsEnabled = EditorGUILayout.Toggle("Gradients", palette.colorGradientsEnabled); + + if (EditorGUI.EndChangeCheck()) + { + paletteEditor.Repaint(); + EditorApplication.RepaintProjectWindow(); + + palette.Dirty(); + + VFolders.folderInfoCache.Clear(); + + } + + + EditorGUIUtility.labelWidth = 0; + + } + void closeOnEsc() + { + if (!curEvent.isKeyDown) return; + if (curEvent.keyCode != KeyCode.Escape) return; + + Close(); + + } + + + header(); + + Space(7); + BeginIndent(10); + + body(); + + EndIndent(5); + + + + closeOnEsc(); + + if (EditorWindow.focusedWindow != this) + Close(); + + Repaint(); // for undo + + } + + public VFoldersPalette palette; + public VFoldersPaletteEditor paletteEditor; + + } + +} +#endif \ No newline at end of file diff --git a/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersPaletteEditor.cs.meta b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersPaletteEditor.cs.meta new file mode 100644 index 0000000..2a387df --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersPaletteEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 085d323b143d5403fb4beda728e65139 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersPaletteWindow.cs b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersPaletteWindow.cs new file mode 100644 index 0000000..5247f2d --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersPaletteWindow.cs @@ -0,0 +1,783 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.Linq; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using Type = System.Type; +using static VFolders.VFolders; +using static VFolders.VFoldersData; +using static VFolders.VFoldersPalette; +using static VFolders.VFoldersCache; +using static VFolders.Libs.VUtils; +using static VFolders.Libs.VGUI; +// using static VTools.VDebug; + + +namespace VFolders +{ + public class VFoldersPaletteWindow : EditorWindow + { + + void OnGUI() + { + if (!palette) { Close(); return; } + + int hoveredColorIndex = -1; + string hoveredIconNameOrGuid = null; + + void background() + { + position.SetPos(0, 0).Draw(windowBackground); + } + void outline() + { + if (Application.platform == RuntimePlatform.OSXEditor) return; + + position.SetPos(0, 0).DrawOutline(Greyscale(.1f)); + + } + void colors() + { + if (!palette.colorsEnabled) return; + + var rowRect = this.position.SetPos(paddingX, paddingY).SetHeight(cellSize); + + + void color(int i) + { + var cellRect = rowRect.MoveX(i * cellSize).SetWidth(cellSize).SetHeightFromMid(cellSize); + + void backgroundSelected() + { + if (!colorIndexes_initial.Contains(i)) return; + + cellRect.Resize(1).DrawRounded(selectedBackground, 2); + + } + void backgroundHovered() + { + if (!cellRect.IsHovered()) return; + + cellRect.Resize(1).DrawRounded(this.hoveredBackground, 2); + + } + void crossIcon() + { + if (i != 0) return; + + GUI.DrawTexture(cellRect.SetSizeFromMid(iconSize), EditorIcons.GetIcon("CrossIcon")); + + } + void colorOutline() + { + if (i == 0) return; + + var outlineColor = i <= VFoldersPalette.greyColorsCount ? Greyscale(.0f, .4f) : Greyscale(.15f, .2f); + + cellRect.Resize(3).DrawRounded(outlineColor, 4); + + } + void color() + { + if (i == 0) return; + + var brightness = palette.colorBrightness; + var saturation = palette.colorSaturation; + var drawGradients = palette.colorGradientsEnabled; + + if (!palette.colorGradientsEnabled) + brightness *= isDarkTheme ? .75f : .97f; + + if (i <= VFoldersPalette.greyColorsCount) + { + saturation = brightness = 1; + drawGradients = false; + } + + + var colorRaw = palette ? palette.colors[i - 1] : VFoldersPalette.GetDefaultColor(i - 1); + + var color = MathUtil.Lerp(Greyscale(.2f), colorRaw, brightness); + + Color.RGBToHSV(color, out float h, out float s, out float v); + color = Color.HSVToRGB(h, s * saturation, v); + + color = MathUtil.Lerp(color, colorRaw, .5f).SetAlpha(1); + + + + + cellRect.Resize(4).DrawRounded(color, 3); + + if (drawGradients) + cellRect.Resize(4).AddWidthFromRight(-2).DrawCurtainLeft(GUIColors.windowBackground.SetAlpha(.45f)); + + } + void recursiveIndicator() + { + if (!curEvent.isRepaint) return; + + + var isRecursive = folderDatas.First().colorIndex == i && folderDatas.First().isColorRecursive; + + if (!isRecursive) return; + + + + var iconRect = cellRect.SetSizeFromMid(16).Move(-6, -7); + var shadowRect = iconRect.Resize(3).Move(2, 1).AddWidthFromRight(3); + var shadowRadius = 4; + + shadowRect.DrawBlurred(GUIColors.windowBackground, shadowRadius); + + + SetGUIColor(Color.white * 2); + + GUI.DrawTexture(iconRect, EditorIcons.GetIcon("UnityEditor.SceneHierarchyWindow@2x")); + + ResetGUIColor(); + + + } + + void setHovered() + { + if (!cellRect.IsHovered()) return; + + hoveredColorIndex = i; + + } + void closeOnClick() + { + if (!cellRect.IsHovered()) return; + if (!curEvent.isMouseUp) return; + + curEvent.Use(); + + Close(); + + } + + + + cellRect.MarkInteractive(); + + backgroundSelected(); + backgroundHovered(); + crossIcon(); + colorOutline(); + color(); + recursiveIndicator(); + + setHovered(); + closeOnClick(); + + } + + + for (int i = 0; i < palette.colors.Count + 1; i++) + color(i); + + } + void icons() + { + void row(int i, IconRow iconRow) + { + var rowRect = this.position.SetPos(paddingX, paddingY).SetHeight(cellSize).MoveY(palette.colorsEnabled ? cellSize + spaceAfterColors : 0).MoveY(i * (cellSize + rowSpacing)); + + var isFirstEnabledRow = palette.iconRows.First(r => r.enabled) == iconRow; + + + void icon(int i) + { + var cellRect = rowRect.MoveX(i * cellSize).SetWidth(cellSize).SetHeightFromMid(cellSize); + + var isCrossIcon = isFirstEnabledRow && i == 0; + var actualIconIndex = isFirstEnabledRow ? i - 1 : i; + var isCustomIcon = !isCrossIcon && actualIconIndex >= iconRow.builtinIcons.Count; + var iconNameOrGuid = isCrossIcon ? "" : isCustomIcon ? iconRow.customIcons[actualIconIndex - iconRow.builtinIcons.Count] : iconRow.builtinIcons[actualIconIndex]; + + + void backgroundSelected() + { + if (!iconNamesOrGuids_initial.Contains(iconNameOrGuid)) return; + + cellRect.Resize(1).DrawRounded(selectedBackground, 2); + + } + void backgroundHovered() + { + if (!cellRect.IsHovered()) return; + + cellRect.Resize(1).DrawRounded(this.hoveredBackground, 2); + + } + void crossIcon() + { + if (!isCrossIcon) return; + + GUI.DrawTexture(cellRect.SetSizeFromMid(iconSize), EditorIcons.GetIcon("CrossIcon")); + + } + void normalIcon() + { + if (isCrossIcon) return; + + var iconNameOrPath = iconNameOrGuid?.Length == 32 ? iconNameOrGuid.ToPath() : iconNameOrGuid; + var icon = EditorIcons.GetIcon(iconNameOrPath) ?? Texture2D.blackTexture; + + var iconRect = cellRect.SetSizeFromMid(iconSize); + + if (icon.width < icon.height) iconRect = iconRect.SetWidthFromMid(iconRect.height * icon.width / icon.height); + if (icon.height < icon.width) iconRect = iconRect.SetHeightFromMid(iconRect.width * icon.height / icon.width); + + + GUI.DrawTexture(iconRect, icon); + + } + void recursiveIndicator() + { + if (!curEvent.isRepaint) return; + + + var isRecursive = folderDatas.First().iconNameOrGuid == iconNameOrGuid && folderDatas.First().isIconRecursive; + + if (!isRecursive) return; + + + + var iconRect = cellRect.SetSizeFromMid(16).Move(-6, -7); + var shadowRect = iconRect.Resize(3).Move(2, 1).AddWidthFromRight(3); + var shadowRadius = 4; + + shadowRect.DrawBlurred(GUIColors.windowBackground, shadowRadius); + + + + SetGUIColor(Color.white * 2); + + GUI.DrawTexture(iconRect, EditorIcons.GetIcon("UnityEditor.SceneHierarchyWindow@2x")); + + ResetGUIColor(); + + + } + + void setHovered() + { + if (!cellRect.IsHovered()) return; + + hoveredIconNameOrGuid = iconNameOrGuid; + + } + void closeOnClick() + { + if (!cellRect.IsHovered()) return; + if (!curEvent.isMouseUp) return; + + curEvent.Use(); + + Close(); + + } + + + + cellRect.MarkInteractive(); + + backgroundSelected(); + backgroundHovered(); + crossIcon(); + normalIcon(); + recursiveIndicator(); + + setHovered(); + closeOnClick(); + + } + + + for (int j = 0; j < iconRow.iconCount + (isFirstEnabledRow ? 1 : 0); j++) + icon(j); + + } + + + var i = 0; + + foreach (var iconRow in palette.iconRows) + { + if (!iconRow.enabled) continue; + if (iconRow.isEmpty) continue; + + row(i, iconRow); + + i++; + } + + } + void editPaletteButton() + { + var buttonRect = position.SetPos(0, 0).SetWidthFromRight(16).SetHeightFromBottom(16).Move(-14, -14); + var buttonColor = isDarkTheme ? Greyscale(.6f) : Greyscale(1, .6f); + + if (!IconButton(buttonRect, "Toolbar Plus", 16, buttonColor)) return; + + + palette.SelectInInspector(frameInProject: false); + + this.Close(); + + } + + void setColorsAndIcons() + { + if (!curEvent.isLayout) return; + + + if (palette.iconRows.Any(r => r.enabled)) + if (hoveredIconNameOrGuid != null) + SetIcon(hoveredIconNameOrGuid, isRecursive: curEvent.holdingAlt); + else + SetInitialIcons(); + + + if (palette.colorsEnabled) + if (hoveredColorIndex != -1) + SetColor(hoveredColorIndex, isRecursive: curEvent.holdingAlt); + else + SetInitialColors(); + + } + void updatePosition() + { + if (!curEvent.isLayout) return; + + void calcDeltaTime() + { + deltaTime = (float)(EditorApplication.timeSinceStartup - lastLayoutTime); + + if (deltaTime > .05f) + deltaTime = .0166f; + + lastLayoutTime = EditorApplication.timeSinceStartup; + + } + void resetCurPos() + { + if (currentPosition != default) return; + + currentPosition = position.position; // position.position is always int, which can't be used for lerping + + } + void lerpCurPos() + { + var speed = 9; + + MathUtil.SmoothDamp(ref currentPosition, targetPosition, speed, ref positionDeriv, deltaTime); + // MathfUtils.Lerp(ref currentPosition, targetPosition, speed, deltaTime); + + } + void setCurPos() + { + position = position.SetPos(currentPosition); + } + + calcDeltaTime(); + resetCurPos(); + lerpCurPos(); + setCurPos(); + + if (!currentPosition.magnitude.Approx(targetPosition.magnitude)) + Repaint(); + + } + void closeOnEscape() + { + if (!curEvent.isKeyDown) return; + if (curEvent.keyCode != KeyCode.Escape) return; + + SetInitialColors(); + SetInitialIcons(); + + Close(); + + } + + + RecordUndoOnDatas(); + + background(); + outline(); + colors(); + icons(); + editPaletteButton(); + + setColorsAndIcons(); + updatePosition(); + closeOnEscape(); + + + + VFolders.folderInfoCache.Clear(); + + EditorApplication.RepaintProjectWindow(); + + EditorApplication.delayCall += EditorApplication.RepaintProjectWindow; // to show icons that will be generated in update + + } + + static float iconSize => 14; + static float iconSpacing => 6; + static float cellSize => iconSize + iconSpacing; + static float spaceAfterColors => 13; + public float rowSpacing = 1; + static float paddingX => 12; + static float paddingY => 12; + + Color windowBackground => isDarkTheme ? Greyscale(.23f) : Greyscale(.75f); + Color selectedBackground => isDarkTheme ? new Color(.3f, .5f, .7f, .8f) : new Color(.3f, .5f, .7f, .6f) * 1.25f; + Color hoveredBackground => isDarkTheme ? Greyscale(1, .3f) : Greyscale(0, .1f); + + public Vector2 targetPosition; + public Vector2 currentPosition; + Vector2 positionDeriv; + float deltaTime; + double lastLayoutTime; + + + + + + + void SetIcon(string iconNameOrGuid, bool isRecursive) + { + foreach (var r in folderDatas) + { + r.isIconRecursive = isRecursive; // setting it firstbecause iconNameOrGuid setter relies on isIconRecursive + r.iconNameOrGuid = iconNameOrGuid; + } + } + void SetColor(int colorIndex, bool isRecursive) + { + foreach (var r in folderDatas) + { + r.isColorRecursive = isRecursive; + r.colorIndex = colorIndex; + } + } + + void SetInitialIcons() + { + for (int i = 0; i < folderDatas.Count; i++) + { + folderDatas[i].isIconRecursive = isIconRecursives_initial[i]; + folderDatas[i].iconNameOrGuid = iconNamesOrGuids_initial[i]; + } + } + void SetInitialColors() + { + for (int i = 0; i < folderDatas.Count; i++) + { + folderDatas[i].isColorRecursive = isColorRecursives_initial[i]; + folderDatas[i].colorIndex = colorIndexes_initial[i]; + } + } + + void RemoveEmptyFolderDatas() + { + if (VFoldersData.storeDataInMetaFiles) return; // empties removed from meta files in SaveData() + + var toRemove = folderDatas.Select(r => r.folderData).Where(r => r.iconNameOrGuid == "" && r.colorIndex == 0); + + foreach (var r in toRemove) + data.folderDatas_byGuid.RemoveValue(r); + + if (toRemove.Any()) + Undo.CollapseUndoOperations(Undo.GetCurrentGroup() - 1); + + } + + void RecordUndoOnDatas() + { + if (!VFoldersData.storeDataInMetaFiles) + if (data) + data.RecordUndo(); + + if (VFoldersData.storeDataInMetaFiles) + foreach (var r in guids) + AssetImporter.GetAtPath(r.ToPath()).RecordUndo(); + + } + void MarkDatasDirty() + { + if (!VFoldersData.storeDataInMetaFiles) + if (data) + data.Dirty(); + + if (VFoldersData.storeDataInMetaFiles) + VFolders.folderDatasFromMetaFiles_byGuid.Clear(); + + } + void SaveData() + { + if (!VFoldersData.storeDataInMetaFiles) return; + // if (!VFoldersData.storeDataInMetaFiles) { data.Save(); return; } + + for (int i = 0; i < guids.Count; i++) + if (folderDatas[i].iconNameOrGuid == "" && folderDatas[i].colorIndex == 0) + AssetImporter.GetAtPath(guids[i].ToPath()).userData = ""; + else + AssetImporter.GetAtPath(guids[i].ToPath()).userData = JsonUtility.ToJson(folderDatas[i].folderData); + + for (int i = 0; i < guids.Count; i++) + AssetImporter.GetAtPath(guids[i].ToPath()).SaveAndReimport(); + + } + + + + + + + + + + void OnLostFocus() + { + if (curEvent.holdingAlt && EditorWindow.focusedWindow?.GetType().Name == "ProjectBrowser") + CloseNextFrameIfNotRefocused(); + else + Close(); + + } + + void CloseNextFrameIfNotRefocused() + { + EditorApplication.delayCall += () => { if (EditorWindow.focusedWindow != this) Close(); }; + } + + + + + static void RepaintOnAlt() // Update + { + if (curEvent.holdingAlt != wasHoldingAlt) + if (EditorWindow.mouseOverWindow is VFoldersPaletteWindow paletteWindow) + paletteWindow.Repaint(); + + wasHoldingAlt = curEvent.holdingAlt; + + } + + static bool wasHoldingAlt; + + + + + + + + + + public void Init(List guids) + { + void createData() + { + if (VFolders.data) return; + + VFolders.data = ScriptableObject.CreateInstance(); + + AssetDatabase.CreateAsset(VFolders.data, GetScriptPath("VFolders").GetParentPath().CombinePath("vFolders Data.asset")); + + } + void createPalette() + { + if (VFolders.palette) return; + + VFolders.palette = ScriptableObject.CreateInstance(); + + AssetDatabase.CreateAsset(VFolders.palette, GetScriptPath("VFolders").GetParentPath().CombinePath("vFolders Palette.asset")); + + } + void setSize() + { + if (!palette.colorsEnabled && !palette.iconRows.Any(r => r.enabled && !r.isEmpty)) // somehow happened on first palette window opening in 2022.3.50 + palette.InvokeMethod("Reset"); + + + + var rowCellCounts = new List(); + + if (palette.colorsEnabled) + rowCellCounts.Add(palette.colors.Count + 1); + + foreach (var r in palette.iconRows.Where(r => r.enabled && !r.isEmpty)) + rowCellCounts.Add(r.iconCount + (r == palette.iconRows.First(r => r.enabled) ? 1 : 0)); + + var width = paddingX + + rowCellCounts.Max() * cellSize + + (rowCellCounts.Last() == rowCellCounts.Max() ? cellSize : 0) + + paddingX; + + + + var iconRowCount = palette.iconRows.Count(r => r.enabled && !r.isEmpty); + + var height = paddingY + + (palette.colorsEnabled ? cellSize : 0) + + (palette.colorsEnabled && palette.iconRows.Any(r => r.enabled && !r.isEmpty) ? spaceAfterColors : 0) + + cellSize * iconRowCount + + rowSpacing * (iconRowCount - 1) + + paddingY; + + + position = position.SetSize(width, height).SetPos(targetPosition); + + } + void getFolderDatas() + { + folderDatas.Clear(); + + foreach (var guid in guids) + folderDatas.Add(new FolderDataWrapper(guid)); + + } + void getInitialState() + { + iconNamesOrGuids_initial = folderDatas.Select(r => r.iconNameOrGuid).ToList(); + colorIndexes_initial = folderDatas.Select(r => r.colorIndex).ToList(); + + isIconRecursives_initial = folderDatas.Select(r => r.isIconRecursive).ToList(); + isColorRecursives_initial = folderDatas.Select(r => r.isColorRecursive).ToList(); + + } + + + this.guids = guids; + + RecordUndoOnDatas(); + + createData(); + createPalette(); + setSize(); + getFolderDatas(); + getInitialState(); + + Undo.undoRedoPerformed -= EditorApplication.RepaintProjectWindow; + Undo.undoRedoPerformed += EditorApplication.RepaintProjectWindow; + + EditorApplication.update -= RepaintOnAlt; + EditorApplication.update += RepaintOnAlt; + + } + + void OnDestroy() + { + RemoveEmptyFolderDatas(); + MarkDatasDirty(); + SaveData(); + + EditorApplication.update -= RepaintOnAlt; + + } + + public List guids = new(); + public List folderDatas = new(); + + public List iconNamesOrGuids_initial = new(); + public List colorIndexes_initial = new(); + + public List isIconRecursives_initial = new(); + public List isColorRecursives_initial = new(); + + static VFoldersPalette palette => VFolders.palette; + static VFoldersData data => VFolders.data; + + + + + + + + public static void CreateInstance(Vector2 position) + { + instance = ScriptableObject.CreateInstance(); + + instance.ShowPopup(); + + instance.position = instance.position.SetPos(position).SetSize(200, 300); + instance.targetPosition = position; + + } + + public static VFoldersPaletteWindow instance; + + + + + + + public class FolderDataWrapper + { + public string iconNameOrGuid + { + get + { + if (folderData != null && folderData.iconNameOrGuid != "") + if (folderData.iconNameOrGuid == "none") return ""; + else return folderData.iconNameOrGuid ?? ""; + + else if (VFoldersMenu.autoIconsEnabled && folderState.autoIconName != "") + return folderState.autoIconName; + + else return ""; + + } + set + { + if (VFoldersMenu.autoIconsEnabled && folderState.autoIconName != "") + if (value == folderState.autoIconName && !folderData.isIconRecursive) + folderData.iconNameOrGuid = ""; + else if (value == "") + folderData.iconNameOrGuid = "none"; + else + folderData.iconNameOrGuid = value; + + + else folderData.iconNameOrGuid = value; + + } + + } + public bool isIconRecursive { get => folderData.isIconRecursive; set => folderData.isIconRecursive = value; } + + public int colorIndex { get => folderData.colorIndex; set => folderData.colorIndex = value; } + public bool isColorRecursive { get => folderData.isColorRecursive; set => folderData.isColorRecursive = value; } + + + public FolderDataWrapper(string guid) + { + folderData = VFolders.GetFolderData(guid, createDataIfDoesntExist: true); + folderState = VFolders.GetFolderState(guid); + } + + public FolderData folderData; + public FolderState folderState; + + + // used as an interlayer between folderData and palette window to account for automatic icons + // it's the only structural difference between vFolders' PaletteWindow and vHierarchy's + + } + + + } +} +#endif \ No newline at end of file diff --git a/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersPaletteWindow.cs.meta b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersPaletteWindow.cs.meta new file mode 100644 index 0000000..6f8ecd4 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vFolders/VFoldersPaletteWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 68011e63381a84f3bacee6e5ee9e0b82 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy.meta b/Assets/OtherAssets/DEV/PAID/vHierarchy.meta new file mode 100644 index 0000000..0d51a57 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dd2b9e231b0ec4a9f97e258072ae2b33 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/Read me.pdf b/Assets/OtherAssets/DEV/PAID/vHierarchy/Read me.pdf new file mode 100644 index 0000000000000000000000000000000000000000..754aee7351aaa2e082a723fcf6e5dd73dab62b01 GIT binary patch literal 116397 zcmaI7Wmr`0`vppOcc(BklETnkGBgMxjdVKF-AI=pFmxjg3X;+xNOy+}NOyC#@AW&^ z|9m>%fZ5ERd7k~Od);fTn?+Mzk(-Z45Q}B%@7@L$AHz!q7YlnVadBQZ7H#I|G}JgPl1q@Qw^l zd;$UtN-i!oz+1?>Sbkv8wsy9%cDHtCmz2Z;e*AY2f&aUgr8xs17O$*2124qI-O1eX z|1J{!zl$IY0$99?c8*ZsPP~ea=1^;SYfBd^Yb@T^*3LFiTLyknVM$4b4^Ve&b0;jX zZ2!b|)gCjF2zmz@l0KP8JUmJow|H^y(n^-BfSW9D-cOW>&RKkNIfJgY%) zYc>qL&2^TjyapGl7pUdCCx3js&Xaj}rC&sFP(?!KK8zHHXjRct=p4T;i?lZKk321& zzE(^0H+clTsxG1SSev|M^seTa15t1G>35Y%&LMZ4-1dGDKW+MbWYWUcL0r?Q*eu!o zdF$@>)v11|L~Zx;``M!-1E_v6i|WNR_u~9losA6U*IE%wSqF(A71>k<1;|mqruU}L zdlel`@)^VS-^%RY(#CG@ajFkiq_419dF5&XQ1N#Od~w^YM}-5?t`qXfkLLN_eXvz1 zR&Kz|{r=J*G#r#6`ta@`!!S;&f$Ctg&q6(QS`c$mDtuI}{oiiXVXjs@=TSBpTbv{}^|ND!IlerDB?gn;Nz_JU73S#jpTie;#LK%bvg|T?$TpV58 zbzIFYtr_0(s>sR7nt!miV)(a{+V6ljQ8)kKz##JPH(<-{pdU1?-Q`@ITwR>4ouLdO z|NmPoBp@s#^8elB0~op=$=uA9`dJ4%ơqDL&YIw>PcfilJm#%W)dsP{g{!u=P{ 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 VJG;&Xa?lQpz`}BJ8Z=Ge{{VZ+>H+`& literal 0 HcmV?d00001 diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/Read me.pdf.meta b/Assets/OtherAssets/DEV/PAID/vHierarchy/Read me.pdf.meta new file mode 100644 index 0000000..0227813 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/Read me.pdf.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 520b2a02f70c04615b08281be020f305 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchy.asmdef b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchy.asmdef new file mode 100644 index 0000000..22702c6 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchy.asmdef @@ -0,0 +1,16 @@ +{ + "name": "VHierarchy", + "rootNamespace": "", + "references": [], + "includePlatforms": [ + "Editor" + ], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchy.asmdef.meta b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchy.asmdef.meta new file mode 100644 index 0000000..d859f2d --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchy.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 2c3f48364a5004fd3a152fbdf5fea703 +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchy.cs b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchy.cs new file mode 100644 index 0000000..bfa82f8 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchy.cs @@ -0,0 +1,1808 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using UnityEditor.IMGUI.Controls; +using Type = System.Type; +using static VHierarchy.Libs.VUtils; +using static VHierarchy.Libs.VGUI; +// using static VTools.VDebug; +using static VHierarchy.VHierarchyData; +using static VHierarchy.VHierarchyCache; + + + +namespace VHierarchy +{ + public static class VHierarchy + { + + static void WrappedGUI(EditorWindow window) + { + var navbarHeight = 26; + + void navbarGui() + { + if (!navbars_byWindow.ContainsKey(window)) + navbars_byWindow[window] = new VHierarchyNavbar(window); + + var navbarRect = window.position.SetPos(0, 0).SetHeight(navbarHeight); + + + navbars_byWindow[window].OnGUI(navbarRect); + + } + void defaultGuiWithOffset() + { + var defaultTopBarHeight = 20; + var topOffset = navbarHeight - defaultTopBarHeight; + + var m_Pos_original = window.GetFieldValue("m_Pos"); + + + + + GUI.BeginGroup(m_Pos_original.SetPos(0, 0).AddHeightFromBottom(-topOffset)); + + window.SetFieldValue("m_Pos", m_Pos_original.AddHeightFromBottom(-topOffset)); + + + try + { + if (curEvent.isMouseDown && m_Pos_original.IsHovered()) + t_SceneHierarchyWindow.SetMemberValue("s_LastInteractedHierarchy", window); + + window.InvokeMethod("DoSceneHierarchy"); + window.InvokeMethod("ExecuteCommands"); + + // same as SceneHierarchyWindow.OnGUI() but without DoToolbarLayout(): + + } + catch (System.Exception exception) + { + if (exception.InnerException is ExitGUIException) + throw exception.InnerException; + else + throw exception; + + // GUIUtility.ExitGUI() works by throwing ExitGUIException, which just exits imgui loop and doesn't appear in console + // but if ExitGUI is called from a reflected method (DoSceneHierarchy in this case), the exception becomes TargetInvokationException + // which gets logged to console (only if debugger is attached, for some reason) + // so here in such cases we rethrow the original ExitGUIException + + } + + + window.SetFieldValue("m_Pos", m_Pos_original); + + GUI.EndGroup(); + + } + void shadow() + { + if (!curEvent.isRepaint) return; + + var shadowLength = 30; + var shadowPos = 21; + var shadowGreyscale = isDarkTheme ? .1f : .28f; + var shadowAlpha = isDarkTheme ? .35f : .15f; + + var minScrollPos = 10; + var maxScrollPos = 20; + + if (StageUtility.GetCurrentStage() is PrefabStage) + shadowPos += 30; + else if (EditorSceneManager.loadedRootSceneCount > 1) + shadowPos += 16; + + + + var scrollPos = window.GetMemberValue("m_SceneHierarchy").GetMemberValue("m_TreeViewState").scrollPos.y; + + if (scrollPos <= minScrollPos) return; + + var opacity = ((scrollPos - minScrollPos) / (maxScrollPos - minScrollPos)).Clamp01(); + + + var rectWidth = window.position.width;// - 12; + + var rect = window.position.SetPos(0, 0).MoveY(shadowPos).SetHeight(shadowLength).SetWidth(rectWidth); + + + + var clipAtY = navbarHeight + 1; + + if (EditorSceneManager.loadedRootSceneCount > 1) + clipAtY += 16; + + + GUI.BeginClip(window.position.SetPos(0, clipAtY)); + + rect.MoveY(-clipAtY).DrawCurtainDown(Greyscale(shadowGreyscale, shadowAlpha * opacity)); + + GUI.EndClip(); + + } + + + + var doNavbarFirst = navbars_byWindow.ContainsKey(window) && navbars_byWindow[window].isSearchActive; + + if (doNavbarFirst) + navbarGui(); + + defaultGuiWithOffset(); + shadow(); + + if (!doNavbarFirst) + navbarGui(); + + } + + static Dictionary navbars_byWindow = new(); + + + + static void UpdateGUIWrapping(EditorWindow window) + { + if (!window.hasFocus) return; + + var curOnGUIMethod = window.GetMemberValue("m_Parent").GetMemberValue("m_OnGUI").Method; + + var isWrapped = curOnGUIMethod == mi_WrappedGUI; + var shouldBeWrapped = VHierarchyMenu.navigationBarEnabled; + + void wrap() + { + var hostView = window.GetMemberValue("m_Parent"); + + var newDelegate = typeof(VHierarchy).GetMethod(nameof(WrappedGUI), maxBindingFlags).CreateDelegate(t_EditorWindowDelegate, window); + + hostView.SetMemberValue("m_OnGUI", newDelegate); + + window.Repaint(); + + } + void unwrap() + { + var hostView = window.GetMemberValue("m_Parent"); + + var originalDelegate = hostView.InvokeMethod("CreateDelegate", "OnGUI"); + + hostView.SetMemberValue("m_OnGUI", originalDelegate); + + window.Repaint(); + + } + + + if (shouldBeWrapped && !isWrapped) + wrap(); + + if (!shouldBeWrapped && isWrapped) + unwrap(); + + } + static void UpdateGUIWrappingForAllHierarchies() => allHierarchies.ForEach(r => UpdateGUIWrapping(r)); + + static void OnDomainReloaded() => toCallInGUI += UpdateGUIWrappingForAllHierarchies; + + static void OnWindowUnmaximized() => UpdateGUIWrappingForAllHierarchies(); + + static void OnHierarchyFocused() => UpdateGUIWrapping(EditorWindow.focusedWindow); + + static void OnDelayCall() => UpdateGUIWrappingForAllHierarchies(); + + + + + + static void CheckIfFocusedWindowChanged() + { + if (prevFocusedWindow != EditorWindow.focusedWindow) + if (EditorWindow.focusedWindow?.GetType() == t_SceneHierarchyWindow) + OnHierarchyFocused(); + + prevFocusedWindow = EditorWindow.focusedWindow; + + } + + static EditorWindow prevFocusedWindow; + + + + static void CheckIfWindowWasUnmaximized() + { + var isMaximized = EditorWindow.focusedWindow?.maximized == true; + + if (!isMaximized && wasMaximized) + OnWindowUnmaximized(); + + wasMaximized = isMaximized; + + } + + static bool wasMaximized; + + + + static void OnSomeGUI() + { + toCallInGUI?.Invoke(); + toCallInGUI = null; + + CheckIfFocusedWindowChanged(); + + } + + static void ProjectWindowItemOnGUI(string _, Rect __) => OnSomeGUI(); + static void HierarchyWindowItemOnGUI(int _, Rect __) => OnSomeGUI(); + + static System.Action toCallInGUI; + + + + static void DelayCallLoop() + { + OnDelayCall(); + + EditorApplication.delayCall -= DelayCallLoop; + EditorApplication.delayCall += DelayCallLoop; + + } + + + + + + + + + + + + + + + static void RowGUI(int instanceId, Rect rowRect) + { + EditorWindow window; + + void findWindow() + { + if (allHierarchies.Count() == 1) { window = allHierarchies.First(); return; } + + + var pointInsideWindow = EditorGUIUtility.GUIToScreenPoint(rowRect.center); + + window = allHierarchies.FirstOrDefault(r => r.position.AddHeight(30).Contains(pointInsideWindow) && r.hasFocus); + + } + void updateWindow() + { + if (!window) return; // happens on half-visible rows during expand animation + + if (curEvent.isLayout && !lastEventWasLayout) + UpdateWindow(window); + + lastEventWasLayout = curEvent.isLayout; + + } + void catchScrollInputForController() + { + if (!window) return; + if (!controllers_byWindow.ContainsKey(window)) return; + + if (curEvent.isScroll) + controllers_byWindow[window].animatingScroll = false; + + } + void callGUI() + { + if (!window) return; + if (!guis_byWindow.ContainsKey(window)) return; + + + + var gui = guis_byWindow[window]; + + if (EditorUtility.InstanceIDToObject(instanceId) is GameObject go) + gui.RowGUI_GameObject(rowRect, go); + else + for (int i = 0; i < EditorSceneManager.sceneCount; i++) + if (EditorSceneManager.GetSceneAt(i).GetHashCode() == instanceId) + gui.RowGUI_Scene(rowRect, EditorSceneManager.GetSceneAt(i)); + + } + + findWindow(); + updateWindow(); + catchScrollInputForController(); + callGUI(); + + } + + static bool lastEventWasLayout; + + + + static void UpdateWindow(EditorWindow window) + { + if (!guis_byWindow.TryGetValue(window, out var gui)) + gui = guis_byWindow[window] = new(window); + + if (!controllers_byWindow.TryGetValue(window, out var controller)) + controller = controllers_byWindow[window] = new(window); + + + gui.UpdateState(); + + controller.UpdateState(); + controller.UpdateExpandQueue(); + controller.UpdateScrollAnimation(); + controller.UpdateHighlightAnimation(); + + } + + public static Dictionary guis_byWindow = new(); + public static Dictionary controllers_byWindow = new(); + + + + + + + + + + + + + + + public static Texture GetComponentIcon(Component component) + { + if (!component) return null; + + if (!componentIcons_byType.ContainsKey(component.GetType())) + componentIcons_byType[component.GetType()] = EditorGUIUtility.ObjectContent(component, component.GetType()).image; + + return componentIcons_byType[component.GetType()]; + + } + + static Dictionary componentIcons_byType = new(); + + + + static Texture2D GetIcon_forVTabs(GameObject gameObject) + { + var goData = GetGameObjectData(gameObject, false); + + if (goData == null) return null; + + var iconNameOrPath = goData.iconNameOrGuid.Length == 32 ? goData.iconNameOrGuid.ToPath() : goData.iconNameOrGuid; + + if (!iconNameOrPath.IsNullOrEmpty()) + return EditorIcons.GetIcon(iconNameOrPath); + + return null; + + } + + static string GetIconName_forVFavorites(GameObject gameObject) + { + var goData = GetGameObjectData(gameObject, false); + + if (goData == null) return ""; + + var iconNameOrPath = goData.iconNameOrGuid.Length == 32 ? goData.iconNameOrGuid.ToPath() : goData.iconNameOrGuid; + + return iconNameOrPath; + + } + static string GetIconName_forVInspector(GameObject gameObject) + { + return GetIconName_forVFavorites(gameObject); + } + + public static void SetIcon(GameObject gameObject, string iconName, bool recursive = false) + { + goDataCache.Clear(); + + var goData = GetGameObjectData(gameObject, createDataIfDoesntExist: true); + + goData.iconNameOrGuid = iconName ?? ""; + goData.isIconRecursive = recursive; + + + goInfoCache.Clear(); + + EditorApplication.RepaintHierarchyWindow(); + + } + public static void SetColor(GameObject gameObject, int colorIndex, bool recursive = false) + { + goDataCache.Clear(); + + var goData = GetGameObjectData(gameObject, createDataIfDoesntExist: true); + + goData.colorIndex = colorIndex; + goData.isColorRecursive = recursive; + + + goInfoCache.Clear(); + + EditorApplication.RepaintHierarchyWindow(); + + } + + + + + + + + + + + + + + + static void Shortcuts() + { + if (!curEvent.isKeyDown) return; + if (curEvent.keyCode == KeyCode.None) return; + if (EditorWindow.mouseOverWindow is not EditorWindow hoveredWindow) return; + if (hoveredWindow?.GetType() != t_SceneHierarchyWindow) return; + + + void toggleExpanded() + { + if (!curEvent.isKeyDown) return; + if (curEvent.keyCode != KeyCode.E) return; + if (curEvent.holdingAnyModifierKey) return; + if (!VHierarchyMenu.toggleExpandedEnabled) return; + + if (Tools.viewTool == ViewTool.FPS) return; + if (hoveredGo == null && hoveredScene == default) return; + + + curEvent.Use(); + + + if (transformToolNeedsReset = Application.unityVersion.Contains("2022")) + previousTransformTool = Tools.current; + + if (hoveredScene == default) + if (hoveredGo.transform.childCount == 0) return; + + + if (hoveredScene != default) + controllers_byWindow[hoveredWindow].ToggleExpanded(hoveredScene.handle); + else + controllers_byWindow[hoveredWindow].ToggleExpanded(hoveredGo.GetInstanceID()); + + } + void collapseAll() + { + if (curEvent.modifiers != (EventModifiers.Shift | EventModifiers.Command) && curEvent.modifiers != (EventModifiers.Shift | EventModifiers.Control)) return; + if (!curEvent.isKeyDown || curEvent.keyCode != KeyCode.E) return; + if (!VHierarchyMenu.collapseEverythingEnabled) return; + + curEvent.Use(); + + + controllers_byWindow[hoveredWindow].CollapseAll(); + + } + void isolate() + { + if (!curEvent.isKeyDown) return; + if (curEvent.keyCode != KeyCode.E) return; + if (curEvent.modifiers != EventModifiers.Shift) return; + if (!VHierarchyMenu.isolateEnabled) return; + + if (hoveredGo == null && hoveredScene == default) return; + + curEvent.Use(); + + if (hoveredGo && hoveredGo.transform.childCount == 0) return; + if (!hoveredGo && hoveredScene.rootCount == 0) return; + + + if (hoveredScene != default) + controllers_byWindow[hoveredWindow].Isolate(hoveredScene.handle); + else + controllers_byWindow[hoveredWindow].Isolate(hoveredGo.GetInstanceID()); + + } + void toggleActive() + { + if (!hoveredGo) return; + if (curEvent.holdingAnyModifierKey) return; + if (!curEvent.isKeyDown || curEvent.keyCode != KeyCode.A) return; + if (Tools.viewTool == ViewTool.FPS) return; + if (!VHierarchyMenu.toggleActiveEnabled) return; + + curEvent.Use(); + + + var gos = Selection.gameObjects.Contains(hoveredGo) ? Selection.gameObjects : new[] { hoveredGo }; + var active = !gos.Any(r => r.activeSelf); + + foreach (var r in gos) + { + r.RecordUndo(); + r.SetActive(active); + } + + } + void delete() + { + if (!hoveredGo) return; + if (curEvent.holdingAnyModifierKey) return; + if (!curEvent.isKeyDown || curEvent.keyCode != KeyCode.X) return; + if (!VHierarchyMenu.deleteEnabled) return; + + var gos = Selection.gameObjects.Contains(hoveredGo) ? Selection.gameObjects : new[] { hoveredGo }; + + foreach (var r in gos) + Undo.DestroyObjectImmediate(r); + + curEvent.Use(); + } + void focus() + { + if (!curEvent.isKeyDown) return; + if (curEvent.modifiers != EventModifiers.None) return; + if (curEvent.keyCode != KeyCode.F) return; + if (SceneView.sceneViews.Count == 0) return; + if (!hoveredGo) return; + if (!VHierarchyMenu.focusEnabled) return; + + var sv = SceneView.lastActiveSceneView; + + if (!sv || !sv.hasFocus) + sv = SceneView.sceneViews.ToArray().FirstOrDefault(r => (r as SceneView).hasFocus) as SceneView; + + if (!sv) + (sv = SceneView.lastActiveSceneView ?? SceneView.sceneViews[0] as SceneView).Focus(); + + sv.Frame(hoveredGo.GetBounds(), false); + + } + void setDefaultParent() + { + if (!curEvent.isKeyDown) return; + if (curEvent.modifiers != EventModifiers.None) return; + if (curEvent.keyCode != KeyCode.D) return; + if (!hoveredGo) return; + if (!VHierarchyMenu.setDefaultParentEnabled) return; + + + var isDefaultParentHovered = hoveredGo == typeof(SceneView).InvokeMethod("GetDefaultParentObjectIfSet")?.gameObject; + + if (isDefaultParentHovered) + EditorUtility.ClearDefaultParentObject(); + else + EditorUtility.SetDefaultParentObject(hoveredGo); + + + hoveredWindow.Repaint(); + + curEvent.Use(); + + } + void resetDefaultParent() + { + if (!curEvent.isKeyDown) return; + if (curEvent.modifiers != EventModifiers.Shift) return; + if (curEvent.keyCode != KeyCode.D) return; + if (!VHierarchyMenu.setDefaultParentEnabled) return; + + + EditorUtility.ClearDefaultParentObject(); + + + hoveredWindow.Repaint(); + + curEvent.Use(); + + } + + + toggleExpanded(); + toggleActive(); + delete(); + collapseAll(); + isolate(); + focus(); + setDefaultParent(); + resetDefaultParent(); + + } + + public static GameObject hoveredGo; + public static Scene hoveredScene; + + + + + + + + + + + + + + + + public static GameObjectInfo GetGameObjectInfo(GameObject go) + { + if (goInfoCache.TryGetValue(go, out var cachedGoInfo)) return cachedGoInfo; + + + var goInfo = new GameObjectInfo(); + + var goData = goInfo.goData = GetGameObjectData(go, createDataIfDoesntExist: false); + + + var recursiveIconNameOrGuid = ""; + var recursiveColorIndex = 0; + + var ruledIconNameOrGuid = ""; + var ruledColorIndex = 0; + + void checkRules() + { + if (rules == null) + rules = TypeCache.GetMethodsWithAttribute() + .Where(r => r.IsStatic + && r.GetParameters().Count() == 1 + && r.GetParameters().First().ParameterType == typeof(ObjectInfo)).ToList(); + + if (!rules.Any()) return; + + + + var objectInfo = new ObjectInfo(go); + + foreach (var rule in rules) + rule.Invoke(null, new[] { objectInfo }); + + + ruledIconNameOrGuid = objectInfo.icon; + ruledColorIndex = objectInfo.color; + + + } + void checkRecursion(Transform transform, int depth) + { + if (!transform.parent) return; + + var parentGoData = GetGameObjectData(transform.parent.gameObject, createDataIfDoesntExist: false); + + if (parentGoData != null) + { + + if (parentGoData.isIconRecursive && parentGoData.iconNameOrGuid != "") + if (recursiveIconNameOrGuid == "") + recursiveIconNameOrGuid = parentGoData.iconNameOrGuid; + + if (parentGoData.isColorRecursive && parentGoData.colorIndex != 0) + if (recursiveColorIndex == 0) + recursiveColorIndex = parentGoData.colorIndex; + + + if (parentGoData.isColorRecursive && parentGoData.colorIndex != 0) + goInfo.maxColorRecursionDepth = depth + 1; + + } + + + + checkRecursion(transform.parent, depth + 1); + + } + void setIcon() + { + var iconNameOrGuid = ""; + + if (goData != null && goData.iconNameOrGuid != "") + iconNameOrGuid = goData.iconNameOrGuid; + + else if (recursiveIconNameOrGuid != "") + iconNameOrGuid = recursiveIconNameOrGuid; + + else if (ruledIconNameOrGuid != "") + iconNameOrGuid = ruledIconNameOrGuid; + + + + if (iconNameOrGuid == "") { goInfo.hasIcon = false; return; } + + goInfo.hasIcon = true; + goInfo.hasIconByRecursion = recursiveIconNameOrGuid != ""; + + goInfo.iconNameOrPath = iconNameOrGuid.Length == 32 ? iconNameOrGuid.ToPath() + : iconNameOrGuid; + + } + void setColor() + { + var colorIndex = 0; + + if (goData != null && goData.colorIndex > 0) + colorIndex = goData.colorIndex; + + else if (recursiveColorIndex != 0) + colorIndex = recursiveColorIndex; + + else if (ruledColorIndex != 0) + colorIndex = ruledColorIndex; + + + + if (colorIndex == 0) { goInfo.hasColor = false; return; } + + goInfo.hasColor = true; + goInfo.hasColorByRecursion = recursiveColorIndex != 0; + + + + + var brightness = palette?.colorBrightness ?? 1; + var saturation = palette?.colorSaturation ?? 1; + + if (colorIndex <= VHierarchyPalette.greyColorsCount) + saturation = brightness = 1; + + + var rawColor = palette ? palette.colors[colorIndex - 1] : VHierarchyPalette.GetDefaultColor(colorIndex - 1); + + var brightenedColor = MathUtil.Lerp(Greyscale(.2f), rawColor, brightness); + + Color.RGBToHSV(brightenedColor, out float h, out float s, out float v); + var saturatedColor = Color.HSVToRGB(h, s * saturation, v); + + + goInfo.color = saturatedColor; + + goInfo.isGreyColor = colorIndex <= VHierarchyPalette.greyColorsCount; + + } + + checkRules(); + checkRecursion(go.transform, 0); + setIcon(); + setColor(); + + + return goInfoCache[go] = goInfo; + + } + + public static Dictionary goInfoCache = new(); + + public static List rules = null; + + public class GameObjectInfo + { + public string iconNameOrPath = ""; + public bool hasIcon; + public bool hasIconByRecursion; + + public Color color; + public bool hasColor; + public bool hasColorByRecursion; + public int maxColorRecursionDepth; + public bool isGreyColor; + + + public GameObjectData goData; + + } + + + + public static GameObjectData GetGameObjectData(GameObject go, bool createDataIfDoesntExist) + { + if (!data) return null; + if (goDataCache.TryGetValue(go, out var cachedResult)) return cachedResult; + + GameObjectData goData = null; + SceneData sceneData = null; + + void sceneObject() + { + if (StageUtility.GetCurrentStage() is PrefabStage) return; + + + SceneIdMap sceneIdMap = null; + + var currentSceneGuid = go.scene.path.ToGuid(); + var originalSceneGuid = cache.originalSceneGuids_byInstanceId.GetValueOrDefault(go.GetInstanceID()) ?? currentSceneGuid; + + + void getSceneDataFromComponents() + { + if (!VHierarchyData.teamModeEnabled) return; + + if (!dataComponents_byScene.ContainsKey(go.scene)) + dataComponents_byScene[go.scene] = Resources.FindObjectsOfTypeAll().FirstOrDefault(r => r.gameObject?.scene == go.scene); + + if (dataComponents_byScene[go.scene]) + sceneData = dataComponents_byScene[go.scene].sceneData; + + } + void getSceneDataFromScriptableObject() + { + if (sceneData != null) return; + + data.sceneDatas_byGuid.TryGetValue(originalSceneGuid, out sceneData); + + } + void createSceneData() + { + if (sceneData != null) return; + if (!createDataIfDoesntExist) return; + + sceneData = new SceneData(); + + data.sceneDatas_byGuid[originalSceneGuid] = sceneData; + + } + + void getSceneIdMap() + { + if (sceneData == null) return; + + cache.sceneIdMaps_bySceneGuid.TryGetValue(originalSceneGuid, out sceneIdMap); + + } + void createSceneIdMap() + { + if (sceneIdMap != null) return; + if (sceneData == null) return; + if (currentSceneGuid != originalSceneGuid) return; + + sceneIdMap = new SceneIdMap(); + + cache.sceneIdMaps_bySceneGuid[currentSceneGuid] = sceneIdMap; + + } + void updateSceneIdMapAndOriginalSceneGuids() + { + if (sceneIdMap == null) return; + if (currentSceneGuid != originalSceneGuid) return; + if (!go.scene.isLoaded) return; // can happen when setting icons via api + + + var curInstanceIdsHash = go.scene.GetRootGameObjects().FirstOrDefault()?.GetInstanceID() ?? 0; + var curGlobalIdsHash = sceneData.goDatas_byGlobalId.Keys.Aggregate(0, (hash, r) => hash ^= r.GetHashCode()); + + if (sceneIdMap.instanceIdsHash == curInstanceIdsHash && sceneIdMap.globalIdsHash == curGlobalIdsHash) return; + + + var globalIds = sceneData.goDatas_byGlobalId.Keys.ToList(); + var instanceIds = globalIds.Select(r => Application.isPlaying ? r.UnpackForPrefab() : r) + .GetObjectInstanceIds(); + void clearIdMap() + { + if (Application.isPlaying) return; // not clearing in playmode fixes data loss on first root object when it's moved to DontDestroyOnLoad (sice it causes map update) + + sceneIdMap.globalIds_byInstanceId = new SerializableDictionary(); + + } + void clearSceneGuids() + { + if (Application.isPlaying) return; // not clearing in playmode fixes data loss on first root object when it's moved to DontDestroyOnLoad (sice it causes map update) + + foreach (var instanceId in sceneIdMap.globalIds_byInstanceId.Keys) + cache.originalSceneGuids_byInstanceId.Remove(instanceId); + + } + void fillIdMap() + { + for (int i = 0; i < instanceIds.Length; i++) + if (instanceIds[i] != 0) + sceneIdMap.globalIds_byInstanceId[instanceIds[i]] = globalIds[i]; + + } + void fillSceneGuids() + { + for (int i = 0; i < instanceIds.Length; i++) + cache.originalSceneGuids_byInstanceId[instanceIds[i]] = currentSceneGuid; + + } + + + clearIdMap(); + clearSceneGuids(); + fillIdMap(); + fillSceneGuids(); + + sceneIdMap.instanceIdsHash = curInstanceIdsHash; + sceneIdMap.globalIdsHash = curGlobalIdsHash; + + } + + void getGoData() + { + if (sceneData == null) return; + if (sceneIdMap == null) return; + if (!sceneIdMap.globalIds_byInstanceId.TryGetValue(go.GetInstanceID(), out var globalId)) return; + + sceneData.goDatas_byGlobalId.TryGetValue(globalId, out goData); + + } + void moveGoDataToCurrentSceneGuid() + { + if (goData == null) return; + if (currentSceneGuid == originalSceneGuid) return; + if (Application.isPlaying) return; + + var originalSceneData = sceneData; + var currentSceneData = dataComponents_byScene.GetValueOrDefault(go.scene)?.sceneData ?? data.sceneDatas_byGuid.GetValueOrDefault(currentSceneGuid); + + if (originalSceneData == null) return; + if (currentSceneData == null) return; + + var globalId = go.GetGlobalID(); + + originalSceneData.goDatas_byGlobalId.Remove(originalSceneData.goDatas_byGlobalId.First(r => r.Value == goData).Key); + currentSceneData.goDatas_byGlobalId[go.GetGlobalID()] = goData; + + } + void createGoData() + { + if (goData != null) return; + if (!createDataIfDoesntExist) return; + + goData = new GameObjectData(); + + sceneData.goDatas_byGlobalId[go.GetGlobalID()] = goData; + + } + + + getSceneDataFromComponents(); + getSceneDataFromScriptableObject(); + createSceneData(); + + getSceneIdMap(); + createSceneIdMap(); + updateSceneIdMapAndOriginalSceneGuids(); + + getGoData(); + moveGoDataToCurrentSceneGuid(); + createGoData(); + + } + void prefabObject() + { + if (StageUtility.GetCurrentStage() is not PrefabStage prefabStage) return; + + + var prefabGuid = prefabStage.assetPath.ToGuid(); + + GlobalID sourceGlobalId; + + void calcGlobalId() + { + + var rawGlobalId = go.GetGlobalID(); + + +#if UNITY_2023_2_OR_NEWER + + var so = new SerializedObject(go); + + so.SetPropertyValue("inspectorMode", UnityEditor.InspectorMode.Debug); + + var rawFileId = so.FindProperty("m_LocalIdentfierInFile").longValue; + + if (rawFileId == 0) // happens for prefab variants in unity 6 + rawFileId = (long)t_Unsupported.InvokeMethod("GetOrGenerateFileIDHint", go); + +#else + var rawFileId = rawGlobalId.fileId; +#endif + + // fixes fileId for prefab variants + // also works for getting prefab's unpacked fileId + var fileId = ((long)rawFileId ^ (long)rawGlobalId.globalObjectId.targetPrefabId) & 0x7fffffffffffffff; + + + sourceGlobalId = new GlobalID($"GlobalObjectId_V1-1-{prefabGuid}-{fileId}-0"); + + } + + + void getSceneDataFromScriptableObject() + { + data.sceneDatas_byGuid.TryGetValue(prefabGuid, out sceneData); + } + void createSceneData() + { + if (sceneData != null) return; + if (!createDataIfDoesntExist) return; + + sceneData = new SceneData(); + + data.sceneDatas_byGuid[prefabGuid] = sceneData; + + } + + void getGoData() + { + if (sceneData == null) return; + + sceneData.goDatas_byGlobalId.TryGetValue(sourceGlobalId, out goData); + + } + void createGoData() + { + if (goData != null) return; + if (!createDataIfDoesntExist) return; + + goData = new GameObjectData(); + + sceneData.goDatas_byGlobalId[sourceGlobalId] = goData; + + } + + + calcGlobalId(); + + getSceneDataFromScriptableObject(); + createSceneData(); + + getGoData(); + createGoData(); + + } + void prefabInstance_editMode() + { + if (!PrefabUtility.IsPartOfPrefabInstance(go)) return; + if (goData != null) return; + + void tryGetForSourceGo(GameObject sourceGo) + { + var sourceGoGlobalId = sourceGo.GetGlobalID(); + var sourcePrefabGuid = sourceGoGlobalId.guid; + + cache.prefabInstanceGlobalIds_byInstanceIds[go.GetInstanceID()] = sourceGoGlobalId; + + + data.sceneDatas_byGuid.TryGetValue(sourcePrefabGuid, out sceneData); + + sceneData?.goDatas_byGlobalId.TryGetValue(sourceGoGlobalId, out goData); + + + if (goData == null) + try + { + if (PrefabUtility.GetCorrespondingObjectFromSource(sourceGo) is GameObject previousSourceGo) + tryGetForSourceGo(previousSourceGo); + + // wrapped in try-catch because GetCorrespondingObjectFromSource throws exceptions on broken prefabs + } + catch { } + + } + + tryGetForSourceGo(PrefabUtility.GetCorrespondingObjectFromSource(go)); + + } + void prefabInstance_playmode() + { + if (!Application.isPlaying) return; + if (goData != null) return; + + + if (!cache.prefabInstanceGlobalIds_byInstanceIds.TryGetValue(go.GetInstanceID(), out var globalId)) return; + + var prefabGuid = globalId.guid; + + + data.sceneDatas_byGuid.TryGetValue(prefabGuid, out sceneData); + + sceneData?.goDatas_byGlobalId.TryGetValue(globalId, out goData); + + } + + sceneObject(); + prefabObject(); + prefabInstance_editMode(); + prefabInstance_playmode(); + + if (goData != null) + goData.sceneData = sceneData; + + return goDataCache[go] = goData; + + } + + public static Dictionary goDataCache = new(); + + public static Dictionary dataComponents_byScene = new(); + + static VHierarchyCache cache => VHierarchyCache.instance; + + + + public static void OnHierarchyChanged() { goInfoCache.Clear(); } + public static void OnDataSerialization() { goInfoCache.Clear(); goDataCache.Clear(); } + + + + + + + + + + + + + + + + + + + static void LoadSceneBookmarkObjects() // update + { + if (!data) return; + + + var scenesToLoadFor = unloadedSceneBookmarks_sceneGuids.Select(r => EditorSceneManager.GetSceneByPath(r.ToPath())) + .Where(r => r.isLoaded); + if (!scenesToLoadFor.Any()) return; + + + + foreach (var scene in scenesToLoadFor) + { + var bookmarksFromThisScene = data.bookmarks.Where(r => r.globalId.guid == scene.path.ToGuid()).ToList(); + + var objectsForTheseBookmarks = bookmarksFromThisScene.Select(r => !Application.isPlaying ? r.globalId + : r.globalId.UnpackForPrefab()).GetObjects(); + + for (int i = 0; i < bookmarksFromThisScene.Count; i++) + if (objectsForTheseBookmarks[i]) + bookmarksFromThisScene[i]._go = objectsForTheseBookmarks[i] as GameObject; + else + bookmarksFromThisScene[i].failedToLoadSceneObject = true; + + } + + unloadedSceneBookmarks_sceneGuids.Clear(); + + + foreach (var window in allHierarchies) + window.Repaint(); + + } + + public static HashSet unloadedSceneBookmarks_sceneGuids = new(); + + + + + static void StashBookmarkObjects() // on playmode enter before awake + { + stashedBookmarkObjects_byBookmark.Clear(); + + foreach (var bookmark in data.bookmarks) + stashedBookmarkObjects_byBookmark[bookmark] = bookmark._go; + + } + static void UnstashBookmarkObjects() // on playmode exit + { + foreach (var bookmark in data.bookmarks) + if (stashedBookmarkObjects_byBookmark.TryGetValue(bookmark, out var stashedObject)) + if (stashedObject != null) + bookmark._go = stashedObject; + + } + + static Dictionary stashedBookmarkObjects_byBookmark = new(); + + + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] + static void OnPlaymodeEnter_beforeAwake() + { + if (!data) return; + + StashBookmarkObjects(); + + } + static void OnPlaymodeExit(PlayModeStateChange state) + { + if (state != PlayModeStateChange.EnteredEditMode) return; + if (!data) return; + + + UnstashBookmarkObjects(); + + // scene objects can get recreated in playmode if the scene was reloaded + // in this case their respective bookmarks will be updated in OnSceneLoaded_inPlaymode to reference the recreated versions + // so we ensure that after playmode bookmarks reference the same objects as they did before playmode + + + + + foreach (var bookmark in data.bookmarks) + if (bookmark.globalId.guid == "00000000000000000000000000000000") + if (bookmark._go is GameObject gameObject) + { + bookmark.globalId = new GlobalID(bookmark.globalId.ToString().Replace("00000000000000000000000000000000", gameObject.scene.path.ToGuid())); + data.Dirty(); + } + + // objects from DontDestroyOnLoad that were bookmarked in playmode have globalIds with blank scene guids + // we fix this after playmode, when scene guids become available + + } + + + + + static void RepaintOnAlt() // Update + { + var lastEvent = typeof(Event).GetFieldValue("s_Current"); + + if (lastEvent.alt != wasAlt) + if (EditorWindow.mouseOverWindow is EditorWindow hoveredWindow) + if (hoveredWindow.GetType() == t_SceneHierarchyWindow || hoveredWindow is VHierarchySceneSelectorWindow) + hoveredWindow.Repaint(); + + wasAlt = lastEvent.alt; + + } + + static bool wasAlt; + + + + + static void SetPreviousTransformTool() + { + if (!transformToolNeedsReset) return; + + Tools.current = previousTransformTool; + + transformToolNeedsReset = false; + + // E shortcut changes transform tool in 2022 + // here we undo this + + } + + static bool transformToolNeedsReset; + static Tool previousTransformTool; + + + + + static void DuplicateSceneData(string originalSceneGuid, string duplicatedSceneGuid) + { + var originalSceneData = data.sceneDatas_byGuid[originalSceneGuid]; + var duplicatedSceneData = data.sceneDatas_byGuid[duplicatedSceneGuid] = new SceneData(); + + foreach (var kvp in originalSceneData.goDatas_byGlobalId) + { + var duplicatedGlobalId = new GlobalID(kvp.Key.ToString().Replace(originalSceneGuid, duplicatedSceneGuid)); + var duplicatedGoData = new GameObjectData() { colorIndex = kvp.Value.colorIndex, iconNameOrGuid = kvp.Value.iconNameOrGuid }; + + duplicatedSceneData.goDatas_byGlobalId[duplicatedGlobalId] = duplicatedGoData; + + } + + data.Dirty(); + + } + + static void OnSceneImported(string importedScenePath) + { + if (curEvent.commandName != "Duplicate" && curEvent.commandName != "Paste") return; + + + var copiedAssets_paths = new List(); + + var assetClipboard = typeof(Editor).Assembly.GetType("UnityEditor.AssetClipboardUtility").GetMemberValue("assetClipboard").InvokeMethod("GetEnumerator"); + + while (assetClipboard.MoveNext()) + copiedAssets_paths.Add(assetClipboard.Current.GetMemberValue("guid").ToString().ToPath()); + + + + var originalScenePath = copiedAssets_paths.FirstOrDefault(r => File.Exists(r) && new FileInfo(r).Length + == new FileInfo(importedScenePath).Length); + var originalSceneGuid = originalScenePath.ToGuid(); + var duplicatedSceneGuid = importedScenePath.ToGuid(); + + if (!data.sceneDatas_byGuid.ContainsKey(originalSceneGuid)) return; + if (data.sceneDatas_byGuid.ContainsKey(duplicatedSceneGuid)) return; + + DuplicateSceneData(originalSceneGuid, duplicatedSceneGuid); + + } + + class SceneImportDetector : AssetPostprocessor + { + // scene data duplication won't work on earlier versions anyway +#if UNITY_2021_2_OR_NEWER + static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths, bool didDomainReload) + { + if (!data) return; + + foreach (var r in importedAssets) + if (r.EndsWith(".unity")) + OnSceneImported(r); + + } +#endif + } + + + + + [UnityEditor.Callbacks.PostProcessBuild] + public static void ClearCacheAfterBuild(BuildTarget _, string __) => VHierarchyCache.Clear(); + + static void ClearCacheOnProjectLoaded() => VHierarchyCache.Clear(); + + + + + + + + + + + + + + + [InitializeOnLoadMethod] + static void Init() + { + if (VHierarchyMenu.pluginDisabled) return; + + void subscribe() + { + + // gui + + EditorApplication.hierarchyWindowItemOnGUI -= RowGUI; + EditorApplication.hierarchyWindowItemOnGUI = RowGUI + EditorApplication.hierarchyWindowItemOnGUI; + + + + + // wrapping updaters + + EditorApplication.projectWindowItemOnGUI -= ProjectWindowItemOnGUI; + EditorApplication.projectWindowItemOnGUI += ProjectWindowItemOnGUI; + + EditorApplication.hierarchyWindowItemOnGUI -= HierarchyWindowItemOnGUI; + EditorApplication.hierarchyWindowItemOnGUI += HierarchyWindowItemOnGUI; + + EditorApplication.delayCall -= DelayCallLoop; + EditorApplication.delayCall += DelayCallLoop; + + EditorApplication.update -= CheckIfFocusedWindowChanged; + EditorApplication.update += CheckIfFocusedWindowChanged; + + + + // shortcuts + + var globalEventHandler = typeof(EditorApplication).GetFieldValue("globalEventHandler"); + typeof(EditorApplication).SetFieldValue("globalEventHandler", Shortcuts + (globalEventHandler - Shortcuts)); + + + + + // loading bookmarked objects + + EditorApplication.update -= LoadSceneBookmarkObjects; + EditorApplication.update += LoadSceneBookmarkObjects; + + + + // other + + EditorApplication.update -= RepaintOnAlt; + EditorApplication.update += RepaintOnAlt; + + EditorApplication.update -= SetPreviousTransformTool; + EditorApplication.update += SetPreviousTransformTool; + + EditorApplication.hierarchyChanged -= OnHierarchyChanged; + EditorApplication.hierarchyChanged += OnHierarchyChanged; + + var projectWasLoaded = typeof(EditorApplication).GetFieldValue("projectWasLoaded"); + typeof(EditorApplication).SetFieldValue("projectWasLoaded", (projectWasLoaded - ClearCacheOnProjectLoaded) + ClearCacheOnProjectLoaded); + + } + void loadData() + { + data = AssetDatabase.LoadAssetAtPath(ProjectPrefs.GetString("vHierarchy-lastKnownDataPath")); + + + if (data) return; + + data = AssetDatabase.FindAssets("t:VHierarchyData").Select(guid => AssetDatabase.LoadAssetAtPath(guid.ToPath())).FirstOrDefault(); + + + if (!data) return; + + ProjectPrefs.SetString("vHierarchy-lastKnownDataPath", data.GetPath()); + + } + void loadPalette() + { + palette = AssetDatabase.LoadAssetAtPath(ProjectPrefs.GetString("vHierarchy-lastKnownPalettePath")); + + + if (palette) return; + + palette = AssetDatabase.FindAssets("t:VHierarchyPalette").Select(guid => AssetDatabase.LoadAssetAtPath(guid.ToPath())).FirstOrDefault(); + + + if (!palette) return; + + ProjectPrefs.SetString("vHierarchy-lastKnownPalettePath", palette.GetPath()); + + } + void loadDataAndPaletteDelayed() + { + if (!data) + EditorApplication.delayCall += () => EditorApplication.delayCall += loadData; + + if (!palette) + EditorApplication.delayCall += () => EditorApplication.delayCall += loadPalette; + + // AssetDatabase isn't up to date at this point (it gets updated after InitializeOnLoadMethod) + // and if current AssetDatabase state doesn't contain the data - it won't be loaded during Init() + // so here we schedule an additional, delayed attempt to load the data + // this addresses reports of data loss when trying to load it on a new machine + + } + void migrateDataFromV1() + { + if (!data) return; + if (ProjectPrefs.GetBool("vHierarchy-dataMigrationFromV1Attempted", false)) return; + + ProjectPrefs.SetBool("vHierarchy-dataMigrationFromV1Attempted", true); + + var lines = System.IO.File.ReadAllLines(data.GetPath()); + + if (lines.Length < 15 || !lines[14].Contains("sceneDatasByGuid")) return; + + var sceneGuids = new List(); + var globalIdLists = new List>(); + var goDatasByInstanceIdCounts = new List(); + var sceneDatas = new List(); + + + void parseSceneGuids() + { + for (int i = 16; i < lines.Length; i++) + { + if (lines[i].Contains("values:")) break; + + var startIndex = lines[i].IndexOf("- ") + 2; + + if (startIndex < lines[i].Length) + sceneGuids.Add(lines[i].Substring(startIndex)); + else + sceneGuids.Add(""); + + } + + } + void parseGlobalIdLists_andCountGoDatasByInstanceId() + { + var parsingGlobalIdList = false; + var parsingGlobalIdListAtIndex = -1; + + for (int i = 0; i < lines.Length; i++) + { + var line = lines[i]; + + void startParsing() + { + if (!line.Contains("goDatasByGlobalId")) return; + + parsingGlobalIdList = true; + parsingGlobalIdListAtIndex++; + + globalIdLists.Add(new List()); + + } + void parse() + { + if (!parsingGlobalIdList) return; + if (!line.Contains("- GlobalObjectId")) return; + + var startIndex = line.IndexOf("- ") + 2; + + if (startIndex < line.Length) + globalIdLists[parsingGlobalIdListAtIndex].Add(line.Substring(startIndex)); + else + globalIdLists[parsingGlobalIdListAtIndex].Add(""); + + } + void stopParsing_andCountDatasByInstanceId() + { + if (!line.Contains("goDatasByInstanceId")) return; + + + parsingGlobalIdList = false; + + + var goDatasByInstanceId_keysLine = lines[i + 1]; + var goDatasByInstanceId_count = (goDatasByInstanceId_keysLine.Length - 14) / 8; + + goDatasByInstanceIdCounts.Add(goDatasByInstanceId_count); + + + } + + startParsing(); + parse(); + stopParsing_andCountDatasByInstanceId(); + + } + + } + void parseSceneDatas() + { + var firstLineIndexOfFirstSceneData = 17 + sceneGuids.Count; + + + + void parseSceneData(int sceneDataIndex) + { + var sceneData = new SceneData(); + + var globalIds = globalIdLists[sceneDataIndex]; + var firstLineIndex = getFirstLineIndex(sceneDataIndex); + + + + void parseGoData(int iGoData) + { + var goData = new GameObjectData(); + + + var colorLine = lines[getColorLineIndex(iGoData)]; + + if (colorLine.Length > 18) + goData.colorIndex = int.Parse(colorLine.Substring(18)); + + + var iconLine = lines[getIconLineIndex(iGoData)]; + + if (iconLine.Length > 16) + goData.iconNameOrGuid = iconLine.Substring(16); + + + + var globalIdString = globalIdLists[sceneDataIndex][iGoData]; + + var globalId = new GlobalID(globalIdString); + + + + sceneData.goDatas_byGlobalId[globalId] = goData; + // sceneData.goDatas_byGlobalId.Add(globalId, goData); + + } + + int getColorLineIndex(int goDataIndex) + { + var index = firstLineIndex; // - goDatasByGlobalId: + + index += 1; // keys: + index += globalIds.Count; + + index += 1; // values: + index += 1; // zeroth godata + index += goDataIndex * 2; + + return index; + + } + int getIconLineIndex(int goDataIndex) => getColorLineIndex(goDataIndex) + 1; + + + + for (int i = 0; i < globalIds.Count; i++) + parseGoData(i); + + sceneDatas.Add(sceneData); + + } + + int getSceneDataLength(int sceneDataIndex) + { + int length = 0; + + length += 1; // - goDatasByGlobalId: + + length += 1; // - keys: + length += globalIdLists[sceneDataIndex].Count; + + length += 1; // - values: + length += globalIdLists[sceneDataIndex].Count * 2; + + + + length += 1; // - goDatasByInstanceId: + + length += 1; // - keys: 123123123 + + length += 1; // - values: + length += goDatasByInstanceIdCounts[sceneDataIndex] * 2; + + + return length; + + } + int getFirstLineIndex(int sceneDataIndex) + { + var index = firstLineIndexOfFirstSceneData; + + for (int i = 0; i < sceneDataIndex; i++) + index += getSceneDataLength(i); + + return index; + + } + + + + for (int i = 0; i < sceneGuids.Count; i++) + parseSceneData(i); + + } + + void remapColorIndexes() + { + foreach (var sceneData in sceneDatas) + foreach (var goData in sceneData.goDatas_byGlobalId.Values) + if (goData.colorIndex == 7) + goData.colorIndex = 1; + else if (goData.colorIndex == 8) + goData.colorIndex = 2; + else if (goData.colorIndex >= 2) + goData.colorIndex += 2; + + } + void setSceneDatasToData() + { + for (int i = 0; i < sceneDatas.Count; i++) + data.sceneDatas_byGuid[sceneGuids[i]] = sceneDatas[i]; + + data.Dirty(); + data.Save(); + + } + + + try + { + parseSceneGuids(); + parseGlobalIdLists_andCountGoDatasByInstanceId(); + parseSceneDatas(); + + remapColorIndexes(); + setSceneDatasToData(); + + } + catch { } + + } + void removeDeletedBookmarks() + { + if (!data) return; + + + var toRemove = data.bookmarks.Where(r => r.isDeleted); + + if (!toRemove.Any()) return; + + + foreach (var r in toRemove.ToList()) + data.bookmarks.Remove(r); + + data.Dirty(); + + + // delayed to give bookmarks a chance to load in update + + } + + + subscribe(); + loadData(); + loadPalette(); + loadDataAndPaletteDelayed(); + migrateDataFromV1(); + + EditorApplication.delayCall += () => removeDeletedBookmarks(); + + OnDomainReloaded(); + + } + + public static VHierarchyData data; + public static VHierarchyPalette palette; + + + + + + static IEnumerable allHierarchies => _allHierarchies ??= t_SceneHierarchyWindow.GetFieldValue("s_SceneHierarchyWindows").Cast(); + static IEnumerable _allHierarchies; + + static Type t_SceneHierarchyWindow = typeof(Editor).Assembly.GetType("UnityEditor.SceneHierarchyWindow"); + static Type t_HostView = typeof(Editor).Assembly.GetType("UnityEditor.HostView"); + static Type t_EditorWindowDelegate = t_HostView.GetNestedType("EditorWindowDelegate", maxBindingFlags); + static Type t_Unsupported = typeof(Editor).Assembly.GetType("UnityEditor.Unsupported"); + + static Type t_VTabs = Type.GetType("VTabs.VTabs") ?? Type.GetType("VTabs.VTabs, VTabs, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"); + static Type t_VFavorites = Type.GetType("VFavorites.VFavorites") ?? Type.GetType("VFavorites.VFavorites, VFavorites, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"); + + static MethodInfo mi_WrappedGUI = typeof(VHierarchy).GetMethod(nameof(WrappedGUI), maxBindingFlags); + + + + + + public const string version = "2.1.1"; + + } + + #region Rules + + public class RuleAttribute : System.Attribute { } + + public class ObjectInfo + { + public int color = 0; + public string icon = ""; + + + public ObjectInfo(GameObject gameObject) => this.gameObject = gameObject; + + public GameObject gameObject; + + + } + + + + #endregion + +} +#endif diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchy.cs.meta b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchy.cs.meta new file mode 100644 index 0000000..1831a9a --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchy.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 67e949be20d3641adbc9494ed5bd764e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyCache.cs b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyCache.cs new file mode 100644 index 0000000..2e27db0 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyCache.cs @@ -0,0 +1,61 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.Linq; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using Type = System.Type; +using static VHierarchy.VHierarchyData; +using static VHierarchy.Libs.VUtils; +using static VHierarchy.Libs.VGUI; +// using static VTools.VDebug; + + +namespace VHierarchy +{ + [FilePath("Library/vHierarchy Cache.asset", FilePathAttribute.Location.ProjectFolder)] + public class VHierarchyCache : ScriptableSingleton + { + // used for finding SceneData and SceneIdMap for objects that were moved out of their original scene + public SerializableDictionary originalSceneGuids_byInstanceId = new(); + + // used as cache for converting GlobalID to InstanceID and as a way to find GameObjectData for prefabs in playmode (when prefabs produce invalid GlobalIDs) + public SerializableDictionary sceneIdMaps_bySceneGuid = new(); + + // used for fetching icons set inside prefab instances in playmode (when prefabs produce invalid GlobalIDs) + public SerializableDictionary prefabInstanceGlobalIds_byInstanceIds = new SerializableDictionary(); + + + + [System.Serializable] + public class SceneIdMap + { + public SerializableDictionary globalIds_byInstanceId = new(); + + public int instanceIdsHash; + public int globalIdsHash; + + } + + + + + + + + public static void Clear() + { + instance.originalSceneGuids_byInstanceId.Clear(); + instance.sceneIdMaps_bySceneGuid.Clear(); + + } + + + } +} +#endif \ No newline at end of file diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyCache.cs.meta b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyCache.cs.meta new file mode 100644 index 0000000..b6d9512 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyCache.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3fd3b966dd497472d86df0d7c9271088 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyComponentEditor.cs b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyComponentEditor.cs new file mode 100644 index 0000000..8fb6b18 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyComponentEditor.cs @@ -0,0 +1,6 @@ + + +// this file was present in a previus version and is supposed to be deleted now +// but asset store update delivery system doesn't allow deleting files +// so instead this file is now emptied +// feel free to delete it if you want diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyComponentEditor.cs.meta b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyComponentEditor.cs.meta new file mode 100644 index 0000000..18dd4d0 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyComponentEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d51d8117d96b64eaa9a83667bf4297d6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyComponentWindow.cs b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyComponentWindow.cs new file mode 100644 index 0000000..b84d74a --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyComponentWindow.cs @@ -0,0 +1,599 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.Linq; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using Type = System.Type; +using static VHierarchy.VHierarchyData; +using static VHierarchy.Libs.VUtils; +using static VHierarchy.Libs.VGUI; +// using static VTools.VDebug; + + +namespace VHierarchy +{ + public class VHierarchyComponentWindow : EditorWindow + { + + void OnGUI() + { + if (!component) component = EditorUtility.InstanceIDToObject(componentIid) as Component; + if (!component) { Close(); return; } + + if (!editor) Init(component); + + + void background() + { + position.SetPos(0, 0).Draw(GUIColors.windowBackground); + } + void header() + { + var headerRect = ExpandWidthLabelRect(18).Resize(-1).AddWidthFromMid(6); + var pinButtonRect = headerRect.SetWidthFromRight(17).SetHeightFromMid(17).Move(-21, .5f); + var closeButtonRect = headerRect.SetWidthFromRight(16).SetHeightFromMid(16).Move(-3, .5f); + + var backgroundColor = isDarkTheme ? Greyscale(.25f) : GUIColors.windowBackground; + + void startDragging() + { + if (isResizingVertically) return; + if (isResizingHorizontally) return; + if (isDragged) return; + if (!curEvent.isMouseDrag) return; + if (!headerRect.IsHovered()) return; + + + isDragged = true; + + dragStartMousePos = EditorGUIUtility.GUIToScreenPoint(curEvent.mousePosition); + dragStartWindowPos = position.position; + + + isPinned = true; + + if (floatingInstance == this) + floatingInstance = null; + + EditorApplication.RepaintHierarchyWindow(); + + + } + void updateDragging() + { + if (!isDragged) return; + + + var draggedPosition = dragStartWindowPos + EditorGUIUtility.GUIToScreenPoint(curEvent.mousePosition) - dragStartMousePos; + + if (!curEvent.isRepaint) + position = position.SetPos(draggedPosition); + + + EditorGUIUtility.hotControl = EditorGUIUtility.GetControlID(FocusType.Passive); + + } + void stopDragging() + { + if (!isDragged) return; + if (!curEvent.isMouseUp) return; + + + isDragged = false; + + EditorGUIUtility.hotControl = 0; + + } + + void background() + { + headerRect.Draw(backgroundColor); + + headerRect.SetHeightFromBottom(1).Draw(isDarkTheme ? Greyscale(.2f) : Greyscale(.7f)); + + } + void icon() + { + var iconRect = headerRect.SetWidth(20).MoveX(14).MoveY(-1); + + GUI.Label(iconRect, VHierarchy.GetComponentIcon(component)); + + } + void toggle() + { + var toggleRect = headerRect.MoveX(36).SetSize(20, 20); + + + var pi_enabled = component.GetType().GetProperty("enabled") ?? + component.GetType().BaseType?.GetProperty("enabled") ?? + component.GetType().BaseType?.BaseType?.GetProperty("enabled") ?? + component.GetType().BaseType?.BaseType?.BaseType?.GetProperty("enabled"); + + + if (pi_enabled == null) return; + + var enabled = (bool)pi_enabled.GetValue(component); + + + if (GUI.Toggle(toggleRect, enabled, "") == enabled) return; + + component.RecordUndo(); + pi_enabled.SetValue(component, !enabled); + + } + void name() + { + var nameRect = headerRect.MoveX(54).MoveY(-1); + + var s = new GUIContent(EditorGUIUtility.ObjectContent(component, component.GetType())).text; + s = s.Substring(s.LastIndexOf('(') + 1); + s = s.Substring(0, s.Length - 1); + + if (isPinned) + s += " of " + component.gameObject.name; + + + SetLabelBold(); + + GUI.Label(nameRect, s); + + ResetLabelStyle(); + + } + void nameCurtain() + { + var flatColorRect = headerRect.SetX(pinButtonRect.x + 3).SetXMax(headerRect.xMax); + var gradientRect = headerRect.SetXMax(flatColorRect.x).SetWidthFromRight(30); + + flatColorRect.Draw(backgroundColor); + gradientRect.DrawCurtainLeft(backgroundColor); + + } + void pinButton() + { + if (!isPinned && closeButtonRect.IsHovered()) return; + + + var normalColor = isDarkTheme ? Greyscale(.65f) : Greyscale(.8f); + var hoveredColor = isDarkTheme ? Greyscale(.9f) : normalColor; + var activeColor = Color.white; + + + + SetGUIColor(isPinned ? activeColor : pinButtonRect.IsHovered() ? hoveredColor : normalColor); + + GUI.Label(pinButtonRect, EditorGUIUtility.IconContent("pinned")); + + ResetGUIColor(); + + + SetGUIColor(Color.clear); + + var clicked = GUI.Button(pinButtonRect, ""); + + ResetGUIColor(); + + + if (!clicked) return; + + isPinned = !isPinned; + + if (isPinned && floatingInstance == this) + floatingInstance = null; + + if (!isPinned && !floatingInstance) + floatingInstance = this; + + EditorApplication.RepaintHierarchyWindow(); + + + } + void closeButton() + { + + SetGUIColor(Color.clear); + + if (GUI.Button(closeButtonRect, "")) + Close(); + + ResetGUIColor(); + + + var normalColor = isDarkTheme ? Greyscale(.65f) : Greyscale(.35f); + var hoveredColor = isDarkTheme ? Greyscale(.9f) : normalColor; + + + SetGUIColor(closeButtonRect.IsHovered() ? hoveredColor : normalColor); + + GUI.Label(closeButtonRect, EditorGUIUtility.IconContent("CrossIcon")); + + ResetGUIColor(); + + + if (isPinned) return; + + var escRect = closeButtonRect.Move(-22, -1).SetWidth(70); + + SetGUIEnabled(false); + + if (closeButtonRect.IsHovered()) + GUI.Label(escRect, "Esc"); + + ResetGUIEnabled(); + + } + void rightClick() + { + if (!curEvent.isMouseDown) return; + if (curEvent.mouseButton != 1) return; + if (!headerRect.IsHovered()) return; + + typeof(EditorUtility).InvokeMethod("DisplayObjectContextMenu", Rect.zero.SetPos(curEvent.mousePosition), component, 0); + + } + + startDragging(); + updateDragging(); + stopDragging(); + + background(); + icon(); + toggle(); + name(); + nameCurtain(); + pinButton(); + closeButton(); + rightClick(); + + } + void body() + { + EditorGUIUtility.labelWidth = (this.position.width * .4f).Max(120); + + + scrollPosition = EditorGUILayout.BeginScrollView(Vector2.up * scrollPosition).y; + BeginIndent(17); + + + editor?.OnInspectorGUI(); + + updateHeight(); + + + EndIndent(1); + EditorGUILayout.EndScrollView(); + + + EditorGUIUtility.labelWidth = 0; + + } + void outline() + { + if (Application.platform == RuntimePlatform.OSXEditor) return; + + position.SetPos(0, 0).DrawOutline(Greyscale(.1f)); + + } + + void updateHeight() + { + + ExpandWidthLabelRect(height: -5); + + if (!curEvent.isRepaint) return; + if (isResizingVertically) return; + + + targetHeight = lastRect.y + 30; + + position = position.SetHeight(targetHeight.Min(maxHeight)); + + + prevHeight = position.height; + + } + void updatePosition() + { + if (!curEvent.isLayout) return; + + void calcDeltaTime() + { + deltaTime = (float)(EditorApplication.timeSinceStartup - lastLayoutTime); + + if (deltaTime > .05f) + deltaTime = .0166f; + + lastLayoutTime = EditorApplication.timeSinceStartup; + + } + void resetCurPos() + { + if (currentPosition != default && !isPinned) return; + + currentPosition = position.position; // position.position is always int, which can't be used for lerping + + } + void lerpCurPos() + { + if (isPinned) return; + + var speed = 9; + + MathUtil.SmoothDamp(ref currentPosition, targetPosition, speed, ref positionDeriv, deltaTime); + // MathfUtils.Lerp(ref currentPosition, targetPosition, speed, deltaTime); + + } + void setCurPos() + { + if (isPinned) return; + + position = position.SetPos(currentPosition); + + } + + calcDeltaTime(); + resetCurPos(); + lerpCurPos(); + setCurPos(); + + } + void closeOnEscape() + { + if (isPinned) return; + if (!curEvent.isKeyDown) return; + if (curEvent.keyCode != KeyCode.Escape) return; + + Close(); + } + + void horizontalResize() + { + var showingScrollbar = targetHeight > maxHeight; + + var resizeArea = this.position.SetPos(0, 0).SetWidthFromRight(showingScrollbar ? 3 : 5).AddHeightFromBottom(-20); + + void startResize() + { + if (isDragged) return; + if (isResizingHorizontally) return; + if (!curEvent.isMouseDown && !curEvent.isMouseDrag) return; + if (!resizeArea.IsHovered()) return; + + isResizingHorizontally = true; + + resizeStartMousePos = curEvent.mousePosition_screenSpace; + resizeStartWindowSize = this.position.size; + + } + void updateResize() + { + if (!isResizingHorizontally) return; + + + var resizedWidth = resizeStartWindowSize.x + curEvent.mousePosition_screenSpace.x - resizeStartMousePos.x; + + var width = resizedWidth.Max(300); + + if (!curEvent.isRepaint) + position = position.SetWidth(width); + + + EditorGUIUtility.hotControl = EditorGUIUtility.GetControlID(FocusType.Passive); + // GUI.focused + + } + void stopResize() + { + if (!isResizingHorizontally) return; + if (!curEvent.isMouseUp) return; + + isResizingHorizontally = false; + + EditorGUIUtility.hotControl = 0; + + } + + + EditorGUIUtility.AddCursorRect(resizeArea, MouseCursor.ResizeHorizontal); + + startResize(); + updateResize(); + stopResize(); + + } + void verticalResize() + { + var resizeArea = this.position.SetPos(0, 0).SetHeightFromBottom(5); + + void startResize() + { + if (isDragged) return; + if (isResizingVertically) return; + if (!curEvent.isMouseDown && !curEvent.isMouseDrag) return; + if (!resizeArea.IsHovered()) return; + + isResizingVertically = true; + + resizeStartMousePos = curEvent.mousePosition_screenSpace; + resizeStartWindowSize = this.position.size; + + } + void updateResize() + { + if (!isResizingVertically) return; + + + var resizedHeight = resizeStartWindowSize.y + curEvent.mousePosition_screenSpace.y - resizeStartMousePos.y; + + var height = resizedHeight.Min(targetHeight).Max(50); + + if (!curEvent.isRepaint) + position = position.SetHeight(height); + + maxHeight = height; + + + EditorGUIUtility.hotControl = EditorGUIUtility.GetControlID(FocusType.Passive); + // GUI.focused + + } + void stopResize() + { + if (!isResizingVertically) return; + if (!curEvent.isMouseUp) return; + + isResizingVertically = false; + + EditorGUIUtility.hotControl = 0; + + } + + + EditorGUIUtility.AddCursorRect(resizeArea, MouseCursor.ResizeVertical); + + startResize(); + updateResize(); + stopResize(); + + } + + + background(); + + horizontalResize(); + verticalResize(); + + + header(); + + Space(3); + body(); + outline(); + + Space(7); + + + updatePosition(); + closeOnEscape(); + + + if (!isPinned) + Repaint(); + + EditorApplication.delayCall -= Repaint; + EditorApplication.delayCall += Repaint; + + } + + public Vector2 targetPosition; + public Vector2 currentPosition; + Vector2 positionDeriv; + float deltaTime; + double lastLayoutTime; + + bool isDragged; + Vector2 dragStartMousePos; + Vector2 dragStartWindowPos; + + public bool isResizingHorizontally; + public bool isResizingVertically; + public Vector2 resizeStartMousePos; + public Vector2 resizeStartWindowSize; + + public float scrollPosition; + + public float targetHeight; + public float maxHeight; + public float prevHeight; + + + + + + void OnLostFocus() + { + if (isPinned) return; + + if (curEvent.holdingAlt && EditorWindow.focusedWindow.GetType().Name == "SceneHierarchyWindow") + CloseNextFrameIfNotRefocused(); + else + Close(); + + } + + void CloseNextFrameIfNotRefocused() + { + EditorApplication.delayCall += () => { if (EditorWindow.focusedWindow != this) Close(); }; + } + + public bool isPinned; + + + + + + public void Init(Component component) + { + if (editor) + editor.DestroyImmediate(); + + this.component = component; + this.componentIid = component.GetInstanceID(); + this.editor = Editor.CreateEditor(component); + + } + + void OnDestroy() + { + editor?.DestroyImmediate(); + + editor = null; + component = null; + + EditorPrefs.SetFloat("vHierarchy-componentWindowWidth", position.width); + + } + + public Component component; + public Editor editor; + + public int componentIid; + + + + + + public static void CreateFloatingInstance(Vector2 position) + { + floatingInstance = ScriptableObject.CreateInstance(); + + floatingInstance.ShowPopup(); + + + floatingInstance.maxHeight = EditorGUIUtility.GetMainWindowPosition().height * .7f; + + + var savedWidth = EditorPrefs.GetFloat("vHierarchy-componentWindowWidth", minWidth); + + var width = savedWidth.Max(minWidth); + + floatingInstance.position = Rect.zero.SetPos(position).SetWidth(width).SetHeight(200); + floatingInstance.prevHeight = floatingInstance.position.height; + + floatingInstance.targetPosition = position; + + } + + public static VHierarchyComponentWindow floatingInstance; + + public static float minWidth => 300; + + } +} +#endif \ No newline at end of file diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyComponentWindow.cs.meta b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyComponentWindow.cs.meta new file mode 100644 index 0000000..741ab1d --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyComponentWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4b48d49a631ab443990f28938cbdedb8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyController.cs b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyController.cs new file mode 100644 index 0000000..ffc54b9 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyController.cs @@ -0,0 +1,511 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using UnityEditor.IMGUI.Controls; +using Type = System.Type; +using static VHierarchy.Libs.VUtils; +using static VHierarchy.Libs.VGUI; +// using static VTools.VDebug; +using static VHierarchy.VHierarchy; +using static VHierarchy.VHierarchyData; +using static VHierarchy.VHierarchyCache; + + + +namespace VHierarchy +{ + public class VHierarchyController + { + + public void UpdateExpandQueue() + { + if (treeViewAnimatesExpansion) return; + + if (!expandQueue_toAnimate.Any()) + { + if (!expandQueue_toCollapseAfterAnimation.Any()) return; + + foreach (var r in expandQueue_toCollapseAfterAnimation) + SetExpanded_withoutAnimation(r, false); + + expandQueue_toCollapseAfterAnimation.Clear(); + + return; + + } + + + var id = expandQueue_toAnimate.First().id; + var expand = expandQueue_toAnimate.First().expand; + + + + var itemIndex = treeViewControllerData.InvokeMethod("GetRow", id); + var items = treeViewControllerData.GetMemberValue>("m_Rows"); + + var stuckCollapsing = itemIndex != -1 && items[itemIndex].id != id; // happens when collapsing long hierarchies due to a bug in TreeViewController + + if (stuckCollapsing) { window.SendEvent(new Event() { type = EventType.KeyDown, keyCode = KeyCode.None }); return; } + + + + + if (expandedIds.Contains(id) != expand) + SetExpanded_withAnimation(id, expand); + + expandQueue_toAnimate.RemoveAt(0); + + + window.Repaint(); + + + // must be called from gui because reflected methods rely on Event.current + + } + + public List expandQueue_toAnimate = new(); + public List expandQueue_toCollapseAfterAnimation = new(); + + public struct ExpandQueueEntry { public int id; public bool expand; } + + public bool animatingExpansion => expandQueue_toAnimate.Any() || expandQueue_toCollapseAfterAnimation.Any(); + + + + + + + public void UpdateScrollAnimation() + { + if (!animatingScroll) return; + + + var lerpSpeed = 10; + + var lerpedScrollPos = MathUtil.SmoothDamp(currentScrollPos, targetScrollPos, lerpSpeed, ref scrollPosDerivative, editorDeltaTime); + + SetScrollPos(lerpedScrollPos); + + window.Repaint(); + + + + if (lerpedScrollPos.DistanceTo(targetScrollPos) > .4f) return; + + SetScrollPos(targetScrollPos); + + animatingScroll = false; + + + } + + public float targetScrollPos; + public float scrollPosDerivative; + + public bool animatingScroll; + + + + + + + + public void UpdateHighlightAnimation() + { + if (!animatingHighlight) return; + + + var lerpSpeed = 1.3f; + + MathUtil.SmoothDamp(ref highlightAmount, 0, lerpSpeed, ref highlightDerivative, editorDeltaTime); + + window.Repaint(); + + + + if (highlightAmount > .05f) return; + + highlightAmount = 0; + + animatingHighlight = false; + + + } + + public float highlightAmount; + public float highlightDerivative; + + public bool animatingHighlight; + + public GameObject objectToHighlight; + + + + + + + + public void UpdateState() + { + var sceneHierarchy = window?.GetFieldValue("m_SceneHierarchy"); + + treeViewController = sceneHierarchy.GetFieldValue("m_TreeView"); + treeViewControllerData = treeViewController.GetMemberValue("data"); + + + + var treeViewControllerState = treeViewController?.GetPropertyValue("state"); + + currentScrollPos = treeViewControllerState?.scrollPos.y ?? 0; + + expandedIds = treeViewControllerState?.expandedIDs ?? new List(); + + + + var treeViewAnimator = treeViewController?.GetMemberValue("m_ExpansionAnimator"); + var treeViewAnimatorSetup = treeViewAnimator?.GetMemberValue("m_Setup"); + + treeViewAnimatesScroll = treeViewController?.GetMemberValue("m_FramingAnimFloat").isAnimating ?? false; + + treeViewAnimatesExpansion = treeViewAnimator?.GetMemberValue("isAnimating") ?? false; + + } + + object treeViewController; + object treeViewControllerData; + + public float currentScrollPos; + + public List expandedIds = new(); + + public bool treeViewAnimatesScroll; + public bool treeViewAnimatesExpansion; + + public int GetRowIndex(int instanceId) + { + return treeViewControllerData.InvokeMethod("GetRow", instanceId); + } + + + + + + + + + + + + + + + + + public void ToggleExpanded(int id) + { + SetExpanded_withAnimation(id, !expandedIds.Contains(id)); + + window.Repaint(); + + } + + public void CollapseAll() + { + var expandedRoots = new List(); + var expandedChildren = new List(); + + foreach (var iid in expandedIds) + if (EditorUtility.InstanceIDToObject(iid) is GameObject expandedGo) + if (expandedGo.transform.parent) + expandedChildren.Add(expandedGo); + else + expandedRoots.Add(expandedGo); + + + + expandQueue_toCollapseAfterAnimation = expandedChildren.Select(r => r.GetInstanceID()).ToList(); + + expandQueue_toAnimate = expandedRoots.Select(r => new ExpandQueueEntry { id = r.GetInstanceID(), expand = false }) + .OrderBy(r => GetRowIndex(r.id)).ToList(); + + StartScrollAnimation(targetScrollPos: 0); + + + window.Repaint(); + + } + + public void Isolate(int targetId) + { + + List getParents(int id) + { + var parentIds = new List(); + + if (EditorUtility.InstanceIDToObject(id) is not GameObject go) return parentIds; + + + while (go.transform.parent) + parentIds.Add((go = go.transform.parent.gameObject).GetInstanceID()); + + parentIds.Add(go.scene.handle); + + + return parentIds; + + } + + var targetItemParents = getParents(targetId); + + + + var itemsToCollapse = expandedIds.ToList(); + + itemsToCollapse.Remove(targetId); + itemsToCollapse.RemoveAll(r => targetItemParents.Contains(r)); + itemsToCollapse.RemoveAll(r => itemsToCollapse.Intersect(getParents(r)).Any()); + + if (EditorUtility.InstanceIDToObject(targetId) is GameObject) + itemsToCollapse.RemoveAll(r => EditorUtility.InstanceIDToObject(r) is not GameObject); // won't collapse scenes + + + + + expandQueue_toAnimate = itemsToCollapse.Select(id => new ExpandQueueEntry { id = id, expand = false }) + .Append(new ExpandQueueEntry { id = targetId, expand = true }) + .OrderBy(r => GetRowIndex(r.id)).ToList(); + + + window.Repaint(); + + } + + + + + + public void StartExpandAnimation(List targetExpandedIds) + { + + var toExpand = targetExpandedIds.Except(expandedIds).ToHashSet(); + var toCollapse = expandedIds.Except(targetExpandedIds).ToHashSet(); + + + + + // hanlde destroyed objects + + var sceneIds = Enumerable.Range(0, EditorSceneManager.sceneCount).Select(i => EditorSceneManager.GetSceneAt(i).handle).ToHashSet(); + + var toExpand_destroyed = toExpand.Where(id => !sceneIds.Contains(id) && Resources.InstanceIDToObject(id) as GameObject == null).ToHashSet(); + var toCollapse_destroyed = toCollapse.Where(id => !sceneIds.Contains(id) && Resources.InstanceIDToObject(id) as GameObject == null).ToHashSet(); + + + foreach (var id in toExpand_destroyed) + expandedIds.Add(id); + + foreach (var id in toCollapse_destroyed) + expandedIds.Remove(id); + + + toExpand.ExceptWith(toExpand_destroyed); + toCollapse.ExceptWith(toCollapse_destroyed); + + + + + + + // hanlde non-animated expansions/collapses + + bool hasParentToCollapse(int id) + { + var go = Resources.InstanceIDToObject(id) as GameObject; + + if (!go) return false; + if (!go.transform.parent) return false; + + + var parentId = go.transform.parent.gameObject.GetInstanceID(); + + return toCollapse.Contains(parentId) + || hasParentToCollapse(parentId); + + } + bool areAllParentsExpanded(int id) + { + var go = Resources.InstanceIDToObject(id) as GameObject; + + if (!go) return true; + if (!go.transform.parent) return true; + + + var parentId = go.transform.parent.gameObject.GetInstanceID(); + + return expandedIds.Contains(parentId) + && areAllParentsExpanded(parentId); + + } + + var toExpand_beforeAnimation = toExpand.Where(id => !areAllParentsExpanded(id)).ToHashSet(); + var toCollapse_afterAnimation = toCollapse.Where(id => hasParentToCollapse(id)).ToHashSet(); + + + foreach (var id in toExpand_beforeAnimation) + SetExpanded_withoutAnimation(id, true); + + foreach (var id in toCollapse_afterAnimation) + expandQueue_toCollapseAfterAnimation.Add(id); + + + toExpand.ExceptWith(toExpand_beforeAnimation); + toCollapse.ExceptWith(toCollapse_afterAnimation); + + + + + + + // setup animation + + expandQueue_toAnimate = toCollapse.Select(id => new ExpandQueueEntry { id = id, expand = false }) + .Concat(toExpand.Select(id => new ExpandQueueEntry { id = id, expand = true })) + .OrderBy(r => GetRowIndex(r.id)).ToList(); + + } + + public void SetExpandedIds(List targetExpandedIds) + { + treeViewControllerData.InvokeMethod("SetExpandedIDs", targetExpandedIds.ToArray()); + } + public void SetExpanded_withAnimation(int instanceId, bool expanded) + { + treeViewController.InvokeMethod("ChangeFoldingForSingleItem", instanceId, expanded); + } + public void SetExpanded_withoutAnimation(int instanceId, bool expanded) + { + treeViewControllerData.InvokeMethod("SetExpanded", instanceId, expanded); + } + + + + public void StartScrollAnimation(float targetScrollPos) + { + if (targetScrollPos.DistanceTo(currentScrollPos) < .05f) return; + + this.targetScrollPos = targetScrollPos; + + animatingScroll = true; + + } + + public void SetScrollPos(float targetScrollPos) + { + window.GetMemberValue("m_SceneHierarchy").GetMemberValue("m_TreeViewState").scrollPos = Vector2.up * targetScrollPos; + } + + + + public void RevealObject(GameObject go, bool expand, bool highlight, bool snapToTopMargin) + { + + var idsToExpand = new List(); + + if (expand && go.transform.childCount > 0) + idsToExpand.Add(go.GetInstanceID()); + + var cur = go.transform; + while (cur = cur.parent) + idsToExpand.Add(cur.gameObject.GetInstanceID()); + + idsToExpand.Add(go.scene.handle); + + idsToExpand.RemoveAll(r => expandedIds.Contains(r)); + + + + + foreach (var id in idsToExpand.SkipLast(1)) + SetExpanded_withoutAnimation(id, true); + + if (idsToExpand.Any()) + SetExpanded_withAnimation(idsToExpand.Last(), true); + + + + + var rowCount = treeViewControllerData.GetMemberValue("m_Rows").Count; + var maxScrollPos = rowCount * 16 - window.position.height + 26.9f; + + var rowIndex = treeViewControllerData.InvokeMethod("GetRow", go.GetInstanceID()); + var rowPos = rowIndex * 16f + 8; + + var scrollAreaHeight = window.GetMemberValue("treeViewRect").height; + + + + + var margin = 48; + + var targetScrollPos = 0f; + + if (expand) + targetScrollPos = (rowPos - margin).Min(maxScrollPos) + .Max(0); + else + targetScrollPos = currentScrollPos.Min(rowPos - margin) + .Max(rowPos - scrollAreaHeight + margin) + .Min(maxScrollPos) + .Max(0); + if (targetScrollPos < 25) + targetScrollPos = 0; + + + + StartScrollAnimation(targetScrollPos); + + + + + if (!highlight) return; + + highlightAmount = 2.2f; + + animatingHighlight = true; + + objectToHighlight = go; + + + } + + + + + + + + + + + + + + + public VHierarchyController(EditorWindow window) => this.window = window; + + public EditorWindow window; + + public VHierarchyGUI gui => VHierarchy.guis_byWindow[window]; + + } +} +#endif \ No newline at end of file diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyController.cs.meta b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyController.cs.meta new file mode 100644 index 0000000..3983a7d --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyController.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3513d7b44a8b94c8d800e841067bf035 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyData.cs b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyData.cs new file mode 100644 index 0000000..75be7b6 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyData.cs @@ -0,0 +1,227 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.Linq; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using Type = System.Type; +using static VHierarchy.VHierarchyCache; +using static VHierarchy.Libs.VUtils; +using static VHierarchy.Libs.VGUI; +// using static VTools.VDebug; + + +namespace VHierarchy +{ + public class VHierarchyData : ScriptableObject, ISerializationCallbackReceiver + { + + public SerializableDictionary sceneDatas_byGuid = new(); + + + [System.Serializable] + public class SceneData + { + public SerializableDictionary goDatas_byGlobalId = new(); + } + + + [System.Serializable] + public class GameObjectData + { + public int colorIndex; + public string iconNameOrGuid = ""; + + public bool isIconRecursive; + public bool isColorRecursive; + + + [System.NonSerialized] + public SceneData sceneData; // set in GetGameObjectData + + } + + public void OnBeforeSerialize() => VHierarchy.OnDataSerialization(); + public void OnAfterDeserialize() { } + + + + + + public List bookmarks = new(); + + [System.Serializable] + public class Bookmark + { + + public GameObject go + { + get + { + if (_go == null) + if (!failedToLoadSceneObject) // to prevent continuous GlobalID.GetObjects() calls if object is deleted + VHierarchy.unloadedSceneBookmarks_sceneGuids.Add(globalId.guid); + + return _go; + + } + } + public GameObject _go; + + [System.NonSerialized] + public bool failedToLoadSceneObject; + + + + public bool isLoadable => go != null; + + public bool isDeleted + { + get + { + if (isLoadable) + return false; + + if (!AssetDatabase.LoadAssetAtPath(globalId.guid.ToPath())) + return true; + + for (int i = 0; i < EditorSceneManager.sceneCount; i++) + if (EditorSceneManager.GetSceneAt(i).path == globalId.guid.ToPath()) + return true; + + return false; + + } + } + + public string assetPath => globalId.guid.ToPath(); + + public string name => go ? go.name : "Can't load object"; + + + + public Bookmark(GameObject o) + { + globalId = o.GetGlobalID(); + + _go = o; + + } + + public GlobalID globalId; + + } + + + + + public List bookmarkedScenePaths = new(); + + + + + + + + [CustomEditor(typeof(VHierarchyData))] + class Editor : UnityEditor.Editor + { + public override void OnInspectorGUI() + { + var style = new GUIStyle(EditorStyles.label) { wordWrap = true }; + + void normal() + { + if (teamModeEnabled) return; + + SetGUIEnabled(false); + BeginIndent(0); + + Space(10); + EditorGUILayout.LabelField("This file stores data about which icons and colors are assigned to objects, along with bookmarks from navigation bar and scene selector.", style); + + Space(6); + GUILayout.Label("If there are multiple people working on the project, you might want to store icon/color data in scenes to avoid merge conflicts. To do that, click the â‹® button at the top right corner and enable Team Mode.", style); + + EndIndent(10); + ResetGUIEnabled(); + } + void teamMode() + { + if (!teamModeEnabled) return; + + SetGUIEnabled(false); + BeginIndent(0); + + Space(10); + EditorGUILayout.LabelField("Now that Team Mode is enabled, create an empty script that inherits from VHierarchy.VHierarchyDataComponent and add it to any object in a scene.", style); + + Space(6); + GUILayout.Label("If such a script is present in a scene - all icon/color data for that scene will be serialized in that script, otherwise icon/color data for the scene will end up in this file.", style); + + EndIndent(10); + ResetGUIEnabled(); + } + + normal(); + teamMode(); + + } + } + + public static bool teamModeEnabled { get => EditorPrefsCached.GetBool("vHierarchy-teamModeEnabled", false); set => EditorPrefsCached.SetBool("vHierarchy-teamModeEnabled", value); } + + + + [ContextMenu("Enable Team Mode", isValidateFunction: false, priority: 1)] + public void EnableTeamMode() + { + var option = EditorUtility.DisplayDialogComplex("Licensing notice", + "To use vHierarchy 2 within a team, licenses must be purchased for each individual user as per the Asset Store EULA.\n\n Sharing one license across the team is illegal and considered piracy.", + "Acknowledge", + "Cancel", + "Purchase more seats"); + if (option == 2) + Application.OpenURL("https://prf.hn/click/camref:1100lGLBn/pubref:teammode/destination:https://assetstore.unity.com/packages/tools/utilities/vhierarchy-2-253397"); + // Application.OpenURL("https://assetstore.unity.com/packages/slug/253397"); + + + + if (option != 0) return; + + teamModeEnabled = true; + + VHierarchy.goInfoCache.Clear(); + VHierarchy.goDataCache.Clear(); + + EditorApplication.RepaintHierarchyWindow(); + + } + + [ContextMenu("Disable Team Mode", isValidateFunction: false, priority: 2)] + public void DisableTeamMode() + { + teamModeEnabled = false; + + VHierarchy.goInfoCache.Clear(); + VHierarchy.goDataCache.Clear(); + + EditorApplication.RepaintHierarchyWindow(); + + } + + [ContextMenu("Enable Team Mode", isValidateFunction: true, priority: 1)] bool asd() => !teamModeEnabled; + [ContextMenu("Disable Team Mode", isValidateFunction: true, priority: 2)] bool ads() => teamModeEnabled; + + + + + + } +} +#endif \ No newline at end of file diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyData.cs.meta b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyData.cs.meta new file mode 100644 index 0000000..cd5eee5 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3a9752b0c8e144801967e6897679604b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyDataComponent.cs b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyDataComponent.cs new file mode 100644 index 0000000..744c302 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyDataComponent.cs @@ -0,0 +1,142 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.Linq; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using UnityEditor.Experimental.SceneManagement; +using Type = System.Type; +using static VHierarchy.VHierarchyData; +using static VHierarchy.Libs.VUtils; +using static VHierarchy.Libs.VGUI; +// using static VTools.VDebug; + + +namespace VHierarchy +{ + [ExecuteInEditMode] + public abstract class VHierarchyDataComponent : MonoBehaviour, ISerializationCallbackReceiver + { + public void Awake() + { + void register() + { + VHierarchy.dataComponents_byScene[gameObject.scene] = this; + } + void handleSceneDuplication() + { + if (sceneData == null) return; + if (!sceneData.goDatas_byGlobalId.Any()) return; + + + var curSceneGuid = gameObject.scene.path.ToGuid(); + var dataSceneGuid = sceneData.goDatas_byGlobalId.Keys.First().guid; + + if (curSceneGuid == dataSceneGuid) return; + + + var newDic = new SerializableDictionary(); + + foreach (var kvp in sceneData.goDatas_byGlobalId) + newDic[new GlobalID(kvp.Key.ToString().Replace(dataSceneGuid, curSceneGuid))] = kvp.Value; + + + sceneData.goDatas_byGlobalId = newDic; + + + EditorSceneManager.MarkSceneDirty(gameObject.scene); + EditorSceneManager.SaveScene(gameObject.scene); + + } + + register(); + handleSceneDuplication(); + + } + + + public SceneData sceneData; + + + public void OnBeforeSerialize() => VHierarchy.goDataCache.Clear(); + public void OnAfterDeserialize() => VHierarchy.goDataCache.Clear(); + + + + [CustomEditor(typeof(VHierarchyDataComponent), true)] + class Editor : UnityEditor.Editor + { + public override void OnInspectorGUI() + { + var style = new GUIStyle(EditorStyles.label) { wordWrap = true }; + + + void teamModeOn() + { + if (!VHierarchyData.teamModeEnabled) return; + + SetGUIEnabled(false); + BeginIndent(0); + + Space(4); + EditorGUILayout.LabelField("This component stores vHierarchy's data about which icons and colors are assigned to objects in this scene", style); + + // Space(6); + // EditorGUILayout.LabelField("You can disable Team Mode to store icon/color data in vHierarchy Data scriptable object, as it is done by default", style); + + + Space(2); + + EndIndent(10); + ResetGUIEnabled(); + + + + + // Space(10); + + // if (!GUILayout.Button("Disable Team Mode", GUILayout.Height(27))) return; + + // VHierarchy.data?.DisableTeamMode(); + + } + void teamModeOff() + { + if (VHierarchyData.teamModeEnabled) return; + + SetGUIEnabled(false); + BeginIndent(0); + + Space(4); + EditorGUILayout.LabelField("Enable Team Mode to store icon/color data for this scene in this component", style); + + Space(2); + + EndIndent(10); + ResetGUIEnabled(); + + + + Space(4); + + if (!GUILayout.Button("Enable Team Mode", GUILayout.Height(27))) return; + + VHierarchy.data?.EnableTeamMode(); + + } + + teamModeOn(); + teamModeOff(); + + + } + } + + } +} +#endif \ No newline at end of file diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyDataComponent.cs.meta b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyDataComponent.cs.meta new file mode 100644 index 0000000..be05b15 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyDataComponent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9e20c7ea1a24b4a899ba82e98ad2b375 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyGUI.cs b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyGUI.cs new file mode 100644 index 0000000..76c7114 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyGUI.cs @@ -0,0 +1,1024 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using UnityEditor.IMGUI.Controls; +using Type = System.Type; +using static VHierarchy.Libs.VUtils; +using static VHierarchy.Libs.VGUI; +// using static VTools.VDebug; +using static VHierarchy.VHierarchy; +using static VHierarchy.VHierarchyData; +using static VHierarchy.VHierarchyCache; + + + +namespace VHierarchy +{ + public class VHierarchyGUI + { + + public void RowGUI_GameObject(Rect rowRect, GameObject go) + { + var fullRowRect = rowRect.SetX(32).SetXMax(rowRect.xMax + 16); + + var isRowHovered = fullRowRect.AddWidthFromRight(32).IsHovered(); + + + var isRowSelected = false; + var isRowBeingRenamed = false; + var isDefaultParent = go == defaultParent; + + void setState() + { + void set_isRowSelected() + { + if (!curEvent.isRepaint) return; + + + var dragging = dragSelectionList != null + && dragSelectionList.Any(); + + isRowSelected = dragging ? (dragSelectionList.Contains(go.GetInstanceID())) : Selection.Contains(go); + + } + void set_lastVisibleSelectedRowRect() + { + if (!Selection.gameObjects.Contains(go)) return; + + lastVisibleSelectedRowRect = rowRect; + + } + void set_mousePressed() + { + if (curEvent.isMouseDown && isRowHovered) + mousePressed = true; + + if (curEvent.isMouseUp || curEvent.isMouseLeaveWindow || curEvent.isDragPerform) + mousePressed = false; + + } + void set_hoveredGo() + { + if (curEvent.isLayout) + VHierarchy.hoveredGo = null; + + if (curEvent.isRepaint && isRowHovered) + VHierarchy.hoveredGo = go; + + } + + set_isRowSelected(); + set_lastVisibleSelectedRowRect(); + set_mousePressed(); + set_hoveredGo(); + + isRowBeingRenamed = renamingRow && isRowSelected; + + } + + + void drawing() + { + if (!curEvent.isRepaint) { hierarchyLines_isFirstRowDrawn = false; return; } + + var goInfo = GetGameObjectInfo(go); + + var drawBackgroundColor = goInfo.hasColor; + var drawCustomIcon = goInfo.hasIcon; + var drawDefaultIcon = !drawCustomIcon && (isRowBeingRenamed || (!VHierarchyMenu.minimalModeEnabled || (PrefabUtility.IsAddedGameObjectOverride(go) && PrefabUtility.IsPartOfPrefabInstance(go)))); + + var makeTriangleBrighter = drawBackgroundColor && !goInfo.isGreyColor && isDarkTheme; + var makeNameBrighter = drawBackgroundColor && !goInfo.isGreyColor && isDarkTheme && !isDefaultParent; + var makeNameBold = isDefaultParent && isDarkTheme; + + Color defaultBackground; + + + void set_defaultBackground() + { + var selectedFocused = GUIColors.selectedBackground; + var selectedUnfocused = isDarkTheme ? Greyscale(.3f) : Greyscale(.68f); + var hovered = isDarkTheme ? Greyscale(.265f) : Greyscale(.7f); + var normal = GUIColors.windowBackground; + + if (isRowSelected && !isRowBeingRenamed) + defaultBackground = isTreeFocused ? selectedFocused : selectedUnfocused; + + else if (isRowHovered) + defaultBackground = hovered; + + else + defaultBackground = normal; + + } + void hideDefaultIcon() + { + if (drawDefaultIcon) return; + + rowRect.SetWidth(16).Draw(defaultBackground); + + } + void hideName() + { + if (!drawBackgroundColor && (drawCustomIcon || drawDefaultIcon) && !makeNameBold) return; + + var nameRect = rowRect.MoveX(16).SetWidth(go.name.GetLabelWidth(isBold: isDefaultParent)); +#if UNITY_2023_2_OR_NEWER + if (!go.activeInHierarchy && PrefabUtility.IsPartOfPrefabInstance(go)) + nameRect.width *= 1.1f; +#endif + + nameRect.Draw(defaultBackground); + + } + + void backgroundColor() + { + if (!drawBackgroundColor) return; + + + + var color = goInfo.color; + + if (isRowHovered) + color *= isDarkTheme ? 1.1f : .92f; + + if (isRowSelected) + color *= isDarkTheme ? 1.2f : .8f; + + if (palette?.colorGradientsEnabled == false) + color = MathUtil.Lerp(color, Greyscale(.2f), isDarkTheme ? .25f : .03f); + + if (goInfo.hasColorByRecursion) + color = MathUtil.Lerp(color, Greyscale(isDarkTheme ? .25f : .8f), .5f); + + + + + + var colorRect = rowRect.AddWidthFromRight(28).AddWidth(16); + + if (goInfo.hasColorByRecursion) + colorRect = colorRect.AddWidthFromRight(goInfo.maxColorRecursionDepth * 14); + + if (!isRowSelected && !goInfo.hasColorByRecursion) + colorRect = colorRect.AddHeightFromMid(EditorGUIUtility.pixelsPerPoint >= 2 ? -.5f : -1); + + if (goInfo.hasColorByRecursion) + colorRect = colorRect.MoveY(EditorGUIUtility.pixelsPerPoint >= 2 ? -.25f : -.5f); + + if (palette?.colorGradientsEnabled == false || goInfo.isGreyColor) { colorRect.Draw(color); return; } + + + + var hasLeftGradient = colorRect.x > 32; + + if (hasLeftGradient) + colorRect = colorRect.AddWidthFromRight(3); + + if (PrefabUtility.HasPrefabInstanceAnyOverrides(go, false) && PrefabUtility.IsOutermostPrefabInstanceRoot(go) && !hasLeftGradient) + colorRect = colorRect.AddWidthFromRight(EditorGUIUtility.pixelsPerPoint >= 2 ? -2.5f : -3); + + + + var leftGradientWith = hasLeftGradient ? 22 : 0; + var rightGradientWidth = (fullRowRect.width * .77f).Min(colorRect.width - leftGradientWith); + + var leftGradientRect = colorRect.SetWidth(leftGradientWith); + var rightGradientRect = colorRect.SetWidthFromRight(rightGradientWidth); + + var flatColorRect = colorRect.SetX(leftGradientRect.xMax).SetXMax(rightGradientRect.x); + + + + + + + leftGradientRect.AddWidth(1).DrawCurtainLeft(color); + + flatColorRect.AddWidth(1).Draw(color); + + rightGradientRect.Draw(color.MultiplyAlpha(.1f)); + rightGradientRect.DrawCurtainRight(color); + + + } + void triangle() + { + if (!drawBackgroundColor) return; + if (go.transform.childCount == 0) return; + + var triangleRect = rowRect.MoveX(-15.5f).SetWidth(16).Resize(1.5f); + + GUI.DrawTexture(triangleRect, EditorIcons.GetIcon(controller.expandedIds.Contains(go.GetInstanceID()) ? "IN_foldout_on" : "IN_foldout")); + + + if (!makeTriangleBrighter) return; + + GUI.DrawTexture(triangleRect, EditorIcons.GetIcon(controller.expandedIds.Contains(go.GetInstanceID()) ? "IN_foldout_on" : "IN_foldout")); + + } + void name() + { + if (!drawBackgroundColor && (drawCustomIcon || drawDefaultIcon) && !makeNameBold) return; + if (isRowBeingRenamed) return; + + + var nameRect = rowRect.MoveX(18).AddHeight(1); + + if (VHierarchyMenu.minimalModeEnabled && !drawCustomIcon && !drawDefaultIcon) + nameRect = nameRect.MoveX(-17); + + if (drawBackgroundColor && !goInfo.isGreyColor) + nameRect = nameRect.MoveY(.5f); + + if (!go.activeInHierarchy) // correcting unity's style padding inconsistencies + if (PrefabUtility.IsPartOfAnyPrefab(go)) + nameRect = nameRect.MoveY(-1); + else + nameRect = nameRect.Move(-1, -1.5f); + + if (makeNameBrighter && go.activeInHierarchy && !makeNameBold) + nameRect = nameRect.MoveX(-2).MoveY(-.5f); + + + + var styleName = PrefabUtility.IsPartOfAnyPrefab(go) ? + (go.activeInHierarchy ? "PR PrefabLabel" : "PR DisabledPrefabLabel") : + (go.activeInHierarchy ? "TV Line" : "PR DisabledLabel"); + + if (makeNameBrighter && go.activeInHierarchy) + styleName = "WhiteLabel"; + + if (makeNameBold) + styleName = "TV LineBold"; + + + + if (makeNameBrighter) + SetGUIColor(Greyscale(!go.activeInHierarchy ? 1.4f : isRowSelected ? 1 : goInfo.hasColorByRecursion ? .9f : .95f)); + + GUI.skin.GetStyle(styleName).Draw(nameRect, go.name, false, false, isRowSelected || makeNameBold, isTreeFocused || makeNameBold); + + if (makeNameBrighter) + ResetGUIColor(); + + } + void defaultIcon() + { + if (!drawBackgroundColor) return; + if (!drawDefaultIcon) return; + + var iconRect = rowRect.SetWidth(16); + var icon = PrefabUtility.GetIconForGameObject(go); + + if (!isDarkTheme && isRowSelected && isTreeFocused && icon.name == "GameObject Icon") + icon = EditorIcons.GetIcon("GameObject On Icon"); + + + SetGUIColor(go.activeInHierarchy ? Color.white : Greyscale(1, .4f)); + + GUI.DrawTexture(iconRect, icon); + + if (PrefabUtility.IsAddedGameObjectOverride(go)) + GUI.DrawTexture(iconRect, EditorIcons.GetIcon("PrefabOverlayAdded Icon")); + + ResetGUIColor(); + + } + void customIcon() + { + if (!drawCustomIcon) return; + + var icon = EditorIcons.GetIcon(goInfo.iconNameOrPath) ?? Texture2D.blackTexture; + + var iconRect = rowRect.SetWidth(16); + + if (icon.width < icon.height) iconRect = iconRect.SetWidthFromMid(iconRect.height * icon.width / icon.height); + if (icon.height < icon.width) iconRect = iconRect.SetHeightFromMid(iconRect.width * icon.height / icon.width); + + + SetGUIColor(go.activeInHierarchy ? Color.white : Greyscale(1, .4f)); + + GUI.DrawTexture(iconRect, icon); + + ResetGUIColor(); + + } + void hierarchyLines() + { + if (!VHierarchyMenu.hierarchyLinesEnabled) return; + + + var lineThickness = 1f; + var lineColor = isDarkTheme ? Greyscale(1, .165f) : Greyscale(0, .23f); + + var depth = ((rowRect.x - 60) / 14).RoundToInt().Max(0); + + bool isLastChild(Transform transform) => transform.parent?.GetChild(transform.parent.childCount - 1) == transform; + bool hasChilren(Transform transform) => transform.childCount > 0; + + void calcVerticalGaps_beforeFirstRowDrawn() + { + if (hierarchyLines_isFirstRowDrawn) return; + + hierarchyLines_verticalGaps.Clear(); + + var curTransform = go.transform.parent; + var curDepth = depth - 1; + + while (curTransform != null && curTransform.parent != null) + { + if (isLastChild(curTransform)) + hierarchyLines_verticalGaps.Add(curDepth - 1); + + curTransform = curTransform.parent; + curDepth--; + } + + } + void updateVerticalGaps_beforeNextRowDrawn() + { + if (isLastChild(go.transform)) + hierarchyLines_verticalGaps.Add(depth - 1); + + if (depth < hierarchyLines_prevRowDepth) + hierarchyLines_verticalGaps.RemoveAll(r => r >= depth); + + } + + void drawVerticals() + { + for (int i = 0; i < depth; i++) + if (!hierarchyLines_verticalGaps.Contains(i)) + rowRect.SetX(53 + i * 14 - lineThickness / 2) + .SetWidth(lineThickness) + .SetHeight(isLastChild(go.transform) && i == depth - 1 ? 8 + lineThickness / 2 : 16) + .Draw(lineColor); + + } + void drawHorizontals() + { + if (depth == 0) return; + + rowRect.MoveX(-21) + .SetHeightFromMid(lineThickness) + .SetWidth(hasChilren(go.transform) ? 7 : 17) + .AddWidthFromRight(-lineThickness / 2f) + .Draw(lineColor); + + } + + + + calcVerticalGaps_beforeFirstRowDrawn(); + + drawVerticals(); + drawHorizontals(); + + updateVerticalGaps_beforeNextRowDrawn(); + + hierarchyLines_prevRowDepth = depth; + hierarchyLines_isFirstRowDrawn = true; + + } + void zebraStriping() + { + if (!VHierarchyMenu.zebraStripingEnabled) return; + if (isRowSelected) return; + if (goInfo.goData?.colorIndex == 1) return; + + + var contrast = isDarkTheme ? .033f : .05f; + + + var t = rowRect.y.PingPong(16f) / 16f; + + if (isRowHovered || isRowSelected) + t = 1; + + if (t.Approx(0)) return; + + + + fullRowRect.Draw(Greyscale(isDarkTheme ? 1 : 0, contrast * t)); + + + } + void highlight() + { + if (!controller.animatingHighlight) return; + if (go != controller.objectToHighlight) return; + + + var highlightBrightness = .16f; + + + var highlightAmount = controller.highlightAmount.Clamp01(); + + highlightAmount = highlightAmount * highlightAmount * (3 - 2 * highlightAmount); + + + fullRowRect.AddWidthFromRight(123).Draw(Greyscale(1, highlightBrightness * highlightAmount)); + + } + + + set_defaultBackground(); + hideDefaultIcon(); + hideName(); + + backgroundColor(); + hierarchyLines(); + + triangle(); + name(); + defaultIcon(); + customIcon(); + zebraStriping(); + highlight(); + + } + + void componentMinimap() + { + if (!VHierarchyMenu.componentMinimapEnabled) return; + + void componentButton(Rect buttonRect, Component component) + { + void componentIcon() + { + if (!curEvent.isRepaint) return; + + + var normalOpacity = isDarkTheme ? .47f : .7f; + var activeOpacity = 1; + var pressedOpacity = isDarkTheme ? .65f : .9f; + + var isActive = (buttonRect.IsHovered() && curEvent.holdingAlt) || VHierarchyComponentWindow.floatingInstance?.component == component; + var isPressed = buttonRect.IsHovered() && mousePressed; + + var icon = GetComponentIcon(component); + + + if (!icon) return; + + SetGUIColor(Greyscale(1, isActive ? (isPressed ? pressedOpacity : activeOpacity) : normalOpacity)); + + GUI.DrawTexture(buttonRect.SetSizeFromMid(12, 12), icon); + + ResetGUIColor(); + + } + + void mouseDown() + { + if (!curEvent.holdingAlt) return; + if (!curEvent.isMouseDown) return; + if (!buttonRect.IsHovered()) return; + + curEvent.Use(); + + mouseDownPos = curEvent.mousePosition; + + } + void mouseUp() + { + if (!curEvent.holdingAlt) return; + if (!curEvent.isMouseUp) return; + if (!buttonRect.IsHovered()) return; + + curEvent.Use(); + + if (VHierarchyComponentWindow.floatingInstance?.component == component) { VHierarchyComponentWindow.floatingInstance.Close(); return; } + + + var position = EditorGUIUtility.GUIToScreenPoint(new Vector2(rowRect.xMax + 25, rowRect.y)); + + if (!VHierarchyComponentWindow.floatingInstance) + VHierarchyComponentWindow.CreateFloatingInstance(position); + + VHierarchyComponentWindow.floatingInstance.Init(component); + VHierarchyComponentWindow.floatingInstance.Focus(); + + VHierarchyComponentWindow.floatingInstance.targetPosition = position; + + } + + + if (curEvent.holdingAlt) + buttonRect.MarkInteractive(); + + componentIcon(); + + mouseDown(); + mouseUp(); + + } + + void transformComponent() + { + if (!isRowHovered) return; + if (!curEvent.holdingAlt) return; + if (!go.GetComponent()) return; + + componentButton(fullRowRect.SetWidth(13).MoveX(1.5f), go.GetComponent()); + + } + void otherComponetns() + { + var buttonWidth = 13; + var minButtonX = rowRect.x + go.name.GetLabelWidth() + buttonWidth + 2; + var buttonRect = fullRowRect.SetWidthFromRight(buttonWidth).MoveX(-1.5f); + + if (PrefabUtility.IsAnyPrefabInstanceRoot(go) && !PrefabUtility.IsPartOfModelPrefab(go)) + buttonRect = buttonRect.MoveX(-13); + + foreach (var component in go.GetComponents()) + { + if (component is Transform) continue; + if (buttonRect.x < minButtonX) continue; + + componentButton(buttonRect, component); + + buttonRect = buttonRect.MoveX(-buttonWidth); + + } + + + } + + transformComponent(); + otherComponetns(); + + } + void activationToggle() + { + if (!VHierarchyMenu.activationToggleEnabled) return; + if (!isRowHovered) return; + + var toggleRect = fullRowRect.SetWidth(16).MoveX(1); + + + SetGUIColor(Greyscale(1, .9f)); + + var newActiveSelf = EditorGUI.Toggle(toggleRect, go.activeSelf); + + ResetGUIColor(); + + + if (newActiveSelf == go.activeSelf) return; + + var gos = Selection.gameObjects.Contains(go) ? Selection.gameObjects : new[] { go }; + var newActive = gos != null && !gos.Any(r => r && r.activeSelf); + + foreach (var r in gos) + r.RecordUndo(); + + foreach (var r in gos) + r.SetActive(newActiveSelf); + + GUI.FocusControl(null); + + } + void defaultParentIndicator() + { + if (!isDefaultParent) return; + + + + var drawCustomIcon = GetGameObjectInfo(go).hasIcon; + var drawDefaultIcon = !drawCustomIcon && (isRowBeingRenamed || (!VHierarchyMenu.minimalModeEnabled || (PrefabUtility.IsAddedGameObjectOverride(go) && PrefabUtility.IsPartOfPrefabInstance(go)))); + + var indicatorRect = rowRect.MoveX(go.name.GetLabelWidth(isBold: true) + 16.5f); + + if (!drawCustomIcon && !drawDefaultIcon) + indicatorRect = indicatorRect.MoveX(-16); + + + + SetGUIColor(Greyscale(1, .6f)); + SetLabelFontSize(10); + + GUI.Label(indicatorRect, "Default parent"); + + ResetLabelStyle(); + ResetGUIColor(); + + + + + + if (!fullRowRect.IsHovered()) return; + + + var buttonRect = indicatorRect.MoveX(68.5f).SetWidth(16).MoveY(.49f); + + var iconName = "CrossIcon"; + var iconSize = 10; + var colorNormal = Greyscale(isDarkTheme ? .75f : .2f, .55f); + var colorHovered = Greyscale(isDarkTheme ? 123f : .2f, 123f); + var colorPressed = Greyscale(isDarkTheme ? .75f : .5f); + var colorDisabled = Greyscale(isDarkTheme ? .53f : .55f); + + + if (!IconButton(buttonRect, iconName, iconSize, colorNormal, colorHovered, colorPressed)) return; + + EditorUtility.ClearDefaultParentObject(); + + + } + + void altDrag() + { + if (!curEvent.holdingAlt) return; + + void mouseDown() + { + if (!curEvent.isMouseDown) return; + if (!rowRect.IsHovered()) return; + + mouseDownPos = curEvent.mousePosition; + + } + void mouseDrag() + { + if (!curEvent.isMouseDrag) return; + if ((curEvent.mousePosition - mouseDownPos).magnitude < 5) return; + if (!rowRect.Contains(mouseDownPos)) return; + if (!rowRect.Contains(curEvent.mousePosition - curEvent.mouseDelta)) return; + if (DragAndDrop.objectReferences.Any()) return; + + DragAndDrop.PrepareStartDrag(); + DragAndDrop.objectReferences = new[] { go }; + DragAndDrop.StartDrag(go.name); + + } + + mouseDown(); + mouseDrag(); + + // altdrag has to be set up manually before altClick because altClick will use() mouseDown event to prevent selection change + } + void altClick() + { + if (!fullRowRect.IsHovered()) return; + if (!curEvent.holdingAlt) return; + if (Application.isPlaying) return; + + void mouseDown() + { + if (!curEvent.isMouseDown) return; + + curEvent.Use(); + + } + void mouseUp() + { + if (!curEvent.isMouseUp) return; + + var editMultiSelection = Selection.gameObjects.Length > 1 && Selection.gameObjects.Contains(go); + + var gosToEdit = (editMultiSelection ? Selection.gameObjects : new[] { go }).ToList(); + + + if (VHierarchyPaletteWindow.instance && VHierarchyPaletteWindow.instance.gameObjects.SequenceEqual(gosToEdit)) { VHierarchyPaletteWindow.instance.Close(); return; } + + var openNearRect = editMultiSelection ? lastVisibleSelectedRowRect : rowRect; + var position = EditorGUIUtility.GUIToScreenPoint(new Vector2(curEvent.mousePosition.x + 20, openNearRect.y - 13)); + // var position = EditorGUIUtility.GUIToScreenPoint(new Vector2(openNearRect.x - 14, openNearRect.y + 18)); + + if (!VHierarchyPaletteWindow.instance) + VHierarchyPaletteWindow.CreateInstance(position); + + VHierarchyPaletteWindow.instance.Init(gosToEdit); + VHierarchyPaletteWindow.instance.Focus(); + + VHierarchyPaletteWindow.instance.targetPosition = position; + + if (editMultiSelection) + Selection.objects = null; + + } + + mouseDown(); + mouseUp(); + + } + + + + setState(); + + drawing(); + + componentMinimap(); + activationToggle(); + defaultParentIndicator(); + + altDrag(); + altClick(); + + } + + List hierarchyLines_verticalGaps = new(); + bool hierarchyLines_isFirstRowDrawn; + int hierarchyLines_prevRowDepth; + + bool mousePressed; + Vector2 mouseDownPos; + + Rect lastVisibleSelectedRowRect; + + + + + public void RowGUI_Scene(Rect rowRect, Scene scene) + { + var fullRowRect = rowRect.SetX(32).SetXMax(rowRect.xMax + 16); + + var isRowHovered = fullRowRect.AddWidthFromRight(32).IsHovered(); + var isActiveScene = EditorSceneManager.GetActiveScene() == scene; + var isStickyHeader = rowRect.y != 0 && EditorGUIUtility.GUIToScreenPoint(rowRect.position).y - window.position.y == 45; + + + void set_hoveredScene() + { + if (curEvent.isLayout) + VHierarchy.hoveredScene = default; + + if (curEvent.isRepaint && isRowHovered) + VHierarchy.hoveredScene = scene; + + } + void sceneSelector() + { + if (!VHierarchyMenu.sceneSelectorEnabled) return; + if (!scene.isLoaded) return; + + + var nameWidth = (scene.name == "" ? "Untitled" : scene.name).GetLabelWidth(isBold: isActiveScene) + (scene.isDirty ? 5 : 0) + (isActiveScene ? -.5f : -1f); + var selectorRect = rowRect.MoveX(18).SetWidth(nameWidth + 16); + + var id = EditorGUIUtility.GUIToScreenRect(selectorRect).GetHashCode(); + var isPressed = id == pressedSceneSelectorId; + + var highlightName = selectorRect.IsHovered() || (VHierarchySceneSelectorWindow.instance && VHierarchySceneSelectorWindow.instance.sceneToReplace == scene); + + + + void dummyRow() + { + if (!highlightName) return; + if (!isDarkTheme) return; + + void background() + { + var backgroundColor = Application.unityVersion.Contains("2021") ? Greyscale(isDarkTheme ? .32f : .9f) + : Greyscale(isDarkTheme ? .16f : .9f); + + rowRect.AddWidthFromMid(123).AddHeight(-1).Draw(backgroundColor); + + } + void tripleDotButton() + { + GUI.DrawTexture(rowRect.SetWidthFromRight(16).MoveX(12), EditorIcons.GetIcon("More")); + } + void sceneIcon() + { + GUI.DrawTexture(rowRect.SetWidth(16), EditorIcons.GetIcon("SceneAsset Icon")); + } + void foldoutIcon() + { + if (scene.rootCount == 0) return; + + var isSceneExpanded = controller.expandedIds.Contains(scene.handle); + + GUI.DrawTexture(rowRect.SetWidth(16).MoveX(-15.5f).SetSizeFromMid(13), EditorIcons.GetIcon(isSceneExpanded ? "IN_foldout_on" : "IN_foldout")); + } + + background(); + tripleDotButton(); + sceneIcon(); + foldoutIcon(); + + } + + void dropdownIcon() + { + var iconRect = rowRect.MoveY(-.5f).MoveX(nameWidth + 14).SetWidth(16); + + + var iconBrightness = highlightName ? 1 : .78f; + + if (!isActiveScene) + iconBrightness *= .82f; + + if (isPressed) + iconBrightness *= .83f; + + if (!isDarkTheme) + iconBrightness = .35f; + + + + SetGUIColor(Greyscale(iconBrightness)); + + GUI.DrawTexture(iconRect, EditorIcons.GetIcon("Dropdown")); + + ResetGUIColor(); + + } + void highlightedName() + { + if (!curEvent.isRepaint) return; + if (!highlightName) return; + + var nameRect = rowRect.MoveX(18).SetWidth(nameWidth + 32); + + + + + var nameStyle = isActiveScene ? "TV LineBold" : "TV Line"; + + var nameText = scene.name == "" ? "Untitled" : scene.name; + + if (scene.isDirty) + nameText += "*"; + + + var nameBrightness = 1f; + + if (isPressed) + nameBrightness *= .83f; + + if (!isDarkTheme) + nameBrightness = .0f; + + + + SetGUIColor(Greyscale(nameBrightness)); + + GUI.skin.GetStyle(nameStyle).Draw(nameRect, nameText, false, false, true, true); + + ResetGUIColor(); + + } + void buttonLogic() + { + void mouseDown() + { + var couldBeMouseDown = isStickyHeader && isRowHovered && curEvent.isUsed && !isPressed; // gets used on sticky headers by default row gui + + if (!curEvent.isMouseDown && !couldBeMouseDown) return; + if (!selectorRect.IsHovered()) return; + + + pressedSceneSelectorId = id; + + mouseDownOnSelectorPos = curEvent.mousePosition; + + curEvent.Use(); + + } + void mouseUp() + { + var couldBeMouseUp = isStickyHeader && isRowHovered && curEvent.isUsed && isPressed; // gets used on sticky headers by default row gui + + if (!curEvent.isMouseUp && !couldBeMouseUp) return; + if (!isPressed) return; + + + pressedSceneSelectorId = 0; + + curEvent.Use(); + + + if (!selectorRect.IsHovered()) return; + + if (VHierarchySceneSelectorWindow.instance) + VHierarchySceneSelectorWindow.instance.Close(); + else + VHierarchySceneSelectorWindow.Open(EditorGUIUtility.GUIToScreenPoint(rowRect.position), scene); + + } + void mouseDrag() + { + if (!curEvent.isMouseDrag) return; + if (!isPressed) return; + + if (curEvent.mousePosition.DistanceTo(mouseDownOnSelectorPos) < 3) { curEvent.Use(); return; } + + pressedSceneSelectorId = 0; + + + var sceneHierarchy = window?.GetFieldValue("m_SceneHierarchy"); + + var treeViewController = sceneHierarchy.GetFieldValue("m_TreeView"); + var treeViewControllerData = treeViewController.GetMemberValue("data"); + + var item = treeViewControllerData.InvokeMethod("FindItem", scene.handle); + + treeViewController.GetMemberValue("dragging").InvokeMethod("StartDrag", item, new List()); + + } + + + selectorRect.MarkInteractive(); + + mouseDown(); + mouseUp(); + mouseDrag(); + + } + + + + dummyRow(); + + dropdownIcon(); + highlightedName(); + buttonLogic(); + + } + + set_hoveredScene(); + sceneSelector(); + + } + + int pressedSceneSelectorId; + + Vector2 mouseDownOnSelectorPos; + + + + + + + + + + public void UpdateState() + { + + var sceneHierarchy = window?.GetFieldValue("m_SceneHierarchy"); + + var treeViewController = sceneHierarchy.GetFieldValue("m_TreeView"); + var treeViewControllerData = treeViewController.GetMemberValue("data"); + + + isTreeFocused = EditorWindow.focusedWindow == window + && GUIUtility.keyboardControl == sceneHierarchy?.GetMemberValue("m_TreeViewKeyboardControlID"); + + renamingRow = EditorGUIUtility.editingTextField + && treeViewController?.GetMemberValue("state")?.GetMemberValue("renameOverlay")?.InvokeMethod("IsRenaming") == true; + + +#if UNITY_2021_1_OR_NEWER + dragSelectionList = treeViewController?.GetFieldValue("m_DragSelection")?.GetFieldValue>("m_List"); +#else + dragSelectionList = treeViewController?.GetFieldValue>("m_DragSelection"); +#endif + + + defaultParent = typeof(SceneView).InvokeMethod("GetDefaultParentObjectIfSet")?.gameObject; + + + } + + public bool isTreeFocused; + public bool renamingRow; + + public List dragSelectionList = new(); + + GameObject defaultParent; + + + + + + + + + + + + public VHierarchyGUI(EditorWindow window) => this.window = window; + + public EditorWindow window; + + public VHierarchyController controller => VHierarchy.controllers_byWindow[window]; + + } +} +#endif \ No newline at end of file diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyGUI.cs.meta b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyGUI.cs.meta new file mode 100644 index 0000000..ceb389a --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyGUI.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 879081410e5ee4ddcb0145a4a9937193 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyIconEditor.cs b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyIconEditor.cs new file mode 100644 index 0000000..8fb6b18 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyIconEditor.cs @@ -0,0 +1,6 @@ + + +// this file was present in a previus version and is supposed to be deleted now +// but asset store update delivery system doesn't allow deleting files +// so instead this file is now emptied +// feel free to delete it if you want diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyIconEditor.cs.meta b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyIconEditor.cs.meta new file mode 100644 index 0000000..90282a9 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyIconEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8710ada63f66c4909ab73d206f31e954 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyLibs.cs b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyLibs.cs new file mode 100644 index 0000000..a9484bd --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyLibs.cs @@ -0,0 +1,2563 @@ +#if UNITY_EDITOR +using Type = System.Type; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using UnityEngine; +using UnityEditor; +using System.Linq; +using System.Text.RegularExpressions; +using System.Diagnostics; +using System.Reflection; +using UnityEngine.Experimental.Rendering; +using static VHierarchy.Libs.VUtils; +using static VHierarchy.Libs.VGUI; + + +namespace VHierarchy.Libs +{ + public static class VUtils + { + + #region Reflection + + + public static object GetFieldValue(this object o, string fieldName) + { + var type = o as Type ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetFieldInfo(fieldName) is FieldInfo fieldInfo) + return fieldInfo.GetValue(target); + + + throw new System.Exception($"Field '{fieldName}' not found in type '{type.Name}' and its parent types"); + + } + public static object GetPropertyValue(this object o, string propertyName) + { + var type = o as Type ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetPropertyInfo(propertyName) is PropertyInfo propertyInfo) + return propertyInfo.GetValue(target); + + + throw new System.Exception($"Property '{propertyName}' not found in type '{type.Name}' and its parent types"); + + } + public static object GetMemberValue(this object o, string memberName) + { + var type = o as Type ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetFieldInfo(memberName) is FieldInfo fieldInfo) + return fieldInfo.GetValue(target); + + if (type.GetPropertyInfo(memberName) is PropertyInfo propertyInfo) + return propertyInfo.GetValue(target); + + + throw new System.Exception($"Member '{memberName}' not found in type '{type.Name}' and its parent types"); + + } + + public static void SetFieldValue(this object o, string fieldName, object value) + { + var type = o as Type ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetFieldInfo(fieldName) is FieldInfo fieldInfo) + fieldInfo.SetValue(target, value); + + + else throw new System.Exception($"Field '{fieldName}' not found in type '{type.Name}' and its parent types"); + + } + public static void SetPropertyValue(this object o, string propertyName, object value) + { + var type = o as Type ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetPropertyInfo(propertyName) is PropertyInfo propertyInfo) + propertyInfo.SetValue(target, value); + + + else throw new System.Exception($"Property '{propertyName}' not found in type '{type.Name}' and its parent types"); + + } + public static void SetMemberValue(this object o, string memberName, object value) + { + var type = o as Type ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetFieldInfo(memberName) is FieldInfo fieldInfo) + fieldInfo.SetValue(target, value); + + else if (type.GetPropertyInfo(memberName) is PropertyInfo propertyInfo) + propertyInfo.SetValue(target, value); + + + else throw new System.Exception($"Member '{memberName}' not found in type '{type.Name}' and its parent types"); + + } + + public static object InvokeMethod(this object o, string methodName, params object[] parameters) // todo handle null params (can't get their type) + { + var type = o as Type ?? o.GetType(); + var target = o is Type ? null : o; + + + if (type.GetMethodInfo(methodName, parameters.Select(r => r.GetType()).ToArray()) is MethodInfo methodInfo) + return methodInfo.Invoke(target, parameters); + + + throw new System.Exception($"Method '{methodName}' not found in type '{type.Name}', its parent types and interfaces"); + + } + + + public static T GetFieldValue(this object o, string fieldName) => (T)o.GetFieldValue(fieldName); + public static T GetPropertyValue(this object o, string propertyName) => (T)o.GetPropertyValue(propertyName); + public static T GetMemberValue(this object o, string memberName) => (T)o.GetMemberValue(memberName); + public static T InvokeMethod(this object o, string methodName, params object[] parameters) => (T)o.InvokeMethod(methodName, parameters); + + + + + public static FieldInfo GetFieldInfo(this Type type, string fieldName) + { + if (fieldInfoCache.TryGetValue(type, out var fieldInfosByNames)) + if (fieldInfosByNames.TryGetValue(fieldName, out var fieldInfo)) + return fieldInfo; + + + if (!fieldInfoCache.ContainsKey(type)) + fieldInfoCache[type] = new Dictionary(); + + for (var curType = type; curType != null; curType = curType.BaseType) + if (curType.GetField(fieldName, maxBindingFlags) is FieldInfo fieldInfo) + return fieldInfoCache[type][fieldName] = fieldInfo; + + + return fieldInfoCache[type][fieldName] = null; + + } + public static PropertyInfo GetPropertyInfo(this Type type, string propertyName) + { + if (propertyInfoCache.TryGetValue(type, out var propertyInfosByNames)) + if (propertyInfosByNames.TryGetValue(propertyName, out var propertyInfo)) + return propertyInfo; + + + if (!propertyInfoCache.ContainsKey(type)) + propertyInfoCache[type] = new Dictionary(); + + for (var curType = type; curType != null; curType = curType.BaseType) + if (curType.GetProperty(propertyName, maxBindingFlags) is PropertyInfo propertyInfo) + return propertyInfoCache[type][propertyName] = propertyInfo; + + + return propertyInfoCache[type][propertyName] = null; + + } + public static MethodInfo GetMethodInfo(this Type type, string methodName, params Type[] argumentTypes) + { + var methodHash = methodName.GetHashCode() ^ argumentTypes.Aggregate(0, (hash, r) => hash ^= r.GetHashCode()); + + + if (methodInfoCache.TryGetValue(type, out var methodInfosByHashes)) + if (methodInfosByHashes.TryGetValue(methodHash, out var methodInfo)) + return methodInfo; + + + + if (!methodInfoCache.ContainsKey(type)) + methodInfoCache[type] = new Dictionary(); + + for (var curType = type; curType != null; curType = curType.BaseType) + if (curType.GetMethod(methodName, maxBindingFlags, null, argumentTypes, null) is MethodInfo methodInfo) + return methodInfoCache[type][methodHash] = methodInfo; + + foreach (var interfaceType in type.GetInterfaces()) + if (interfaceType.GetMethod(methodName, maxBindingFlags, null, argumentTypes, null) is MethodInfo methodInfo) + return methodInfoCache[type][methodHash] = methodInfo; + + + + return methodInfoCache[type][methodHash] = null; + + } + + static Dictionary> fieldInfoCache = new(); + static Dictionary> propertyInfoCache = new(); + static Dictionary> methodInfoCache = new(); + + + + + + + public static T GetCustomAttributeCached(this MemberInfo memberInfo) where T : System.Attribute + { + if (!attributesCache.TryGetValue(memberInfo, out var attributes_byType)) + attributes_byType = attributesCache[memberInfo] = new(); + + if (!attributes_byType.TryGetValue(typeof(T), out var attribute)) + attribute = attributes_byType[typeof(T)] = memberInfo.GetCustomAttribute(); + + return attribute as T; + + } + + static Dictionary> attributesCache = new(); + + + + + + + public static List GetSubclasses(this Type t) => t.Assembly.GetTypes().Where(type => type.IsSubclassOf(t)).ToList(); + + public static object GetDefaultValue(this FieldInfo f, params object[] constructorVars) => f.GetValue(System.Activator.CreateInstance(((MemberInfo)f).ReflectedType, constructorVars)); + public static object GetDefaultValue(this FieldInfo f) => f.GetValue(System.Activator.CreateInstance(((MemberInfo)f).ReflectedType)); + + public static IEnumerable GetFieldsWithoutBase(this Type t) => t.GetFields().Where(r => !t.BaseType.GetFields().Any(rr => rr.Name == r.Name)); + public static IEnumerable GetPropertiesWithoutBase(this Type t) => t.GetProperties().Where(r => !t.BaseType.GetProperties().Any(rr => rr.Name == r.Name)); + + + public const BindingFlags maxBindingFlags = (BindingFlags)62; + + + + + + + + + #endregion + + #region Collections + + + public static class CollectionUtils + { + public static Dictionary MergeDictionaries(IEnumerable> dicts) + { + if (dicts.Count() == 0) return null; + if (dicts.Count() == 1) return dicts.First(); + + var mergedDict = new Dictionary(dicts.First()); + + foreach (var dict in dicts.Skip(1)) + foreach (var r in dict) + if (!mergedDict.ContainsKey(r.Key)) + mergedDict.Add(r.Key, r.Value); + + return mergedDict; + } + + } + + + public static T NextTo(this IEnumerable e, T to) => e.SkipWhile(r => !r.Equals(to)).Skip(1).FirstOrDefault(); + public static T PreviousTo(this IEnumerable e, T to) => e.Reverse().SkipWhile(r => !r.Equals(to)).Skip(1).FirstOrDefault(); + public static T NextToOrFirst(this IEnumerable e, T to) => e.NextTo(to) ?? e.First(); + public static T PreviousToOrLast(this IEnumerable e, T to) => e.PreviousTo(to) ?? e.Last(); + + public static IEnumerable InsertFirst(this IEnumerable ie, T t) => new[] { t }.Concat(ie); + + public static int IndexOfFirst(this List list, System.Func f) => list.FirstOrDefault(f) is T t ? list.IndexOf(t) : -1; + public static int IndexOfLast(this List list, System.Func f) => list.LastOrDefault(f) is T t ? list.IndexOf(t) : -1; + + public static void SortBy(this List list, System.Func keySelector) where T2 : System.IComparable => list.Sort((q, w) => keySelector(q).CompareTo(keySelector(w))); + + public static void RemoveValue(this IDictionary dictionary, TValue value) + { + if (dictionary.FirstOrDefault(r => r.Value.Equals(value)) is var kvp) + dictionary.Remove(kvp); + } + + public static void ForEach(this IEnumerable sequence, System.Action action) { foreach (T item in sequence) action(item); } + + + + public static T AddAt(this List l, T r, int i) + { + if (i < 0) i = 0; + if (i >= l.Count) + l.Add(r); + else + l.Insert(i, r); + return r; + } + public static T RemoveLast(this List l) + { + if (!l.Any()) return default; + + var r = l.Last(); + + l.RemoveAt(l.Count - 1); + + return r; + } + + public static void Add(this List list, params T[] items) + { + foreach (var r in items) + list.Add(r); + } + + + + + + + #endregion + + #region Math + + + public static class MathUtil // MathUtils name is taken by UnityEditor.MathUtils + { + + public static float TriangleArea(Vector2 A, Vector2 B, Vector2 C) => Vector3.Cross(A - B, A - C).z.Abs() / 2; + + public static Vector2 LineIntersection(Vector2 A, Vector2 B, Vector2 C, Vector2 D) + { + var a1 = B.y - A.y; + var b1 = A.x - B.x; + var c1 = a1 * A.x + b1 * A.y; + + var a2 = D.y - C.y; + var b2 = C.x - D.x; + var c2 = a2 * C.x + b2 * C.y; + + var d = a1 * b2 - a2 * b1; + + var x = (b2 * c1 - b1 * c2) / d; + var y = (a1 * c2 - a2 * c1) / d; + + return new Vector2(x, y); + + } + + + + + public static float Lerp(float f1, float f2, float t) => Mathf.LerpUnclamped(f1, f2, t); + public static float Lerp(ref float f1, float f2, float t) + { + return f1 = Lerp(f1, f2, t); + } + + public static Vector2 Lerp(Vector2 f1, Vector2 f2, float t) => Vector2.LerpUnclamped(f1, f2, t); + public static Vector2 Lerp(ref Vector2 f1, Vector2 f2, float t) + { + return f1 = Lerp(f1, f2, t); + } + + public static Vector3 Lerp(Vector3 f1, Vector3 f2, float t) => Vector3.LerpUnclamped(f1, f2, t); + public static Vector3 Lerp(ref Vector3 f1, Vector3 f2, float t) + { + return f1 = Lerp(f1, f2, t); + } + + public static Color Lerp(Color f1, Color f2, float t) => Color.LerpUnclamped(f1, f2, t); + public static Color Lerp(ref Color f1, Color f2, float t) + { + return f1 = Lerp(f1, f2, t); + } + + + public static float Lerp(float current, float target, float speed, float deltaTime) => Mathf.Lerp(current, target, GetLerpT(speed, deltaTime)); + public static float Lerp(ref float current, float target, float speed, float deltaTime) + { + return current = Lerp(current, target, speed, deltaTime); + } + + public static Vector2 Lerp(Vector2 current, Vector2 target, float speed, float deltaTime) => Vector2.Lerp(current, target, GetLerpT(speed, deltaTime)); + public static Vector2 Lerp(ref Vector2 current, Vector2 target, float speed, float deltaTime) + { + return current = Lerp(current, target, speed, deltaTime); + } + + public static Vector3 Lerp(Vector3 current, Vector3 target, float speed, float deltaTime) => Vector3.Lerp(current, target, GetLerpT(speed, deltaTime)); + public static Vector3 Lerp(ref Vector3 current, Vector3 target, float speed, float deltaTime) + { + return current = Lerp(current, target, speed, deltaTime); + } + + public static float SmoothDamp(float current, float target, float speed, ref float derivative, float deltaTime, float maxSpeed) => Mathf.SmoothDamp(current, target, ref derivative, .5f / speed, maxSpeed, deltaTime); + public static float SmoothDamp(float current, float target, float speed, ref float derivative, float deltaTime) + { + return Mathf.SmoothDamp(current, target, ref derivative, .5f / speed, Mathf.Infinity, deltaTime); + } + public static float SmoothDamp(float current, float target, float speed, ref float derivative) + { + return SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + } + public static float SmoothDamp(ref float current, float target, float speed, ref float derivative, float deltaTime, float maxSpeed) + { + return current = SmoothDamp(current, target, speed, ref derivative, deltaTime, maxSpeed); + } + public static float SmoothDamp(ref float current, float target, float speed, ref float derivative, float deltaTime) + { + return current = SmoothDamp(current, target, speed, ref derivative, deltaTime); + } + public static float SmoothDamp(ref float current, float target, float speed, ref float derivative) + { + return current = SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + } + + public static Vector2 SmoothDamp(Vector2 current, Vector2 target, float speed, ref Vector2 derivative, float deltaTime) => Vector2.SmoothDamp(current, target, ref derivative, .5f / speed, Mathf.Infinity, deltaTime); + public static Vector2 SmoothDamp(Vector2 current, Vector2 target, float speed, ref Vector2 derivative) + { + return SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + } + public static Vector2 SmoothDamp(ref Vector2 current, Vector2 target, float speed, ref Vector2 derivative, float deltaTime) + { + return current = SmoothDamp(current, target, speed, ref derivative, deltaTime); + } + public static Vector2 SmoothDamp(ref Vector2 current, Vector2 target, float speed, ref Vector2 derivative) + { + return current = SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + } + + public static Vector3 SmoothDamp(Vector3 current, Vector3 target, float speed, ref Vector3 derivative, float deltaTime) => Vector3.SmoothDamp(current, target, ref derivative, .5f / speed, Mathf.Infinity, deltaTime); + public static Vector3 SmoothDamp(Vector3 current, Vector3 target, float speed, ref Vector3 derivative) + { + return SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + } + public static Vector3 SmoothDamp(ref Vector3 current, Vector3 target, float speed, ref Vector3 derivative, float deltaTime) + { + return current = SmoothDamp(current, target, speed, ref derivative, deltaTime); + } + public static Vector3 SmoothDamp(ref Vector3 current, Vector3 target, float speed, ref Vector3 derivative) + { + return current = SmoothDamp(current, target, speed, ref derivative, Time.deltaTime); + } + + + public static float GetLerpT(float lerpSpeed, float deltaTime) => 1 - Mathf.Exp(-lerpSpeed * 2f * deltaTime); + public static float GetLerpT(float lerpSpeed) + { + return GetLerpT(lerpSpeed, Time.deltaTime); + } + + + + } + + + public static float DistanceTo(this float f1, float f2) => Mathf.Abs(f1 - f2); + public static float DistanceTo(this Vector2 f1, Vector2 f2) => (f1 - f2).magnitude; + public static float DistanceTo(this Vector3 f1, Vector3 f2) => (f1 - f2).magnitude; + + public static float Sign(this float f) => f == 0 ? 0 : Mathf.Sign(f); + + public static int Abs(this int f) => Mathf.Abs(f); + public static float Abs(this float f) => Mathf.Abs(f); + + public static int Clamp(this int f, int f0, int f1) => Mathf.Clamp(f, f0, f1); + public static float Clamp(this float f, float f0, float f1) => Mathf.Clamp(f, f0, f1); + + + public static float Clamp01(this float f) => Mathf.Clamp(f, 0, 1); + public static Vector2 Clamp01(this Vector2 f) => new(f.x.Clamp01(), f.y.Clamp01()); + public static Vector3 Clamp01(this Vector3 f) => new(f.x.Clamp01(), f.y.Clamp01(), f.z.Clamp01()); + + + public static int Pow(this int f, int pow) => (int)Mathf.Pow(f, pow); + public static float Pow(this float f, float pow) => Mathf.Pow(f, pow); + + public static float Round(this float f) => Mathf.Round(f); + public static float Ceil(this float f) => Mathf.Ceil(f); + public static float Floor(this float f) => Mathf.Floor(f); + + public static int RoundToInt(this float f) => Mathf.RoundToInt(f); + public static int CeilToInt(this float f) => Mathf.CeilToInt(f); + public static int FloorToInt(this float f) => Mathf.FloorToInt(f); + + public static int ToInt(this float f) => (int)f; + public static float ToFloat(this int f) => (float)f; + public static float ToFloat(this double f) => (float)f; + + + + public static float Sqrt(this float f) => Mathf.Sqrt(f); + + public static int Max(this int f, int ff) => Mathf.Max(f, ff); + public static int Min(this int f, int ff) => Mathf.Min(f, ff); + public static float Max(this float f, float ff) => Mathf.Max(f, ff); + public static float Min(this float f, float ff) => Mathf.Min(f, ff); + + public static float ClampMin(this float f, float limitMin) => Mathf.Max(f, limitMin); + public static float ClampMax(this float f, float limitMax) => Mathf.Min(f, limitMax); + + public static Vector3 ClampMaxMagnitude(this Vector3 v, float maxMagnitude) + { + if (v.sqrMagnitude <= maxMagnitude * maxMagnitude) + return v; + else + return v.normalized * maxMagnitude; + } + + + public static float Loop(this float f, float boundMin, float boundMax) + { + while (f < boundMin) f += boundMax - boundMin; + while (f > boundMax) f -= boundMax - boundMin; + return f; + } + public static float Loop(this float f, float boundMax) => f.Loop(0, boundMax); + + public static float PingPong(this float f, float boundMin, float boundMax) => boundMin + Mathf.PingPong(f - boundMin, boundMax - boundMin); + public static float PingPong(this float f, float boundMax) => f.PingPong(0, boundMax); + + + public static float Dot(this Vector3 v1, Vector3 v2) => Vector3.Dot(v1, v2); + + public static Vector3 Cross(this Vector3 v1, Vector3 v2) => Vector3.Cross(v1, v2); + + + public static Vector2 ProjectOn(this Vector2 v, Vector2 on) => Vector3.Project(v, on); + public static Vector3 ProjectOn(this Vector3 v, Vector3 on) => Vector3.Project(v, on); + + public static Vector3 ProjectOnPlane(this Vector3 v, Vector3 normal) => Vector3.ProjectOnPlane(v, normal); + + + public static float AngleTo(this Vector2 v, Vector2 to) => Vector2.Angle(v, to); + + public static Vector2 Rotate(this Vector2 v, float deg) => Quaternion.AngleAxis(deg, Vector3.forward) * v; + + public static float Smoothstep(this float f) { f = f.Clamp01(); return f * f * (3 - 2 * f); } + + public static float InverseLerp(this Vector2 v, Vector2 a, Vector2 b) + { + var ab = b - a; + var av = v - a; + return Vector2.Dot(av, ab) / Vector2.Dot(ab, ab); + } + + + public static bool IsOdd(this int i) => i % 2 == 1; + public static bool IsEven(this int i) => i % 2 == 0; + + public static bool IsInRange(this int i, int a, int b) => i >= a && i <= b; + public static bool IsInRange(this float i, float a, float b) => i >= a && i <= b; + + public static bool IsInRangeOf(this int i, IList list) => i.IsInRange(0, list.Count - 1); + public static bool IsInRangeOf(this int i, T[] array) => i.IsInRange(0, array.Length - 1); + + public static bool Approx(this float f1, float f2) => Mathf.Approximately(f1, f2); + + + + [System.Serializable] + public class GaussianKernel + { + public static float[,] GenerateArray(int size, float sharpness = .5f) + { + float[,] kr = new float[size, size]; + + if (size == 1) { kr[0, 0] = 1; return kr; } + + + var sigma = 1f - Mathf.Pow(sharpness, .1f) * .99999f; + var radius = (size / 2f).FloorToInt(); + + + var a = -2f * radius * radius / Mathf.Log(sigma); + var sum = 0f; + + for (int y = 0; y < size; y++) + for (int x = 0; x < size; x++) + { + var rX = size % 2 == 1 ? (x - radius) : (x - radius) + .5f; + var rY = size % 2 == 1 ? (y - radius) : (y - radius) + .5f; + var dist = Mathf.Sqrt(rX * rX + rY * rY); + kr[x, y] = Mathf.Exp(-dist * dist / a); + sum += kr[x, y]; + } + + for (int y = 0; y < size; y++) + for (int x = 0; x < size; x++) + kr[x, y] /= sum; + + return kr; + } + + + + public GaussianKernel(bool isEvenSize = false, int radius = 7, float sharpness = .5f) + { + this.isEvenSize = isEvenSize; + this.radius = radius; + this.sharpness = sharpness; + } + + public bool isEvenSize = false; + public int radius = 7; + public float sharpness = .5f; + + public int size => radius * 2 + (isEvenSize ? 0 : 1); + public float sigma => 1 - Mathf.Pow(sharpness, .1f) * .99999f; + + public float[,] Array2d() // todo test and use GenerateArray + { + float[,] kr = new float[size, size]; + + if (size == 1) { kr[0, 0] = 1; return kr; } + + var a = -2f * radius * radius / Mathf.Log(sigma); + var sum = 0f; + + for (int y = 0; y < size; y++) + for (int x = 0; x < size; x++) + { + var rX = size % 2 == 1 ? (x - radius) : (x - radius) + .5f; + var rY = size % 2 == 1 ? (y - radius) : (y - radius) + .5f; + var dist = Mathf.Sqrt(rX * rX + rY * rY); + kr[x, y] = Mathf.Exp(-dist * dist / a); + sum += kr[x, y]; + } + + for (int y = 0; y < size; y++) + for (int x = 0; x < size; x++) + kr[x, y] /= sum; + + return kr; + } + public float[] ArrayFlat() + { + var gk = Array2d(); + float[] flat = new float[size * size]; + + for (int i = 0; i < size; i++) + for (int j = 0; j < size; j++) + flat[(i * size + j)] = gk[i, j]; + + return flat; + } + + } + + + + + + + + #endregion + + #region Rects + + + public static Rect Resize(this Rect rect, float px) { rect.x += px; rect.y += px; rect.width -= px * 2; rect.height -= px * 2; return rect; } + + public static Rect SetPos(this Rect rect, Vector2 v) => rect.SetPos(v.x, v.y); + public static Rect SetPos(this Rect rect, float x, float y) { rect.x = x; rect.y = y; return rect; } + + public static Rect SetX(this Rect rect, float x) => rect.SetPos(x, rect.y); + public static Rect SetY(this Rect rect, float y) => rect.SetPos(rect.x, y); + public static Rect SetXMax(this Rect rect, float xMax) { rect.xMax = xMax; return rect; } + public static Rect SetYMax(this Rect rect, float yMax) { rect.yMax = yMax; return rect; } + + public static Rect SetMidPos(this Rect r, Vector2 v) => r.SetPos(v).MoveX(-r.width / 2).MoveY(-r.height / 2); + public static Rect SetMidPos(this Rect r, float x, float y) => r.SetMidPos(new Vector2(x, y)); + + public static Rect Move(this Rect rect, Vector2 v) { rect.position += v; return rect; } + public static Rect Move(this Rect rect, float x, float y) { rect.x += x; rect.y += y; return rect; } + public static Rect MoveX(this Rect rect, float px) { rect.x += px; return rect; } + public static Rect MoveY(this Rect rect, float px) { rect.y += px; return rect; } + + public static Rect SetWidth(this Rect rect, float f) { rect.width = f; return rect; } + public static Rect SetWidthFromMid(this Rect rect, float px) { rect.x += rect.width / 2; rect.width = px; rect.x -= rect.width / 2; return rect; } + public static Rect SetWidthFromRight(this Rect rect, float px) { rect.x += rect.width; rect.width = px; rect.x -= rect.width; return rect; } + + public static Rect SetHeight(this Rect rect, float f) { rect.height = f; return rect; } + public static Rect SetHeightFromMid(this Rect rect, float px) { rect.y += rect.height / 2; rect.height = px; rect.y -= rect.height / 2; return rect; } + public static Rect SetHeightFromBottom(this Rect rect, float px) { rect.y += rect.height; rect.height = px; rect.y -= rect.height; return rect; } + + public static Rect AddWidth(this Rect rect, float f) => rect.SetWidth(rect.width + f); + public static Rect AddWidthFromMid(this Rect rect, float f) => rect.SetWidthFromMid(rect.width + f); + public static Rect AddWidthFromRight(this Rect rect, float f) => rect.SetWidthFromRight(rect.width + f); + + public static Rect AddHeight(this Rect rect, float f) => rect.SetHeight(rect.height + f); + public static Rect AddHeightFromMid(this Rect rect, float f) => rect.SetHeightFromMid(rect.height + f); + public static Rect AddHeightFromBottom(this Rect rect, float f) => rect.SetHeightFromBottom(rect.height + f); + + public static Rect SetSize(this Rect rect, Vector2 v) => rect.SetWidth(v.x).SetHeight(v.y); + public static Rect SetSize(this Rect rect, float w, float h) => rect.SetWidth(w).SetHeight(h); + public static Rect SetSize(this Rect rect, float f) { rect.height = rect.width = f; return rect; } + + public static Rect SetSizeFromMid(this Rect r, Vector2 v) => r.Move(r.size / 2).SetSize(v).Move(-v / 2); + public static Rect SetSizeFromMid(this Rect r, float x, float y) => r.SetSizeFromMid(new Vector2(x, y)); + public static Rect SetSizeFromMid(this Rect r, float f) => r.SetSizeFromMid(new Vector2(f, f)); + + public static Rect AlignToPixelGrid(this Rect r) => GUIUtility.AlignRectToDevice(r); + + + + + + #endregion + + #region Vectors + + + public static Vector2 AddX(this Vector2 v, float f) => new(v.x + f, v.y + 0); + public static Vector2 AddY(this Vector2 v, float f) => new(v.x + 0, v.y + f); + + public static Vector3 AddX(this Vector3 v, float f) => new(v.x + f, v.y + 0, v.z + 0); + public static Vector3 AddY(this Vector3 v, float f) => new(v.x + 0, v.y + f, v.z + 0); + public static Vector3 AddZ(this Vector3 v, float f) => new(v.x + 0, v.y + 0, v.z + f); + + public static Vector3 SetX(this Vector3 v, float f) => new(f, v.y, v.z); + public static Vector3 SetY(this Vector3 v, float f) => new(v.x, f, v.z); + public static Vector3 SetZ(this Vector3 v, float f) => new(v.x, v.y, f); + + public static Vector2 xx(this Vector3 v) { return new Vector2(v.x, v.x); } + public static Vector2 xy(this Vector3 v) { return new Vector2(v.x, v.y); } + public static Vector2 xz(this Vector3 v) { return new Vector2(v.x, v.z); } + public static Vector2 yx(this Vector3 v) { return new Vector2(v.y, v.x); } + public static Vector2 yy(this Vector3 v) { return new Vector2(v.y, v.y); } + public static Vector2 yz(this Vector3 v) { return new Vector2(v.y, v.z); } + public static Vector2 zx(this Vector3 v) { return new Vector2(v.z, v.x); } + public static Vector2 zy(this Vector3 v) { return new Vector2(v.z, v.y); } + public static Vector2 zz(this Vector3 v) { return new Vector2(v.z, v.z); } + + + + + + #endregion + + #region Colors + + + public class ColorUtils + { + public static Color HSLToRGB(float h, float s, float l) + { + float hue2Rgb(float v1, float v2, float vH) + { + if (vH < 0f) + vH += 1f; + + if (vH > 1f) + vH -= 1f; + + if (6f * vH < 1f) + return v1 + (v2 - v1) * 6f * vH; + + if (2f * vH < 1f) + return v2; + + if (3f * vH < 2f) + return v1 + (v2 - v1) * (2f / 3f - vH) * 6f; + + return v1; + } + + if (s.Approx(0)) return new Color(l, l, l); + + float k1; + + if (l < .5f) + k1 = l * (1f + s); + else + k1 = l + s - s * l; + + + var k2 = 2f * l - k1; + + float r, g, b; + r = hue2Rgb(k2, k1, h + 1f / 3); + g = hue2Rgb(k2, k1, h); + b = hue2Rgb(k2, k1, h - 1f / 3); + + return new Color(r, g, b); + } + public static Color LCHtoRGB(float l, float c, float h) + { + l *= 100; + c *= 100; + h *= 360; + + double xw = 0.948110; + double yw = 1.00000; + double zw = 1.07304; + + float a = c * Mathf.Cos(Mathf.Deg2Rad * h); + float b = c * Mathf.Sin(Mathf.Deg2Rad * h); + + float fy = (l + 16) / 116; + float fx = fy + (a / 500); + float fz = fy - (b / 200); + + float x = (float)System.Math.Round(xw * ((System.Math.Pow(fx, 3) > 0.008856) ? System.Math.Pow(fx, 3) : ((fx - 16 / 116) / 7.787)), 5); + float y = (float)System.Math.Round(yw * ((System.Math.Pow(fy, 3) > 0.008856) ? System.Math.Pow(fy, 3) : ((fy - 16 / 116) / 7.787)), 5); + float z = (float)System.Math.Round(zw * ((System.Math.Pow(fz, 3) > 0.008856) ? System.Math.Pow(fz, 3) : ((fz - 16 / 116) / 7.787)), 5); + + float r = x * 3.2406f - y * 1.5372f - z * 0.4986f; + float g = -x * 0.9689f + y * 1.8758f + z * 0.0415f; + float bValue = x * 0.0557f - y * 0.2040f + z * 1.0570f; + + r = r > 0.0031308f ? 1.055f * (float)System.Math.Pow(r, 1 / 2.4) - 0.055f : r * 12.92f; + g = g > 0.0031308f ? 1.055f * (float)System.Math.Pow(g, 1 / 2.4) - 0.055f : g * 12.92f; + bValue = bValue > 0.0031308f ? 1.055f * (float)System.Math.Pow(bValue, 1 / 2.4) - 0.055f : bValue * 12.92f; + + // r = (float)System.Math.Round(System.Math.Max(0, System.Math.Min(1, r))); + // g = (float)System.Math.Round(System.Math.Max(0, System.Math.Min(1, g))); + // bValue = (float)System.Math.Round(System.Math.Max(0, System.Math.Min(1, bValue))); + + return new Color(r, g, bValue); + + } + + } + + + public static Color Greyscale(float brightness, float alpha = 1) => new(brightness, brightness, brightness, alpha); + + public static Color SetAlpha(this Color color, float alpha) { color.a = alpha; return color; } + public static Color MultiplyAlpha(this Color color, float k) { color.a *= k; return color; } + + + + + + #endregion + + #region Objects + + + public static Object[] FindObjects(Type type) + { +#if UNITY_2023_1_OR_NEWER + return Object.FindObjectsByType(type, FindObjectsSortMode.None); +#else + return Object.FindObjectsOfType(type); +#endif + } + public static T[] FindObjects() where T : Object + { +#if UNITY_2023_1_OR_NEWER + return Object.FindObjectsByType(FindObjectsSortMode.None); +#else + return Object.FindObjectsOfType(); +#endif + } + + public static void Destroy(this Object r) + { + if (Application.isPlaying) + Object.Destroy(r); + else + Object.DestroyImmediate(r); + + } + + public static void DestroyImmediate(this Object o) => Object.DestroyImmediate(o); + + + + + + #endregion + + #region GameObjects + + + public static bool IsPrefab(this GameObject go) => go.scene.name == null || go.scene.name == go.name; + + public static Bounds GetBounds(this GameObject go, bool local = false) + { + Bounds bounds = default; + + foreach (var r in go.GetComponentsInChildren()) + { + var b = local ? r.gameObject.GetComponent().sharedMesh.bounds : r.bounds; + + if (bounds == default) + bounds = b; + else + bounds.Encapsulate(b); + } + + foreach (var r in go.GetComponentsInChildren()) + { + var b = local ? new Bounds(r.terrainData.size / 2, r.terrainData.size) : new Bounds(r.transform.position + r.terrainData.size / 2, r.terrainData.size); + + if (bounds == default) + bounds = b; + else + bounds.Encapsulate(new Bounds(r.transform.position + r.terrainData.size / 2, r.terrainData.size)); + + } + + foreach (var r in go.GetComponentsInChildren()) + { + var localBounds = RectTransformUtility.CalculateRelativeRectTransformBounds(r); + var worldBounds = new Bounds(r.TransformPoint(localBounds.center), r.TransformVector(localBounds.size)); + + if (bounds == default) + bounds = worldBounds; + else + bounds.Encapsulate(worldBounds); + + } + + + if (bounds == default) + bounds.center = go.transform.position; + + return bounds; + } + + + + + + #endregion + + #region Text + + + public static bool IsEmpty(this string s) => s == ""; + public static bool IsNullOrEmpty(this string s) => string.IsNullOrEmpty(s); + + public static bool IsLower(this char c) => System.Char.IsLower(c); + public static bool IsUpper(this char c) => System.Char.IsUpper(c); + public static bool IsDigit(this char c) => System.Char.IsDigit(c); + public static bool IsLetter(this char c) => System.Char.IsLetter(c); + public static bool IsWhitespace(this char c) => System.Char.IsWhiteSpace(c); + + public static char ToLower(this char c) => System.Char.ToLower(c); + public static char ToUpper(this char c) => System.Char.ToUpper(c); + + + + public static string Decamelcase(this string s) + { + return Regex.Replace(Regex.Replace(s, @"(\P{Ll})(\P{Ll}\p{Ll})", "$1 $2"), @"(\p{Ll})(\P{Ll})", "$1 $2"); + } + public static string FormatVariableName(this string s, bool lowercaseFollowingWords = true) + { + return string.Join(" ", s.Decamelcase() + .Split(' ') + .Select(r => new[] { "", "and", "or", "with", "without", "by", "from" }.Contains(r.ToLower()) || (lowercaseFollowingWords && !s.Trim().StartsWith(r)) ? r.ToLower() + : r.Substring(0, 1).ToUpper() + r.Substring(1))).Trim(' '); + } + + public static string Remove(this string s, string toRemove) + { + if (toRemove == "") return s; + return s.Replace(toRemove, ""); + } + + + + + + + #endregion + + #region Paths + + + public static bool HasParentPath(this string path) => path.LastIndexOf('/') > 0; + public static string GetParentPath(this string path) => path.HasParentPath() ? path.Substring(0, path.LastIndexOf('/')) : ""; + + public static string GetFilename(this string path, bool withExtension = false) => withExtension ? Path.GetFileName(path) : Path.GetFileNameWithoutExtension(path); + public static string GetExtension(this string path) => Path.GetExtension(path); + + + public static string ToGlobalPath(this string localPath) => Application.dataPath + "/" + localPath.Substring(0, localPath.Length - 1); + public static string ToLocalPath(this string globalPath) => "Assets" + globalPath.Replace(Application.dataPath, ""); + + + + public static string CombinePath(this string p1, string p2, bool useBackslashOnWindows = false) + { + if (useBackslashOnWindows) // false by default because all paths in unity use forward slashes, even on Windows + return Path.Combine(p1, p2); + else + return Path.Combine(p1, p2).Replace('\\', '/'); + + } + + public static bool IsSubpathOf(this string path, string of) => path.StartsWith(of + "/") || of == ""; + + public static string GetDirectory(this string pathOrDirectory) + { + var directory = pathOrDirectory.Contains('.') ? pathOrDirectory.Substring(0, pathOrDirectory.LastIndexOf('/')) : pathOrDirectory; + + if (directory.Contains('.')) + directory = directory.Substring(0, directory.LastIndexOf('/')); + + return directory; + + } + + public static bool DirectoryExists(this string pathOrDirectory) => Directory.Exists(pathOrDirectory.GetDirectory()); + + public static string EnsureDirExists(this string pathOrDirectory) // todo to EnsureDirectoryExists + { + var directory = pathOrDirectory.GetDirectory(); + + if (directory.HasParentPath() && !Directory.Exists(directory.GetParentPath())) + EnsureDirExists(directory.GetParentPath()); + + if (!Directory.Exists(directory)) + Directory.CreateDirectory(directory); + + return pathOrDirectory; + + } + + + + public static string ClearDir(this string dir) + { + if (!Directory.Exists(dir)) return dir; + + var diri = new DirectoryInfo(dir); + foreach (var r in diri.EnumerateFiles()) r.Delete(); + foreach (var r in diri.EnumerateDirectories()) r.Delete(true); + + return dir; + } + + + + + + +#if UNITY_EDITOR + + public static string EnsurePathIsUnique(this string path) + { + if (!path.DirectoryExists()) return path; + + var s = AssetDatabase.GenerateUniqueAssetPath(path); // returns empty if parent dir doesnt exist + + return s == "" ? path : s; + + } + + public static void EnsureDirExistsAndRevealInFinder(string dir) + { + EnsureDirExists(dir); + UnityEditor.EditorUtility.OpenWithDefaultApp(dir); + } + +#endif + + + + #endregion + + #region AssetDatabase + +#if UNITY_EDITOR + + public static AssetImporter GetImporter(this Object t) => AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(t)); + + public static string ToPath(this string guid) => AssetDatabase.GUIDToAssetPath(guid); // returns empty string if not found + public static List ToPaths(this IEnumerable guids) => guids.Select(r => r.ToPath()).ToList(); + + + public static string ToGuid(this string pathInProject) => AssetDatabase.AssetPathToGUID(pathInProject); + public static List ToGuids(this IEnumerable pathsInProject) => pathsInProject.Select(r => r.ToGuid()).ToList(); + + public static string GetPath(this Object o) => AssetDatabase.GetAssetPath(o); + public static string GetGuid(this Object o) => AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(o)); + + public static string GetScriptPath(string scriptName) => AssetDatabase.FindAssets("t: script " + scriptName, null).FirstOrDefault()?.ToPath() ?? "scirpt not found"; // todonow to editorutils + + + +#endif + + + + + + #endregion + + #region Serialization + + + [System.Serializable] + public class SerializableDictionary : Dictionary, ISerializationCallbackReceiver + { + [SerializeField] List keys = new(); + [SerializeField] List values = new(); + + public void OnBeforeSerialize() + { + keys.Clear(); + values.Clear(); + + foreach (KeyValuePair kvp in this) + { + keys.Add(kvp.Key); + values.Add(kvp.Value); + } + + } + public void OnAfterDeserialize() + { + this.Clear(); + + for (int i = 0; i < keys.Count; i++) + this[keys[i]] = values[i]; + + } + + } + + +#if UNITY_EDITOR + + public static object GetBoxedValue(this SerializedProperty p) + { + +#if UNITY_2022_1_OR_NEWER + switch (p.propertyType) + { + case SerializedPropertyType.Integer: + switch (p.numericType) + { + case SerializedPropertyNumericType.Int8: return (sbyte)p.intValue; + case SerializedPropertyNumericType.UInt8: return (byte)p.uintValue; + case SerializedPropertyNumericType.Int16: return (short)p.intValue; + case SerializedPropertyNumericType.UInt16: return (ushort)p.uintValue; + case SerializedPropertyNumericType.Int32: return p.intValue; + case SerializedPropertyNumericType.UInt32: return p.uintValue; + case SerializedPropertyNumericType.Int64: return p.longValue; + case SerializedPropertyNumericType.UInt64: return p.ulongValue; + default: return p.intValue; + + } + + case SerializedPropertyType.Float: + if (p.numericType == SerializedPropertyNumericType.Double) + return p.doubleValue; + else + return p.floatValue; + + case SerializedPropertyType.Hash128: return p.hash128Value; + case SerializedPropertyType.Character: return (ushort)p.uintValue; + case SerializedPropertyType.Gradient: return p.gradientValue; + case SerializedPropertyType.ManagedReference: return p.managedReferenceValue; + + + } +#endif + + switch (p.propertyType) + { + case SerializedPropertyType.Integer: return p.intValue; + case SerializedPropertyType.Float: return p.floatValue; + case SerializedPropertyType.Vector2: return p.vector2Value; + case SerializedPropertyType.Vector3: return p.vector3Value; + case SerializedPropertyType.Vector4: return p.vector4Value; + case SerializedPropertyType.Vector2Int: return p.vector2IntValue; + case SerializedPropertyType.Vector3Int: return p.vector3IntValue; + case SerializedPropertyType.Quaternion: return p.quaternionValue; + case SerializedPropertyType.Rect: return p.rectValue; + case SerializedPropertyType.RectInt: return p.rectIntValue; + case SerializedPropertyType.Bounds: return p.boundsValue; + case SerializedPropertyType.BoundsInt: return p.boundsIntValue; + case SerializedPropertyType.Enum: return p.enumValueIndex; + case SerializedPropertyType.Boolean: return p.boolValue; + case SerializedPropertyType.String: return p.stringValue; + case SerializedPropertyType.Color: return p.colorValue; + case SerializedPropertyType.ArraySize: return p.intValue; + case SerializedPropertyType.Character: return (ushort)p.intValue; + case SerializedPropertyType.AnimationCurve: return p.animationCurveValue; + case SerializedPropertyType.ObjectReference: return p.objectReferenceValue; + case SerializedPropertyType.ExposedReference: return p.exposedReferenceValue; + case SerializedPropertyType.FixedBufferSize: return p.intValue; + case SerializedPropertyType.LayerMask: return (LayerMask)p.intValue; + + } + + + return _noValue; + + } + public static void SetBoxedValue(this SerializedProperty p, object value) + { + if (value == _noValue) return; + + try + { + +#if UNITY_2022_1_OR_NEWER + switch (p.propertyType) + { + case SerializedPropertyType.ArraySize: + case SerializedPropertyType.Integer: + if (p.numericType == SerializedPropertyNumericType.UInt64) + p.ulongValue = System.Convert.ToUInt64(value); + else + p.longValue = System.Convert.ToInt64(value); + return; + + case SerializedPropertyType.Float: + if (p.numericType == SerializedPropertyNumericType.Double) + p.doubleValue = System.Convert.ToDouble(value); + else + p.floatValue = System.Convert.ToSingle(value); + return; + + case SerializedPropertyType.Character: p.uintValue = System.Convert.ToUInt16(value); return; + case SerializedPropertyType.Gradient: p.gradientValue = (Gradient)value; return; + case SerializedPropertyType.Hash128: p.hash128Value = (Hash128)value; return; + + } +#endif + + switch (p.propertyType) + { + case SerializedPropertyType.ArraySize: + case SerializedPropertyType.Integer: p.intValue = System.Convert.ToInt32(value); return; + case SerializedPropertyType.Float: p.floatValue = System.Convert.ToSingle(value); return; + case SerializedPropertyType.Vector2: p.vector2Value = (Vector2)value; return; + case SerializedPropertyType.Vector3: p.vector3Value = (Vector3)value; return; + case SerializedPropertyType.Vector4: p.vector4Value = (Vector4)value; return; + case SerializedPropertyType.Vector2Int: p.vector2IntValue = (Vector2Int)value; return; + case SerializedPropertyType.Vector3Int: p.vector3IntValue = (Vector3Int)value; return; + case SerializedPropertyType.Quaternion: p.quaternionValue = (Quaternion)value; return; + case SerializedPropertyType.Rect: p.rectValue = (Rect)value; return; + case SerializedPropertyType.RectInt: p.rectIntValue = (RectInt)value; return; + case SerializedPropertyType.Bounds: p.boundsValue = (Bounds)value; return; + case SerializedPropertyType.BoundsInt: p.boundsIntValue = (BoundsInt)value; return; + case SerializedPropertyType.String: p.stringValue = (string)value; return; + case SerializedPropertyType.Boolean: p.boolValue = (bool)value; return; + case SerializedPropertyType.Enum: p.enumValueIndex = (int)value; return; + case SerializedPropertyType.Color: p.colorValue = (Color)value; return; + case SerializedPropertyType.AnimationCurve: p.animationCurveValue = (AnimationCurve)value; return; + case SerializedPropertyType.ObjectReference: p.objectReferenceValue = (UnityEngine.Object)value; return; + case SerializedPropertyType.ExposedReference: p.exposedReferenceValue = (UnityEngine.Object)value; return; + case SerializedPropertyType.ManagedReference: p.managedReferenceValue = value; return; + + case SerializedPropertyType.LayerMask: + try + { + p.intValue = ((LayerMask)value).value; return; + } + catch (System.InvalidCastException) + { + p.intValue = System.Convert.ToInt32(value); return; + } + + } + + } + catch { } + + } + + static object _noValue = new(); + +#endif + + + + + #endregion + + #region GlobalID + +#if UNITY_EDITOR + + [System.Serializable] + public struct GlobalID : System.IEquatable + { + public Object GetObject() => GlobalObjectId.GlobalObjectIdentifierToObjectSlow(globalObjectId); + public int GetObjectInstanceId() => GlobalObjectId.GlobalObjectIdentifierToInstanceIDSlow(globalObjectId); + + + public int idType => globalObjectId.identifierType; + public string guid => globalObjectId.assetGUID.ToString(); + public ulong fileId => globalObjectId.targetObjectId; + public ulong prefabId => globalObjectId.targetPrefabId; + + public bool isNull => globalObjectId.identifierType == 0; + public bool isAsset => globalObjectId.identifierType == 1; + public bool isSceneObject => globalObjectId.identifierType == 2; + + public GlobalObjectId globalObjectId => _globalObjectId.Equals(default) && globalObjectIdString != null && GlobalObjectId.TryParse(globalObjectIdString, out var r) ? _globalObjectId = r : _globalObjectId; + public GlobalObjectId _globalObjectId; + + public GlobalID(Object o) => globalObjectIdString = (_globalObjectId = GlobalObjectId.GetGlobalObjectIdSlow(o)).ToString(); + public GlobalID(string s) => globalObjectIdString = GlobalObjectId.TryParse(s, out _globalObjectId) ? s : s; + + public string globalObjectIdString; + + + + public bool Equals(GlobalID other) => this.globalObjectIdString.Equals(other.globalObjectIdString); + + public static bool operator ==(GlobalID a, GlobalID b) => a.Equals(b); + public static bool operator !=(GlobalID a, GlobalID b) => !a.Equals(b); + + public override bool Equals(object other) => other is GlobalID otherglobalID && this.Equals(otherglobalID); + public override int GetHashCode() => globalObjectIdString == null ? 0 : globalObjectIdString.GetHashCode(); + + + public override string ToString() => globalObjectIdString; + + + + + public GlobalID UnpackForPrefab() + { + var unpackedFileId = (this.fileId ^ this.prefabId) & 0x7fffffffffffffff; + + var unpackedGId = new GlobalID($"GlobalObjectId_V1-{this.idType}-{this.guid}-{unpackedFileId}-0"); + + return unpackedGId; + + } + + } + + public static GlobalID GetGlobalID(this Object o) => new(o); + public static GlobalID[] GetGlobalIDs(this IEnumerable instanceIds) + { + var unityGlobalIds = new GlobalObjectId[instanceIds.Count()]; + + GlobalObjectId.GetGlobalObjectIdsSlow(instanceIds.ToArray(), unityGlobalIds); + + var globalIds = unityGlobalIds.Select(r => new GlobalID(r.ToString())); + + return globalIds.ToArray(); + + } + + public static Object[] GetObjects(this IEnumerable globalIDs) + { + var goids = globalIDs.Select(r => r.globalObjectId).ToArray(); + + var objects = new Object[goids.Length]; + + GlobalObjectId.GlobalObjectIdentifiersToObjectsSlow(goids, objects); + + return objects; + + } + public static int[] GetObjectInstanceIds(this IEnumerable globalIDs) + { + var goids = globalIDs.Select(r => r.globalObjectId).ToArray(); + + var iids = new int[goids.Length]; + + GlobalObjectId.GlobalObjectIdentifiersToInstanceIDsSlow(goids, iids); + + return iids; + + } + + +#endif + + + + + #endregion + + #region Editor + +#if UNITY_EDITOR + + + public static class EditorUtils + { + + public static void OpenFolder(string path) + { + var folder = AssetDatabase.LoadAssetAtPath(path, typeof(Object)); + + var t = typeof(Editor).Assembly.GetType("UnityEditor.ProjectBrowser"); + var w = (EditorWindow)t.GetField("s_LastInteractedProjectBrowser").GetValue(null); + + var m_ListAreaState = t.GetField("m_ListAreaState", maxBindingFlags).GetValue(w); + + m_ListAreaState.GetType().GetField("m_SelectedInstanceIDs").SetValue(m_ListAreaState, new List { folder.GetInstanceID() }); + + t.GetMethod("OpenSelectedFolders", maxBindingFlags).Invoke(null, null); + + } + + public static void PingObject(Object o, bool select = false, bool focusProjectWindow = true) + { + if (select) + { + Selection.activeObject = null; + Selection.activeObject = o; + } + if (focusProjectWindow) EditorUtility.FocusProjectWindow(); + EditorGUIUtility.PingObject(o); + + } + public static void PingObject(string guid, bool select = false, bool focusProjectWindow = true) => PingObject(AssetDatabase.LoadAssetAtPath(guid.ToPath())); + + public static EditorWindow OpenObjectPicker(Object obj = null, bool allowSceneObjects = false, string searchFilter = "", int controlID = 0) where T : Object + { + EditorGUIUtility.ShowObjectPicker(obj, allowSceneObjects, searchFilter, controlID); + + return Resources.FindObjectsOfTypeAll(typeof(Editor).Assembly.GetType("UnityEditor.ObjectSelector")).FirstOrDefault() as EditorWindow; + + } + public static EditorWindow OpenColorPicker(System.Action colorChangedCallback, Color color, bool showAlpha = true, bool hdr = false) + { + typeof(Editor).Assembly.GetType("UnityEditor.ColorPicker").InvokeMethod("Show", colorChangedCallback, color, showAlpha, hdr); + + return typeof(Editor).Assembly.GetType("UnityEditor.ColorPicker").GetPropertyValue("instance"); + + } + + + + public static bool CheckUnityVersion(string versionQuery) + { + if (versionQueryCache.TryGetValue(versionQuery, out var cachedResult)) return cachedResult; + + if (versionQuery.Any(r => r.IsLetter() && !versionQuery.EndsWith(" or older") && !versionQuery.EndsWith(" or newer"))) throw new System.ArgumentException("Invalid unity version query"); + + + + + var curVersion = new string(Application.unityVersion.TakeWhile(r => !r.IsLetter()).ToArray()); + + var curMajor = int.Parse(curVersion.Split('.')[0]); + var curMinor = int.Parse(curVersion.Split('.')[1]); + var curPatch = int.Parse(curVersion.Split('.')[2]); + + + + + + var givenVersion = new string(versionQuery.TakeWhile(r => !r.IsWhitespace()).ToArray()); + + var isMinorGiven = givenVersion.Count(r => r == '.') >= 1; + var isPatchGiven = givenVersion.Count(r => r == '.') >= 2; + + var givenMajor = int.Parse(givenVersion.Split('.')[0]); + var givenMinor = isMinorGiven ? int.Parse(givenVersion.Split('.')[1]) : 0; + var givenPatch = isPatchGiven ? int.Parse(givenVersion.Split('.')[2]) : 0; + + + + + + + var curVersionCanBeNewer = versionQuery.Contains("or newer"); + var curVersionCanBeOlder = versionQuery.Contains("or older"); + + + if (curMajor > givenMajor) return versionQueryCache[versionQuery] = curVersionCanBeNewer; + if (curMajor < givenMajor) return versionQueryCache[versionQuery] = curVersionCanBeOlder; + + if (!isMinorGiven) return versionQueryCache[versionQuery] = true; + + + if (curMinor > givenMinor) return versionQueryCache[versionQuery] = curVersionCanBeNewer; + if (curMinor < givenMinor) return versionQueryCache[versionQuery] = curVersionCanBeOlder; + + if (!isPatchGiven) return versionQueryCache[versionQuery] = true; + + + if (curPatch > givenPatch) return versionQueryCache[versionQuery] = curVersionCanBeNewer; + if (curPatch < givenPatch) return versionQueryCache[versionQuery] = curVersionCanBeOlder; + + return versionQueryCache[versionQuery] = true; + + + + + // query examples: + // + // "2022.3.5 or newer" + // "2022.3.5 or older" + // "2022.3 or older" + // "2022.3" + // "2022" + + } + + static Dictionary versionQueryCache = new(); + + + + public static void SetSymbolDefinedInAsmdef(string asmdefName, string symbol, bool defined) + { + var isDefined = IsSymbolDefinedInAsmdef(asmdefName, symbol); + var shouldBeDefined = defined; + + if (shouldBeDefined && !isDefined) + DefineSymbolInAsmdef(asmdefName, symbol); + + if (!shouldBeDefined && isDefined) + UndefineSymbolInAsmdef(asmdefName, symbol); + + } + public static bool IsSymbolDefinedInAsmdef(string asmdefName, string symbol) + { + var path = AssetDatabase.FindAssets("t: asmdef " + asmdefName, null).First().ToPath(); + var importer = AssetImporter.GetAtPath(path); + + var editorType = typeof(Editor).Assembly.GetType("UnityEditor.AssemblyDefinitionImporterInspector"); + var editor = Editor.CreateEditor(importer, editorType); + + var state = editor.GetFieldValue("m_ExtraDataTargets").First(); + + + var definesList = state.GetFieldValue("versionDefines"); + var isSymbolDefined = Enumerable.Range(0, definesList.Count).Any(i => definesList[i].GetFieldValue("define") == symbol); + + + Object.DestroyImmediate(editor); + + return isSymbolDefined; + + } + + static void DefineSymbolInAsmdef(string asmdefName, string symbol) + { + var path = AssetDatabase.FindAssets("t: asmdef " + asmdefName, null).First().ToPath(); + var importer = AssetImporter.GetAtPath(path); + + var editorType = typeof(Editor).Assembly.GetType("UnityEditor.AssemblyDefinitionImporterInspector"); + var editor = Editor.CreateEditor(importer, editorType); + + var state = editor.GetFieldValue("m_ExtraDataTargets").First(); + + + var definesList = state.GetFieldValue("versionDefines"); + + var defineType = definesList.GetType().GenericTypeArguments[0]; + var newDefine = System.Activator.CreateInstance(defineType); + + newDefine.SetFieldValue("name", "Unity"); + newDefine.SetFieldValue("define", symbol); + + definesList.Add(newDefine); + + + editor.InvokeMethod("Apply"); + + Object.DestroyImmediate(editor); + + } + static void UndefineSymbolInAsmdef(string asmdefName, string symbol) + { + var path = AssetDatabase.FindAssets("t: asmdef " + asmdefName, null).First().ToPath(); + var importer = AssetImporter.GetAtPath(path); + + var editorType = typeof(Editor).Assembly.GetType("UnityEditor.AssemblyDefinitionImporterInspector"); + var editor = Editor.CreateEditor(importer, editorType); + + var state = editor.GetFieldValue("m_ExtraDataTargets").First(); + + + var definesList = state.GetFieldValue("versionDefines"); + + var defineIndex = Enumerable.Range(0, definesList.Count).First(i => definesList[i].GetFieldValue("define") == symbol); + + definesList.RemoveAt(defineIndex); + + + editor.InvokeMethod("Apply"); + + Object.DestroyImmediate(editor); + + } + + + + + public static int GetCurrendUndoGroupIndex() + { + var args = new object[] { _dummyList, 0 }; + + typeof(Undo).GetMethodInfo("GetRecords", typeof(List), typeof(int).MakeByRefType()) + .Invoke(null, args); + + + return (int)args[1]; + + } + + static List _dummyList = new(); + + + + + + public static void Hide(string path) + { + if (IsHidden(path)) return; + + if (File.Exists(path)) + File.Move(path, path + "~"); + + + path += ".meta"; + if (File.Exists(path)) + File.Move(path, path + "~"); + } + public static void Unhide(string path) + { + if (!IsHidden(path)) return; + if (path.EndsWith("~")) path = path.Substring(0, path.Length - 1); + + if (File.Exists(path + "~")) + File.Move(path + "~", path); + + path += ".meta"; + if (File.Exists(path + "~")) + File.Move(path + "~", path); + } + public static bool IsHidden(string path) => path.EndsWith("~") || File.Exists(path + "~"); + + + public static void CopyDirectoryDeep(string sourcePath, string destinationPath) + { + CopyDirectoryRecursively(sourcePath, destinationPath); + + var metas = GetFilesRecursively(destinationPath, (f) => f.EndsWith(".meta")); + var guidTable = new List<(string originalGuid, string newGuid)>(); + + foreach (string meta in metas) + { + StreamReader file = new(meta); + file.ReadLine(); + string guidLine = file.ReadLine(); + file.Close(); + string originalGuid = guidLine.Substring(6, guidLine.Length - 6); + string newGuid = GUID.Generate().ToString().Replace("-", ""); + guidTable.Add((originalGuid, newGuid)); + } + + var allFiles = GetFilesRecursively(destinationPath); + + foreach (string fileToModify in allFiles) + { + string content = File.ReadAllText(fileToModify); + + foreach (var guidPair in guidTable) + { + content = content.Replace(guidPair.originalGuid, guidPair.newGuid); + } + + File.WriteAllText(fileToModify, content); + } + + AssetDatabase.Refresh(); + } + + private static void CopyDirectoryRecursively(string sourceDirName, string destDirName) + { + DirectoryInfo dir = new(sourceDirName); + + DirectoryInfo[] dirs = dir.GetDirectories(); + + if (!Directory.Exists(destDirName)) + { + Directory.CreateDirectory(destDirName); + } + + FileInfo[] files = dir.GetFiles(); + foreach (FileInfo file in files) + { + string temppath = Path.Combine(destDirName, file.Name); + file.CopyTo(temppath, false); + } + + foreach (DirectoryInfo subdir in dirs) + { + string temppath = Path.Combine(destDirName, subdir.Name); + CopyDirectoryRecursively(subdir.FullName, temppath); + } + } + + private static List GetFilesRecursively(string path, System.Func criteria = null, List files = null) + { + if (files == null) + { + files = new List(); + } + + files.AddRange(Directory.GetFiles(path).Where(f => criteria == null || criteria(f))); + + foreach (string directory in Directory.GetDirectories(path)) + { + GetFilesRecursively(directory, criteria, files); + } + + return files; + } + + + + + + // for non-extension methods + + } + + + public static class EditorPrefsCached + { + public static int GetInt(string key, int defaultValue = 0) + { + if (ints_byKey.ContainsKey(key)) + return ints_byKey[key]; + else + return ints_byKey[key] = EditorPrefs.GetInt(key, defaultValue); + + } + public static bool GetBool(string key, bool defaultValue = false) + { + if (bools_byKey.ContainsKey(key)) + return bools_byKey[key]; + else + return bools_byKey[key] = EditorPrefs.GetBool(key, defaultValue); + + } + public static float GetFloat(string key, float defaultValue = 0) + { + if (floats_byKey.ContainsKey(key)) + return floats_byKey[key]; + else + return floats_byKey[key] = EditorPrefs.GetFloat(key, defaultValue); + + } + public static string GetString(string key, string defaultValue = "") + { + if (strings_byKey.ContainsKey(key)) + return strings_byKey[key]; + else + return strings_byKey[key] = EditorPrefs.GetString(key, defaultValue); + + } + + public static void SetInt(string key, int value) + { + ints_byKey[key] = value; + + EditorPrefs.SetInt(key, value); + + } + public static void SetBool(string key, bool value) + { + bools_byKey[key] = value; + + EditorPrefs.SetBool(key, value); + + } + public static void SetFloat(string key, float value) + { + floats_byKey[key] = value; + + EditorPrefs.SetFloat(key, value); + + } + public static void SetString(string key, string value) + { + strings_byKey[key] = value; + + EditorPrefs.SetString(key, value); + + } + + + static Dictionary ints_byKey = new(); + static Dictionary bools_byKey = new(); + static Dictionary floats_byKey = new(); + static Dictionary strings_byKey = new(); + + } + + public static class ProjectPrefs + { + public static int GetInt(string key, int defaultValue = 0) => EditorPrefsCached.GetInt(key + projectId, defaultValue); + public static bool GetBool(string key, bool defaultValue = false) => EditorPrefsCached.GetBool(key + projectId, defaultValue); + public static float GetFloat(string key, float defaultValue = 0) => EditorPrefsCached.GetFloat(key + projectId, defaultValue); + public static string GetString(string key, string defaultValue = "") => EditorPrefsCached.GetString(key + projectId, defaultValue); + + public static void SetInt(string key, int value) => EditorPrefsCached.SetInt(key + projectId, value); + public static void SetBool(string key, bool value) => EditorPrefsCached.SetBool(key + projectId, value); + public static void SetFloat(string key, float value) => EditorPrefsCached.SetFloat(key + projectId, value); + public static void SetString(string key, string value) => EditorPrefsCached.SetString(key + projectId, value); + + + + public static bool HasKey(string key) => EditorPrefs.HasKey(key + projectId); + public static void DeleteKey(string key) => EditorPrefs.DeleteKey(key + projectId); + + + + public static int projectId => PlayerSettings.productGUID.GetHashCode(); + + } + + + + public static void RecordUndo(this Object o, string operationName = "") => Undo.RecordObject(o, operationName); + public static void Dirty(this Object o) => UnityEditor.EditorUtility.SetDirty(o); + public static void Save(this Object o) => AssetDatabase.SaveAssetIfDirty(o); + + + + public static void SelectInInspector(this Object[] objects, bool frameInHierarchy = false, bool frameInProject = false) + { + void setHierarchyLocked(bool isLocked) => allHierarchies.ForEach(r => r?.GetMemberValue("m_SceneHierarchy")?.SetMemberValue("m_RectSelectInProgress", isLocked)); + void setProjectLocked(bool isLocked) => allProjectBrowsers.ForEach(r => r?.SetMemberValue("m_InternalSelectionChange", isLocked)); + + + if (!frameInHierarchy) setHierarchyLocked(true); + if (!frameInProject) setProjectLocked(true); + + Selection.objects = objects?.ToArray(); + + if (!frameInHierarchy) EditorApplication.delayCall += () => setHierarchyLocked(false); + if (!frameInProject) EditorApplication.delayCall += () => setProjectLocked(false); + + } + public static void SelectInInspector(this Object obj, bool frameInHierarchy = false, bool frameInProject = false) => new[] { obj }.SelectInInspector(frameInHierarchy, frameInProject); + + static IEnumerable allHierarchies => _allHierarchies ??= typeof(Editor).Assembly.GetType("UnityEditor.SceneHierarchyWindow").GetFieldValue("s_SceneHierarchyWindows").Cast(); + static IEnumerable _allHierarchies; + + static IEnumerable allProjectBrowsers => _allProjectBrowsers ??= typeof(Editor).Assembly.GetType("UnityEditor.ProjectBrowser").GetFieldValue("s_ProjectBrowsers").Cast(); + static IEnumerable _allProjectBrowsers; + + + + public static void MoveTo(this EditorWindow window, Vector2 position, bool ensureFitsOnScreen = true) + { + if (!ensureFitsOnScreen) { window.position = window.position.SetPos(position); return; } + + var windowRect = window.position; + var unityWindowRect = EditorGUIUtility.GetMainWindowPosition(); + + position.x = position.x.Max(unityWindowRect.position.x); + position.y = position.y.Max(unityWindowRect.position.y); + + position.x = position.x.Min(unityWindowRect.xMax - windowRect.width); + position.y = position.y.Min(unityWindowRect.yMax - windowRect.height); + + window.position = windowRect.SetPos(position); + + } + + + +#endif + + #endregion + + } + + public static class VGUI + { + + #region Drawing + + + public static Rect Draw(this Rect rect, Color color) + { + EditorGUI.DrawRect(rect, color); + + return rect; + + } + public static Rect Draw(this Rect rect) => rect.Draw(Color.black); + + public static Rect DrawOutline(this Rect rect, Color color, float thickness = 1) + { + + rect.SetWidth(thickness).Draw(color); + rect.SetWidthFromRight(thickness).Draw(color); + + rect.SetHeight(thickness).Draw(color); + rect.SetHeightFromBottom(thickness).Draw(color); + + + return rect; + + } + public static Rect DrawOutline(this Rect rect, float thickness = 1) => rect.DrawOutline(Color.black, thickness); + + + + + public static Rect DrawRounded(this Rect rect, Color color, int cornerRadius) + { + if (!curEvent.isRepaint) return rect; + + cornerRadius = cornerRadius.Min((rect.height / 2).FloorToInt()).Min((rect.width / 2).FloorToInt()); + + if (cornerRadius < 0) return rect; + + GUIStyle style; + + void getStyle() + { + if (_roundedStylesByCornerRadius.TryGetValue(cornerRadius, out style)) return; + + var pixelsPerPoint = 2; + + var res = cornerRadius * 2 * pixelsPerPoint; + var pixels = new Color[res * res]; + + var white = Greyscale(1, 1); + var clear = Greyscale(1, 0); + var halfRes = res / 2; + + for (int x = 0; x < res; x++) + for (int y = 0; y < res; y++) + { + var sqrMagnitude = (new Vector2(x - halfRes + .5f, y - halfRes + .5f)).sqrMagnitude; + pixels[x + y * res] = sqrMagnitude <= halfRes * halfRes ? white : clear; + } + + var texture = new Texture2D(res, res); + texture.SetPropertyValue("pixelsPerPoint", pixelsPerPoint); + texture.hideFlags = HideFlags.DontSave; + texture.SetPixels(pixels); + texture.Apply(); + + + + style = new GUIStyle(); + style.normal.background = texture; + style.alignment = TextAnchor.MiddleCenter; + style.border = new RectOffset(cornerRadius, cornerRadius, cornerRadius, cornerRadius); + + + _roundedStylesByCornerRadius[cornerRadius] = style; + + } + void draw() + { + SetGUIColor(color); + + style.Draw(rect, false, false, false, false); + + ResetGUIColor(); + + } + + getStyle(); + draw(); + + return rect; + + } + public static Rect DrawRounded(this Rect rect, Color color, float cornerRadius) => rect.DrawRounded(color, cornerRadius.RoundToInt()); + + static Dictionary _roundedStylesByCornerRadius = new(); + + + + + public static Rect DrawBlurred(this Rect rect, Color color, int blurRadius) + { + if (!curEvent.isRepaint) return rect; + + var pixelsPerPoint = .5f; + // var pixelsPerPoint = 1f; + + var blurRadiusScaled = (blurRadius * pixelsPerPoint).RoundToInt().Max(1).Min(123); + + var croppedRectWidth = (rect.width * pixelsPerPoint).RoundToInt().Min(blurRadiusScaled * 2); + var croppedRectHeight = (rect.height * pixelsPerPoint).RoundToInt().Min(blurRadiusScaled * 2); + + var textureWidth = croppedRectWidth + blurRadiusScaled * 2; + var textureHeight = croppedRectHeight + blurRadiusScaled * 2; + + if (textureWidth <= 0 || textureWidth > 1232) return rect; + if (textureHeight <= 0 || textureHeight > 1232) return rect; + + + GUIStyle style; + + void getStyle() + { + if (_blurredStylesByTextureSize.TryGetValue((textureWidth, textureHeight), out style)) return; + + // VDebug.LogStart(blurRadius + ""); + + var pixels = new Color[textureWidth * textureHeight]; + var kernel = GaussianKernel.GenerateArray(blurRadiusScaled * 2 + 1); + + for (int x = 0; x < textureWidth; x++) + for (int y = 0; y < textureHeight; y++) + { + var sum = 0f; + + for (int xSample = (x - blurRadiusScaled).Max(blurRadiusScaled); xSample <= (x + blurRadiusScaled).Min(textureWidth - 1 - blurRadiusScaled); xSample++) + for (int ySample = (y - blurRadiusScaled).Max(blurRadiusScaled); ySample <= (y + blurRadiusScaled).Min(textureHeight - 1 - blurRadiusScaled); ySample++) + sum += kernel[blurRadiusScaled + xSample - x, blurRadiusScaled + ySample - y]; + + pixels[x + y * textureWidth] = Greyscale(1, sum); + + } + + var texture = new Texture2D(textureWidth, textureHeight); + texture.SetPropertyValue("pixelsPerPoint", pixelsPerPoint); + texture.hideFlags = HideFlags.DontSave; + texture.SetPixels(pixels); + texture.Apply(); + + + style = new GUIStyle(); + style.normal.background = texture; + style.alignment = TextAnchor.MiddleCenter; + + var borderX = ((textureWidth / 2f - 1) / pixelsPerPoint).FloorToInt(); + var borderY = ((textureHeight / 2f - 1) / pixelsPerPoint).FloorToInt(); + style.border = new RectOffset(borderX, borderX, borderY, borderY); + + _blurredStylesByTextureSize[(textureWidth, textureHeight)] = style; + + // VDebug.LogFinish(); + + } + void draw() + { + SetGUIColor(color); + + style.Draw(rect.SetSizeFromMid(rect.width + blurRadius * 2, rect.height + blurRadius * 2), false, false, false, false); + + ResetGUIColor(); + + } + + getStyle(); + draw(); + + return rect; + + } + public static Rect DrawBlurred(this Rect rect, Color color, float blurRadius) => rect.DrawBlurred(color, blurRadius.RoundToInt()); + + static Dictionary<(int, int), GUIStyle> _blurredStylesByTextureSize = new(); + + + + + static void DrawCurtain(this Rect rect, Color color, int dir) + { + void genTextures() + { + if (_gradientTextures != null) return; + + _gradientTextures = new Texture2D[4]; + + // var pixels = Enumerable.Range(0, 256).Select(r => Greyscale(1, r / 255f)); + var pixels = Enumerable.Range(0, 256).Select(r => Greyscale(1, (r / 255f).Smoothstep())); + + var up = new Texture2D(1, 256); + up.SetPixels(pixels.Reverse().ToArray()); + up.Apply(); + up.hideFlags = HideFlags.DontSave; + up.wrapMode = TextureWrapMode.Clamp; + _gradientTextures[0] = up; + + var down = new Texture2D(1, 256); + down.SetPixels(pixels.ToArray()); + down.Apply(); + down.hideFlags = HideFlags.DontSave; + down.wrapMode = TextureWrapMode.Clamp; + _gradientTextures[1] = down; + + var left = new Texture2D(256, 1); + left.SetPixels(pixels.ToArray()); + left.Apply(); + left.hideFlags = HideFlags.DontSave; + left.wrapMode = TextureWrapMode.Clamp; + _gradientTextures[2] = left; + + var right = new Texture2D(256, 1); + right.SetPixels(pixels.Reverse().ToArray()); + right.Apply(); + right.hideFlags = HideFlags.DontSave; + right.wrapMode = TextureWrapMode.Clamp; + _gradientTextures[3] = right; + + } + void draw() + { + SetGUIColor(color); + + GUI.DrawTexture(rect, _gradientTextures[dir]); + + ResetGUIColor(); + + } + + genTextures(); + draw(); + + } + + static Texture2D[] _gradientTextures; + + public static void DrawCurtainUp(this Rect rect, Color color) => rect.DrawCurtain(color, 0); + public static void DrawCurtainDown(this Rect rect, Color color) => rect.DrawCurtain(color, 1); + public static void DrawCurtainLeft(this Rect rect, Color color) => rect.DrawCurtain(color, 2); + public static void DrawCurtainRight(this Rect rect, Color color) => rect.DrawCurtain(color, 3); + + + + + + + #endregion + + #region Events + + + public class WrappedEvent + { + public Event e; + + public bool isRepaint => e.type == EventType.Repaint; + public bool isLayout => e.type == EventType.Layout; + public bool isUsed => e.type == EventType.Used; + public bool isMouseLeaveWindow => e.type == EventType.MouseLeaveWindow; + public bool isMouseEnterWindow => e.type == EventType.MouseEnterWindow; + public bool isContextClick => e.type == EventType.ContextClick; + public bool isIgnore => e.type == EventType.Ignore; + + public bool isKeyDown => e.type == EventType.KeyDown; + public bool isKeyUp => e.type == EventType.KeyUp; + public KeyCode keyCode => e.keyCode; + public char characted => e.character; + + public bool isExecuteCommand => e.type == EventType.ExecuteCommand; + public string commandName => e.commandName; + + public bool isMouse => e.isMouse; + public bool isMouseDown => e.type == EventType.MouseDown; + public bool isMouseUp => e.type == EventType.MouseUp; + public bool isMouseDrag => e.type == EventType.MouseDrag; + public bool isMouseMove => e.type == EventType.MouseMove; + public bool isScroll => e.type == EventType.ScrollWheel; + public int mouseButton => e.button; + public int clickCount => e.clickCount; + public Vector2 mousePosition => e.mousePosition; + public Vector2 mousePosition_screenSpace => GUIUtility.GUIToScreenPoint(e.mousePosition); + public Vector2 mouseDelta => e.delta; + + public bool isDragUpdate => e.type == EventType.DragUpdated; + public bool isDragPerform => e.type == EventType.DragPerform; + public bool isDragExit => e.type == EventType.DragExited; + + public EventModifiers modifiers => e.modifiers; + public bool holdingAnyModifierKey => modifiers != EventModifiers.None; + + public bool holdingAlt => e.alt; + public bool holdingShift => e.shift; + public bool holdingCtrl => e.control; + public bool holdingCmd => e.command; + public bool holdingCmdOrCtrl => e.command || e.control; + + public bool holdingAltOnly => e.modifiers == EventModifiers.Alt; // in some sessions FunctionKey is always pressed? + public bool holdingShiftOnly => e.modifiers == EventModifiers.Shift; // in some sessions FunctionKey is always pressed? + public bool holdingCtrlOnly => e.modifiers == EventModifiers.Control; + public bool holdingCmdOnly => e.modifiers == EventModifiers.Command; + public bool holdingCmdOrCtrlOnly => (e.modifiers == EventModifiers.Command || e.modifiers == EventModifiers.Control); + + public EventType type => e.type; + + public void Use() => e?.Use(); + + + public WrappedEvent(Event e) => this.e = e; + + public override string ToString() => e.ToString(); + + } + + public static WrappedEvent Wrap(this Event e) => new(e); + + public static WrappedEvent curEvent => _curEvent ??= typeof(Event).GetFieldValue("s_Current").Wrap(); + static WrappedEvent _curEvent; + + + + + + #endregion + + #region Shortcuts + + + public static Rect lastRect => GUILayoutUtility.GetLastRect(); + + public static bool isDarkTheme => EditorGUIUtility.isProSkin; + + public static bool IsHovered(this Rect r) => r.Contains(curEvent.mousePosition); + + public static float GetLabelWidth(this string s) => GUI.skin.label.CalcSize(new GUIContent(s)).x; + public static float GetLabelWidth(this string s, int fontSize) + { + SetLabelFontSize(fontSize); + + var r = s.GetLabelWidth(); + + ResetLabelStyle(); + + return r; + + } + public static float GetLabelWidth(this string s, bool isBold) + { + if (isBold) + SetLabelBold(); + + var r = s.GetLabelWidth(); + + if (isBold) + ResetLabelStyle(); + + return r; + + } + public static float GetLabelWidth(this string s, int fontSize, bool isBold) + { + if (isBold) + SetLabelBold(); + + SetLabelFontSize(fontSize); + + var r = s.GetLabelWidth(); + + ResetLabelStyle(); + + return r; + + } + + public static void SetGUIEnabled(bool enabled) { _prevGuiEnabled = GUI.enabled; GUI.enabled = enabled; } + public static void ResetGUIEnabled() => GUI.enabled = _prevGuiEnabled; + static bool _prevGuiEnabled = true; + + public static void SetLabelFontSize(int size) => GUI.skin.label.fontSize = size; + public static void SetLabelBold() => GUI.skin.label.fontStyle = FontStyle.Bold; + public static void SetLabelAlignmentCenter() => GUI.skin.label.alignment = TextAnchor.MiddleCenter; + public static void ResetLabelStyle() + { + GUI.skin.label.fontSize = 0; + GUI.skin.label.fontStyle = FontStyle.Normal; + GUI.skin.label.alignment = TextAnchor.MiddleLeft; + GUI.skin.label.wordWrap = false; + } + + + public static void SetGUIColor(Color c) + { + _guiColorStack.Push(GUI.color); + + GUI.color *= c; + + } + public static void ResetGUIColor() + { + GUI.color = _guiColorStack.Pop(); + } + + static Stack _guiColorStack = new(); + + + + public static float editorDeltaTime = .0166f; + + static void EditorDeltaTime_Update() + { + editorDeltaTime = (float)(EditorApplication.timeSinceStartup - _lastUpdateTime); + + _lastUpdateTime = EditorApplication.timeSinceStartup; + + } + static double _lastUpdateTime; + + [InitializeOnLoadMethod] + static void EditorDeltaTime_Subscribe() + { + EditorApplication.update -= EditorDeltaTime_Update; + EditorApplication.update += EditorDeltaTime_Update; + } + + + + + #endregion + + #region Controls + + + public static bool IconButton(Rect rect, string iconName, float iconSize = default, Color color = default, Color colorHovered = default, Color colorPressed = default) + { + var id = EditorGUIUtility.GUIToScreenRect(rect).GetHashCode();// GUIUtility.GetControlID(FocusType.Passive, rect); + var isPressed = id == _pressedIconButtonId; + + var wasActivated = false; + + void icon() + { + if (!curEvent.isRepaint) return; + + + if (color == default) + color = Color.white; + + if (colorHovered == default) + colorHovered = Color.white; + + if (colorPressed == default) + colorPressed = Color.white.SetAlpha(.6f); + + + if (rect.IsHovered()) + color = colorHovered; + + if (isPressed) + color = colorPressed; + + + if (iconSize == default) + iconSize = rect.width.Min(rect.height); + + var iconRect = rect.SetSizeFromMid(iconSize); + + + + SetGUIColor(color); + + GUI.DrawTexture(iconRect, EditorIcons.GetIcon(iconName)); + + ResetGUIColor(); + + + } + void mouseDown() + { + if (!curEvent.isMouseDown) return; + if (!rect.IsHovered()) return; + + _pressedIconButtonId = id; + + curEvent.Use(); + + } + void mouseUp() + { + if (!curEvent.isMouseUp) return; + if (!isPressed) return; + + _pressedIconButtonId = 0; + + if (rect.IsHovered()) + wasActivated = true; + + curEvent.Use(); + + } + void mouseDrag() + { + if (!curEvent.isMouseDrag) return; + if (!isPressed) return; + + curEvent.Use(); + + } + + rect.MarkInteractive(); + + icon(); + mouseDown(); + mouseUp(); + mouseDrag(); + + return wasActivated; + + } + + static int _pressedIconButtonId; + + + + + + #endregion + + #region Layout + + + public static void Space(float px = 6) => GUILayout.Space(px); + + public static Rect ExpandWidthLabelRect() { GUILayout.Label(""/* , GUILayout.Height(0) */, GUILayout.ExpandWidth(true)); return lastRect; } + public static Rect ExpandWidthLabelRect(float height) { GUILayout.Label("", GUILayout.Height(height), GUILayout.ExpandWidth(true)); return lastRect; } + + + + + public static void BeginIndent(float f) + { + GUILayout.BeginHorizontal(); + GUILayout.Space(f); + GUILayout.BeginVertical(); + + _indentLabelWidthStack.Push(EditorGUIUtility.labelWidth); + + EditorGUIUtility.labelWidth -= f; + } + + public static void EndIndent(float f = 0) + { + GUILayout.EndVertical(); + GUILayout.Space(f); + GUILayout.EndHorizontal(); + + EditorGUIUtility.labelWidth = _indentLabelWidthStack.Pop(); + } + static Stack _indentLabelWidthStack = new(); + + + + + + #endregion + + #region GUIColors + + + public static class GUIColors + { + public static Color windowBackground => isDarkTheme ? Greyscale(.22f) : Greyscale(.78f); // prev backgroundCol + public static Color pressedButtonBackground => isDarkTheme ? new Color(.48f, .76f, 1f, 1f) * 1.4f : new Color(.48f, .7f, 1f, 1f) * 1.2f; // prev pressedButtonCol + public static Color greyedOutTint => Greyscale(.7f); + public static Color selectedBackground => isDarkTheme ? new Color(.17f, .365f, .535f) : new Color(.2f, .375f, .555f) * 1.2f; + } + + + + + #endregion + + #region EditorIcons + + + public static partial class EditorIcons + { + public static Texture2D GetIcon(string iconNameOrPath, bool returnNullIfNotFound = false) + { + iconNameOrPath ??= ""; + + if (icons_byName.TryGetValue(iconNameOrPath, out var cachedResult) && cachedResult) return cachedResult; + + + Texture2D icon = null; + + void getCustom() + { + if (icon) return; + if (!customIcons.ContainsKey(iconNameOrPath)) return; + + var pngBytesString = customIcons[iconNameOrPath]; + var pngBytes = pngBytesString.Split("-").Select(r => System.Convert.ToByte(r, 16)).ToArray(); + + icon = new Texture2D(1, 1); + + icon.LoadImage(pngBytes); + + } + void getBuiltin() + { + if (icon) return; + + icon = typeof(EditorGUIUtility).InvokeMethod("LoadIcon", iconNameOrPath) as Texture2D; + + } + + getCustom(); + getBuiltin(); + + icons_byName[iconNameOrPath] = icon; + + if (icon == null && !returnNullIfNotFound) return Texture2D.grayTexture; + else return icon; + + } + + static Dictionary icons_byName = new(); + + static Dictionary customIcons = new() + { + ["Search_"] = "89-50-4E-47-0D-0A-1A-0A-00-00-00-0D-49-48-44-52-00-00-00-20-00-00-00-20-08-06-00-00-00-73-7A-7A-F4-00-00-00-09-70-48-59-73-00-00-0B-13-00-00-0B-13-01-00-9A-9C-18-00-00-00-01-73-52-47-42-00-AE-CE-1C-E9-00-00-00-04-67-41-4D-41-00-00-B1-8F-0B-FC-61-05-00-00-02-00-49-44-41-54-78-01-ED-56-3D-4F-02-41-10-1D-3E-22-C6-C4-CA-D0-9A-58-6B-87-8D-12-6B-0B-1B-7F-07-85-89-95-F4-34-D6-F2-07-2C-FD-23-D0-41-A2-8D-FF-00-8A-0B-0D-7A-70-7C-1C-EB-7B-B8-47-16-C4-70-7B-77-1C-0D-2F-B9-DC-DE-DD-CE-CE-DB-99-D9-37-27-B2-C7-8E-91-B1-99-EC-38-CE-71-A1-50-38-E0-78-34-1A-8D-8B-C5-E2-97-6C-1B-4A-A9-43-38-AB-8E-C7-E3-CF-D9-6C-E6-29-0D-DF-F7-5D-BE-C3-B7-1A-E7-C8-36-E0-BA-EE-1D-1C-39-6A-03-40-CC-E5-5C-49-12-9E-E7-55-B0-B0-AF-1D-F8-93-C9-A4-81-77-0F-B8-97-79-E9-71-DB-20-E1-83-44-45-92-00-77-13-38-47-04-BA-83-C1-E0-FA-BF-B9-20-71-85-39-3D-83-44-BC-48-30-9F-0C-A9-5E-B0-D7-E9-74-8E-36-D9-70-0E-89-6A-C2-4E-AC-9A-D0-45-35-07-43-1D-D6-0E-51-2A-07-51-63-D1-4A-54-B0-B2-B5-F3-B6-58-02-36-4D-DA-72-8D-B0-36-59-F3-81-E7-3C-9B-CD-9E-72-8C-75-5E-C5-12-B0-79-E3-3D-9F-CF-9F-71-AD-30-36-4B-04-28-32-B9-5C-6E-9E-F3-4C-26-D3-12-4B-4C-A7-D3-96-B6-2D-04-82-65-45-60-17-58-22-40-79-65-0D-71-8C-7B-49-2C-81-D0-97-B4-AD-CB-B5-24-0A-8C-22-6C-8A-25-70-04-DB-B6-45-F8-07-3C-42-86-FA-59-1D-43-E3-F8-D6-24-2A-28-22-81-FE-53-5C-C2-0A-11-45-2B-E8-0B-2A-6E-73-32-A5-98-0B-87-90-E2-6E-62-52-6C-90-58-34-A3-40-98-90-DB-D5-66-D4-80-F3-45-C3-62-03-A3-ED-70-38-BC-C1-AB-0B-89-0B-1D-09-57-6D-00-53-16-EC-1C-24-6E-F1-3C-82-DD-37-08-5F-4A-5C-A8-DF-1F-92-1A-2B-1B-42-E3-1A-ED-D7-D3-3F-24-D5-20-E7-DC-39-9D-1B-73-26-F8-7E-2F-49-81-F2-DA-EF-F7-4F-78-AD-93-5A-38-3B-E7-CE-D5-F2-CF-CA-14-A9-8A-DE-9C-6C-C1-B0-73-E7-AB-69-42-6A-5E-24-2D-30-EC-DC-F9-1A-12-8F-92-16-50-0B-4F-6B-8A-F5-5D-D2-04-76-5C-37-09-20-3D-75-49-1B-28-C0-67-5C-1F-74-0E-0E-79-D9-63-8F-15-FC-00-17-02-EB-AB-1A-4B-B3-E7-00-00-00-00-49-45-4E-44-AE-42-60-82", + ["Cross"] = "89-50-4E-47-0D-0A-1A-0A-00-00-00-0D-49-48-44-52-00-00-00-20-00-00-00-20-08-06-00-00-00-73-7A-7A-F4-00-00-00-09-70-48-59-73-00-00-0B-13-00-00-0B-13-01-00-9A-9C-18-00-00-00-01-73-52-47-42-00-AE-CE-1C-E9-00-00-00-04-67-41-4D-41-00-00-B1-8F-0B-FC-61-05-00-00-00-C5-49-44-41-54-78-01-ED-96-D1-0D-83-30-0C-44-9D-4E-D0-51-BA-02-13-B5-23-A4-1B-A4-13-31-42-3B-4A-37-70-8D-6A-04-42-E0-D8-88-E0-1F-3F-29-8A-50-1C-DF-05-48-62-80-20-08-9C-49-D2-20-22-5E-A9-BB-53-1B-FA-67-4A-E9-0B-0A-66-F3-06-5E-DA-79-6B-89-32-4E-BC-39-71-55-9C-63-47-B2-14-7F-01-3D-37-6A-BD-64-82-C7-7A-8E-1D-A9-9A-06-29-E1-62-35-9B-6F-C2-12-7B-B8-89-66-E2-1A-81-E6-E2-0A-13-ED-C5-2B-26-CE-11-57-98-D8-25-6E-D9-86-FE-B8-7E-02-D7-9F-10-3D-B7-21-7A-1E-44-96-C4-4D-4C-D0-E4-62-49-B8-61-22-4B-1A-B5-6D-38-BF-C7-3F-D4-3A-E9-6E-E7-B1-8E-63-55-68-0A-92-07-3F-16-63-41-92-E1-BF-80-B2-BB-20-09-82-E0-0C-7E-54-36-6A-69-F6-3F-13-EF-00-00-00-00-49-45-4E-44-AE-42-60-82", + ["Plus"] = "89-50-4E-47-0D-0A-1A-0A-00-00-00-0D-49-48-44-52-00-00-00-20-00-00-00-20-08-06-00-00-00-73-7A-7A-F4-00-00-00-09-70-48-59-73-00-00-0B-13-00-00-0B-13-01-00-9A-9C-18-00-00-00-01-73-52-47-42-00-AE-CE-1C-E9-00-00-00-04-67-41-4D-41-00-00-B1-8F-0B-FC-61-05-00-00-00-A7-49-44-41-54-78-01-ED-D5-01-09-84-30-14-06-E0-7F-C7-05-B8-06-77-0D-CE-08-46-31-C2-1A-68-04-4D-A0-51-8C-A0-0D-B4-81-0D-E6-13-14-C6-10-C5-4D-11-E1-FF-60-F8-78-0C-F7-B3-E1-04-88-02-18-63-B4-8C-04-77-98-17-5F-64-F0-F4-82-BF-C8-AA-BF-F0-14-12-E0-14-0C-C0-00-0C-A0-D6-9A-72-B1-A4-F2-F8-61-5B-6C-CD-E9-64-D4-3B-F3-1B-A5-54-81-3D-D3-D5-6A-AE-A3-DD-F5-D6-8E-E0-83-EB-0C-6E-E3-ED-36-64-9B-72-49-3A-95-7F-6C-8B-71-EC-08-7A-79-77-85-B3-48-C8-CA-DA-DA-12-9E-F8-19-32-00-03-3C-3A-40-67-D5-2D-EE-30-FF-37-34-88-02-8C-19-03-9B-84-46-97-A8-ED-00-00-00-00-49-45-4E-44-AE-42-60-82", + ["Star"] = "89-50-4E-47-0D-0A-1A-0A-00-00-00-0D-49-48-44-52-00-00-00-20-00-00-00-20-08-06-00-00-00-73-7A-7A-F4-00-00-00-09-70-48-59-73-00-00-0B-13-00-00-0B-13-01-00-9A-9C-18-00-00-00-01-73-52-47-42-00-AE-CE-1C-E9-00-00-00-04-67-41-4D-41-00-00-B1-8F-0B-FC-61-05-00-00-01-16-49-44-41-54-78-01-ED-94-6D-0D-C2-30-10-86-DF-11-04-20-61-12-70-C0-1C-50-07-AB-03-90-80-04-50-00-28-19-0E-90-00-0E-C0-C1-71-CD-BA-B0-B1-86-B5-BD-0E-FE-EC-49-2E-6D-2E-D7-CB-F5-BE-80-89-09-01-44-94-1B-81-80-19-64-28-16-0D-01-73-C8-28-59-9E-F8-07-36-FD-0D-39-22-91-94-40-B5-EE-1A-91-48-02-58-B7-EE-2B-FC-92-8F-F4-37-2C-10-41-6C-06-0A-87-4E-23-82-DE-14-F0-4F-0A-3E-F2-81-77-1B-87-AE-E4-B7-43-13-71-CF-B2-EC-F2-D5-C2-A4-92-E5-44-E9-39-05-95-89-8D-B7-2C-0F-92-63-7C-6C-11-03-D5-CD-76-A3-78-AE-24-5C-D5-4D-20-3B-0A-67-8F-94-B0-43-E5-99-0D-63-53-60-0C-D8-71-E5-11-40-85-31-A0-7A-3A-7C-30-4D-E7-DD-ED-21-8B-48-79-DA-2D-02-6C-83-02-58-3B-74-07-96-B3-43-5F-22-25-8E-F4-77-66-9B-EF-9A-BA-3B-23-A8-0C-3E-01-E8-96-73-E7-6C-53-7F-67-68-A4-82-9D-1D-AD-D3-C1-D9-A6-F7-CE-38-22-15-F6-D7-45-80-BD-B2-6F-E4-65-60-27-4B-8A-58-A7-B6-24-E9-FA-60-62-62-2C-5E-30-1D-6B-34-83-5B-F0-2B-00-00-00-00-49-45-4E-44-AE-42-60-82", + ["Star Hollow"] = "89-50-4E-47-0D-0A-1A-0A-00-00-00-0D-49-48-44-52-00-00-00-20-00-00-00-20-08-06-00-00-00-73-7A-7A-F4-00-00-00-09-70-48-59-73-00-00-0B-13-00-00-0B-13-01-00-9A-9C-18-00-00-00-01-73-52-47-42-00-AE-CE-1C-E9-00-00-00-04-67-41-4D-41-00-00-B1-8F-0B-FC-61-05-00-00-01-5A-49-44-41-54-78-01-ED-96-FD-6D-C2-30-10-C5-2F-15-03-B0-41-33-42-47-48-37-C8-06-64-03-BA-41-D9-80-6E-00-1B-B4-9D-20-DD-20-EA-04-C9-06-65-83-EB-3B-F1-2C-8C-04-F9-B0-AD-F0-4F-7E-D2-29-16-3A-5F-EC-77-1F-41-64-61-21-02-55-CD-CD-24-82-27-89-A3-84-55-12-C1-4A-E2-D8-C0-4E-F2-08-28-BF-23-97-40-62-52-60-F2-77-72-56-A0-92-40-32-09-04-B7-AE-79-00-8B-F1-9C-65-D9-AB-CC-85-27-7F-09-2B-B8-5E-CB-5C-E0-65-15-EC-8F-EB-B5-AD-61-6F-12-C0-EA-46-F0-02-8F-7C-60-DF-16-F6-65-0B-48-7F-C2-9E-6F-2C-37-78-0E-75-44-07-FF-9F-5E-0F-DE-E8-A8-C3-94-FE-A1-47-F8-1F-27-A5-C9-24-A5-B4-6D-48-9B-B1-4E-9A-98-F4-B8-20-ED-D4-20-F0-DD-72-4F-A3-91-A3-DA-05-DC-51-C6-43-5F-40-A6-EF-40-DF-0F-49-09-5B-CE-D4-68-7B-7C-1A-FA-14-32-92-D1-93-10-D5-6B-55-DF-C1-7E-7B-DC-AC-0B-86-2B-3D-04-CA-6B-54-DE-6F-57-9F-63-AF-70-D3-0F-25-3D-0F-1F-75-2F-64-EB-B9-2E-A9-EE-1D-32-E5-01-3E-61-35-5F-B2-77-85-A6-97-99-F1-4E-3F-F3-A9-25-25-DE-CD-76-B7-7A-9B-EA-38-35-F6-C9-D3-E0-C9-AF-F7-7A-DB-9B-19-9A-3C-0D-53-7A-5B-BD-99-21-A9-E0-AD-8B-09-FE-25-F7-C4-A7-01-41-5E-34-FC-5B-30-DF-7F-84-85-85-50-FE-01-12-E7-01-A3-5F-51-F9-4C-00-00-00-00-49-45-4E-44-AE-42-60-82", + ["Collapse"] = "89-50-4E-47-0D-0A-1A-0A-00-00-00-0D-49-48-44-52-00-00-00-20-00-00-00-20-08-06-00-00-00-73-7A-7A-F4-00-00-00-09-70-48-59-73-00-00-0B-13-00-00-0B-13-01-00-9A-9C-18-00-00-00-01-73-52-47-42-00-AE-CE-1C-E9-00-00-00-04-67-41-4D-41-00-00-B1-8F-0B-FC-61-05-00-00-00-CE-49-44-41-54-78-01-ED-95-01-0D-C2-30-10-45-7F-A7-60-12-2A-01-09-93-80-04-1C-80-03-EA-60-16-70-30-1C-20-01-09-93-C0-1C-1C-BF-61-24-CD-18-0C-9A-DE-1A-92-BE-A4-49-D7-25-F7-7F-72-FF-5A-A0-90-19-33-3D-10-91-1D-F4-18-8C-31-E7-B7-7F-29-7E-10-7D-5C-A8-59-4D-3C-D4-D0-67-08-3F-E6-5A-B0-D5-34-C2-16-9C-50-48-05-DB-B5-F7-C1-45-0E-28-BC-19-53-7D-F3-7B-AC-09-05-2D-57-1F-8C-96-DF-5B-AC-05-C5-AE-33-F3-ED-CF-F4-C7-98-22-ED-87-4B-A6-85-26-14-38-CA-32-3A-A1-64-E1-46-BE-A7-41-4A-E4-35-74-4B-F8-C9-B0-48-01-0B-D5-3F-8A-3F-E9-25-45-28-59-A4-93-78-3A-68-C1-E2-2E-10-72-88-A4-42-66-8A-81-62-A0-18-C8-6E-20-1A-79-BC-0F-97-71-59-14-FE-95-3B-1B-9A-F9-A3-61-43-3F-A9-00-00-00-00-49-45-4E-44-AE-42-60-82", + ["Plus Thicker"] = "89-50-4E-47-0D-0A-1A-0A-00-00-00-0D-49-48-44-52-00-00-00-20-00-00-00-20-08-06-00-00-00-73-7A-7A-F4-00-00-00-09-70-48-59-73-00-00-0B-13-00-00-0B-13-01-00-9A-9C-18-00-00-00-01-73-52-47-42-00-AE-CE-1C-E9-00-00-00-04-67-41-4D-41-00-00-B1-8F-0B-FC-61-05-00-00-00-A1-49-44-41-54-78-01-ED-96-D1-09-84-30-10-44-27-C7-15-70-D7-81-25-D9-C1-9D-95-A8-1D-D9-89-25-C4-0E-4C-07-71-85-80-12-22-91-B8-E8-CF-3C-98-8F-2C-21-79-10-D8-0D-40-48-21-DE-FB-5A-32-4B-AC-E4-87-BB-91-4B-47-BF-61-51-C8-0B-E5-7C-A0-C0-15-01-15-28-40-01-0A-98-B8-10-BA-5A-87-3C-55-B4-9E-32-FB-9D-A4-37-C6-0C-39-01-9B-38-5C-0B-27-02-DF-7D-21-F5-04-2A-1D-EE-48-20-2E-BC-13-9B-1A-49-7B-42-A4-8A-D6-13-F2-F4-D0-22-4C-C1-47-87-91-0A-14-A0-00-05-B4-04-1C-EE-46-9A-CF-3F-34-A3-F5-6B-5E-83-90-42-16-B4-42-4C-CD-3F-8F-0E-C4-00-00-00-00-49-45-4E-44-AE-42-60-82", + ["Dropdown"] = "89-50-4E-47-0D-0A-1A-0A-00-00-00-0D-49-48-44-52-00-00-00-20-00-00-00-20-08-06-00-00-00-73-7A-7A-F4-00-00-00-09-70-48-59-73-00-00-0B-13-00-00-0B-13-01-00-9A-9C-18-00-00-00-01-73-52-47-42-00-AE-CE-1C-E9-00-00-00-04-67-41-4D-41-00-00-B1-8F-0B-FC-61-05-00-00-00-78-49-44-41-54-78-01-ED-D0-B1-0D-80-20-14-84-E1-D3-D2-15-98-82-41-18-C6-51-5C-85-41-98-C2-15-AC-F1-E8-2C-50-91-04-8C-F1-BE-E4-35-0A-FC-04-40-44-44-FE-6E-28-59-14-09-15-06-BA-5B-33-A2-CC-82-E7-6A-F6-9C-E3-23-F8-58-CE-A3-05-1E-1C-0A-E2-01-AD-F0-F0-89-B3-5E-C4-D3-BF-09-2D-31-60-38-5B-26-9E-BE-19-F4-C0-90-CD-5C-C0-A2-27-06-DD-21-EE-F0-06-86-E7-34-10-11-91-2F-DB-01-40-06-CB-42-92-53-E4-4B-00-00-00-00-49-45-4E-44-AE-42-60-82", + }; + } + + + + #endregion + + #region Other + + + public static void MarkInteractive(this Rect rect) + { + if (!curEvent.isRepaint) return; + + var unclippedRect = (Rect)_mi_GUIClip_UnclipToWindow.Invoke(null, new object[] { rect }); + + var curGuiView = _pi_GUIView_current.GetValue(null); + + _mi_GUIView_MarkHotRegion.Invoke(curGuiView, new object[] { unclippedRect }); + + } + + static PropertyInfo _pi_GUIView_current = typeof(Editor).Assembly.GetType("UnityEditor.GUIView").GetProperty("current", maxBindingFlags); + static MethodInfo _mi_GUIView_MarkHotRegion = typeof(Editor).Assembly.GetType("UnityEditor.GUIView").GetMethod("MarkHotRegion", maxBindingFlags); + static MethodInfo _mi_GUIClip_UnclipToWindow = typeof(GUI).Assembly.GetType("UnityEngine.GUIClip").GetMethod("UnclipToWindow", maxBindingFlags, null, new[] { typeof(Rect) }, null); + + + + + + + + + + #endregion + + } + +} +#endif \ No newline at end of file diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyLibs.cs.meta b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyLibs.cs.meta new file mode 100644 index 0000000..68cfb99 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyLibs.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cd278e4ae9a8649f59f016c7d739ce1a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyLightingWindow.cs b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyLightingWindow.cs new file mode 100644 index 0000000..8fb6b18 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyLightingWindow.cs @@ -0,0 +1,6 @@ + + +// this file was present in a previus version and is supposed to be deleted now +// but asset store update delivery system doesn't allow deleting files +// so instead this file is now emptied +// feel free to delete it if you want diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyLightingWindow.cs.meta b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyLightingWindow.cs.meta new file mode 100644 index 0000000..6a1a7b3 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyLightingWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 25324ddc1ebdc4ee5b1d9902a8efafa7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyMenu.cs b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyMenu.cs new file mode 100644 index 0000000..b04b012 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyMenu.cs @@ -0,0 +1,159 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using static VHierarchy.Libs.VUtils; +using static VHierarchy.Libs.VGUI; +// using static VTools.VDebug; + + +namespace VHierarchy +{ + class VHierarchyMenu + { + + public static bool navigationBarEnabled { get => EditorPrefsCached.GetBool("vHierarchy-navigationBarEnabled", false); set => EditorPrefsCached.SetBool("vHierarchy-navigationBarEnabled", value); } + public static bool sceneSelectorEnabled { get => EditorPrefsCached.GetBool("vHierarchy-sceneSelectorEnabled", false); set => EditorPrefsCached.SetBool("vHierarchy-sceneSelectorEnabled", value); } + public static bool componentMinimapEnabled { get => EditorPrefsCached.GetBool("vHierarchy-componentMinimapEnabled", false); set => EditorPrefsCached.SetBool("vHierarchy-componentMinimapEnabled", value); } + public static bool activationToggleEnabled { get => EditorPrefsCached.GetBool("vHierarchy-acctivationToggleEnabled", false); set => EditorPrefsCached.SetBool("vHierarchy-acctivationToggleEnabled", value); } + public static bool hierarchyLinesEnabled { get => EditorPrefsCached.GetBool("vHierarchy-hierarchyLinesEnabled", false); set => EditorPrefsCached.SetBool("vHierarchy-hierarchyLinesEnabled", value); } + public static bool minimalModeEnabled { get => EditorPrefsCached.GetBool("vHierarchy-minimalModeEnabled", false); set => EditorPrefsCached.SetBool("vHierarchy-minimalModeEnabled", value); } + public static bool zebraStripingEnabled { get => EditorPrefsCached.GetBool("vHierarchy-zebraStripingEnabled", false); set => EditorPrefsCached.SetBool("vHierarchy-zebraStripingEnabled", value); } + + public static bool toggleActiveEnabled { get => EditorPrefsCached.GetBool("vHierarchy-toggleActiveEnabled", true); set => EditorPrefsCached.SetBool("vHierarchy-toggleActiveEnabled", value); } + public static bool focusEnabled { get => EditorPrefsCached.GetBool("vHierarchy-focusEnabled", true); set => EditorPrefsCached.SetBool("vHierarchy-focusEnabled", value); } + public static bool deleteEnabled { get => EditorPrefsCached.GetBool("vHierarchy-deleteEnabled", true); set => EditorPrefsCached.SetBool("vHierarchy-deleteEnabled", value); } + public static bool toggleExpandedEnabled { get => EditorPrefsCached.GetBool("vHierarchy-toggleExpandedEnabled", true); set => EditorPrefsCached.SetBool("vHierarchy-toggleExpandedEnabled", value); } + public static bool isolateEnabled { get => EditorPrefsCached.GetBool("vHierarchy-collapseEverythingElseEnabled", true); set => EditorPrefsCached.SetBool("vHierarchy-collapseEverythingElseEnabled", value); } + public static bool collapseEverythingEnabled { get => EditorPrefsCached.GetBool("vHierarchy-collapseEverythingEnabled", true); set => EditorPrefsCached.SetBool("vHierarchy-collapseEverythingEnabled", value); } + public static bool setDefaultParentEnabled { get => EditorPrefsCached.GetBool("vHierarchy-setDefaultParentEnabled", true); set => EditorPrefsCached.SetBool("vHierarchy-setDefaultParentEnabled", value); } + + public static bool pluginDisabled { get => EditorPrefsCached.GetBool("vHierarchy-pluginDisabled", false); set => EditorPrefsCached.SetBool("vHierarchy-pluginDisabled", value); } + + + + + const string dir = "Tools/vHierarchy/"; + + const string navigationBar = dir + "Navigation bar"; + const string sceneSelector = dir + "Scene selector"; + const string componentMinimap = dir + "Component minimap"; + const string activationToggle = dir + "Activation toggle"; + const string hierarchyLines = dir + "Hierarchy lines"; + const string zebraStriping = dir + "Zebra striping"; + const string minimalMode = dir + "Minimal mode"; + + const string toggleActive = dir + "A to toggle active"; + const string focus = dir + "F to focus"; + const string delete = dir + "X to delete"; + const string toggleExpanded = dir + "E to expand or collapse"; + const string isolate = dir + "Shift-E to isolate"; + const string collapseEverything = dir + "Ctrl-Shift-E to collapse all"; + const string setDefaultParent = dir + "D to set default parent"; + + const string disablePlugin = dir + "Disable vHierarchy"; + + + + + + + [MenuItem(dir + "Features", false, 1)] static void daasddsas() { } + [MenuItem(dir + "Features", true, 1)] static bool dadsdasas123() => false; + + [MenuItem(navigationBar, false, 2)] static void dadsaadsdsadasdsadadsas() { navigationBarEnabled = !navigationBarEnabled; EditorApplication.RepaintHierarchyWindow(); } + [MenuItem(navigationBar, true, 2)] static bool dadsaddasdsasadadsdasadsas() { Menu.SetChecked(navigationBar, navigationBarEnabled); return !pluginDisabled; } + + [MenuItem(sceneSelector, false, 3)] static void dadsaadsdsadassddsadadsas() { sceneSelectorEnabled = !sceneSelectorEnabled; EditorApplication.RepaintHierarchyWindow(); } + [MenuItem(sceneSelector, true, 3)] static bool dadsaddasdsasadsdadsdasadsas() { Menu.SetChecked(sceneSelector, sceneSelectorEnabled); return !pluginDisabled; } + + [MenuItem(componentMinimap, false, 4)] static void daadsdsadasdadsas() { componentMinimapEnabled = !componentMinimapEnabled; EditorApplication.RepaintHierarchyWindow(); } + [MenuItem(componentMinimap, true, 4)] static bool dadsadasddasadsas() { Menu.SetChecked(componentMinimap, componentMinimapEnabled); return !pluginDisabled; } + + [MenuItem(activationToggle, false, 5)] static void daadsdsadadsasdadsas() { activationToggleEnabled = !activationToggleEnabled; EditorApplication.RepaintHierarchyWindow(); } + [MenuItem(activationToggle, true, 5)] static bool dadsadasdsaddasadsas() { Menu.SetChecked(activationToggle, activationToggleEnabled); return !pluginDisabled; } + + [MenuItem(hierarchyLines, false, 6)] static void dadsadadsadadasss() { hierarchyLinesEnabled = !hierarchyLinesEnabled; EditorApplication.RepaintHierarchyWindow(); } + [MenuItem(hierarchyLines, true, 6)] static bool dadsaddasdasaasddsas() { Menu.SetChecked(hierarchyLines, hierarchyLinesEnabled); return !pluginDisabled; } + + [MenuItem(zebraStriping, false, 7)] static void dadsadadadssadsadass() { zebraStripingEnabled = !zebraStripingEnabled; EditorApplication.RepaintHierarchyWindow(); } + [MenuItem(zebraStriping, true, 7)] static bool dadsaddadaadsssadsaasddsas() { Menu.SetChecked(zebraStriping, zebraStripingEnabled); return !pluginDisabled; } + + [MenuItem(minimalMode, false, 8)] static void dadsadadasdsdasadadasss() { minimalModeEnabled = !minimalModeEnabled; EditorApplication.RepaintHierarchyWindow(); } + [MenuItem(minimalMode, true, 8)] static bool dadsaddadsasdadsasaasddsas() { Menu.SetChecked(minimalMode, minimalModeEnabled); return !pluginDisabled; } + + + + + + + [MenuItem(dir + "Shortcuts", false, 101)] static void dadsas() { } + [MenuItem(dir + "Shortcuts", true, 101)] static bool dadsas123() => false; + + + + [MenuItem(setDefaultParent, false, 102)] static void dadsadasdsdasadasdsadadsas() => setDefaultParentEnabled = !setDefaultParentEnabled; + [MenuItem(setDefaultParent, true, 102)] static bool dadsadsdadssdaasadadsdasadsas() { Menu.SetChecked(setDefaultParent, setDefaultParentEnabled); return !pluginDisabled; } + + + [MenuItem(toggleActive, false, 103)] static void dadsadadsas() => toggleActiveEnabled = !toggleActiveEnabled; + [MenuItem(toggleActive, true, 103)] static bool dadsaddasadsas() { Menu.SetChecked(toggleActive, toggleActiveEnabled); return !pluginDisabled; } + + [MenuItem(focus, false, 104)] static void dadsadasdadsas() => focusEnabled = !focusEnabled; + [MenuItem(focus, true, 104)] static bool dadsadsaddasadsas() { Menu.SetChecked(focus, focusEnabled); return !pluginDisabled; } + + [MenuItem(delete, false, 105)] static void dadsadsadasdadsas() => deleteEnabled = !deleteEnabled; + [MenuItem(delete, true, 105)] static bool dadsaddsasaddasadsas() { Menu.SetChecked(delete, deleteEnabled); return !pluginDisabled; } + + [MenuItem(toggleExpanded, false, 106)] static void dadsadsadasdsadadsas() => toggleExpandedEnabled = !toggleExpandedEnabled; + [MenuItem(toggleExpanded, true, 106)] static bool dadsaddsasadadsdasadsas() { Menu.SetChecked(toggleExpanded, toggleExpandedEnabled); return !pluginDisabled; } + + [MenuItem(isolate, false, 107)] static void dadsadsasdadasdsadadsas() => isolateEnabled = !isolateEnabled; + [MenuItem(isolate, true, 107)] static bool dadsaddsdasasadadsdasadsas() { Menu.SetChecked(isolate, isolateEnabled); return !pluginDisabled; } + + [MenuItem(collapseEverything, false, 108)] static void dadsadsdasadasdsadadsas() => collapseEverythingEnabled = !collapseEverythingEnabled; + [MenuItem(collapseEverything, true, 108)] static bool dadsaddssdaasadadsdasadsas() { Menu.SetChecked(collapseEverything, collapseEverythingEnabled); return !pluginDisabled; } + + + + + [MenuItem(dir + "More", false, 1001)] static void daasadsddsas() { } + [MenuItem(dir + "More", true, 1001)] static bool dadsadsdasas123() => false; + + + [MenuItem(dir + "Open manual", false, 1002)] + static void dadadssadsas() => Application.OpenURL("https://kubacho-lab.gitbook.io/vhierarchy-2"); + + [MenuItem(dir + "Join our Discord", false, 1003)] + static void dadasdsas() => Application.OpenURL("https://discord.gg/pUektnZeJT"); + + + + + [MenuItem(dir + "Deals ending soon/Get vFolders 2 at 50% off", false, 1004)] + static void dadadssadasdsas() => Application.OpenURL("https://assetstore.unity.com/packages/slug/255470?aid=1100lGLBn&pubref=deal50menu"); + + [MenuItem(dir + "Deals ending soon/Get vInspector 2 at 50% off", false, 1005)] + static void dadadssasddsas() => Application.OpenURL("https://assetstore.unity.com/packages/slug/252297?aid=1100lGLBn&pubref=deal50menu"); + + [MenuItem(dir + "Deals ending soon/Get vTabs 2 at 50% off", false, 1006)] + static void dadadadsssadsas() => Application.OpenURL("https://assetstore.unity.com/packages/slug/253396?aid=1100lGLBn&pubref=deal50menu"); + + [MenuItem(dir + "Deals ending soon/Get vFavorites 2 at 50% off", false, 1007)] + static void dadadadsssadsadsas() => Application.OpenURL("https://assetstore.unity.com/packages/slug/263643?aid=1100lGLBn&pubref=deal50menu"); + + + + + + [MenuItem(disablePlugin, false, 10001)] static void dadsadsdasadasdasdsadadsas() { pluginDisabled = !pluginDisabled; UnityEditor.Compilation.CompilationPipeline.RequestScriptCompilation(); } + [MenuItem(disablePlugin, true, 10001)] static bool dadsaddssdaasadsadadsdasadsas() { Menu.SetChecked(disablePlugin, pluginDisabled); return true; } + + + + + + } +} +#endif \ No newline at end of file diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyMenu.cs.meta b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyMenu.cs.meta new file mode 100644 index 0000000..764244b --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyMenu.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4d7f0448bdeda4aad9cfdd9e7c7ee27f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyMenuItems.cs b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyMenuItems.cs new file mode 100644 index 0000000..8fb6b18 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyMenuItems.cs @@ -0,0 +1,6 @@ + + +// this file was present in a previus version and is supposed to be deleted now +// but asset store update delivery system doesn't allow deleting files +// so instead this file is now emptied +// feel free to delete it if you want diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyMenuItems.cs.meta b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyMenuItems.cs.meta new file mode 100644 index 0000000..70b2abb --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyMenuItems.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9ddf1decb62f94768bcaec3173017c87 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyNavbar.cs b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyNavbar.cs new file mode 100644 index 0000000..af5370a --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyNavbar.cs @@ -0,0 +1,1081 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using UnityEditor.IMGUI.Controls; +using Type = System.Type; +using static VHierarchy.Libs.VUtils; +using static VHierarchy.Libs.VGUI; +// using static VTools.VDebug; +using static VHierarchy.VHierarchyData; +using static VHierarchy.VHierarchy; + + + +namespace VHierarchy +{ + public class VHierarchyNavbar + { + + public void OnGUI(Rect navbarRect) + { + void updateState() + { + if (!curEvent.isLayout) return; + + + + var isTreeFocused = window.GetFieldValue("m_SceneHierarchy").GetMemberValue("m_TreeViewKeyboardControlID") == GUIUtility.keyboardControl; + + var isWindowFocused = window == EditorWindow.focusedWindow; + + + + if (!isTreeFocused && isSearchActive) + EditorGUI.FocusTextInControl("SearchFilter"); + + + if (isTreeFocused || !isWindowFocused) + if (window.GetMemberValue("m_SearchFilter").ToString().IsNullOrEmpty()) + isSearchActive = false; + + + // in vFolders the following is used to check if search is active: + // GUI.GetNameOfFocusedControl() == "SearchFilter"; + // but in hierarchy focused control changes erratically when multiple scene headers are visible + // so a bool state is used instead + + + + + this.defaultParent = typeof(SceneView).InvokeMethod("GetDefaultParentObjectIfSet")?.gameObject; + + } + + void background() + { + var backgroundColor = Greyscale(isDarkTheme ? .235f : .8f); + var lineColor = Greyscale(isDarkTheme ? .13f : .58f); + + navbarRect.Draw(backgroundColor); + + navbarRect.SetHeightFromBottom(1).MoveY(1).Draw(lineColor); + + } + void hiddenMenu() + { + if (!curEvent.holdingAlt) return; + if (!curEvent.isMouseUp) return; + if (curEvent.mouseButton != 1) return; + if (!navbarRect.IsHovered()) return; + + + void selectData() + { + Selection.activeObject = data; + } + void selectPalette() + { + Selection.activeObject = palette; + } + void clearCache() + { + VHierarchyCache.Clear(); + } + + + + GenericMenu menu = new(); + + menu.AddDisabledItem(new GUIContent("vHierarchy hidden menu")); + + menu.AddSeparator(""); + menu.AddItem(new GUIContent("Select data"), false, selectData); + menu.AddItem(new GUIContent("Select palette"), false, selectPalette); + + menu.AddSeparator(""); + menu.AddItem(new GUIContent("Clear cache"), false, clearCache); + + menu.ShowAsContext(); + + } + + + void plusButton() + { + + var buttonRect = navbarRect.SetWidth(28).MoveX(4.5f); + + if (Application.unityVersion.StartsWith("6000")) + buttonRect = buttonRect.MoveY(-.49f); + + + var iconName = "Plus Thicker"; + var iconSize = 16; + var colorNormal = Greyscale(isDarkTheme ? .7f : .44f); + var colorHovered = Greyscale(isDarkTheme ? 1f : .42f); + var colorPressed = Greyscale(isDarkTheme ? .75f : .6f); + + if (!IconButton(buttonRect, iconName, iconSize, colorNormal, colorHovered, colorPressed)) return; + + + GUIUtility.hotControl = 0; + + var sceneHierarchy = window.GetMemberValue("m_SceneHierarchy"); + var m_CustomParentForNewGameObjects = window.GetMemberValue("m_SceneHierarchy").GetMemberValue("m_CustomParentForNewGameObjects"); + var targetSceneHandle = m_CustomParentForNewGameObjects != null ? m_CustomParentForNewGameObjects.gameObject.scene.handle : 0; + + + var menu = new GenericMenu(); + + sceneHierarchy.GetType().GetMethod("AddCreateGameObjectItemsToMenu", maxBindingFlags).Invoke(sceneHierarchy, new object[] { menu, null, true, true, false, targetSceneHandle, 3 }); + + typeof(UnityEditor.SceneManagement.SceneHierarchyHooks).InvokeMethod("AddCustomItemsToCreateMenu", menu); + + menu.DropDown(buttonRect); + + + } + + void searchButton() + { + if (searchAnimationT == 1) return; + + + var buttonRect = navbarRect.SetWidthFromRight(28).MoveX(-5); + + var iconName = "Search_"; + var iconSize = 16; + var colorNormal = Greyscale(isDarkTheme ? .75f : .2f); + var colorHovered = Greyscale(isDarkTheme ? 1f : .2f); + var colorPressed = Greyscale(isDarkTheme ? .75f : .5f); + + + if (!IconButton(buttonRect, iconName, iconSize, colorNormal, colorHovered, colorPressed)) return; + + EditorGUI.FocusTextInControl("SearchFilter"); + + EditorApplication.delayCall += () => EditorGUI.FocusTextInControl("SearchFilter"); + + isSearchActive = true; + + } + void searchOnCtrlF() + { + if (searchAnimationT == 1) return; + + if (!curEvent.isKeyDown) return; + if (!curEvent.holdingCmd && !curEvent.holdingCtrl) return; + if (curEvent.keyCode != KeyCode.F) return; + + + EditorGUI.FocusTextInControl("SearchFilter"); + + EditorApplication.delayCall += () => EditorGUI.FocusTextInControl("SearchFilter"); + + isSearchActive = true; + + + curEvent.Use(); + + } + void collapseAllButton() + { + if (searchAnimationT == 1) return; + + + var buttonRect = navbarRect.SetWidthFromRight(28).MoveX(-33); + + var iconName = "Collapse"; + var iconSize = 16; + var colorNormal = Greyscale(isDarkTheme ? .71f : .44f); + var colorHovered = Greyscale(isDarkTheme ? 1f : .42f); + var colorPressed = Greyscale(isDarkTheme ? .75f : .6f); + + + if (!IconButton(buttonRect, iconName, iconSize, colorNormal, colorHovered, colorPressed)) return; + + controller.CollapseAll(); + + } + void bookmarks() + { + if (searchAnimationT == 1) return; + if (isSearchActive && !curEvent.isRepaint) return; + + void createData() + { + if (data) return; + if (!navbarRect.IsHovered()) return; + if (!DragAndDrop.objectReferences.Any()) return; + + data = ScriptableObject.CreateInstance(); + + AssetDatabase.CreateAsset(data, GetScriptPath("VHierarchy").GetParentPath().CombinePath("vHierarchy Data.asset")); + + } + void divider() + { + if (!data) return; + if (!data.bookmarks.Any(r => r.go)) return; + + + var dividerRect = navbarRect.SetWidthFromRight(1).SetHeightFromMid(16).MoveX(-65).MoveX(1.5f); + + var dividerColor = Greyscale(isDarkTheme ? .33f : .64f); + + + dividerRect.Draw(dividerColor); + + } + void gui() + { + if (!data) return; + + this.navbarRect = navbarRect; + this.bookmarksRect = navbarRect.AddWidth(-69).AddWidthFromRight(-60).MoveX(2).MoveX(-3); + + BookmarksGUI(); + + } + + createData(); + divider(); + gui(); + + } + + void searchField() + { + if (searchAnimationT == 0) return; + + var searchFieldRect = navbarRect.SetHeightFromMid(20).AddWidth(-33).SetWidthFromRight(200f.Min(window.position.width - 120)).Move(-1, 2); + + + GUILayout.BeginArea(searchFieldRect); + GUILayout.BeginHorizontal(); + + Space(2); + window.InvokeMethod("SearchFieldGUI"); + + GUILayout.EndHorizontal(); + GUILayout.EndArea(); + + } + void closeSearchButton() + { + if (searchAnimationT == 0) return; + + + var buttonRect = navbarRect.SetWidthFromRight(30).MoveX(-4); + + var iconName = "CrossIcon"; + var iconSize = 15; + var colorNormal = Greyscale(isDarkTheme ? .9f : .2f); + var colorHovered = Greyscale(isDarkTheme ? 1f : .2f); + var colorPressed = Greyscale(isDarkTheme ? .75f : .5f); + + + if (!IconButton(buttonRect, iconName, iconSize, colorNormal, colorHovered, colorPressed)) return; + + window.InvokeMethod("ClearSearchFilter"); + + GUIUtility.keyboardControl = 0; + + isSearchActive = false; + + } + void closeSearchOnEsc() + { + if (!isSearchActive) return; + if (curEvent.keyCode != KeyCode.Escape) return; + + window.InvokeMethod("ClearSearchFilter"); + + GUIUtility.keyboardControl = 0; + + isSearchActive = false; + + } + + void searchAnimation() + { + if (!curEvent.isLayout) return; + + + var lerpSpeed = 8f; + + if (isSearchActive) + MathUtil.SmoothDamp(ref searchAnimationT, 1, lerpSpeed, ref searchAnimationDerivative, editorDeltaTime); + else + MathUtil.SmoothDamp(ref searchAnimationT, 0, lerpSpeed, ref searchAnimationDerivative, editorDeltaTime); + + + if (isSearchActive && searchAnimationT > .99f) + searchAnimationT = 1; + + if (!isSearchActive && searchAnimationT < .01f) + searchAnimationT = 0; + + + animatingSearch = searchAnimationT != 0 && searchAnimationT != 1; + + } + + void buttonsAndBookmarks() + { + SetGUIColor(Greyscale(1, (1 - searchAnimationT).Pow(2))); + GUI.BeginGroup(window.position.SetPos(0, 0).MoveX(-searchAnimationDistance * searchAnimationT)); + + searchButton(); + searchOnCtrlF(); + collapseAllButton(); + bookmarks(); + + GUI.EndGroup(); + ResetGUIColor(); + + } + void search() + { + SetGUIColor(Greyscale(1, searchAnimationT.Pow(2))); + GUI.BeginGroup(window.position.SetPos(0, 0).MoveX(searchAnimationDistance * (1 - searchAnimationT))); + + searchField(); + closeSearchButton(); + closeSearchOnEsc(); + + GUI.EndGroup(); + ResetGUIColor(); + + } + + + + updateState(); + + background(); + hiddenMenu(); + + plusButton(); + + searchAnimation(); + buttonsAndBookmarks(); + search(); + + + + if (draggingBookmark || animatingDroppedBookmark || animatingGaps || animatingTooltip || animatingSearch) + window.Repaint(); + + } + + bool animatingSearch; + + float searchAnimationDistance = 90; + float searchAnimationT; + float searchAnimationDerivative; + + string openedFolderPath; + + public bool isSearchActive; + + bool isDefaultParentTextPressed; + + GameObject defaultParent; + + GUIStyle defaultParentTextGUIStyle; + + Rect navbarRect; + Rect bookmarksRect; + + + + + + + + + + + + + void BookmarksGUI() + { + void bookmark(Vector2 centerPosition, Bookmark bookmark) + { + if (bookmark == null) return; + if (curEvent.isLayout) return; + + + var bookmarkRect = Rect.zero.SetSize(bookmarkWidth, bookmarksRect.height).SetMidPos(centerPosition); + + + void shadow() + { + if (!draggingBookmark) return; + if (draggedBookmark != bookmark) return; + + bookmarkRect.SetSizeFromMid(bookmarkWidth - 4, bookmarkWidth - 4).DrawBlurred(Greyscale(0, .3f), 15); + + } + void background() + { + if (!bookmarkRect.IsHovered()) return; + if (draggingBookmark && draggedBookmark != bookmark) return; + + var backgroundColor = Greyscale(isDarkTheme ? .35f : .7f); + + var backgroundRect = bookmarkRect.SetSizeFromMid(bookmarkRect.width - 2, bookmarkWidth - 2); + + backgroundRect.DrawRounded(backgroundColor, 4); + + + } + void icon() + { + var opacity = 1f; + var iconTexture = default(Texture); + + void set_opacity() + { + var opacityNormal = .9f; + var opacityHovered = 1f; + var opacityPressed = .75f; + var opacityDragged = .75f; + var opacityDisabled = .4f; + + var isDisabled = !bookmark.isLoadable; + + + opacity = opacityNormal; + + if (draggingBookmark) + opacity = bookmark == draggedBookmark ? opacityDragged : opacityNormal; + + else if (bookmark == pressedBookmark) + opacity = opacityPressed; + + else if (bookmarkRect.IsHovered()) + opacity = opacityHovered; + + if (isDisabled) + opacity = opacityDisabled; + + } + void getTexture() + { + var iconName = ""; + + if (bookmark.go) + if (VHierarchy.GetGameObjectData(bookmark.go, createDataIfDoesntExist: false) is GameObjectData goData && !goData.iconNameOrGuid.IsNullOrEmpty()) + iconName = goData.iconNameOrGuid.Length == 32 ? goData.iconNameOrGuid.ToPath() : goData.iconNameOrGuid; + else + iconName = AssetPreview.GetMiniThumbnail(bookmark.go).name; + + if (iconName.IsNullOrEmpty()) + iconName = "GameObject icon"; + + + iconTexture = EditorIcons.GetIcon(iconName); + + } + void drawTexture() + { + if (!iconTexture) return; + + + SetGUIColor(Greyscale(1, opacity)); + + GUI.DrawTexture(bookmarkRect.SetSizeFromMid(iconSize), iconTexture); + + ResetGUIColor(); + + } + + + set_opacity(); + getTexture(); + drawTexture(); + + } + void tooltip() + { + if (bookmark != (draggingBookmark ? (draggedBookmark) : (lastHoveredBookmark))) return; + if (tooltipOpacity == 0) return; + + var fontSize = 11; + var tooltipText = bookmark.name; + + Rect tooltipRect; + + void set_tooltipRect() + { + var width = tooltipText.GetLabelWidth(fontSize) + 6; + var height = 16 + (fontSize - 12) * 2; + + var yOffset = 28; + var rightMargin = -1; + + + tooltipRect = Rect.zero.SetMidPos(centerPosition.x, centerPosition.y + yOffset).SetSizeFromMid(width, height); + + + var maxXMax = navbarRect.xMax - rightMargin; + + if (tooltipRect.xMax > maxXMax) + tooltipRect = tooltipRect.MoveX(maxXMax - tooltipRect.xMax); + + } + void shadow() + { + var shadowAmount = .33f; + var shadowRadius = 10; + + tooltipRect.DrawBlurred(Greyscale(0, shadowAmount).MultiplyAlpha(tooltipOpacity), shadowRadius); + + } + void background() + { + var cornerRadius = 5; + + var backgroundColor = Greyscale(isDarkTheme ? .13f : .9f); + var outerEdgeColor = Greyscale(isDarkTheme ? .25f : .6f); + var innerEdgeColor = Greyscale(isDarkTheme ? .0f : .95f); + + tooltipRect.Resize(-1).DrawRounded(outerEdgeColor.SetAlpha(tooltipOpacity.Pow(2)), cornerRadius + 1); + tooltipRect.Resize(0).DrawRounded(innerEdgeColor.SetAlpha(tooltipOpacity.Pow(2)), cornerRadius + 0); + tooltipRect.Resize(1).DrawRounded(backgroundColor.SetAlpha(tooltipOpacity), cornerRadius - 1); + + } + void text() + { + var textRect = tooltipRect.MoveY(-.5f); + + var textColor = Greyscale(1f); + + SetLabelAlignmentCenter(); + SetLabelFontSize(fontSize); + SetGUIColor(textColor.SetAlpha(tooltipOpacity)); + + GUI.Label(textRect, tooltipText); + + ResetLabelStyle(); + ResetGUIColor(); + + } + + set_tooltipRect(); + shadow(); + background(); + text(); + + } + void click() + { + if (!bookmarkRect.IsHovered()) return; + if (!curEvent.isMouseUp) return; + + curEvent.Use(); + + + if (draggingBookmark) return; + if ((curEvent.mousePosition - mouseDownPosiion).magnitude > 2) return; + if (!bookmark.isLoadable) return; + + controller.RevealObject(bookmark.go, expand: true, highlight: true, snapToTopMargin: true); + + lastClickedBookmark = bookmark; + + hideTooltip = true; + + + + if (curEvent.mouseButton == 2 && VHierarchyMenu.setDefaultParentEnabled) + EditorUtility.SetDefaultParentObject(bookmark.go); + + } + + + bookmarkRect.MarkInteractive(); + + shadow(); + background(); + icon(); + tooltip(); + click(); + + } + + void normalBookmark(int i, float centerX) + { + if (data.bookmarks[i] == droppedBookmark && animatingDroppedBookmark) return; + + var centerY = bookmarksRect.height / 2; + + + var minX = centerX - bookmarkWidth / 2; + + if (minX < bookmarksRect.x) return; + + lastBookmarkX = minX; + + + bookmark(new Vector2(centerX, centerY), data.bookmarks[i]); + + } + void normalBookmarks() + { + var curCenterX = bookmarksRect.xMax - bookmarkWidth / 2; + + for (int i = 0; i < data.bookmarks.Count; i++) + { + curCenterX -= gaps[i]; + + if (!data.bookmarks[i].go) continue; + + + normalBookmark(i, curCenterX); + + + curCenterX -= bookmarkWidth; + + } + + } + void draggedBookmark_() + { + if (!draggingBookmark) return; + + var centerX = curEvent.mousePosition.x + draggedBookmarkHoldOffset.x; + var centerY = bookmarksRect.IsHovered() ? bookmarksRect.height / 2 : curEvent.mousePosition.y; + + bookmark(new Vector2(centerX, centerY), draggedBookmark); + + } + void droppedBookmark_() + { + if (!animatingDroppedBookmark) return; + + var centerX = droppedBookmarkX; + var centerY = bookmarksRect.height / 2; + + bookmark(new Vector2(centerX, centerY), droppedBookmark); + + } + + + BookmarksMouseState(); + BookmarksDragging(); + BookmarksAnimations(); + + normalBookmarks(); + draggedBookmark_(); + droppedBookmark_(); + + } + + float bookmarkWidth => 24; + float iconSize => 16; + + float lastBookmarkX; + + + + int GetBookmarkIndex(float mouseX) + { + var curBookmarkWidthSum = 0f; + + for (int i = 0; i < data.bookmarks.Count; i++) + { + if (!data.bookmarks[i].go) continue; + + curBookmarkWidthSum += bookmarkWidth; + + if (bookmarksRect.xMax - curBookmarkWidthSum < mouseX + .5f) + return i; + } + + return data.bookmarks.IndexOfLast(r => r.go) + 1; + + } + + float GetBookmarkCenterX(int i, bool includeGaps = true) + { + return bookmarksRect.xMax + - bookmarkWidth / 2 + - data.bookmarks.Take(i).Sum(r => r.go ? bookmarkWidth : 0) + - (includeGaps ? gaps.Take(i + 1).Sum() : 0); + } + + + + + + + + void BookmarksMouseState() + { + void down() + { + if (!curEvent.isMouseDown) return; + if (!bookmarksRect.IsHovered()) return; + + mousePressed = true; + + mouseDownPosiion = curEvent.mousePosition; + + var pressedBookmarkIndex = GetBookmarkIndex(mouseDownPosiion.x); + + if (pressedBookmarkIndex.IsInRangeOf(data.bookmarks)) + pressedBookmark = data.bookmarks[pressedBookmarkIndex]; + + doubleclickUnhandled = curEvent.clickCount == 2; + + curEvent.Use(); + + } + void up() + { + if (!curEvent.isMouseUp) return; + + mousePressed = false; + pressedBookmark = null; + + } + void hover() + { + var hoveredBookmarkIndex = GetBookmarkIndex(curEvent.mousePosition.x); + + mouseHoversBookmark = bookmarksRect.IsHovered() && hoveredBookmarkIndex.IsInRangeOf(data.bookmarks); + + if (mouseHoversBookmark) + lastHoveredBookmark = data.bookmarks[hoveredBookmarkIndex]; + + + } + + down(); + up(); + hover(); + + } + + bool mouseHoversBookmark; + bool mousePressed; + bool doubleclickUnhandled; + + Vector2 mouseDownPosiion; + + Bookmark pressedBookmark; + Bookmark lastHoveredBookmark; + + + + + + + void BookmarksDragging() + { + void initFromOutside() + { + if (draggingBookmark) return; + if (!bookmarksRect.IsHovered()) return; + if (!curEvent.isDragUpdate) return; + if (DragAndDrop.objectReferences.FirstOrDefault() is not GameObject draggedGo) return; + + animatingDroppedBookmark = false; + + draggingBookmark = true; + draggingBookmarkFromInside = false; + + draggedBookmark = new Bookmark(draggedGo); + draggedBookmarkHoldOffset = Vector2.zero; + + } + void initFromInside() + { + if (draggingBookmark) return; + if (!mousePressed) return; + if ((curEvent.mousePosition - mouseDownPosiion).magnitude <= 2) return; + if (pressedBookmark == null) return; + + var i = GetBookmarkIndex(mouseDownPosiion.x); + + if (i >= data.bookmarks.Count) return; + if (i < 0) return; + + + animatingDroppedBookmark = false; + + draggingBookmark = true; + draggingBookmarkFromInside = true; + + draggedBookmark = data.bookmarks[i]; + draggedBookmarkHoldOffset = new Vector2(GetBookmarkCenterX(i) - mouseDownPosiion.x, bookmarksRect.center.y - mouseDownPosiion.y); + + gaps[i] = bookmarkWidth; + + + data.RecordUndo(); + + data.bookmarks.Remove(draggedBookmark); + + } + + void acceptFromOutside() + { + if (!draggingBookmark) return; + if (!curEvent.isDragPerform) return; + if (!bookmarksRect.IsHovered()) return; + + DragAndDrop.AcceptDrag(); + curEvent.Use(); + + data.RecordUndo(); + + accept(); + + data.Dirty(); + + } + void acceptFromInside() + { + if (!draggingBookmark) return; + if (!curEvent.isMouseUp) return; + if (!bookmarksRect.IsHovered()) return; + + curEvent.Use(); + EditorGUIUtility.hotControl = 0; + + DragAndDrop.PrepareStartDrag(); // fixes phantom dragged component indicator after reordering bookmarks + + data.RecordUndo(); + data.Dirty(); + + accept(); + + } + void accept() + { + draggingBookmark = false; + draggingBookmarkFromInside = false; + mousePressed = false; + + data.bookmarks.AddAt(draggedBookmark, insertDraggedBookmarkAtIndex); + + gaps[insertDraggedBookmarkAtIndex] -= bookmarkWidth; + gaps.AddAt(0, insertDraggedBookmarkAtIndex); + + droppedBookmark = draggedBookmark; + + droppedBookmarkX = curEvent.mousePosition.x + draggedBookmarkHoldOffset.x; + droppedBookmarkXDerivative = 0; + animatingDroppedBookmark = true; + + draggedBookmark = null; + + EditorGUIUtility.hotControl = 0; + + } + + void cancelFromOutside() + { + if (!draggingBookmark) return; + if (draggingBookmarkFromInside) return; + if (bookmarksRect.IsHovered()) return; + + draggingBookmark = false; + mousePressed = false; + + } + void cancelFromInsideAndDelete() + { + if (!draggingBookmark) return; + if (!curEvent.isMouseUp) return; + if (bookmarksRect.IsHovered()) return; + + draggingBookmark = false; + + DragAndDrop.PrepareStartDrag(); // fixes phantom dragged component indicator after reordering bookmarks + + data.Dirty(); + + } + + void update() + { + if (!draggingBookmark) return; + + DragAndDrop.visualMode = DragAndDropVisualMode.Generic; + + if (draggingBookmarkFromInside) // otherwise it breaks vTabs dragndrop + EditorGUIUtility.hotControl = EditorGUIUtility.GetControlID(FocusType.Passive); + + + + insertDraggedBookmarkAtIndex = GetBookmarkIndex(curEvent.mousePosition.x + draggedBookmarkHoldOffset.x); + + } + + + initFromOutside(); + initFromInside(); + + acceptFromOutside(); + acceptFromInside(); + + cancelFromOutside(); + cancelFromInsideAndDelete(); + + update(); + + + } + + bool draggingBookmark; + bool draggingBookmarkFromInside; + + int insertDraggedBookmarkAtIndex; + + Vector2 draggedBookmarkHoldOffset; + + Bookmark draggedBookmark; + Bookmark droppedBookmark; + + + + + + + void BookmarksAnimations() + { + if (!curEvent.isLayout) return; + + void gaps_() + { + var makeSpaceForDraggedBookmark = draggingBookmark && bookmarksRect.IsHovered(); + + var lerpSpeed = 12; + + for (int i = 0; i < gaps.Count; i++) + if (makeSpaceForDraggedBookmark && i == insertDraggedBookmarkAtIndex) + gaps[i] = MathUtil.Lerp(gaps[i], bookmarkWidth, lerpSpeed, editorDeltaTime); + else + gaps[i] = MathUtil.Lerp(gaps[i], 0, lerpSpeed, editorDeltaTime); + + + + for (int i = 0; i < gaps.Count; i++) + if (gaps[i].Approx(0)) + gaps[i] = 0; + + + + animatingGaps = gaps.Any(r => r > .1f); + + + } + void droppedBookmark_() + { + if (!animatingDroppedBookmark) return; + + var lerpSpeed = 8; + + var targX = GetBookmarkCenterX(data.bookmarks.IndexOf(droppedBookmark), includeGaps: false); + + MathUtil.SmoothDamp(ref droppedBookmarkX, targX, lerpSpeed, ref droppedBookmarkXDerivative, editorDeltaTime); + + if ((droppedBookmarkX - targX).Abs() < .5f) + animatingDroppedBookmark = false; + + } + void tooltip() + { + if (!mouseHoversBookmark || lastHoveredBookmark != lastClickedBookmark) + hideTooltip = false; + + + var lerpSpeed = UnityEditorInternal.InternalEditorUtility.isApplicationActive ? 15 : 12321; + + if (mouseHoversBookmark && !draggingBookmark && !hideTooltip) + MathUtil.SmoothDamp(ref tooltipOpacity, 1, lerpSpeed, ref tooltipOpacityDerivative, editorDeltaTime); + else + MathUtil.SmoothDamp(ref tooltipOpacity, 0, lerpSpeed, ref tooltipOpacityDerivative, editorDeltaTime); + + + if (tooltipOpacity > .99f) + tooltipOpacity = 1; + + if (tooltipOpacity < .01f) + tooltipOpacity = 0; + + + animatingTooltip = tooltipOpacity != 0 && tooltipOpacity != 1; + + } + + gaps_(); + droppedBookmark_(); + tooltip(); + + } + + float droppedBookmarkX; + float droppedBookmarkXDerivative; + + float tooltipOpacity; + float tooltipOpacityDerivative; + + bool animatingDroppedBookmark; + bool animatingGaps; + bool animatingTooltip; + bool animatingBookmarks => animatingDroppedBookmark || animatingGaps; + + bool hideTooltip; + + List gaps + { + get + { + while (_gaps.Count < data.bookmarks.Count + 1) _gaps.Add(0); + while (_gaps.Count > data.bookmarks.Count + 1) _gaps.RemoveLast(); + + return _gaps; + + } + } + List _gaps = new(); + + Bookmark lastClickedBookmark; + + + + + + + + + + + + + + public VHierarchyNavbar(EditorWindow window) => this.window = window; + + public EditorWindow window; + + public VHierarchyController controller => VHierarchy.controllers_byWindow[window]; + + + } +} +#endif \ No newline at end of file diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyNavbar.cs.meta b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyNavbar.cs.meta new file mode 100644 index 0000000..611fae8 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyNavbar.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b0951d821533c4f9486bb1430540ef3b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyPalette.cs b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyPalette.cs new file mode 100644 index 0000000..9c2395e --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyPalette.cs @@ -0,0 +1,221 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.Linq; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using UnityEditorInternal; +using static VHierarchy.Libs.VUtils; +using static VHierarchy.Libs.VGUI; +// using static VTools.VDebug; + + +namespace VHierarchy +{ + public class VHierarchyPalette : ScriptableObject + { + public List colors = new(); + + public bool colorsEnabled; + + public float colorSaturation = 1; + public float colorBrightness = 1; + public bool colorGradientsEnabled = true; + + public void ResetColors() + { + colors.Clear(); + + for (int i = 0; i < colorsCount; i++) + colors.Add(GetDefaultColor(i)); + + colorsEnabled = true; + + this.Dirty(); + + } + + public static Color GetDefaultColor(int colorIndex) + { + Color color = default; + + void grey() + { + if (colorIndex >= greyColorsCount) return; + +#if UNITY_2022_1_OR_NEWER + color = Greyscale(isDarkTheme ? .16f : .9f); +#else + color = Greyscale(isDarkTheme ? .315f : .9f); +#endif + + } + void rainbowDarkTheme() + { + if (colorIndex < greyColorsCount) return; + if (!isDarkTheme) return; + + color = ColorUtils.HSLToRGB((colorIndex - greyColorsCount.ToFloat()) / rainbowColorsCount, .45f, .35f); + + if (colorIndex == 1) + color *= 1.2f; + + if (colorIndex == 2) + color *= 1.1f; + + if (colorIndex == 6) + color *= 1.35f; + + if (colorIndex == 7) + color *= 1.3f; + + if (colorIndex == 8) + color *= 1.05f; + + + color.a = .1f; + + } + void rainbowLightTheme() + { + if (colorIndex < greyColorsCount) return; + if (isDarkTheme) return; + + color = ColorUtils.HSLToRGB((colorIndex - greyColorsCount.ToFloat()) / rainbowColorsCount, .62f, .8f); + + color.a = .1f; + + } + + grey(); + rainbowDarkTheme(); + rainbowLightTheme(); + + return color; + + } + + public static int greyColorsCount = 1; + public static int rainbowColorsCount = 8; + public static int colorsCount => greyColorsCount + rainbowColorsCount; + + + + + public List iconRows = new(); + + [System.Serializable] + public class IconRow + { + public List builtinIcons = new(); // names + public List customIcons = new(); // names or guids + + public bool enabled = true; + + public bool isCustom => !builtinIcons.Any() || customIcons.Any(); + public bool isEmpty => !builtinIcons.Any() && !customIcons.Any(); + public int iconCount => builtinIcons.Count + customIcons.Count; + + public IconRow(string[] builtinIcons) => this.builtinIcons = builtinIcons.ToList(); + public IconRow() { } + + } + + public void ResetIcons() + { + iconRows.Clear(); + + iconRows.Add(new IconRow(new[] + { + "Folder Icon", + "Canvas Icon", + "AvatarMask On Icon", + "cs Script Icon", + "StandaloneInputModule Icon", + "EventSystem Icon", + "Terrain Icon", + "ScriptableObject Icon", + + })); + iconRows.Add(new IconRow(new[] + { + "Camera Icon", + "ParticleSystem Icon", + "TrailRenderer Icon", + "Material Icon", + "ReflectionProbe Icon", + + })); + iconRows.Add(new IconRow(new[] + { + "Light Icon", + "DirectionalLight Icon", + "LightmapParameters Icon", + "LightProbes Icon", + + })); + iconRows.Add(new IconRow(new[] + { + "Rigidbody Icon", + "BoxCollider Icon", + "SphereCollider Icon", + "CapsuleCollider Icon", + "WheelCollider Icon", + "MeshCollider Icon", + + })); + iconRows.Add(new IconRow(new[] + { + "AudioSource Icon", + "AudioClip Icon", + "AudioListener Icon", + "AudioEchoFilter Icon", + "AudioReverbZone Icon", + + })); + iconRows.Add(new IconRow(new[] + { + "PreMatCube", + "PreMatSphere", + "PreMatCylinder", + "PreMatQuad", + "Favorite", + #if UNITY_2021_3_OR_NEWER + "Settings Icon", + #endif + + })); + + this.Dirty(); + + } + + + + + [ContextMenu("Export palette")] + public void Export() + { + var packagePath = EditorUtility.SaveFilePanel("Export vHierarchy Palette", "", this.GetPath().GetFilename(withExtension: false), "unitypackage"); + + var iconPaths = iconRows.SelectMany(r => r.customIcons).Select(r => r.ToPath()).Where(r => !r.IsNullOrEmpty()); + + AssetDatabase.ExportPackage(iconPaths.Append(this.GetPath()).ToArray(), packagePath); + + EditorUtility.RevealInFinder(packagePath); + + } + + + + + void Reset() { ResetColors(); ResetIcons(); } + + } +} +#endif \ No newline at end of file diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyPalette.cs.meta b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyPalette.cs.meta new file mode 100644 index 0000000..bec7d1c --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyPalette.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 61290d425f1c94a8cbfb56f754ca6757 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyPaletteEditor.cs b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyPaletteEditor.cs new file mode 100644 index 0000000..4add5a8 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyPaletteEditor.cs @@ -0,0 +1,1374 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.Linq; +using UnityEditorInternal; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using UnityEditor.IMGUI.Controls; +using static VHierarchy.Libs.VUtils; +using static VHierarchy.Libs.VGUI; +// using static VTools.VDebug; +using static VHierarchy.VHierarchyPalette; + + +namespace VHierarchy +{ + [CustomEditor(typeof(VHierarchyPalette))] + class VHierarchyPaletteEditor : Editor + { + + public override void OnInspectorGUI() + { + void colors() + { + var rowRect = ExpandWidthLabelRect(cellSize).SetX(rowsOffsetX).SetWidth(rowWidth + 16); + + void backgroundHovered() + { + if (!rowRect.IsHovered()) return; + if (pickingColor) return; + if (draggingRow) return; + + rowRect.Draw(hoveredRowBackground); + + } + void toggle() + { + var toggleRect = rowRect.SetWidth(16).MoveX(5); + + var prevEnabled = palette.colorsEnabled; + var newEnabled = EditorGUI.Toggle(toggleRect, palette.colorsEnabled); + + if (prevEnabled != newEnabled) + palette.RecordUndo(); + + palette.colorsEnabled = newEnabled; + + if (prevEnabled != newEnabled) + palette.Dirty(); + + } + void crossIcon() + { + var crossIconRect = rowRect.SetX(rowsOffsetX + iconsOffsetX + iconSpacing / 2).SetWidth(iconSize).SetHeightFromMid(iconSize); + + SetGUIColor(palette.colorsEnabled ? Color.white : disabledRowTint); + + GUI.DrawTexture(crossIconRect, EditorIcons.GetIcon("CrossIcon")); + + ResetGUIColor(); + + } + void color(int i) + { + var cellRect = rowRect.MoveX(iconsOffsetX + (i + 1) * cellSize).SetWidth(cellSize).SetHeightFromMid(cellSize); + + void backgroundPicking() + { + if (!pickingColor) return; + if (i != pickingColorAtIndex) return; + + cellRect.DrawRounded(pickingBackground, 2); + + } + void backgroundHovered_andStartPickingColor() + { + if (pickingColor) return; + if (!cellRect.IsHovered()) return; + + + SetGUIColor(Color.clear); + + var clicked = GUI.Button(cellRect.Resize(1), ""); + + ResetGUIColor(); + + + + + var isPressed = GUIUtility.hotControl == typeof(EditorGUIUtility).GetFieldValue("s_LastControlID"); + + cellRect.DrawRounded(Greyscale(isPressed ? .39f : .43f), 2); + + + + + if (!clicked) return; + + colorPickerWindow = EditorUtils.OpenColorPicker((c) => { palette.RecordUndo(); palette.Dirty(); palette.colors[i] = c; }, palette.colors[i], true, false); + + colorPickerWindow.MoveTo(EditorGUIUtility.GUIToScreenPoint(cellRect.Move(-3, 50).position)); + + pickingColor = true; + pickingColorAtIndex = i; + + } + void colorOutline() + { + var outlineColor = i < VHierarchyPalette.greyColorsCount ? Greyscale(.0f, .4f) : Greyscale(.15f, .2f); + + if (!palette.colorsEnabled) + outlineColor *= disabledRowTint; + + + cellRect.Resize(3).DrawRounded(outlineColor, 4); + + } + void color() + { + var brightness = palette.colorBrightness; + var saturation = palette.colorSaturation; + var drawGradients = palette.colorGradientsEnabled; + + if (!palette.colorGradientsEnabled) + brightness *= isDarkTheme ? .75f : .97f; + + if (i < VHierarchyPalette.greyColorsCount) + { + saturation = brightness = 1; + drawGradients = false; + } + + + var colorRaw = palette.colors[i]; + + var color = MathUtil.Lerp(Greyscale(.2f), colorRaw, brightness); + + Color.RGBToHSV(color, out float h, out float s, out float v); + color = Color.HSVToRGB(h, s * saturation, v); + + color = MathUtil.Lerp(color, colorRaw, .5f).SetAlpha(1); + + if (!palette.colorsEnabled) + color *= disabledRowTint; + + if (i >= VHierarchyPalette.greyColorsCount && isDarkTheme) + color *= 1.41f; + + + + + cellRect.Resize(4).DrawRounded(color, 3); + + if (drawGradients) + cellRect.Resize(4).AddWidthFromRight(-2).DrawCurtainLeft(GUIColors.windowBackground.SetAlpha(.45f)); + + } + void updatePickingColor() + { + if (!pickingColor) return; + + EditorApplication.RepaintHierarchyWindow(); + + } + void stopPickingColor() + { + if (!pickingColor) return; + if (colorPickerWindow) return; + + pickingColor = false; + + } + + + cellRect.MarkInteractive(); + + + backgroundPicking(); + backgroundHovered_andStartPickingColor(); + + colorOutline(); + color(); + + updatePickingColor(); + stopPickingColor(); + + } + void adjustColorsButton() + { + var cellRect = rowRect.MoveX(iconsOffsetX + (palette.colors.Count + 1) * cellSize).SetWidth(cellSize).SetHeightFromMid(cellSize).MoveX(-1f); + + + var iconSize = 16; + var iconName = "Preset.Context"; + var iconColor = Greyscale(.75f, palette.colorsEnabled ? (isDarkTheme ? 1 : .8f) : .5f); + + if (!IconButton(cellRect, iconName, iconSize, iconColor)) return; + + + if (adjustColorsWindow) { adjustColorsWindow.Close(); return; } + + var windowX = 107f.Min(this.GetMemberValue("propertyViewer").position.width - 310); + var windowY = cellRect.y + 25; + var windowWidth = 270; + var windowHeight = 92; + + adjustColorsWindow = ScriptableObject.CreateInstance(); + adjustColorsWindow.palette = palette; + adjustColorsWindow.paletteEditor = this; + + adjustColorsWindow.ShowPopup(); + adjustColorsWindow.Focus(); + + adjustColorsWindow.position = EditorGUIUtility.GUIToScreenRect(new Rect(windowX, windowY, windowWidth, windowHeight)); + + } + + + backgroundHovered(); + toggle(); + crossIcon(); + + for (int i = 0; i < palette.colors.Count; i++) + color(i); + + adjustColorsButton(); + + Space(rowSpacing - 2); + + } + void icons() + { + void row(Rect rowRect, IconRow row) + { + var isLastRow = row == palette.iconRows.Last(); + var isDraggedRow = row == draggedRow; + var spaceForCrossIcon = 0f; + + + void updatePickingIcon() + { + if (!pickingIcon) return; + if (pickingIconAtRow != row) return; + if (pickingIconAtIndex >= row.customIcons.Count) return; // somehow happens if RecordUndo is used + + palette.RecordUndo(); + palette.Dirty(); + + row.customIcons[pickingIconAtIndex] = addIconWindow.hoveredIconName; + + } + void stopPickingIcon() + { + if (!pickingIcon) return; + if (pickingIconAtRow != row) return; + if (addIconWindow) return; + + if (pickingIconAtIndex < row.customIcons.Count) + if (row.customIcons[pickingIconAtIndex] == null) + row.customIcons.RemoveAt(pickingIconAtIndex); + + pickingIcon = false; + + } + void dragndrop() + { + if (!rowRect.IsHovered()) return; + if (!row.isCustom) return; + + if (curEvent.isDragUpdate && DragAndDrop.objectReferences.First() is Texture2D) + DragAndDrop.visualMode = DragAndDropVisualMode.Copy; + + if (!curEvent.isDragPerform) return; + if (!(DragAndDrop.objectReferences.Any(r => r is Texture2D))) return; + + DragAndDrop.AcceptDrag(); + + palette.RecordUndo(); + palette.Dirty(); + + foreach (var icon in DragAndDrop.objectReferences.Where(r => r is Texture2D)) + row.customIcons.Add(icon.GetPath().ToGuid()); + + } + + void calcSpaceForCrossIcon() + { + if (row == curFirstEnabledRow) + spaceForCrossIcon = crossIconAnimationT * cellSize; + + if (row == crossIconAnimationSourceRow) + spaceForCrossIcon = (1 - crossIconAnimationT) * cellSize; + + } + + void backgroundHovered() + { + if (!rowRect.IsHovered()) return; + if (pickingColor) return; + if (pickingIcon) return; + if (draggingRow) return; + if (DragAndDrop.objectReferences.Any() && !row.isCustom) return; + + + rowRect.Draw(hoveredRowBackground); + + } + void backgroundDragged() + { + if (!isDraggedRow) return; + + rowRect.DrawBlurred(Greyscale(0, .3f), 12); + rowRect.Draw(draggedRowBackground); + + } + void toggle() + { + var prevEnabled = row.enabled; + var newEnabled = EditorGUI.Toggle(rowRect.SetWidth(16).MoveX(5), row.enabled); + + if (prevEnabled != newEnabled) + palette.RecordUndo(); + + row.enabled = newEnabled; + + if (prevEnabled != newEnabled) + palette.Dirty(); + + } + void addIconButton() + { + if (!row.isCustom) return; + if (pickingIcon && pickingIconAtRow == row) return; + + + var cellRect = rowRect.MoveX(iconsOffsetX + row.customIcons.Count * cellSize + spaceForCrossIcon).SetWidth(cellSize).SetHeightFromMid(cellSize); + + var iconSize = 16; + var iconName = "Toolbar Plus"; + var iconColor = Greyscale(.73f, row.enabled ? (isDarkTheme ? 1 : .65f) : .5f); + + if (!IconButton(cellRect, iconName, iconSize, iconColor)) return; + + + + palette.RecordUndo(); + + row.customIcons.Add(null); + + + + var windowX = 15; + var windowY = cellRect.y + 23; + var windowWidth = (this.GetMemberValue("propertyViewer").position.width - 26).Min(679); + var windowHeight = windowWidth * 1.2f; + + windowWidth = (windowWidth / AddIconWindow.cellSize).FloorToInt() * AddIconWindow.cellSize - 3; + + + addIconWindow = ScriptableObject.CreateInstance(); + addIconWindow.palette = palette; + addIconWindow.paletteEditor = this; + + addIconWindow.ShowPopup(); + addIconWindow.Focus(); + + addIconWindow.position = EditorGUIUtility.GUIToScreenRect(new Rect(windowX, windowY, windowWidth, windowHeight)); + + addIconWindow.Init(); + + + pickingIcon = true; + pickingIconAtIndex = row.customIcons.Count - 1; + pickingIconAtRow = row; + + + } + void icon(int i) + { + var cellRect = rowRect.MoveX(iconsOffsetX + spaceForCrossIcon + i * cellSize).SetWidth(cellSize).SetHeightFromMid(cellSize); + + + void backgroundPicking() + { + if (!pickingIcon) return; + if (pickingIconAtRow != row) return; + if (pickingIconAtIndex != i) return; + + cellRect.DrawRounded(pickingBackground, 2); + + } + void backgroundHovered_andEditIconButton() + { + if (!row.isCustom) return; + if (pickingIcon) return; + if (!cellRect.IsHovered()) return; + if (DragAndDrop.objectReferences.Any()) return; + + + SetGUIColor(Color.clear); + + var clicked = GUI.Button(cellRect.Resize(1), ""); + + ResetGUIColor(); + + + + + var isPressed = GUIUtility.hotControl == typeof(EditorGUIUtility).GetFieldValue("s_LastControlID"); + + cellRect.DrawRounded(Greyscale(isPressed ? .39f : .45f), 2); + + + + + if (!clicked) return; + + GenericMenu menu = new(); + + menu.AddItem(new GUIContent("Move left"), false, i == 0 ? null : () => + { + palette.RecordUndo(); + palette.Dirty(); + + var icon = row.customIcons[i]; + + row.customIcons.RemoveAt(i); + row.customIcons.AddAt(icon, i - 1); + + }); + menu.AddItem(new GUIContent("Move right"), false, i == row.customIcons.Count - 1 ? null : () => + { + palette.RecordUndo(); + palette.Dirty(); + + var icon = row.customIcons[i]; + + row.customIcons.RemoveAt(i); + row.customIcons.AddAt(icon, i + 1); + + }); + + menu.AddSeparator(""); + + menu.AddItem(new GUIContent("Remove icon"), false, () => { palette.RecordUndo(); row.customIcons.RemoveAt(i); palette.Dirty(); }); + + + menu.ShowAsContext(); + + } + + void drawIcon() + { + var iconNameOrGuid = row.isCustom ? row.customIcons[i] : row.builtinIcons[i]; + + if (iconNameOrGuid == null) return; + + var iconNameOrPath = iconNameOrGuid.Length == 32 ? iconNameOrGuid.ToPath() : iconNameOrGuid; + var icon = EditorIcons.GetIcon(iconNameOrPath) ?? Texture2D.blackTexture; + + + var cellRect = rowRect.MoveX(iconsOffsetX + spaceForCrossIcon + i * cellSize).SetWidth(cellSize).SetHeightFromMid(cellSize); + var iconRect = cellRect.SetSizeFromMid(iconSize); + + if (icon.width < icon.height) iconRect = iconRect.SetWidthFromMid(iconRect.height * icon.width / icon.height); + if (icon.height < icon.width) iconRect = iconRect.SetHeightFromMid(iconRect.width * icon.height / icon.width); + + + + SetGUIColor(row.enabled ? Color.white : disabledRowTint); + + GUI.DrawTexture(iconRect, icon); + + ResetGUIColor(); + + } + + + cellRect.MarkInteractive(); + + backgroundPicking(); + backgroundHovered_andEditIconButton(); + + drawIcon(); + + } + + + rowRect.MarkInteractive(); + + updatePickingIcon(); + stopPickingIcon(); + dragndrop(); + + calcSpaceForCrossIcon(); + backgroundHovered(); + backgroundDragged(); + toggle(); + addIconButton(); + + for (int i = 0; i < row.iconCount; i++) + icon(i); + + } + + void updateRowsCount() + { + palette.iconRows.RemoveAll(r => r.isEmpty && r != palette.iconRows.Last()); + + if (!palette.iconRows.Last().isEmpty) + palette.iconRows.Add(new IconRow()); + + } + void updateRowGapsCount() + { + while (rowGaps.Count < palette.iconRows.Count) + rowGaps.Add(0); + + while (rowGaps.Count > palette.iconRows.Count) + rowGaps.RemoveLast(); + + } + + void normalRow(int i) + { + Space(rowGaps[i] * (cellSize + rowSpacing)); + + if (i == 0 && lastRect.y != 0) + firstRowY = lastRect.y; + + Space(cellSize + rowSpacing); + + var rowRect = Rect.zero.SetPos(rowsOffsetX, lastRect.y).SetSize(rowWidth, cellSize); + + if (curEvent.isRepaint) + if (rowRect.IsHovered()) + hoveredRow = palette.iconRows[i]; + + + row(rowRect, palette.iconRows[i]); + + } + void draggedRow_() + { + if (!draggingRow) return; + + draggedRowY = (curEvent.mousePosition.y + draggedRowHoldOffset).Clamp(firstRowY, firstRowY + (palette.iconRows.Count - 1) * (cellSize + rowSpacing)); + + var rowRect = Rect.zero.SetPos(rowsOffsetX, draggedRowY).SetSize(rowWidth, cellSize); + + row(rowRect, draggedRow); + + } + void crossIcon() + { + if (!palette.iconRows.Any(r => r.enabled)) return; + + var rect = Rect.zero.SetPos(rowsOffsetX + iconsOffsetX, crossIconY).SetSize(cellSize, cellSize).Resize(iconSpacing / 2); + + GUI.DrawTexture(rect, EditorIcons.GetIcon("CrossIcon")); + + } + + + updateRowsCount(); + updateRowGapsCount(); + + + if (curEvent.isRepaint) + hoveredRow = null; + + for (int i = 0; i < palette.iconRows.Count; i++) + normalRow(i); + + crossIcon(); + + draggedRow_(); + + + } + void tutor() + { + SetGUIEnabled(false); + + + Space(4); + GUILayout.Label("Add icons with drag-and-drop or by clicking '+'"); + + Space(4); + GUILayout.Label("Click added icon to move or remove it"); + + Space(4); + GUILayout.Label("Drag rows to reorder them"); + + + ResetGUIEnabled(); + + } + + + Space(15); + colors(); + + Space(15); + icons(); + + Space(22); + tutor(); + + UpdateAnimations(); + + UpdateDragging(); + + palette.Dirty(); + + if (draggingRow || animatingCrossIcon) + Repaint(); + + } + + float iconSize => 14; + float iconSpacing => 6; + float cellSize => iconSize + iconSpacing; + float rowSpacing = 1; + float rowsOffsetX => 14; + float iconsOffsetX => 27; + + Color hoveredRowBackground => Greyscale(isDarkTheme ? 1 : 0, .05f); + Color draggedRowBackground => Greyscale(isDarkTheme ? .3f : .9f); + Color pickingBackground => Greyscale(1, .17f); + Color disabledRowTint => Greyscale(1, .45f); + + float rowWidth => cellSize * Mathf.Max(palette.colors.Count, palette.iconRows.Max(r => r.iconCount + 1)) + 55; + + bool pickingColor; + int pickingColorAtIndex; + EditorWindow colorPickerWindow; + + bool pickingIcon; + int pickingIconAtIndex; + IconRow pickingIconAtRow; + AddIconWindow addIconWindow; + + IconRow hoveredRow; + + float firstRowY = 51; + + static AdjustColorsWindow adjustColorsWindow; + + + + + + void UpdateAnimations() + { + void lerpRowGaps() + { + if (!curEvent.isLayout) return; + + var lerpSpeed = draggingRow ? 12 : 12321; + + for (int i = 0; i < rowGaps.Count; i++) + rowGaps[i] = MathUtil.Lerp(rowGaps[i], draggingRow && i == insertDraggedRowAtIndex ? 1 : 0, lerpSpeed, editorDeltaTime); + + for (int i = 0; i < rowGaps.Count; i++) + if (rowGaps[i].Approx(0)) + rowGaps[i] = 0; + else if (rowGaps[i].Approx(1)) + rowGaps[i] = 1; + + + } + + void lerpCrossIconAnimationT() + { + if (!curEvent.isLayout) return; + + var lerpSpeed = 12; + + MathUtil.Lerp(ref crossIconAnimationT, 1, lerpSpeed, editorDeltaTime); + + } + void startCrossIconAnimation() + { + if (prevFirstEnabledRow == null) { prevFirstEnabledRow = curFirstEnabledRow; return; } + if (prevFirstEnabledRow == curFirstEnabledRow) return; + + crossIconAnimationT = 0; + crossIconAnimationSourceRow = prevFirstEnabledRow; + + prevFirstEnabledRow = curFirstEnabledRow; + + } + void stopCrossIconAnimation() + { + if (!crossIconAnimationT.Approx(1)) return; + + crossIconAnimationT = 1; + crossIconAnimationSourceRow = null; + + } + void calcCrossIconY() + { + var indexOfFirstEnabled = palette.iconRows.IndexOfFirst(r => r.enabled); + var yOfFirstEnabled = firstRowY + indexOfFirstEnabled * (cellSize + rowSpacing); + for (int i = 0; i < indexOfFirstEnabled + 1; i++) + yOfFirstEnabled += rowGaps[i] * (cellSize + rowSpacing); + + + var indexOfSourceRow = palette.iconRows.IndexOf(crossIconAnimationSourceRow); + var yOfSourceRow = firstRowY + indexOfSourceRow * (cellSize + rowSpacing); + for (int i = 0; i < indexOfSourceRow + 1; i++) + yOfSourceRow += rowGaps[i] * (cellSize + rowSpacing); + + if (crossIconAnimationSourceRow == draggedRow) + yOfSourceRow = draggedRowY; + + + crossIconY = MathUtil.Lerp(yOfSourceRow, yOfFirstEnabled, crossIconAnimationT); + + if (indexOfFirstEnabled == indexOfSourceRow) + crossIconAnimationT = 1; + + } + + + lerpRowGaps(); + + lerpCrossIconAnimationT(); + startCrossIconAnimation(); + stopCrossIconAnimation(); + calcCrossIconY(); + + } + + List rowGaps = new(); + + float crossIconY = 51; + float crossIconAnimationT = 1; + IconRow crossIconAnimationSourceRow; + bool animatingCrossIcon => crossIconAnimationT != 1; + + [System.NonSerialized] IconRow prevFirstEnabledRow; + IconRow curFirstEnabledRow => palette.iconRows.FirstOrDefault(r => r.enabled); + + + + + + + void UpdateDragging() + { + void startDragging() + { + if (draggingRow) return; + if (!curEvent.isMouseDrag) return; + if (hoveredRow == null) return; + if (hoveredRow == palette.iconRows.Last()) return; + + palette.RecordUndo(); + + draggingRow = true; + draggedRow = hoveredRow; + draggingRowFromIndex = palette.iconRows.IndexOf(hoveredRow); + draggedRowHoldOffset = firstRowY + draggingRowFromIndex * (cellSize + rowSpacing) - curEvent.mousePosition.y; + + palette.iconRows.Remove(hoveredRow); + rowGaps[draggingRowFromIndex] = 1; + + } + void updateDragging() + { + if (!draggingRow) return; + + insertDraggedRowAtIndex = ((curEvent.mousePosition.y - firstRowY) / (cellSize + rowSpacing)).FloorToInt().Clamp(0, palette.iconRows.Count - 1); + + EditorGUIUtility.hotControl = EditorGUIUtility.GetControlID(FocusType.Passive); + + } + void stopDragging() + { + if (!draggingRow) return; + if (!curEvent.isMouseUp) return; + + palette.RecordUndo(); + palette.Dirty(); + + palette.iconRows.AddAt(draggedRow, insertDraggedRowAtIndex); + + rowGaps[insertDraggedRowAtIndex] = 0; + + draggingRow = false; + draggedRow = null; + + EditorGUIUtility.hotControl = 0; + + } + + + startDragging(); + updateDragging(); + stopDragging(); + + } + + IconRow draggedRow; + bool draggingRow; + int draggingRowFromIndex; + float draggedRowHoldOffset; + float draggedRowY; + int insertDraggedRowAtIndex; + + + + + + + VHierarchyPalette palette => target as VHierarchyPalette; + + } + + + class AddIconWindow : EditorWindow + { + + void OnGUI() + { + void header() + { + var headerRect = Rect.zero.SetHeight(20).SetWidth(position.width); + var closeButtonRect = headerRect.SetWidthFromRight(16).SetHeightFromMid(16).Move(-3, -.5f); + + void background() + { + headerRect.Draw(EditorGUIUtility.isProSkin ? Greyscale(.18f) : Greyscale(.7f)); + } + void title() + { + SetGUIColor(Greyscale(.8f)); + SetLabelAlignmentCenter(); + + GUI.Label(headerRect.MoveY(-1), "Add icon"); + + ResetLabelStyle(); + ResetGUIColor(); + + } + void closeButton() + { + var colorNormal = isDarkTheme ? Greyscale(.55f) : Greyscale(.35f); + var colorHovered = isDarkTheme ? Greyscale(.9f) : colorNormal; + + var iconSize = 14; + + if (IconButton(closeButtonRect, "CrossIcon", iconSize, colorNormal, colorHovered)) + Close(); + + } + void escHint() + { + if (!closeButtonRect.IsHovered()) return; + + var textRect = headerRect.SetWidthFromRight(42).MoveY(-.5f).MoveX(1); + var fontSize = 11; + var color = Greyscale(.65f); + + + SetLabelFontSize(fontSize); + SetGUIColor(color); + + GUI.Label(textRect, "Esc"); + + ResetGUIColor(); + ResetLabelStyle(); + + } + + background(); + title(); + closeButton(); + escHint(); + + Space(headerRect.height); + + } + void search() + { + var backgroundRect = ExpandWidthLabelRect(height: 21).SetWidthFromMid(position.width); + var backgroundColor = isDarkTheme ? Greyscale(.25f) : Greyscale(.8f); + + backgroundRect.Draw(backgroundColor); + + + var lineRect = backgroundRect.SetHeightFromBottom(1).MoveY(.5f); + var lineColor = isDarkTheme ? Greyscale(.15f) : Greyscale(.7f); + + lineRect.Draw(lineColor); + + + var searchRect = backgroundRect.Resize(2); + + EditorGUI.BeginChangeCheck(); + + searchString = searchField.OnGUI(searchRect, searchString); + + if (EditorGUI.EndChangeCheck()) + { + GenerateRows(); + FilterIconsBySearch(); + GenerateRows(); + } + + } + void icons() + { + void row(int i) + { + var rowRect = position.SetPos(0, 0).SetHeight(cellSize).MoveY(i * rowHeight); + var iconNames = rows[i]; + + void icon(int i) + { + var iconName = iconNames[i]; + var icon = EditorIcons.GetIcon(iconName); + + var cellRect = rowRect.SetWidth(cellSize).MoveX(i * cellSize + paddingLeft); + var iconRect = cellRect.SetSizeFromMid(iconSize); + + if (icon.width < icon.height) iconRect = iconRect.SetWidthFromMid(iconRect.height * icon.width / icon.height); + if (icon.height < icon.width) iconRect = iconRect.SetHeightFromMid(iconRect.width * icon.height / icon.width); + + + + var hoverRect = cellRect.AddHeightFromMid(rowSpacing); + + hoverRect.MarkInteractive(); + + if (hoverRect.IsHovered()) + cellRect.Draw(Greyscale(isDarkTheme ? .42f : .69f)); + + + + GUI.DrawTexture(iconRect, EditorIcons.GetIcon(iconName)); + + + + if (hoverRect.IsHovered()) + hoveredIconName = iconName; + + if (hoverRect.IsHovered() && curEvent.isMouseDown) + Close(); + + } + + for (int ii = 0; ii < iconNames.Count; ii++) + icon(ii); + } + + + if (curEvent.isRepaint) + hoveredIconName = null; + + + scrollPos = GUILayout.BeginScrollView(new Vector2(0, scrollPos)).y; + + GUILayout.Space(rows.Count * rowHeight + 23); + + + var i0 = (scrollPos / rowHeight).FloorToInt(); + var i1 = (i0 + ((position.height - 30) / rowHeight).CeilToInt()).Min(rows.Count); + + for (int ii = i0; ii < i1; ii++) + row(ii); + + + GUILayout.EndScrollView(); + + } + void hoveredIconLabel() + { + if (hoveredIconName == null) return; + + + var nameRect = position.SetPos(0, 0).SetHeightFromBottom(18).SetWidth(hoveredIconName.GetLabelWidth() + 6).Move(1, -1); + + var shadowRect = nameRect.AddWidthFromRight(10).AddHeight(10); + + + shadowRect.DrawBlurred(GUIColors.windowBackground, 12); + shadowRect.DrawBlurred(GUIColors.windowBackground.SetAlpha(.4f), 8); + + + SetLabelAlignmentCenter(); + + GUI.Label(nameRect, hoveredIconName); + + ResetLabelStyle(); + + } + void closeOnEsc() + { + if (!curEvent.isKeyDown) return; + if (curEvent.keyCode != KeyCode.Escape) return; + + hoveredIconName = null; + + Close(); + + } + void outline() + { + if (Application.platform == RuntimePlatform.OSXEditor) return; + + position.SetPos(0, 0).DrawOutline(Greyscale(.1f)); + + } + + header(); + search(); + icons(); + hoveredIconLabel(); + closeOnEsc(); + outline(); + + paletteEditor.Repaint(); + + if (EditorWindow.focusedWindow != this) + Close(); + + } + + public static float iconSize => 16; + public static float iconSpacing => 6; + public static float cellSize => iconSize + iconSpacing; + public static float rowSpacing = 1; + + public static float rowHeight => cellSize + rowSpacing; + + public static float paddingLeft => 3; + public static float paddingRight => 3; + + public string hoveredIconName; + + static string searchString = ""; + static float scrollPos; + + + + + + void LoadAllIcons() + { + if (allIconNames != null) return; + + + (float h, float s, float v, float a) GetAverageColor(Texture2D texture) + { + + var readableTexture = new Texture2D(texture.width, texture.height, texture.format, texture.mipmapCount > 1); + + Graphics.CopyTexture(texture, readableTexture); + + var pixels = readableTexture.GetPixels32(); + + readableTexture.DestroyImmediate(); + + + float hSum = 0; + float sSum = 0; + float vSum = 0; + + int nonTransparentPxCount = pixels.Length; + int coloredPxCount = pixels.Length; + + for (var i = 0; i < pixels.Length; i++) + { + if (pixels[i].a <= .1f) { nonTransparentPxCount--; coloredPxCount--; continue; } + + Color.RGBToHSV(pixels[i], out float h, out float s, out float v); + + if (s > .1f) + hSum += h; + else + coloredPxCount--; + + sSum += s; + + vSum += v; + + } + + var hAvg = hSum / coloredPxCount; + var sAvg = sSum / nonTransparentPxCount; + var vAvg = vSum / nonTransparentPxCount; + var aAvg = nonTransparentPxCount / pixels.Length.ToFloat(); + + if (coloredPxCount == 0) + hAvg = -1; + + + return (hAvg, sAvg, vAvg, aAvg); + + } + + + + var editorAssetBundle = typeof(EditorGUIUtility).InvokeMethod("GetEditorAssetBundle"); + + allIconNames = ( + + from path in editorAssetBundle.GetAllAssetNames() + + + let icon = editorAssetBundle.LoadAsset(path) + + where icon + + + where path.StartsWith("icons/") + where !path.Contains("avatarinspector") + + where !icon.name.ToLower().StartsWith("d_") + + where !icon.name.ToLower().EndsWith(".small") + where !icon.name.ToLower().EndsWith("_sml") + + where !icon.name.Contains("@") + where !icon.name.Contains("TreeEditor") + where !icon.name.Contains("scene-template") + where !icon.name.Contains("StateMachineEditor.Background") + where !icon.name.Contains("SpeedTree") + where !icon.name.Contains("TextMesh") + where !icon.name.Contains("Profiler.Instrumentation") + where !icon.name.Contains("Profiler.Record") + where !icon.name.Contains("SocialNetworks") + where !icon.name.Contains("Groove") + + + + let avgColor = GetAverageColor(icon) + + where avgColor.a > .1f + + + + orderby avgColor.h * -3f + + avgColor.s * .09f + + avgColor.v * 0f, icon.name + + + + select icon.name + + ).ToHashSet() + .ToList(); + + } + + static List allIconNames; + + + + void FilterIconsBySearch() + { + filteredIcons = ( + + from iconName in allIconNames + + where iconName.ToLower().Contains(searchString.ToLower()) + + orderby iconName.ToLower().IndexOf(searchString.ToLower(), System.StringComparison.Ordinal) + + select iconName + + ).ToList(); + } + + static List filteredIcons; + + + + void GenerateRows() + { + + var iconsPerRow = ((position.width - paddingLeft - paddingRight) / cellSize).FloorToInt(); + + rows = new(); + + var curRow = new List(); + + foreach (var icon in filteredIcons) + { + curRow.Add(icon); + + if (curRow.Count == iconsPerRow) + { + rows.Add(curRow); + curRow = new(); + } + } + + if (curRow.Any()) + rows.Add(curRow); + + } + + static List> rows; + + + + + + + public void Init() + { + LoadAllIcons(); + FilterIconsBySearch(); + GenerateRows(); + + searchField = new(); + + } + + SearchField searchField; + + + + + + public VHierarchyPalette palette; + public VHierarchyPaletteEditor paletteEditor; + + } + + class AdjustColorsWindow : EditorWindow + { + void OnGUI() + { + void header() + { + var headerRect = Rect.zero.SetHeight(20).SetWidth(position.width); + var closeButtonRect = headerRect.SetWidthFromRight(16).SetHeightFromMid(16).Move(-3, -.5f); + + void background() + { + headerRect.Draw(EditorGUIUtility.isProSkin ? Greyscale(.18f) : Greyscale(.7f)); + } + void title() + { + SetGUIColor(Greyscale(.8f)); + SetLabelAlignmentCenter(); + + GUI.Label(headerRect.MoveY(-1), "Adjust colors"); + + ResetLabelStyle(); + ResetGUIColor(); + + } + void closeButton() + { + var colorNormal = isDarkTheme ? Greyscale(.55f) : Greyscale(.35f); + var colorHovered = isDarkTheme ? Greyscale(.9f) : colorNormal; + + var iconSize = 14; + + if (IconButton(closeButtonRect, "CrossIcon", iconSize, colorNormal, colorHovered)) + Close(); + + } + void escHint() + { + if (!closeButtonRect.IsHovered()) return; + + var textRect = headerRect.SetWidthFromRight(42).MoveY(-.5f).MoveX(1); + var fontSize = 11; + var color = Greyscale(.65f); + + + SetLabelFontSize(fontSize); + SetGUIColor(color); + + GUI.Label(textRect, "Esc"); + + ResetGUIColor(); + ResetLabelStyle(); + + } + void outline() + { + if (Application.platform == RuntimePlatform.OSXEditor) return; + + position.SetPos(0, 0).DrawOutline(Greyscale(.1f)); + + } + + background(); + title(); + closeButton(); + escHint(); + outline(); + + Space(headerRect.height); + + } + void body() + { + EditorGUIUtility.labelWidth = 85; + EditorGUIUtility.keyboardControl = 0; + + palette.RecordUndo(); + + + EditorGUI.BeginChangeCheck(); + + palette.colorBrightness = (EditorGUILayout.Slider("Brightness", palette.colorBrightness, 0, 2) / .1f).RoundToInt() * .1f; + palette.colorSaturation = (EditorGUILayout.Slider("Saturation", palette.colorSaturation, 0, 2) / .1f).RoundToInt() * .1f; + + palette.colorGradientsEnabled = EditorGUILayout.Toggle("Gradients", palette.colorGradientsEnabled); + + if (EditorGUI.EndChangeCheck()) + { + VHierarchy.goInfoCache.Clear(); + + paletteEditor.Repaint(); + EditorApplication.RepaintHierarchyWindow(); + palette.Dirty(); + } + + + EditorGUIUtility.labelWidth = 0; + + } + void closeOnEsc() + { + if (!curEvent.isKeyDown) return; + if (curEvent.keyCode != KeyCode.Escape) return; + + Close(); + + } + + + header(); + + Space(7); + BeginIndent(10); + + body(); + + EndIndent(5); + + + + closeOnEsc(); + + if (EditorWindow.focusedWindow != this) + Close(); + + Repaint(); // for undo + + } + + public VHierarchyPalette palette; + public VHierarchyPaletteEditor paletteEditor; + + } + +} +#endif \ No newline at end of file diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyPaletteEditor.cs.meta b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyPaletteEditor.cs.meta new file mode 100644 index 0000000..a1fcf36 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyPaletteEditor.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2b7dce55e9e9b476fb5d1d669c006123 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyPaletteWindow.cs b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyPaletteWindow.cs new file mode 100644 index 0000000..1751200 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyPaletteWindow.cs @@ -0,0 +1,711 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.Linq; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using Type = System.Type; +using static VHierarchy.VHierarchy; +using static VHierarchy.VHierarchyData; +using static VHierarchy.VHierarchyPalette; +using static VHierarchy.Libs.VUtils; +using static VHierarchy.Libs.VGUI; +// using static VTools.VDebug; + + + +namespace VHierarchy +{ + public class VHierarchyPaletteWindow : EditorWindow + { + + void OnGUI() + { + if (!palette) { Close(); return; } + + int hoveredColorIndex = -1; + string hoveredIconNameOrGuid = null; + + void background() + { + position.SetPos(0, 0).Draw(windowBackground); + } + void outline() + { + if (Application.platform == RuntimePlatform.OSXEditor) return; + + position.SetPos(0, 0).DrawOutline(Greyscale(.1f)); + + } + void colors() + { + if (!palette.colorsEnabled) return; + + var rowRect = this.position.SetPos(paddingX, paddingY).SetHeight(cellSize); + + + void color(int i) + { + var cellRect = rowRect.MoveX(i * cellSize).SetWidth(cellSize).SetHeightFromMid(cellSize); + + void backgroundSelected() + { + if (!colorIndexes_initial.Contains(i)) return; + + cellRect.Resize(1).DrawRounded(selectedBackground, 2); + + } + void backgroundHovered() + { + if (!cellRect.IsHovered()) return; + + cellRect.Resize(1).DrawRounded(this.hoveredBackground, 2); + + } + void crossIcon() + { + if (i != 0) return; + + GUI.DrawTexture(cellRect.SetSizeFromMid(iconSize), EditorIcons.GetIcon("CrossIcon")); + + } + void colorOutline() + { + if (i == 0) return; + + var outlineColor = i <= VHierarchyPalette.greyColorsCount ? Greyscale(.0f, .4f) : Greyscale(.15f, .2f); + + cellRect.Resize(3).DrawRounded(outlineColor, 4); + + } + void color() + { + if (i == 0) return; + + var brightness = palette.colorBrightness; + var saturation = palette.colorSaturation; + var drawGradients = palette.colorGradientsEnabled; + + if (!palette.colorGradientsEnabled) + brightness *= isDarkTheme ? .75f : .97f; + + if (i <= VHierarchyPalette.greyColorsCount) + { + saturation = brightness = 1; + drawGradients = false; + } + + + var colorRaw = palette ? palette.colors[i - 1] : VHierarchyPalette.GetDefaultColor(i - 1); + + var color = MathUtil.Lerp(Greyscale(.2f), colorRaw, brightness); + + Color.RGBToHSV(color, out float h, out float s, out float v); + color = Color.HSVToRGB(h, s * saturation, v); + + color = MathUtil.Lerp(color, colorRaw, .5f).SetAlpha(1); + + if (i > VHierarchyPalette.greyColorsCount && isDarkTheme) + color *= 1.41f; + + + + + cellRect.Resize(4).DrawRounded(color, 3); + + if (drawGradients) + cellRect.Resize(4).AddWidthFromRight(-2).DrawCurtainLeft(GUIColors.windowBackground.SetAlpha(.45f)); + + } + void recursiveIndicator() + { + if (!curEvent.isRepaint) return; + + + var isRecursive = goDatas.First().colorIndex == i && goDatas.First().isColorRecursive; + + if (!isRecursive) return; + + + + var iconRect = cellRect.SetSizeFromMid(16).Move(-6, -7); + var shadowRect = iconRect.Resize(3).Move(2, 1).AddWidthFromRight(3); + var shadowRadius = 4; + + shadowRect.DrawBlurred(GUIColors.windowBackground, shadowRadius); + + + SetGUIColor(Color.white * 2); + + GUI.DrawTexture(iconRect, EditorIcons.GetIcon("UnityEditor.SceneHierarchyWindow@2x")); + + ResetGUIColor(); + + + } + + void setHovered() + { + if (!cellRect.IsHovered()) return; + + hoveredColorIndex = i; + + } + void closeOnClick() + { + if (!cellRect.IsHovered()) return; + if (!curEvent.isMouseUp) return; + + curEvent.Use(); + + Close(); + + } + + + + cellRect.MarkInteractive(); + + backgroundSelected(); + backgroundHovered(); + crossIcon(); + colorOutline(); + color(); + recursiveIndicator(); + + setHovered(); + closeOnClick(); + + } + + + for (int i = 0; i < palette.colors.Count + 1; i++) + color(i); + + } + void icons() + { + void row(int i, IconRow iconRow) + { + var rowRect = this.position.SetPos(paddingX, paddingY).SetHeight(cellSize).MoveY(palette.colorsEnabled ? cellSize + spaceAfterColors : 0).MoveY(i * (cellSize + rowSpacing)); + + var isFirstEnabledRow = palette.iconRows.First(r => r.enabled) == iconRow; + + + void icon(int i) + { + var cellRect = rowRect.MoveX(i * cellSize).SetWidth(cellSize).SetHeightFromMid(cellSize); + + var isCrossIcon = isFirstEnabledRow && i == 0; + var actualIconIndex = isFirstEnabledRow ? i - 1 : i; + var isCustomIcon = !isCrossIcon && actualIconIndex >= iconRow.builtinIcons.Count; + var iconNameOrGuid = isCrossIcon ? "" : isCustomIcon ? iconRow.customIcons[actualIconIndex - iconRow.builtinIcons.Count] : iconRow.builtinIcons[actualIconIndex]; + + + void backgroundSelected() + { + if (!iconNamesOrGuids_initial.Contains(iconNameOrGuid)) return; + + cellRect.Resize(1).DrawRounded(selectedBackground, 2); + + } + void backgroundHovered() + { + if (!cellRect.IsHovered()) return; + + cellRect.Resize(1).DrawRounded(this.hoveredBackground, 2); + + } + void crossIcon() + { + if (!isCrossIcon) return; + + GUI.DrawTexture(cellRect.SetSizeFromMid(iconSize), EditorIcons.GetIcon("CrossIcon")); + + } + void normalIcon() + { + if (isCrossIcon) return; + + var iconNameOrPath = iconNameOrGuid?.Length == 32 ? iconNameOrGuid.ToPath() : iconNameOrGuid; + var icon = EditorIcons.GetIcon(iconNameOrPath) ?? Texture2D.blackTexture; + + var iconRect = cellRect.SetSizeFromMid(iconSize); + + if (icon.width < icon.height) iconRect = iconRect.SetWidthFromMid(iconRect.height * icon.width / icon.height); + if (icon.height < icon.width) iconRect = iconRect.SetHeightFromMid(iconRect.width * icon.height / icon.width); + + + GUI.DrawTexture(iconRect, icon); + + } + void recursiveIndicator() + { + if (!curEvent.isRepaint) return; + + + var isRecursive = goDatas.First().iconNameOrGuid == iconNameOrGuid && goDatas.First().isIconRecursive; + + if (!isRecursive) return; + + + + var iconRect = cellRect.SetSizeFromMid(16).Move(-6, -7); + var shadowRect = iconRect.Resize(3).Move(2, 1).AddWidthFromRight(3); + var shadowRadius = 4; + + shadowRect.DrawBlurred(GUIColors.windowBackground, shadowRadius); + + + + SetGUIColor(Color.white * 2); + + GUI.DrawTexture(iconRect, EditorIcons.GetIcon("UnityEditor.SceneHierarchyWindow@2x")); + + ResetGUIColor(); + + + } + + void setHovered() + { + if (!cellRect.IsHovered()) return; + + hoveredIconNameOrGuid = iconNameOrGuid; + + } + void closeOnClick() + { + if (!cellRect.IsHovered()) return; + if (!curEvent.isMouseUp) return; + + curEvent.Use(); + + Close(); + + } + + + + cellRect.MarkInteractive(); + + backgroundSelected(); + backgroundHovered(); + crossIcon(); + normalIcon(); + recursiveIndicator(); + + setHovered(); + closeOnClick(); + + } + + + for (int j = 0; j < iconRow.iconCount + (isFirstEnabledRow ? 1 : 0); j++) + icon(j); + + } + + + var i = 0; + + foreach (var iconRow in palette.iconRows) + { + if (!iconRow.enabled) continue; + if (iconRow.isEmpty) continue; + + row(i, iconRow); + + i++; + } + + } + void editPaletteButton() + { + var buttonRect = position.SetPos(0, 0).SetWidthFromRight(16).SetHeightFromBottom(16).Move(-14, -14); + var buttonColor = isDarkTheme ? Greyscale(.6f) : Greyscale(1, .6f); + + if (!IconButton(buttonRect, "Toolbar Plus", 16, buttonColor)) return; + + + palette.SelectInInspector(frameInProject: false); + + EditorWindow.GetWindow(typeof(Editor).Assembly.GetType("UnityEditor.InspectorWindow"))?.Show(); + + this.Close(); + + } + + void setColorsAndIcons() + { + if (!curEvent.isLayout) return; + + + if (palette.iconRows.Any(r => r.enabled)) + if (hoveredIconNameOrGuid != null) + SetIcon(hoveredIconNameOrGuid, isRecursive: curEvent.holdingAlt); + else + SetInitialIcons(); + + + if (palette.colorsEnabled) + if (hoveredColorIndex != -1) + SetColor(hoveredColorIndex, isRecursive: curEvent.holdingAlt); + else + SetInitialColors(); + + } + void updatePosition() + { + if (!curEvent.isLayout) return; + + void calcDeltaTime() + { + deltaTime = (float)(EditorApplication.timeSinceStartup - lastLayoutTime); + + if (deltaTime > .05f) + deltaTime = .0166f; + + lastLayoutTime = EditorApplication.timeSinceStartup; + + } + void resetCurPos() + { + if (currentPosition != default) return; + + currentPosition = position.position; // position.position is always int, which can't be used for lerping + + } + void lerpCurPos() + { + var speed = 9; + + MathUtil.SmoothDamp(ref currentPosition, targetPosition, speed, ref positionDeriv, deltaTime); + // MathfUtils.Lerp(ref currentPosition, targetPosition, speed, deltaTime); + + } + void setCurPos() + { + position = position.SetPos(currentPosition); + } + + calcDeltaTime(); + resetCurPos(); + lerpCurPos(); + setCurPos(); + + if (!currentPosition.magnitude.Approx(targetPosition.magnitude)) + Repaint(); + + } + void closeOnEscape() + { + if (!curEvent.isKeyDown) return; + if (curEvent.keyCode != KeyCode.Escape) return; + + SetInitialColors(); + SetInitialIcons(); + + Close(); + + } + + + RecordUndoOnDatas(); + + background(); + outline(); + colors(); + icons(); + editPaletteButton(); + + setColorsAndIcons(); + updatePosition(); + closeOnEscape(); + + + VHierarchy.goInfoCache.Clear(); + + EditorApplication.RepaintHierarchyWindow(); + + } + + static float iconSize => 14; + static float iconSpacing => 6; + static float cellSize => iconSize + iconSpacing; + static float spaceAfterColors => 13; + public float rowSpacing = 1; + static float paddingX => 12; + static float paddingY => 12; + + Color windowBackground => isDarkTheme ? Greyscale(.23f) : Greyscale(.75f); + Color selectedBackground => isDarkTheme ? new Color(.3f, .5f, .7f, .8f) : new Color(.3f, .5f, .7f, .6f) * 1.25f; + Color hoveredBackground => isDarkTheme ? Greyscale(1, .3f) : Greyscale(0, .1f); + + public Vector2 targetPosition; + public Vector2 currentPosition; + Vector2 positionDeriv; + float deltaTime; + double lastLayoutTime; + + + + + + + void SetIcon(string iconNameOrGuid, bool isRecursive) + { + foreach (var r in goDatas) + { + r.isIconRecursive = isRecursive; // setting it firstbecause iconNameOrGuid setter relies on isIconRecursive + r.iconNameOrGuid = iconNameOrGuid; + } + } + void SetColor(int colorIndex, bool isRecursive) + { + foreach (var r in goDatas) + { + r.isColorRecursive = isRecursive; + r.colorIndex = colorIndex; + } + } + + void SetInitialIcons() + { + for (int i = 0; i < goDatas.Count; i++) + { + goDatas[i].isIconRecursive = isIconRecursives_initial[i]; + goDatas[i].iconNameOrGuid = iconNamesOrGuids_initial[i]; + } + } + void SetInitialColors() + { + for (int i = 0; i < goDatas.Count; i++) + { + goDatas[i].isColorRecursive = isColorRecursives_initial[i]; + goDatas[i].colorIndex = colorIndexes_initial[i]; + } + } + + void RemoveEmptyGoDatas() + { + var toRemove = goDatas.Where(r => r.iconNameOrGuid == "" && r.colorIndex == 0); + + foreach (var goData in toRemove) + goData.sceneData.goDatas_byGlobalId.RemoveValue(goData); + + if (toRemove.Any()) + Undo.CollapseUndoOperations(Undo.GetCurrentGroup() - 1); + + } + + void RecordUndoOnDatas() + { + if (usingDataSO) + if (data) + data.RecordUndo(); + + foreach (var r in usedDataComponents) + r.RecordUndo(); + + } + void MarkDatasDirty() + { + if (usingDataSO) + if (data) + data.Dirty(); + + foreach (var r in usedDataComponents) + r.Dirty(); + } + void SaveData() + { + // if (usingDataSO) + // data.Save(); + } + + bool usingDataSO => !gameObjects.Select(r => r.scene).All(r => VHierarchy.dataComponents_byScene.GetValueOrDefault(r) != null); + IEnumerable usedDataComponents => VHierarchy.dataComponents_byScene.Where(kvp => kvp.Value && gameObjects.Select(r => r.scene).Contains(kvp.Key)).Select(kvp => kvp.Value); + + + + + + + + void OnLostFocus() + { + if (curEvent.holdingAlt && EditorWindow.focusedWindow?.GetType().Name == "SceneHierarchyWindow") + CloseNextFrameIfNotRefocused(); + else + Close(); + + } + + void CloseNextFrameIfNotRefocused() + { + EditorApplication.delayCall += () => { if (EditorWindow.focusedWindow != this) Close(); }; + } + + + + + static void RepaintOnAlt() // Update + { + if (curEvent.holdingAlt != wasHoldingAlt) + if (EditorWindow.mouseOverWindow is VHierarchyPaletteWindow paletteWindow) + paletteWindow.Repaint(); + + wasHoldingAlt = curEvent.holdingAlt; + + } + + static bool wasHoldingAlt; + + + + + + + + + + public void Init(List gameObjects) + { + void createData() + { + if (VHierarchy.data) return; + + VHierarchy.data = ScriptableObject.CreateInstance(); + + AssetDatabase.CreateAsset(VHierarchy.data, GetScriptPath("VHierarchy").GetParentPath().CombinePath("vHierarchy Data.asset")); + + } + void createPalette() + { + if (VHierarchy.palette) return; + + VHierarchy.palette = ScriptableObject.CreateInstance(); + + AssetDatabase.CreateAsset(VHierarchy.palette, GetScriptPath("VHierarchy").GetParentPath().CombinePath("vHierarchy Palette.asset")); + + } + void setSize() + { + if (!palette.colorsEnabled && !palette.iconRows.Any(r => r.enabled && !r.isEmpty)) // somehow happened on first palette window opening in 2022.3.50 + palette.InvokeMethod("Reset"); + + + + var rowCellCounts = new List(); + + if (palette.colorsEnabled) + rowCellCounts.Add(palette.colors.Count + 1); + + foreach (var r in palette.iconRows.Where(r => r.enabled && !r.isEmpty)) + rowCellCounts.Add(r.iconCount + (r == palette.iconRows.First(r => r.enabled) ? 1 : 0)); + + var width = paddingX + + rowCellCounts.Max() * cellSize + + (rowCellCounts.Last() == rowCellCounts.Max() ? cellSize : 0) + + paddingX; + + + + var iconRowCount = palette.iconRows.Count(r => r.enabled && !r.isEmpty); + + var height = paddingY + + (palette.colorsEnabled ? cellSize : 0) + + (palette.colorsEnabled && palette.iconRows.Any(r => r.enabled && !r.isEmpty) ? spaceAfterColors : 0) + + cellSize * iconRowCount + + rowSpacing * (iconRowCount - 1) + + paddingY; + + + position = position.SetSize(width, height).SetPos(targetPosition); + + } + void getInfos() + { + goDatas.Clear(); + + foreach (var r in gameObjects) + goDatas.Add(VHierarchy.GetGameObjectData(r, createDataIfDoesntExist: true)); + + } + void getInitialState() + { + iconNamesOrGuids_initial = goDatas.Select(r => r.iconNameOrGuid).ToList(); + colorIndexes_initial = goDatas.Select(r => r.colorIndex).ToList(); + + isIconRecursives_initial = goDatas.Select(r => r.isIconRecursive).ToList(); + isColorRecursives_initial = goDatas.Select(r => r.isColorRecursive).ToList(); + + + } + + + this.gameObjects = gameObjects; + + RecordUndoOnDatas(); + + createData(); + createPalette(); + setSize(); + getInfos(); + getInitialState(); + + EditorApplication.update -= RepaintOnAlt; + EditorApplication.update += RepaintOnAlt; + + } + + void OnDestroy() + { + RemoveEmptyGoDatas(); + MarkDatasDirty(); + SaveData(); + + EditorApplication.update -= RepaintOnAlt; + + } + + public List gameObjects = new(); + public List goDatas = new(); + + public List iconNamesOrGuids_initial = new(); + public List colorIndexes_initial = new(); + + public List isIconRecursives_initial = new(); + public List isColorRecursives_initial = new(); + + static VHierarchyPalette palette => VHierarchy.palette; + static VHierarchyData data => VHierarchy.data; + + + + + + + + public static void CreateInstance(Vector2 position) + { + instance = ScriptableObject.CreateInstance(); + + instance.ShowPopup(); + + instance.position = instance.position.SetPos(position).SetSize(200, 300); + instance.targetPosition = position; + + } + + public static VHierarchyPaletteWindow instance; + + } +} +#endif \ No newline at end of file diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyPaletteWindow.cs.meta b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyPaletteWindow.cs.meta new file mode 100644 index 0000000..c978574 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchyPaletteWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8ae240588f29744208e627125db9c9e4 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchySceneSelectorWindow.cs b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchySceneSelectorWindow.cs new file mode 100644 index 0000000..4b4e06c --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchySceneSelectorWindow.cs @@ -0,0 +1,1189 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.Linq; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using UnityEditor.IMGUI.Controls; +using System.Diagnostics; +using Type = System.Type; +using Delegate = System.Delegate; +using Action = System.Action; +using static VHierarchy.VHierarchy; +using static VHierarchy.Libs.VUtils; +using static VHierarchy.Libs.VGUI; +// using static VTools.VDebug; + + +namespace VHierarchy +{ + public class VHierarchySceneSelectorWindow : EditorWindow + { + + void OnGUI() + { + + void background() + { + windowRect.Draw(windowBackground); + + } + void closeOnEscape() + { + if (!curEvent.isKeyDown) return; + if (curEvent.keyCode != KeyCode.Escape) return; + + Close(); + + EditorApplication.RepaintHierarchyWindow(); + + GUIUtility.ExitGUI(); + + } + void addTabOnEnter() + { + // if (!curEvent.isKeyDown) return; // searchfield steals fpcus + if (curEvent.keyCode != KeyCode.Return) return; + + if (keyboardFocusedRowIndex == -1) return; + if (keyboardFocusedEntry == null) return; + + OpenScene(keyboardFocusedEntry, curEvent.holdingAlt); + + this.Close(); + + } + void arrowNavigation() + { + if (!curEvent.isKeyDown) return; + if (curEvent.keyCode != KeyCode.UpArrow && curEvent.keyCode != KeyCode.DownArrow) return; + + curEvent.Use(); + + + if (curEvent.keyCode == KeyCode.UpArrow) + if (keyboardFocusedRowIndex == 0) + keyboardFocusedRowIndex = rowCount - 1; + else + keyboardFocusedRowIndex--; + + if (curEvent.keyCode == KeyCode.DownArrow) + if (keyboardFocusedRowIndex == rowCount - 1) + keyboardFocusedRowIndex = 0; + else + keyboardFocusedRowIndex++; + + + keyboardFocusedRowIndex = keyboardFocusedRowIndex.Clamp(0, rowCount - 1); + + } + void updateSearch() + { + if (searchString == prevSearchString) return; + + prevSearchString = searchString; + + + if (searchString == "") { keyboardFocusedRowIndex = -1; return; } + + UpdateSearch(); + + keyboardFocusedRowIndex = 0; + + } + + + void searchField_() + { + var searchRect = windowRect.SetHeight(18).MoveY(1).AddWidthFromMid(-2); + + + if (searchField == null) + { + searchField = new SearchField(); + searchField.SetFocus(); + + } + + + searchString = searchField.OnGUI(searchRect, searchString); + + } + void rows() + { + void bookmarked() + { + if (searchString != "") return; + if (!bookmarkedEntries.Any()) return; + + bookmarksRect = windowRect.SetHeight(bookmarkedEntries.Count * rowHeight + gaps.Sum()); + + BookmarksGUI(); + + } + void divider() + { + if (searchString != "") return; + if (!bookmarkedEntries.Any()) return; + + var splitterColor = Greyscale(.36f); + var splitterRect = bookmarksRect.SetHeightFromBottom(0).SetHeight(dividerHeight).SetHeightFromMid(1).AddWidthFromMid(-10); + + splitterRect.Draw(splitterColor); + + } + void notBookmarked() + { + if (searchString != "") return; + + if (bookmarkedEntries.Any()) + nextRowY = bookmarksRect.yMax + dividerHeight; + + foreach (var entry in allEntries) + { + if (bookmarkedEntries.Contains(entry)) continue; + if (entry == draggedBookmark) continue; + + RowGUI(windowRect.SetHeight(rowHeight).SetY(nextRowY), entry); + + nextRowY += rowHeight; + nextRowIndex++; + + } + + } + void searched() + { + if (searchString == "") return; + + foreach (var entry in searchedEntries) + { + RowGUI(windowRect.SetHeight(rowHeight).SetY(nextRowY), entry); + + nextRowY += rowHeight; + nextRowIndex++; + + } + + } + + + scrollPos = GUI.BeginScrollView(windowRect.AddHeightFromBottom(-firstRowOffsetTop), Vector2.up * scrollPos, windowRect.SetHeight(scrollAreaHeight), GUIStyle.none, GUIStyle.none).y; + + nextRowY = 0; + nextRowIndex = 0; + + bookmarked(); + divider(); + notBookmarked(); + searched(); + + scrollAreaHeight = nextRowY + 23; + rowCount = nextRowIndex; + + GUI.EndScrollView(); + + } + void noResults() + { + if (searchString == "") return; + if (searchedEntries.Any()) return; + + + GUI.enabled = false; + GUI.skin.label.alignment = TextAnchor.MiddleCenter; + + GUI.Label(windowRect.AddHeightFromBottom(-14), "No results"); + + GUI.skin.label.alignment = TextAnchor.MiddleLeft; + GUI.enabled = true; + + } + + void outline() + { + if (Application.platform == RuntimePlatform.OSXEditor) return; + + position.SetPos(0, 0).DrawOutline(Greyscale(.1f)); + + } + // void resizing() + // { + + // } + + + background(); + closeOnEscape(); + addTabOnEnter(); + arrowNavigation(); + updateSearch(); + + searchField_(); + rows(); + noResults(); + + outline(); + + + if (draggingBookmark || animatingDroppedBookmark || animatingGaps) + this.Repaint(); + + } + + Rect windowRect => position.SetPos(0, 0); + Rect bookmarksRect; + + SearchField searchField; + + Color windowBackground => Greyscale(isDarkTheme ? .23f : .8f); + + string searchString = ""; + string prevSearchString = ""; + + float scrollPos; + + float rowHeight => 22; + float dividerHeight => 11; + float firstRowOffsetTop => bookmarkedEntries.Any() && searchString == "" ? 21 : 20; + + int nextRowIndex; + float nextRowY; + + float scrollAreaHeight = 1232; + int rowCount = 123; + + int keyboardFocusedRowIndex = -1; + + + + + + + + + + void RowGUI(Rect rowRect, SceneEntry entry) + { + + var isHovered = rowRect.IsHovered(); + var isPressed = entry == pressedEntry; + var isDragged = draggingBookmark && draggedBookmark == entry; + var isDropped = animatingDroppedBookmark && droppedBookmark == entry; + var isFocused = entry == keyboardFocusedEntry; + var isBookmarked = bookmarkedEntries.Contains(entry) || entry == draggedBookmark; + + if (isDropped) + isHovered = rowRect.SetY(droppedBookmarkYTarget).IsHovered(); + + + var showBlueBackground = isFocused || isPressed || isDragged; + var showAdditiveIndicator = curEvent.holdingAlt && ((keyboardFocusedEntry?.scenePath).IsNullOrEmpty() ? isHovered : isFocused); + var showStarButton = (isHovered || (isBookmarked && !isFocused)) && !showAdditiveIndicator; + var showEnterHint = (isFocused && !isHovered && isDarkTheme) && !showAdditiveIndicator; + + + void draggedShadow() + { + if (!curEvent.isRepaint) return; + if (!isDragged) return; + + var shadowRect = rowRect.AddHeightFromMid(-4); + + var shadowOpacity = .3f; + var shadowRadius = 13; + + shadowRect.DrawBlurred(Greyscale(0, shadowOpacity), shadowRadius); + + } + void blueBackground() + { + if (!curEvent.isRepaint) return; + if (!showBlueBackground) return; + + + var backgroundRect = rowRect.AddHeightFromMid(-3); + + backgroundRect.Draw(GUIColors.selectedBackground); + + + } + void icon() + { + if (!curEvent.isRepaint) return; + + + Texture iconTexture = EditorIcons.GetIcon("SceneAsset Icon"); + + if (!iconTexture) return; + + + var iconRect = rowRect.SetWidth(16).SetHeightFromMid(16).MoveX(4 + 1); + + iconRect = iconRect.SetWidthFromMid(iconRect.height * iconTexture.width / iconTexture.height); + + + GUI.DrawTexture(iconRect, iconTexture); + + } + void name() + { + if (!curEvent.isRepaint) return; + + + var nameRect = rowRect.MoveX(21 + 1); + + var nameText = searchString != "" ? namesFormattedForFuzzySearch_byEntry[entry] + : entry.sceneName; + + + if (entry.scenePath == sceneToReplace.path) + { + SetLabelBold(); + SetGUIColor(Greyscale(1.15f)); + + GUI.skin.label.richText = true; + + GUI.Label(nameRect, nameText); + + GUI.skin.label.richText = false; + + ResetGUIColor(); + ResetLabelStyle(); + + } + + else + { + var color = showBlueBackground ? Greyscale(123, 123) + : isHovered && !isPressed ? Greyscale(1.1f) + : Greyscale(1); + SetGUIColor(color); + + GUI.skin.label.richText = true; + + GUI.Label(nameRect, nameText); + + GUI.skin.label.richText = false; + + ResetGUIColor(); + } + + } + void curtain() + { + if (!curEvent.isRepaint) return; + + + var curtainWidth = 20; + + var curtainColor = showBlueBackground ? GUIColors.selectedBackground : windowBackground; + + var curtainXMax = rowRect.xMax - (showStarButton ? 21 : showEnterHint ? 33 : showAdditiveIndicator ? 80 : 0); + + + rowRect.SetHeightFromMid(18).SetXMax(curtainXMax).SetWidthFromRight(curtainWidth).DrawCurtainLeft(curtainColor); + + rowRect.SetHeightFromMid(18).SetX(curtainXMax).SetXMax(rowRect.xMax).Draw(curtainColor); + + } + void starButton() + { + if (!showStarButton) return; + + + var buttonRect = rowRect.SetWidthFromRight(16).MoveX(-6 + 1).SetSizeFromMid(rowHeight); + + + var iconName = isBookmarked ^ buttonRect.IsHovered() ? "Star" : "Star Hollow"; + var iconSize = 16; + var colorNormal = Greyscale(isDarkTheme ? (isBookmarked ? .5f : .7f) : .3f); + var colorHovered = Greyscale(isDarkTheme ? (isBookmarked ? .9f : 1) : 0f); + var colorPressed = Greyscale(isDarkTheme ? .75f : .5f); + var colorDisabled = Greyscale(isDarkTheme ? .53f : .55f); + + + if (!IconButton(buttonRect, iconName, iconSize, colorNormal, colorHovered, colorPressed)) return; + + if (isBookmarked) + bookmarkedEntries.Remove(entry); + else + bookmarkedEntries.Add(entry); + + } + void enterHint() + { + if (!curEvent.isRepaint) return; + if (!showEnterHint) return; + + + var hintRect = rowRect.SetWidthFromRight(33); + + + SetLabelFontSize(10); + SetGUIColor(Greyscale(.9f)); + + GUI.Label(hintRect, "Enter"); + + ResetGUIColor(); + ResetLabelStyle(); + + + } + void additiveIndicator() + { + if (!curEvent.isRepaint) return; + if (!showAdditiveIndicator) return; + + + var indicatorRect = rowRect.SetWidthFromRight(81); + + + SetLabelFontSize(10); + SetGUIColor(Greyscale(.9f)); + + GUI.Label(indicatorRect, "Load additively"); + + ResetGUIColor(); + ResetLabelStyle(); + + } + void hoverHighlight() + { + if (!curEvent.isRepaint) return; + if (!isHovered) return; + if (isPressed || isDragged) return; + + + var backgroundRect = rowRect.AddHeightFromMid(-2); + + var backgroundColor = Greyscale(isDarkTheme ? 1 : 0, isPressed ? .085f : .12f); + + + backgroundRect.Draw(backgroundColor); + + } + + void mouse() + { + void down() + { + if (!curEvent.isMouseDown) return; + if (!rowRect.IsHovered()) return; + + isMousePressedOnEntry = true; + pressedEntry = entry; + + mouseDownPosition = curEvent.mousePosition; + + this.Repaint(); + + } + void up() + { + if (!curEvent.isMouseUp) return; + + isMousePressedOnEntry = false; + pressedEntry = null; + + this.Repaint(); + + + if (!isHovered) return; + if (draggingBookmark) return; + if ((curEvent.mousePosition - mouseDownPosition).magnitude > 2) return; + + curEvent.Use(); + + OpenScene(entry, curEvent.holdingAlt); + + this.Close(); + + } + + down(); + up(); + + } + void setFocusedEntry() + { + var rowIndex = (rowRect.y / rowHeight).FloorToInt(); + + if (rowIndex == keyboardFocusedRowIndex) + keyboardFocusedEntry = entry; + + } + + + rowRect.MarkInteractive(); + + draggedShadow(); + blueBackground(); + icon(); + name(); + curtain(); + starButton(); + enterHint(); + additiveIndicator(); + hoverHighlight(); + mouse(); + setFocusedEntry(); + + } + + SceneEntry pressedEntry; + + bool isMousePressedOnEntry; + + Vector2 mouseDownPosition; + + SceneEntry keyboardFocusedEntry; + + + + + void OpenScene(SceneEntry entry, bool openAdditive) + { + if (!Application.isPlaying) + if (!EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo()) return; // if clicked cancel + + + void openScene(string path, bool additive) + { + if (!EditorApplication.isPlaying) + EditorSceneManager.OpenScene(path, additive ? OpenSceneMode.Additive : OpenSceneMode.Single); + else + SceneManager.LoadScene(path, additive ? LoadSceneMode.Additive : LoadSceneMode.Single); + + } + + + + if (openAdditive) + openScene(entry.scenePath, additive: true); + + else if (EditorSceneManager.sceneCount == 1) + openScene(entry.scenePath, additive: false); + + else + { + var openedScene = EditorSceneManager.OpenScene(entry.scenePath, OpenSceneMode.Additive); + + EditorSceneManager.MoveSceneAfter(openedScene, sceneToReplace); + + if (EditorSceneManager.GetActiveScene() == sceneToReplace) + EditorSceneManager.SetActiveScene(openedScene); + + EditorSceneManager.CloseScene(sceneToReplace, removeScene: true); + + } + + + } + + + + + + + + + + + + + public void BookmarksGUI() + { + void normalBookmark(int i) + { + if (bookmarkedEntries[i] == droppedBookmark && animatingDroppedBookmark) return; + + var bookmarkRect = bookmarksRect.SetHeight(rowHeight) + .SetY(GetBookmarY(i)); + + RowGUI(bookmarkRect, bookmarkedEntries[i]); + + } + void normalBookmarks() + { + for (int i = 0; i < bookmarkedEntries.Count; i++) + normalBookmark(i); + + } + void draggedBookmark_() + { + if (!draggingBookmark) return; + + + var bookmarkRect = bookmarksRect.SetHeight(rowHeight) + .SetY(draggedBookmarkY); + + RowGUI(bookmarkRect, draggedBookmark); + + } + void droppedBookmark_() + { + if (!animatingDroppedBookmark) return; + + var bookmarkRect = bookmarksRect.SetHeight(rowHeight) + .SetY(droppedBookmarkY); + + RowGUI(bookmarkRect, droppedBookmark); + + } + + + BookmarksDragging(); + BookmarksAnimations(); + + normalBookmarks(); + draggedBookmark_(); + droppedBookmark_(); + + } + + int GetBookmarkIndex(float mouseY) + { + return ((mouseY - bookmarksRect.y) / rowHeight).FloorToInt(); + } + + float GetBookmarY(int i, bool includeGaps = true) + { + var centerY = bookmarksRect.y + + i * rowHeight + + (includeGaps ? gaps.Take(i + 1).Sum() : 0); + + + return centerY; + + } + + + + + + + + + + void BookmarksDragging() + { + void init() + { + if (draggingBookmark) return; + if ((curEvent.mousePosition - mouseDownPosition).magnitude <= 2) return; + + if (!isMousePressedOnEntry) return; + if (!bookmarkedEntries.Contains(pressedEntry)) return; + + + var i = GetBookmarkIndex(mouseDownPosition.y); + + if (i >= bookmarkedEntries.Count) return; + if (i < 0) return; + + + animatingDroppedBookmark = false; + + draggingBookmark = true; + + draggedBookmark = bookmarkedEntries[i]; + draggedBookmarkHoldOffsetY = GetBookmarY(i) - mouseDownPosition.y; + + gaps[i] = rowHeight; + + + this.RecordUndo(); + + bookmarkedEntries.Remove(draggedBookmark); + + } + void update() + { + if (!draggingBookmark) return; + + + EditorGUIUtility.hotControl = EditorGUIUtility.GetControlID(FocusType.Passive); + + draggedBookmarkY = (curEvent.mousePosition.y + draggedBookmarkHoldOffsetY).Clamp(0, bookmarksRect.yMax - rowHeight); + + insertDraggedBookmarkAtIndex = GetBookmarkIndex(curEvent.mousePosition.y + draggedBookmarkHoldOffsetY + rowHeight / 2).Clamp(0, bookmarkedEntries.Count); + + } + void accept() + { + if (!draggingBookmark) return; + if (!curEvent.isMouseUp && !curEvent.isIgnore) return; + + curEvent.Use(); + EditorGUIUtility.hotControl = 0; + + // DragAndDrop.PrepareStartDrag(); // fixes phantom dragged component indicator after reordering bookmarks + + this.RecordUndo(); + + draggingBookmark = false; + isMousePressedOnEntry = false; + + bookmarkedEntries.AddAt(draggedBookmark, insertDraggedBookmarkAtIndex); + + gaps[insertDraggedBookmarkAtIndex] -= rowHeight; + gaps.AddAt(0, insertDraggedBookmarkAtIndex); + + droppedBookmark = draggedBookmark; + + droppedBookmarkY = draggedBookmarkY; + droppedBookmarkYDerivative = 0; + animatingDroppedBookmark = true; + + draggedBookmark = null; + pressedEntry = null; + + EditorGUIUtility.hotControl = 0; + + } + + init(); + accept(); + update(); + + } + + bool draggingBookmark; + + float draggedBookmarkHoldOffsetY; + + float draggedBookmarkY; + int insertDraggedBookmarkAtIndex; + + SceneEntry draggedBookmark; + SceneEntry droppedBookmark; + + + + + + + void BookmarksAnimations() + { + if (!curEvent.isLayout) return; + + void gaps_() + { + var makeSpaceForDraggedBookmark = draggingBookmark; + + // var lerpSpeed = 1; + var lerpSpeed = 11; + + for (int i = 0; i < gaps.Count; i++) + if (makeSpaceForDraggedBookmark && i == insertDraggedBookmarkAtIndex) + gaps[i] = MathUtil.Lerp(gaps[i], rowHeight, lerpSpeed, editorDeltaTime); + else + gaps[i] = MathUtil.Lerp(gaps[i], 0, lerpSpeed, editorDeltaTime); + + + + for (int i = 0; i < gaps.Count; i++) + if (gaps[i].Approx(0)) + gaps[i] = 0; + + + + animatingGaps = gaps.Any(r => r > .1f); + + + } + void droppedBookmark_() + { + if (!animatingDroppedBookmark) return; + + // var lerpSpeed = 1; + var lerpSpeed = 8; + + droppedBookmarkYTarget = GetBookmarY(bookmarkedEntries.IndexOf(droppedBookmark), includeGaps: false); + + MathUtil.SmoothDamp(ref droppedBookmarkY, droppedBookmarkYTarget, lerpSpeed, ref droppedBookmarkYDerivative, editorDeltaTime); + + if ((droppedBookmarkY - droppedBookmarkYTarget).Abs() < .5f) + animatingDroppedBookmark = false; + + } + + gaps_(); + droppedBookmark_(); + + } + + float droppedBookmarkY; + float droppedBookmarkYTarget; + float droppedBookmarkYDerivative; + + bool animatingDroppedBookmark; + bool animatingGaps; + + List gaps + { + get + { + while (_gaps.Count < bookmarkedEntries.Count + 1) _gaps.Add(0); + while (_gaps.Count > bookmarkedEntries.Count + 1) _gaps.RemoveLast(); + + return _gaps; + + } + } + List _gaps = new(); + + + + + + + + + + + + + + + + + + + + + public static void UpdateAllEntries() + { + void fillWithAllScenes() + { + + allEntries.Clear(); + + allEntries = AssetDatabase.FindAssets("t:scene") + .Select(r => new SceneEntry() { scenePath = r.ToPath() }) + .ToList(); + + allEntries.SortBy(r => r.scenePath.GetFilename()); + + } + // void removeAllOpen() + // { + // for (int i = 0; i < EditorSceneManager.sceneCount; i++) + // allEntries.RemoveAll(r => r.scenePath == EditorSceneManager.GetSceneAt(i).path); + // } + + fillWithAllScenes(); + // removeAllOpen(); + + } + + static List allEntries = new(); + + [System.Serializable] + public class SceneEntry + { + public string scenePath = ""; + + public string sceneName => scenePath.GetFilename(withExtension: false); + + } + + + + void GetBookmarkedEntries() + { + bookmarkedEntries = data.bookmarkedScenePaths.Select(r => allEntries.FirstOrDefault(rr => rr.scenePath == r)) + .Where(r => r != null) + .ToList(); + } + void SaveBookmarkedEntries() + { + data.bookmarkedScenePaths = bookmarkedEntries.Select(r => r.scenePath).ToList(); + + data.Dirty(); + + } + + List bookmarkedEntries = new(); + + + + void OnEnable() { UpdateAllEntries(); GetBookmarkedEntries(); } + + void OnDisable() { SaveBookmarkedEntries(); } + + + + + + + + + + + void UpdateSearch() + { + + bool tryMatch(string name, string query, int[] matchIndexes, ref float cost) + { + + var wordInitialsIndexes = new List { 0 }; + + for (int i = 1; i < name.Length; i++) + { + var separators = new[] { ' ', '-', '_', '.', '(', ')', '[', ']', }; + + var prevChar = name[i - 1]; + var curChar = name[i]; + var nextChar = i + 1 < name.Length ? name[i + 1] : default(char); + + var isSeparatedWordStart = separators.Contains(prevChar) && !separators.Contains(curChar); + var isCamelcaseHump = (curChar.IsUpper() && prevChar.IsLower()) || (curChar.IsUpper() && nextChar.IsLower()); + var isNumberStart = curChar.IsDigit() && (!prevChar.IsDigit() || prevChar == '0'); + var isAfterNumber = prevChar.IsDigit() && !curChar.IsDigit(); + + if (isSeparatedWordStart || isCamelcaseHump || isNumberStart || isAfterNumber) + wordInitialsIndexes.Add(i); + + } + + + + var nextWordInitialsIndexMap = new int[name.Length]; + + var nextWordIndex = 0; + + for (int i = 0; i < name.Length; i++) + { + if (i == wordInitialsIndexes[nextWordIndex]) + if (nextWordIndex + 1 < wordInitialsIndexes.Count) + nextWordIndex++; + else break; + + nextWordInitialsIndexMap[i] = wordInitialsIndexes[nextWordIndex]; + + } + + + + + + var iName = 0; + var iQuery = 0; + + var prevMatchIndex = -1; + + void registerMatch(int matchIndex) + { + matchIndexes[iQuery] = matchIndex; + iQuery++; + + iName = matchIndex + 1; + + prevMatchIndex = matchIndex; + + + } + + + cost = 0; + + while (iName < name.Length && iQuery < query.Length) + { + var curQuerySymbol = query[iQuery].ToLower(); + var curNameSymbol = name[iName].ToLower(); + + if (curNameSymbol == curQuerySymbol) + { + var gapLength = iName - prevMatchIndex - 1; + + cost += gapLength; + + + registerMatch(iName); + + continue; + + // consecutive matches cost 0 + // distance between index 0 and first match also counts as a gap + + } + + + + var nextWordInitialIndex = nextWordInitialsIndexMap[iName]; // wordInitialsIndexes.FirstOrDefault(i => i > iName); + var nextWordInitialSymbol = nextWordInitialIndex == default ? default : name[nextWordInitialIndex].ToLower(); + + if (nextWordInitialSymbol == curQuerySymbol) + { + var gapLength = nextWordInitialIndex - prevMatchIndex - 1; + + cost += (gapLength * .01f).ClampMax(.9f); + + + registerMatch(nextWordInitialIndex); + + continue; + + // word-initial match costs less than a gap (1+) + // but more than a consecutive match (0) + + } + + + + iName++; + + } + + + + + + + var allCharsMatched = iQuery >= query.Length; + + return allCharsMatched; + + + + // this search works great in practice + // but fails in more theoretical scenarios, mostly when user skips first letters of words + // eg searching "arn" won't find "barn_a" because search will jump to last a (word-initial) and fail afterwards + // so unity search is used as a fallback + + } + bool tryMatch_unitySearch(string name, string query, int[] matchIndexes, ref float cost) + { + long score = 0; + + List matchIndexesList = new(); + + + var matched = UnityEditor.Search.FuzzySearch.FuzzyMatch(searchString, name, ref score, matchIndexesList); + + + for (int i = 0; i < matchIndexesList.Count; i++) + matchIndexes[i] = matchIndexesList[i]; + + cost = 123212 - score; + + + return matched; + + + // this search is fast but isn't tuned for real use cases + // quering "vis" ranks "Invisible" higher than "VInspectorState" + // quering "lst" ranks "SmallShadowTemp" higher than "List" + // also sometimes it favors matches that are further away from zeroth index + + } + + string formatName(string name, IEnumerable matchIndexes) + { + var formattedName = ""; + + for (int i = 0; i < name.Length; i++) + if (matchIndexes.Contains(i)) + formattedName += "" + name[i] + ""; + else + formattedName += name[i]; + + + return formattedName; + + } + + + + var costs_byEntry = new Dictionary(); + + var matchIndexes = new int[searchString.Length]; + var matchCost = 0f; + + + foreach (var entry in allEntries) + if (tryMatch(entry.sceneName, searchString, matchIndexes, ref matchCost) || tryMatch_unitySearch(entry.sceneName, searchString, matchIndexes, ref matchCost)) + { + costs_byEntry[entry] = matchCost; + namesFormattedForFuzzySearch_byEntry[entry] = formatName(entry.sceneName, matchIndexes); + } + + + searchedEntries = costs_byEntry.Keys.OrderBy(r => costs_byEntry[r]) + .ThenBy(r => r.sceneName) + .ToList(); + } + + List searchedEntries = new(); + + Dictionary namesFormattedForFuzzySearch_byEntry = new(); + + + + + + + + void OnLostFocus() + { + EditorApplication.delayCall += () => + { + if (EditorWindow.focusedWindow != this) + { + EditorApplication.RepaintHierarchyWindow(); + + Close(); + } + }; + + // delay is needed to prevent reopening after clicking + button for the second time + } + + + + public static void Open(Vector2 rowPos, Scene sceneToReplace) + { + if (!VHierarchy.data) + { + VHierarchy.data = ScriptableObject.CreateInstance(); + + AssetDatabase.CreateAsset(VHierarchy.data, GetScriptPath("VHierarchy").GetParentPath().CombinePath("vHierarchy Data.asset")); + } + + + instance = ScriptableObject.CreateInstance(); + + instance.ShowPopup(); + instance.Focus(); + + + + var width = 190; + var height = 296; + + var offsetX = -14; + var offsetY = 18; + + instance.position = instance.position.SetPos(rowPos + new Vector2(offsetX, offsetY)).SetSize(width, height); + + + + instance.sceneToReplace = sceneToReplace; + + } + + public Scene sceneToReplace; + + public static VHierarchySceneSelectorWindow instance; + + + } +} +#endif \ No newline at end of file diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchySceneSelectorWindow.cs.meta b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchySceneSelectorWindow.cs.meta new file mode 100644 index 0000000..6751b5c --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/VHierarchySceneSelectorWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d50f2aa4a50724ddbaf21dca1fd4b816 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/vHierarchy Data.asset b/Assets/OtherAssets/DEV/PAID/vHierarchy/vHierarchy Data.asset new file mode 100644 index 0000000..de8c70b --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/vHierarchy Data.asset @@ -0,0 +1,53 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 3a9752b0c8e144801967e6897679604b, type: 3} + m_Name: vHierarchy Data + m_EditorClassIdentifier: + sceneDatas_byGuid: + keys: + - f6bb9d76e96b15f4e9cc842d12308e59 + values: + - goDatas_byGlobalId: + keys: + - globalObjectIdString: GlobalObjectId_V1-2-f6bb9d76e96b15f4e9cc842d12308e59-1364312246-0 + - globalObjectIdString: GlobalObjectId_V1-2-f6bb9d76e96b15f4e9cc842d12308e59-2075842263-0 + - globalObjectIdString: GlobalObjectId_V1-2-f6bb9d76e96b15f4e9cc842d12308e59-2071633267-0 + - globalObjectIdString: GlobalObjectId_V1-2-f6bb9d76e96b15f4e9cc842d12308e59-94633464-0 + - globalObjectIdString: GlobalObjectId_V1-2-f6bb9d76e96b15f4e9cc842d12308e59-1783470267-0 + - globalObjectIdString: GlobalObjectId_V1-2-f6bb9d76e96b15f4e9cc842d12308e59-519969443-0 + values: + - colorIndex: 4 + iconNameOrGuid: + isIconRecursive: 0 + isColorRecursive: 0 + - colorIndex: 6 + iconNameOrGuid: + isIconRecursive: 0 + isColorRecursive: 0 + - colorIndex: 2 + iconNameOrGuid: + isIconRecursive: 0 + isColorRecursive: 0 + - colorIndex: 9 + iconNameOrGuid: + isIconRecursive: 0 + isColorRecursive: 0 + - colorIndex: 2 + iconNameOrGuid: + isIconRecursive: 0 + isColorRecursive: 0 + - colorIndex: 7 + iconNameOrGuid: + isIconRecursive: 0 + isColorRecursive: 0 + bookmarks: [] + bookmarkedScenePaths: [] diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/vHierarchy Data.asset.meta b/Assets/OtherAssets/DEV/PAID/vHierarchy/vHierarchy Data.asset.meta new file mode 100644 index 0000000..3920dd7 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/vHierarchy Data.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1fd2c46161d242446a2bf772facad359 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/vHierarchy Palette.asset b/Assets/OtherAssets/DEV/PAID/vHierarchy/vHierarchy Palette.asset new file mode 100644 index 0000000..3bb67e9 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/vHierarchy Palette.asset @@ -0,0 +1,81 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &11400000 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 61290d425f1c94a8cbfb56f754ca6757, type: 3} + m_Name: vHierarchy Palette + m_EditorClassIdentifier: + colors: + - {r: 0.16, g: 0.16, b: 0.16, a: 1} + - {r: 0.609, g: 0.231, b: 0.23100014, a: 0.1} + - {r: 0.55825, g: 0.471625, b: 0.21175, a: 0.1} + - {r: 0.34999996, g: 0.5075, b: 0.1925, a: 0.1} + - {r: 0.1925, g: 0.5075, b: 0.27124998, a: 0.1} + - {r: 0.1925, g: 0.50750005, b: 0.5075, a: 0.1} + - {r: 0.259875, g: 0.36618757, b: 0.685125, a: 0.1} + - {r: 0.4550001, g: 0.25024998, b: 0.65975, a: 0.1} + - {r: 0.53287494, g: 0.20212498, b: 0.4501876, a: 0.1} + colorsEnabled: 1 + colorSaturation: 1 + colorBrightness: 1 + colorGradientsEnabled: 1 + iconRows: + - builtinIcons: + - Folder Icon + - Canvas Icon + - AvatarMask On Icon + - cs Script Icon + - StandaloneInputModule Icon + - EventSystem Icon + - Terrain Icon + - ScriptableObject Icon + customIcons: [] + enabled: 1 + - builtinIcons: + - Camera Icon + - ParticleSystem Icon + - TrailRenderer Icon + - Material Icon + - ReflectionProbe Icon + customIcons: [] + enabled: 1 + - builtinIcons: + - Light Icon + - DirectionalLight Icon + - LightmapParameters Icon + - LightProbes Icon + customIcons: [] + enabled: 1 + - builtinIcons: + - Rigidbody Icon + - BoxCollider Icon + - SphereCollider Icon + - CapsuleCollider Icon + - WheelCollider Icon + - MeshCollider Icon + customIcons: [] + enabled: 1 + - builtinIcons: + - AudioSource Icon + - AudioClip Icon + - AudioListener Icon + - AudioEchoFilter Icon + - AudioReverbZone Icon + customIcons: [] + enabled: 1 + - builtinIcons: + - PreMatCube + - PreMatSphere + - PreMatCylinder + - PreMatQuad + - Favorite + - Settings Icon + customIcons: [] + enabled: 1 diff --git a/Assets/OtherAssets/DEV/PAID/vHierarchy/vHierarchy Palette.asset.meta b/Assets/OtherAssets/DEV/PAID/vHierarchy/vHierarchy Palette.asset.meta new file mode 100644 index 0000000..cc4acf7 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vHierarchy/vHierarchy Palette.asset.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: dba10c20202881040b64e11560990009 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 11400000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vInspector.meta b/Assets/OtherAssets/DEV/PAID/vInspector.meta new file mode 100644 index 0000000..0030194 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vInspector.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f42d0374f4587401395165a2788ee07c +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vInspector/Read me.pdf b/Assets/OtherAssets/DEV/PAID/vInspector/Read me.pdf new file mode 100644 index 0000000000000000000000000000000000000000..5ca244047e0e839f4687c85af1c836d340840c9f GIT binary patch literal 42072 zcmeFXWmsHW(;yl=c!IkGcZc8*w1I};?lczMAwY*fa0%}2uEAY`1xRprcX#ONoO8Z; z=gT{H=FZ&vV}1@D6j9RN)R{sBWM>9)0y!~=h@e2h-;}xj#lh5=f&+!^9gu=e(ca0{*ydj^@%`&1 zMG7tyHfbvx7bqt-X&Yk~kR-^|-VB7orU0_DaIvJ|TF_0vX$)c%=D!ZIkOU z!U-h*Adb@~9)y8GNTN6SYzgknFW1u3{i0FG4Gf#j{93S!u)DpwPs_UeX%|HQ5@UUp zCOE6so`@Quc;|HcqsK)I+m0A@AR|$b;*9ff{%7TKQUcWzg`yykj!)28{^X7PS095X zI=+emVz-s?J4(+A))_kD^*)WxLXjD`_S3swXQ8uiW5)(f^zGP{b<&L@-3c4_cYJ3W zg~CXmSNtXH9GS#&tDh1`hH>rnT=>YHID5MmRzrXif_c8~lzQ>=^ zZX1(jsE;4LdqAr=ToSuRrDc<-^hLnjdG!&!4K%_FM!bp2#y_6r(08_!%9pK0&TL@U z^#4Ma#Q&&&n4}#cT1#{|-e&@go8(3w7yU9K-}c)Z|CsC_zwrFa`(ejlGkqgRv=yLYEC7A@R=G z8DvKB+mkB#P$EEM=MNOTzeiA)Te&#D2RTXD+dA0Wf$Ur;prrnXF6QHW```NbNPj^E zTldwL{FxH7h?viXAJWz@EC~wOH;IJs0kVXwYbI93UFU-xt7R$b6BdkvKMs@U67{@< z^WMpt-xw^lwA}a@i)X*5$3gk>?mzwOCK3TZd%L(??oAZ-z>|ymF3!*E=t3~8j@Z=)ivr9`$3kxl+t&*Uqs3=SfjCIEs#Dao?$JEBJ-wTJu^1aY`0}pri z9zIXXba8Rfg)Ri?>-7`72RY4v&MN&Wg4$v`z`Xk>2*UHc~XiiQRNO zeEdw|kBIZi=_aEHNXQ!4jjuwK3dRUIpM~^SaW^rs6u#iCwi%lmk)6mh(bwg!NvPV; zvJ1ICN(j|ezIA=huc;nUL3PM@j7ObIft&2=@%NsL#%10nsk{|AkQ(mCuE7joGXER^ z1_{?Y#8_0rHwgevw}ulBW50NRr;-j(=^vnm;iP)!DnQzCh-VA}foyHj-B4kH%qW%e z^76NXBO@b~h8<+sW*2XKv42P)s727L-IC`p9jM5%E$3>lp${OrI?}F*i_=GLy}1jB z4VwGOlS~i0nOi`nvZeBqFC#r&96_6j^h*+n!NL)5JLG&zeiC;1MW&vfUih}uxlfHI z9JkJ0EFuYAij*ULoWQ$z@Br&>gXv`F3{ z0Aw}wt(Z3mN4&U``mM!z$R>FN!ZQl_dhGhnd!5#I?p1Fxe@q-wx>*U(;WjCJvQtbm zU*(p<48W%<#bA6hTBi|<1G~8qw2Vc4uOw01KT}az8HLr^dD~CJ7C&yx>}@?&O5-^*m=M|-!hzQl|h-6$e(mKEuGo+)~BOUWsw`#5pyKj|pf z6@tmMnds((n`?wER%JITdRfF+1jEW=DJybcrMeV^^70gMLh7ao8RGEpzH3o>Ht@yW z!otG0K^Yl1BDIG~^h<;kg$XhDTiabZ1&&wT;zmcV%p^z3I}`A^7xUYjA4VrgNRq|T z%7;V?r{0B$qQ1D}3G{y(nb$RgQsM?5nm9*a@|h{3^qgF0pAaT#^Bt$ec@SPGec9^$ z{k(%8{7m4N1owhYeMJAR&H%{6!vkT4G+J;=iy-zh&ygdHqEURzm=s&~mvnK5oO%}( zH0#eO?@$F(SGnbiFMj=)c>PM9pCnpFWaeur#4%ueN<2SI7YAM33-vaWd;^oS!oUb3 zo|Nc^q20C1eLsLmqdkg~H*G4>xxAcVVLVX(DpC|QRfo#rAo|(HG_Fmy&0tKo50t&F z_1w_V@DxmmsuM`PMmzNMwc&_(~=~(;)p4v}r zW2V)>-t9ieN)q?aJMjY`-dAOwj`>-x5ksr04@FjFxbOrLrgfY!K|v0b;*KG8D4ni) z+mZ9x15{sfjPmh2Xn>xoVRo@X6Pkjv}6}_l40$zP9Ta4`lwio{bc$!RzuPG6-1) z!%poD`KPpR@|(SeQ&_?-@AyY4Xq>p(KeTP!@l2aKKN&=QM4tdPi%|x`L0P8*u zPYn4dB={-T>a4NUpx9V!O#NSFo*u!soOv4Y=wy*e)1|WFK1z4Y5RCnaVd+gyUK66ZS(0aP|X1;)H9?5 z4A~BGs4vlb@fET^RjOFHVeySlDM-wcYwTSrIepg!!AO{?=l~q?A*j{!f|gfsYY@e3 zx+ehL{N)H}<7)~ar1}B*EsQl?k57ihJ5O^R4qUYKdU!7WS3MBPyhhS7RNo?ZGCYAH zIKe<6oXP1&)B*d31&4Z5%)-wE;&{vOde&d=&n4|zojwv-LT*l0qR2(;;t1+9%={7#kimt$G!->Ui)ET9bNui$KHM#bD{bf()LXwZX3LR}i%;*~R~c5oMHvmKdI_@ULSk)M95{9xO*?mB|cA-f~u zw8^eNBPY^RXQ#6qkPtQ<74&jkc1R*~(?8i6aow?~bBlpNw2s2&J@j%8ZoIU)o|A}M zHZeU=26p<%>Gs59W9TsBzvL+#&B5HsHUIcv5NBZ4lb)wRmgHXLj4sAYAl0xk)YaW){2sE|KvRIWOTdX#@;TAH`?9XKo$G4ffSI$&MdFIEZBmQaR0m}FxhqJ=3a&_mp z-Wzz_fN-bxnpAg)y3fYL*u;&nqwWhbzjmC-C$mwhH}&VQl9u3G`DZj9wuW}9kvPtr zTj-)V8Y?R+o#O--mR3Rij#(U?^h=kd`7;&b?~W%oTv`{kZK{8^3Rh{Len3mdO9b~? zg?&vgT=YO?IeqhPH+V!yt`Z@fh38caiA9WUlO=&v&*a*s=T74y-jRtwhG470G@LVC z!POe62)nL~qffa;>BL^A3?MI8+QXk$cVrf@kc}zR;s2SJmp2JhR%rcs{ZOjs+*yW? z)FD|sUsa1C@CsP$&Ajk-^Yy~;XCfGguCA_4$oYbeLKNByb*GMtSwY9f_A~aj{aXb+o7DO#^x9n7HC^L^mvC+pxpoL7%q#$m(zpugJJ*H@^NDZN((#vUw| z>0SKP;NhPG-JAnkj`E_S_$=R(vBDNO3o1l@s}x>k$i-HaVnGS|Ofn&l zM?>Sib(Rd%;JgvkXS2FCv5s-XdewUUa+{bnV9)zWW7oIK2C9wr75>#3y}%`^P$Ea% z`kYxoK*45L2J-e2zPrA`taOTvXIv8z2Tb7bnC7=wZ>zJJ+-4qZ*b`@!aRP%@pKyj$ z#dl-ETl}wy;l0w0Ue(Zhu+d z2da%2Ri%vQJu_>S!vr3a6ApV_D<_2lP&@5|W{}o?$ug6g@b$gDzO1WXPn>!`>q>>S zWgkpkM_IEoKZi?6@MC&-kc@gvgnKV-a}jE*{eYVAha8f4MBrlIS%Y<67CY|v{pVQ< zzNv2fBSux>ge^s5Hc@KrcXm0G?zl<;297i%^m~eR36f*)+q*w`I8jt$3RaOkT5RQ| zdyC%9+WKH7cDdGNZFFHihr06ZX`Z8p`p+>NulZilo3I3Tkh%*^YwLZ9A&}|*w1;?} z_+e=QmBHRS`sSitEWN_2wnFJ19>n+>0N^ft1@jMqa?`QIJi^*$! zFH;ly&t#=Nm2vU!Bi_(H+3F2&Jvf+y9dp4^UF6#I5;s$*pI3WMmX#~v&a)K{sq^f! z+3FPh_fFR3$okzSHc2xa3k_{2{7aQ0L0P^SLFc!)V#~!D@=rTYd-ubQas6lPDXm$- zx=(LXm=1qz?Nd=WXYW^TXpr^Ks9x1JPLJgb=r-hj+{Hh_;iC`u;7NixJvp{%ASywX zZ*&v)@yV-2(MQQmKabQ#>xlWpaZK?Eoq0E6a2|ZH;eRwne7mBJZUei)*=`SHRLXE(c&N4X8IU&5SF8% zQo$69>M%_s7g-GPiJOW7?32M)Z@>*1f8iIK;25)v&ZnaWNOy_EggAL)vlHQUYx%iM za|FzJ<5psJUwQBb+iAbJgBkj?7%AMHYxBO3k~R27NS-g;SXW%*?SP3E9ksx+=6C|d zCBA=x%!}e=Jom4-q3n-eViJndY0h4-q`u&9>c^uP2Ex|Pgh+0gEl-V>_=6PeernH9 zQnwHtC7duSmWl^&-AtmG5H+%MQBuqIe$xqt?*ortRB0O%WP{K!eLWk}^R@DP3iaJ}h1#2|x9WqE@~0ZIc)G*u;(?tHR98uybKr{^fGmDl7hn@1NB zLI6C-<&sWZvSnV6;sxg>I}^W)|DKGPu&>!>lA$bQqZ5{$xo8&B-A;UUE1YEz*gYnw!80{lT<(+)@~24wAEIV3R@&esK~_7x6;Ok6?{#(cw}O*u4{&^KPnC_`qw^ zAtJ(9eKZu@THGW5MA%=n#EWs{j>CYpr!}GVuk){3<+~4Iaj2E_kQm>UiW)bN!S1^I#jJbN<`mr-@`aGP_aD z#Bc%>8P=uLXx&jU+wuYVFci0=(&uLDGbB6yh)Yg_&znz!AA=2_2nLWj(aSde`FxYux;I}>=J-H~ayYZ{? zKxzOHPI^S|RSlMY!%Hy&JS^@H0i-pnU;jEWPoYPr=_68NfM@={7Zz{-e=IEiADegl z|9j0l4u1Ck)Sx{|?^3}L#0m6KTf8Hxa`iv-eyPtu^SKB97=eU7%r7UDl-URMiOF!J zI6+aTIr-QnYDZTq_u57AnVOD{ZZAW#lBD19fAFNC?~J7K*{`(g3*eHGlY^fh?+#`v zpUog_H4P1gWBH|m($vq;j?~^>vD&@_Bt4xpMNA65Fwl&jpFdgz+Nto_Wpja)mX_Am z)XP-k1KTU9&S$Z1?N4)Nsy<4o~`FLHcTTbFD@>|aY?CSq}F9~G6G+? zXN;IL_Q`Z%QwN(xzr7q6xx{aVG}x~|`-%$Kw~{35bxaK0Tx6uA08hYV>@CHa`qv@# z5F*DE7dh1TSn7sv(j=+dIfwKAL0-1 z7c+b8rPqgFupn7}2Ac_F)et=!wtHt^+)$rrZ}vx#L)#6Qn3zo;v~WHwTj*=(WcVs0 zr~{kZnln=<7{WGVEMpXBO4|g2qk=TG&z+i6HQdnASIuY-xpHy zCrV6A!~)K6N$t0k_tJ#~WLS%faFO7JNmaDnP8TR5;jIM`JkUr{yiJq&I!e+#Q)!r4 z2@W=h+@3}njQp5*d#@`0X|tg*>3jElj8{1(o}gq>nV9p`47eD1*H0nrEqq8OHcQBm zinvlUN6fxR(&SJ0cZQYq&o0?`8pV1=raqM-k9QwRU6WDd5U-~T2x4{Z*h#exFzKe7 zIWS)g4q^GN=AYpz*?kT#k9o)K;$+-sq~?f~383^u3X7lUBtlal{0KfU^b}#In zhP))T1^gV?$ERF7l~s*Am8#dfOdu*;iPgI#`nw_a`R>Xzm5QsaVd6?%f?(%@&|-do6BDuvGhIis%VZdzKL zI$PiOla&q&oN#M_PU`!py7*$(hqH_DNR8A_aJ%FtY2j>dgkugxu^=1$QAkKgyw0vF zA4VvcNgv*-G^Dv@TiXhbvEK&`y((aBN{Uc;&E<6V6URR!fjEs0vcNJTnDnec!t;i` zy}4O#SyZjnGJ3C5C-eF1Aq!Czv_JjxQ(vVUo|)`ZkF};EIkkv(wAbsB^U+ZZ^1zjT zZd6d`^|hN5GS!Hv&wU>4OXZ$XJd8?k(V-kR{+1m0>HcbYnM~qim5)LW0^#-XQVXu?zia`)#R`HJZ3ls72@v+%HQJabOlLQTG%x$e~&3gFFE4c1& zN=Wc?!Uh8XQJoRl?pXK&R0l=LdW2tf`M@w)*#6aM9*ev8yaHmI5Mz=D#$PxO^v!8<0v1CLqotc-pg2{o1AenXt zoy<$-hhOfrZgux?C|O!gII|2g`!VvO>k?IjW3dN!&7CCAFK|hko{i&Qt-chOCAIh_ z+_i696^5cGMx5>KgV|kEbDKu_fzRnlq7R=Z&9R1yj92TH)p5h5x-;sFTAJyCGddEk zmkHLrzbz>6k!WPRj~hN)utl_~Ho8|iKXOP(7E(2IIi8(i8dc@lE&ZY{qFGM<2V$Pyb8Tqis;8S<|zl=A~Q-k-9)3FOC-S+&~ss( zls2yl1bOW~vG0m4?7$Fjrh~ZxtgQ#BX?zAbJ`Jz`PZts{6i>h#FsI}7_OLrgD`44= z5s&(|s(!VL<(E4=@B0iR>j-6~$$YG*Aa}<_%j3LKuh?(Y)G*rLrj9`O!!;BIl4YOy zHO&{?AD9z|C#D3)1V7<<=-QPi3JVYRJXo#jP^`qrSy`MkxfR@DN@A*H@Icih4ZKW53(<|CZCnn>ae^`Cx zk1fD?`&Q#I3i49G^y2jsg4fmcO(+DiVSYI^)(f!tlvnAse+0nt@~14*74^AM1hj?8 zwO!&eyW#VPdClUFvG41ruGU`PXG@y5b-fxd`PNP&8MrK!Tz^kI{#`gk`f+1W`{=Ni z?$pdwCGOMnwo(=T`WLN{$Ubr*jC=f})yBRFa&2IJP6x z!vT&~4|UWhT%ohwfq4_$8Hjt2VAz@Ucg%0sCxjjb)y_Um5eFxWyO#5T2~;aZ6E`wB z#*|8Y(}GRU817?67>w zI8_qJv(RWZY?oE)J#er)KE>|^RXDhF7b(A;AlMuYa1ViQq(Rj#IPI))je)RxrdS(h z4={4d_qR9=0$*-qi#OR3d@a#k`*T;krV)SAJVoN|Y0Etti5`xoT07?Br88m95lSl)$9t#n@ahJQXL_T>+ls*yAwpzWH=bbU^zLH_|9WH>UgP=El)>IfO-rhrp zc}gt=vs}W{GFeRsJ`w6L4^Fz2*N;8_yDI3^9KUug)=8 zHJ21b&&N*1q*zTa^2YxYoCC#Zb=?P_hfF+Sl9xgv%HvzGFaGb}|HJ~FfTt74BLwsJ zYeJ16nK9AdP!%>gAoe%#mW&)Z?l+)VL@N?Q3j>A9Sl9*sfeNj>se!+ZoTNW?E1Loj zh1k>m&Z4=Ip%6vKT5+`4Ur_lP%s+FrYaw6%Uaehw{a>NvsK4jR*O32#YS*G8|KmME z#S(HTy%%I?N;aI3-w?Y{PVz1i6j-$hMB-HaYmV9812%TEupYM~4**^1ObOiK1^DW? zAVLR5Fg?zx)S^Dke@U3QmJQ6vr1XJOJ`=ehrFvsWLW_&XMjG~_7IQPR@sPufAHcXXZ#&=Ez` zS`#oP^KtZU5<2SC`iqA1uir9cMg2vic<;A)ehxsf#>O~Zeh*%93)Kx?)G`8pOP7O& z>0l;9YDoo!Ux7UGs6?!RnovnUiS=NUiOYOQL4z*z5@p=U^3Gc=oEmy9kIk&q+df=C zNcC|J*t@c#9vTjBM5Q(1_2=wpL93q<17*b4bKTIo^2n|a4ibDWcCHo+~+Vr@f}Fv7Dn=z4aHs#_Q(lH z0iLxRtQ8HpL7&BuDol-9lH;~egWrhgNEFf!zLNP92WPIENT3ja*cLc}4@@@t>32Xd z3pKzUEbJgQ9tAKZ_|NGO9P=4bInGO-w06rOcz+DN{?5O|ekGsB&SN#M_ixiB?3I+t zF^-i`ZB=OrTvi1T>r(;R?VlVze*pku3pV~c(-Cs9;U75)*-|*WvfySos+JUn&JpS; zw30?U=?Am$o&V6A>hqT-*;w<A9LJRHotCEoraHJ4S7Lys!e265kBSb+ljZl^2S-|_o|O!A#5gNSg2{twR( zyqjkqI&B@eMf^j_&R4KO%7Hslhc5NG<+bbe)SLbXw;bH=wrd*R;YiV42a$SoIL$B@QHW*4;1PE%p)|+U^GiAD9V)a zXP>t79sIke$^|x$%01Pcvl_1cpjDeAufnXLbmG~2f$MXYgj2`&rM9Ct9*`lJu z(g_Tg-!8kFcR z*&l^@XdKNXUJd{ZN2VJYD)k@M7($lVINerJ77>r19owJzsWsiU=Y~sUrTuO^Vtpjw$JgdYT*NdX?;*e*Mg{s(Cd$(^Kj807l>2DR;f; zuz|3`_{dWJX8rVdF-(ESxS;znL&Cs78pY*#_f$kd$87-q;r;<<<~Yv>!mw5zeD1WW zb7|IpJRFH21_PK%%GW z!5U0_9bWn10h#$J`10Vj4D^tDP1s&wUH@zP+m|$S>-vWw>`{822s!m1ovQBh3VS-b zkif&#OTDLf3Ylj2htGO*wPuZ(zMIdS#3GHP;Sp{V70Ph&t~JgnaX(K()E=?W4SVR8 z9zKx+sG+B9Fq+;69%9Rh?m!s;zSa1yN#IJPeZzw>CN*rJ ztUMo5ZU6mXwtS(7PN|#6I;)24I_;yC;du2gyOHyFWAseN*KoenaW6T9sP5PL4HMPM z%@1t!XJ_g6-T;X-f1LKLwLREV1l;VX4odi|wR!E-bLOR(Yxb4Pi*WdI%TqgLN<<#d zfeSoN1n3V8cDNa)et0@CRbEIk+R2yjcqd4qy&Y)Kdlxaa~s+e!Z8$=+8shAsPH z@jHBuRjTn2xgOIu8&l$Qk#jDU3RpDyd4P@3L53cXZGx(Mx7glvbDDDJRPsb#X#m#_ zqbG)^>-n&a_Cy|FOrRsMwr(q{E{x!m0Y)l+jUzgGKg+z*Mp-H2Z-o_fde}Z-r#Gvn z7uolP9}i`uxNU)(H&jN_O7A0z3j@#(a|`r+7}IuzkNCrAt%t=6eUOZWIYUjL67@{V zj7Sq2{LsAOLo#u9_^n+GvB&$ggUR$wNnxqDIza3(buxbGJ7mDces>Fa}C0_6^*m%o!`8@AsmPvoD#M`GDNeL3^7l?)yE_Q}uhLWtltY zq?K_9Og@vP^T4Ci^iI2G@L!rpb8>)b2P1PSbhy?&_(pGzL;RlGc{xfj7!cWDQM?d9*1_|G3 zB}1x!T(f{~mfA$Vozy5#t{u3s#Z_tBdf2gHWz=$BEQ`TY%5M!2oxN9J1GYKC&9YG* zmrCKTND8^^I8t;!C3DSBXF@Ns`6a33rI(p{IL{)i4keajC#A?&DWH(O=XD;f+U8%y zSlm|_7$k+5pPDklk@u;S9+2XqKG8$n_rqks*8RKxjpmK$39sgFD=rhbzD_-`2h6)? z4BG&~&A9vj3KJ) zG85o0fs)Xm;7in4eG)#XWl4l_?Zd8iLchi;O-LvLJz4hoiwa@^ zq5Qkgc~6hbH;}DKx+A0qrL)Jp6IQCRj%|5A{duo8{D=y~{*MoPi=~;OTGq z^b&EvRD23RZ^z!#s@>+<05tDDidqA)b3`ka+f=+ra@-gVAA88$2gVJ(=nvICgY*IUi?JUXN!C! ztw6Xe(|mBsr9d|tv*(`uXMf>Pv=VZf6tD$HaEjbo@f_CfS=ufs^Q4D_L*y+diqF*H zFP=2gR*IoG8VHel)`9Qxx)&rO)kCc(YTp@g+N)Q!C8E$?h;K^J%sw zh3+=?riPudkIAZ9N%W>Gr>(o2Pvkk~!>kXz+Btb)+*W!VV_&v9j_MDLv6OG3-B_BR z*Zo#L9@Y;`g5S^gF`m*ccuCd=)7SKEBI^}20}m;!y*Qh%xAzNwx?0(4>N*~%Go>-fHRp9?wW&@lK(8tKf1hVKlcDT?4)|j0(-DKi3NcZKgz}K4bly!A zb<6+c{C1l8t(dU!*Ejc=3sUBa5=ai|IYx%{P*)5!F&ny4gyQT4TC?q!m6eqah;kF; zdhzpx;ZK7SEBg`?0rG}4EXN=LrD%Y&w(cM&Hbw1xm@)|3i87x=U8g3>ZD_zd+XRckgZ(lP< ztCU+3IcbyZm4%B`eu?p2<6OCGqMR7oO73r4>-q(_PkVm=bl4YmZ^j-~FQF}D+@|=| z8)dzGaJo%UIjk0@UY~lXaii2U(kN_Os_=m19I+}kx7-zyP#KhBp(pT({&Qq1ln!A^ zn#Kf_4*eeI4)RXSp=5eV(ncBc5v2j+c~O&fUPmU8rD<(F2PavLqBAcnJO~@2 z=M!!Zf_HSv&Rg|VSCCj#F)!ENT8*C$5%Xa04)R%4d=HX6xaRGU7F>l@9tX{1D*_v4 zShuNoa#xNGS>^M~q^e;Qx7a~RR+QUNB4!kxjC+RQ7@3!O$o;ruX|pRBMa<^JZDVpS zFxma5hn7>{M|1ix8l~f0lCJ|)Cr-bG-N`<2lRdvI$GxFbBC3jL50^}~s~&QI=um_@ zCbtzLlQX39vm}h{B~%uzD+%5h(Y%XVoBI``pDufg(WvkboeLLr74kER!pA=~)AsU=tY* z*7NOd9GOnQ1wNryMI>1)T|nv~moRn5E{B*x;`fGQxFM*}PCyVXS0 z+SxC;kujtfVM7jGG#;_XKXnM_UmH!K>O35N~jy3(mrH!G@#Tz~7ZQFBZB2Pw! z4Fq__R6jJOuL*mbtvU4_S2=mZb#!Sd+m-qXFonH7rh=xHb_Kd+frWth%Il)`HnZgB zalfcdZSHe5mG7u8^%U=}@}S&_Y}51yt{cq$;STwmyS= zarJl0YqjZWV9I5llQ5ya$g9-=z`!ZI)iq*8w#P|}Ex1$~kZ7g!mPew>jRT}N;U)_g zljK;P!VMha_iV*Tq^VCG9TKPq#AbNF_ze%XXK)LNr!;?LDqeiRi~QSw(#A(7L(Fam z;p)?AXjUo%8#^2uCOkqw#t)jKH}jzDnqIdFZS8(ut8r{L3J3nVnb^P}*^7ER4xW!I z^M8leh*+7v0%2hb9ZaXOH&q z#b=mHpS}@fEBU!#H1arUbG?I`xEe&G?~U!0Mz&!yfU!CA z89{yfRCg=8um+*)7Ua*j5_qQvnn^>?4J>4$5;Qz{fXWl}77g;jM``?0h1hYC8AmRWhj7$eTA)`S>dQr4 z9w5z_vqcLi*z!B4{kxPt}=u#KpCW(y6uH;Myl*A0@>?Gg{i>$RreZETEEi zB_NqkrkN;-7L=-^%&b8w!nc{5s&mzZk+L;+f`7VBNzBwr$;E0o7W&WxO)nuWPL|g+ zXIUO6rE?q>bKG>oYm;ar^L;p_$;fM%oG11cE3&cO+GPUXb^|he-}aqJx%+AjXO4>T zPoW7W?I=0}O%kL75N!^12JVSZeCjsN!*(7tza6z(l1$<|LP)f}^WL3U8Up@BMIGDA z!neXdM)cu`CS5?L*?Ka0EnZFF4l0c%DLiMYwYux+!VyzxOpvDJJ~H@2a|dVbAO%kq_U zn$Y`{%K%l4DsHR(Q*Nv3)L?Fgw7!y_9Sr-KD8*-kICRS(3qu>2l{|ob+9LwIfex)G zR0Nf|YtlF1Ebcwz!ZIVP@yKecaWB5tRw{49)Pu%Imn+EAAKU1Oe@>6JFaPD9w@~R@ zYo!C;$o*z4D0eOf&3#4fuJJrlxQRR(`#0lx4tS*)nS!nQq5^jxm3gy(nLdLE-q0j> zJjZ|KVc|z171t{WT5!)==&+A*piBP`p*y*)pf{)pbwmD_j7Ek-7R?GEv@oSWw_0*04{Zy^HR%cY@a`j))pWB%Eq4iUzh}F0Y=ICo4V#8Yo zngw5hy$Bxl3sqdzGN+70pWW%zu}0yJ+RSB{cLtC5XX%$qHWuRtTH0M#{#w^fl;lg3 zB~TelETV)-mlE$=aoi0$`K-o?`=0cm$IA6cYN>4Ota8)Y7ZQ20%{%!fy0+?sZ*9sW z#i0xuLf91@9QuzT${m$Y-sMF$Qd{wPzTR{`d<)5!0vZ|)1CNAPzkq+|8U0voe@0jh z%W5k3UPO(|tcE9yANdGDd2r0EX2+R&27oN@UUQiiO5L^nFY41^S{rdgh0tdI!@B&e z(XZTDWukp|22QaC{X;0wJ*$QVcw@aSw!I_3I#w$~8Y;lN3i?v613-snaIec=D1C?z zCGkp$-*_HhWKF$%teZ;)@(e_M3qN*#NjyXTXd>Z$tS!XrQHZuJG9H&1=rt&>e7(A6 zO5{YcU;7C^5nuN^)L(GFsp%1h(T9-7A9;<@r9H$dotZ;#>p&y^yYjR%G;G(k^|KU&l4%VkxIcCLVZcXt^DH+ za9yU4cxAZFzFG{BQm4fMx<|r~XH#xSm31tJMiEp|=?J4OCcLZLYTBhL*!T$ZdhX&K zSOYyX8^TytLrkda#cQcqa;PKe7uwyg+v8#L0b_`ur3BhGE@~j-Lu2^~aPtjN&h#&7 z^qig*nvD^L*t4*upiLi@qN@a)taOHMJY_7Iy4eR4xO~y}j*gGln``3T4--1trTKsp zmv)b7@5Ri-eCGpbfaA;DbO60biFrPk768J8e-&~H`94kV^W%YB?Z)%ye1-P|&B=nj)E(KjTqH|@R-Jte#%<^OhOg0zTpa@*#t z-8NBhjQ}+GVw=8`4p4aRuZIChChbm8(cFdYi6jdndKV>SrqDnO!GCqDcD|ag8hj}q$K6)&aRt_IJiOmu z4WW>+pi|OnT=RyF7FUxAh{Ct$YO{LE!V6|3w{W@R;0!O|F7AZ-zwKy+EgxTN8v$M< zp7;HEIdvq{@&kq)c+F4cAh5LzoM2t4@{LcO17TY(*4$G(4`vM-PUs}<@!Ml^V=7b< zNWXPbf$Fqk9ofj#_DRey4LO`UWJACP?B_Q={gmmEdSDG!pVqP(uO0H#y?)7)7=4OC zQtYOd;^G+C!S!3L+48tf8;CEDqn|}_bT%BJwWa&8 zy?d$k48clht!RQ0dIFYYer~kwZSwL+*+c{LKnWervT%XxLq$QgESE^OoM@mxA|1^Z zKAHFUlT#@2P(m8Qw5CMR5L`@&$Iy< z(8d&4+5uXY-g1X=?zxtkAMT6#eBXM~9;pzx&Ll@qG^Xd8{MkGYOmVR&z11)5GZ4#C zsAs#r)xaWWSb!N;6yZXY<1*k=QBN&NQevu1E`6HF8wJ0-v|i?Bs0v4e8Zm;jk!M?J zi}o9fOY#VNkS-c0qn@-REt z7xFB8CHHMPvxeb7B9MDeATj437s-qmYSfqC!ou>z^m67TKlYh<+Nz~+|^i=NQ} zbqbRF8KZ(5_Vlm!`Yvo`C(M;%q2f~hwIiijJpX7{vHhTL&V{zFXR54W>ro7?DP}%1 zL9QSCuHfd(URp>3aOjuenUoAEtyD6>>*XTAeeb0r23P2}*-QR6(-!5m+_oMmYI;2q z9JNSLd-t>QPX__u^4FNu-x1!w_tXhy+{mg?X_zPkH0b9x`YnV)4PoUWNd?U5>;$q? zzu$MjPCKrME*Vwpd!+Aqt(-Dh7`Rp*#r`SiWyAclzE?%IFAd>&)Vfsi zv4ESphC}_`1Y~NsN`*#kIuzQsl1@=Gs@N($S4uKqf&P?vg^0+LLp>C}gaikdU%r)X zQqdv}jQ)UMOXa{ADHn@)ZR?SKW$NBW@q?It#Fey#f$8nq<6hIijG?(^+n~lZbzWc- z-s!p<)a$>_T^ATabsNebU|}iq06##t8U#*Z_D`C7fr~u#2K^c9C`zfcK49<)^ro!r z+*M+9M!7unEQXhh&A6azj$C45db{(ucX8eH<9OiJ#*c5G5~rHdIf2c%45J7sL|8My zG4u(>Oh3Rem6fM%kGl>5MOLfxeXW-dY-;kK;L^Hm$ zb!tQHwIfuua!f3qCEb=Jqy~luoGMVb3F&Dwl`U*VITuy;|%HrU|0wHot0It3=Q zoZ9-CTW8b0!)D85>i%P^=~TFpQMjtA^p2>Lu7;Uft{o?vVM7~*nX~o-0(_3kGs;(c z8s%IY2^)8vip63$%m9|57WLW7GA+GX&+)w3i#0r-FJ;VSx^~)n*17YrNOfRLCQ;T4 zR~s4auECwLqnQxK^J@!s3G0QxNDozjN!mSv_raAeW1kdMFXy{vQM!P~go*ll)R*uv zv>ajU=+(+8YMw^z$G6ZUr25;Erk3imPxR4o2}nIXJ85`@@22R&*rSV*i{(*{Sj&2X zn_`ouZORjpxe)IKAs-T<-*G*Z>Dw50@u4r>UuKC|+&3RR!bg65HVX3hBkR&v5#V86aAXv@#iThd!olWr#m0AT;| z)Fk$DUD5XB%0PRD-LbMx9jjT5l)hEZfsK0(hPK4=w`wOwk+Gi2!cw^{mG{@5KvCE^ zWvklTN@u9ISMK42x(Z?KG|HlGYaB*_CT=)0c)HR)E&F50iG^_LMv*w`ln#tOoFvLv z@z(Wycvk@=+q#LGstYaFlJDp>bVEXuUaXmOmJl8G4w=8geq`K6O>u3p>r)<}B zB?o8jtyGE5=DS&giT)RxR8exPHkJHwmsJt#jc(N21QDRzmKqzyE4y*-5aBx zaSy>QxVuAe_u$sJdm49lohILR-@SL2Ze_(V)TKe7y9SQrB)Uom%L|0i zm7zz>WH@>}v>b5+k0nIwnX2x*IpwXxp{dWN3fWkg3AT@mDVLNBR+DdTIgjq=X7u|+ z`4!72TD?Eli_V3!1ifW#GTfUN9R7a5c3#MUHs&o)%*dTG-LFAgF;Njj*M^}FZ|MfF;? z^zr?{V!8r#63i3(MP5`?uF8Xt>g=WpitmSr+2@LnPl|SSKSgiVWBIb)CcX@e@^*Y_ zpN0hu>^s$Fj$kX1>2#JPtmYm8`J{81s51!O#J-slfNEE7F8ku<=Vv20v_$?J*xV1a z%0v}w%5$^p(OZf>ogj4z>C?*>B}OvQ93@H8Rhp5cy^KEN&?_a$!r_3MqbdXxiyaK! zPumeJg%Q=!Vln3mzn!P99D}>JG74Ta9)Y)wKW-hs-Udpz35&lfn&Wbt#*L+PcpXE@ zi@|C(oU=5Go{namZn`ka@->)9BD&-5J@vx6c#{IfJ61JP!eKU-U_ay`Ax)@i5Uq(v z)jfWbOOUAy!Ou5L-kymDR2uWpvhkuy&xBd}pFu(=_I3Q1Js&JVl8=g)SItvABH5_K z^3U;5@e~s(AVyB6q zy0gqeyzx?4n7fYmr1I#j!4b+rY!d@QB2NQ%>47>rGVh6e(%i<+MsXbcWOJVk-DdhN z=R-?{VAH)$eqdo@-y6TmZk);AuLcw?t*hI$;B7Y~@}sH*q+u&fnV4B7Vjag@?o6b_ zdmQh+z(pR36c!&{8P43kMe!l`d@^{lVcqH9x=}a7yBMubr4REwbmj&ew89dzT5hR2 zKY9@^dBemm-BtL|UIZhsPHf)PxL!XIKG+oBoS?L<*aKofhVOaoWSrAX6~Pr-#+;|2 z2nP)F*~Emu`8%^en4*U&yPR_Nn^NrtUN2UR$xbVu=q)td7C958HCs2QuyL17H9aMd zjo4ywlUIh_-4zaCrKcvct<%iUdByV8*kpTm2S!2D4vqMw@w5qH7!l;rp}B4_y?>=8 zUI$V?C(C)Saru2|6MoM3g9$k<`S5%UkN%UTc>L~ffFLLq7?^(mM11)xtx+cCISucZ z*k7;&R1(iwjL^s6f1xOT6A9f5hA4lN%=jLYfxg%-hOj{R$>9g+ z*Y2-;Scxx)F~8B>^|yfLqJrpx9sPIrQlIhYfiT+FXV00n|F^xIrJAOpCl#B))g*sg znvQ0`3z*q&shr3YH@lNuSTLUrmL6^Hzq~nWoPPHLCTZt(Yi(zLz77=$X7pFCxL%An zWOtN{1Lm`KJf)v1FIBUfDIqCLM$*z_aVqEUCyf9^m;%Un4r4k-Y2I|jOBfX_6NOQx zX%raGpGy;Z&e9TZRAo!n;9-9L25xIqwmKZccvm*Igjp(#j&UNwc!ty0hzp2peSo<} zTsH31Ou;DKPI(1`W(B>KP+zY}4=UiD3f zDYsqXb7gDxp4hW-R>%`pkl1`4)HX`OkXe&FN=6p1Oj^mzt+Aa%IPz<(k zwoQ%rmgXUHB~tqy=4~c)!uMe@f#@=KD9z7sen}V#+{Av{+A0j!zo}q;7LQsub3y?` zy>4mLkYIkxB+eb5audTDASr!;DppM(Gt)eTix>{YN8?$n4Hx*xT(#vJYLhogOJ%Bb zF!wrZl{s=bXYzq<4>w`jbyhIfsdmJ8Z=gcq2?-oSc~a|aThEbeJd^KJ0+W zyK|h12jOlF*U@)0KHfO3_-n~Tg|Y5w1MR9xe6QOb5f4W4XE2|sLY#|wP9b)Jt!Suq zbVUq1bS)m|{M(uVmFl`@o%hpxJrCF*YS+0RgcY`yjTA-eInlvf{^O2}`koY3AR>PaI!#a@ly6qw^Aac~kU z^Iy}@=hp%cM1mgX>%vX!g=a#c5M53X`*mda%EzailJ(-=q)TN`DW<9@*Vsc zczv-*+>RC1F~t~Rx#BdxSsEsVH00uZ52Uhc6;(MDYaxp{ynH)zd-2;r+@FA zwTZyr|2Oo_=vPeyj0vdBckQywoz{Xt62R|>*{%G`7L|Ymu-w^}1n^qA57`i2i(`g& zfh#QmN_}cr{BQPu`JG8}IE1YQZCAR=SJB#i;s6(H1t<{9<3 zI&|5X7wKvTcSTzMDGmG9?u<#jru&`!&_5@x)G{qfYt_Hp^*D|OA1D97?L)4w@Pk;b zis4R9y$Si(At4F&Q1fTeG4>$<`(MFLMt7lX#ls zIS3p2_oPUEU4mEzLAiD%z9(1;w#*G4OkR}U(G;uZG=x790+f)G0a!M5Qly6l0=}on zT@|r4VlGHv#>I^Zv+OCUCNUG&A+ z|C}Oyn0H=6K2N~xd!3m${VozW$($X!7M@#E!7|^0w8~n`{}hV&FQFPz?RN^;hB;au zZnO@b`Gvh|6>La*Yzsv-!dmD?tB=bqZ2is~*-3E>kEb*UGw3Lod4b}&hEZlOdjx42M9u(LI5 z!ZZ%{rceDZI}f`Q5GxK0%&sXt%XJ-~Cr5qjh_{|RLwSDZ+p%z#%cUp@E8K%h*FA@0 zV=`WVRw>`(sSKPiR9eQ{;v{&*Qp(f(HA1miIKUC3K?M*dNKgoX!;TGTGOFQs+Uo&H zrg*UF=UPQE6$fQ{02A788iKiL1sz2Rhc%JZ3h2syKEYk=}z`t?D_a6C=U^a;f!?>KwO zjiETux#@M{;`WM}&Xt<*d*#H0nsMuw!};67ADKJ%>=mSY>79&!FFUsxs9GNI=AEY zbk$ZAey9wOgMTE>GM~)!gJS#3BhSN&Htwdk&a#1Fn*(M+}V`4JjSb8OV`156A=4psO zaq+TE9n-_xGAv2M?`+M@m$Ya^prl$+l9NEiM?aHM3RQR<)<0mgPJ*Qei2Gy z%lH+GVWYI8Jtg01)U~Ch8*v6}Hh|QGgUSv~vsc#9*GKp_oZ5M%p7ze1{zRX~@r-~$ zr_z&Vb*e{oLJwHiLz!%D_#BxQ5_4(V7p~1x1pFKb>LxbEH)o+&cc3tcRsX@rRNitc zQ=@>|N|e!xWbz)No>*$jmIUebpM!hUta%uaX5hY+<7BvhBNx@!t)G+t|~|Z&v6DQXd&yI5zpHemA@pp*i6ed`HDPRbw;9^U9#PZ_F|>EkvR(jHb2t~zo#S}RiD4mKg=&1Zm8KtLQ6~(Xaf!D zG{Dn&a@WPVGQMdgSHI$TdS_i&1__vvbF_wn?G}$>8vER_P7@yHJu)^Y4F$3`MyeTQ zwn=o&+bK+d-@4%{AGHoVc68-|(VS+!-PRow3=JahDt2n&N4pwhL>5i~_v{{`g~#&L z#*V7f@E}}VS%JbU(&-eUXet1dqatQPTI((9T%z3g;WCxxde^hvqrXJ)nE&dYq4pY~A9>fl zLHxBt+;{+|v#Ijor#H>E;2~rUp&-Okl5p(~^-{u6g)-L}-T_s+fkPXi&Trrhz5|s9 zjbpSLsNc@H+=@F(4YgvNR{w1NI?S5hD+Zh)ou_^&Afuz{YrWgugW496T4i*C*f575djzi|udWl9@M_qaX;@FR_f^ z4*A#I2=bedV|9>P`h(qcn+;J=KA7mDHA_a6efV>5MiXYtwBAmu&VAyy7YS5eASY zm>H5QyQkCa5T;WEeE?0sxjO2Bw=<1*W zL7a%LtW+vC5nAfQ`mvD0zgqZ`BT?YSZ%EoXTD18cs#a`lU9WZ!4wfPO4aR$C5wdWt zUEcWd*V>zQRKctw!7Wr-XpSe^3Aix<@H$kOzne$t&bR=%gGF@mdaX=vBc@MCPY}0! zn0-%?0OD}qw-1Z3L1MK5@h+a~KX45zPj$Uw#M1caEzaZW*)MT|p8ybrqWeqrtVFc+30Dp*$r~i!M z&ZcKlaA6#{S5TR+xayMrY{Sklgv@L_!Cm+r)4z$Ln!nD{@cMn@ZQK`Rv{+ojHMzO| zpIz@0uC8dye){44f=Zb)}X0BXdR?Uwk!vNJjwD8e)yC_>y;FKw4X3BHlo zLG5%6f6*8-i5MF>5L;wjo11 ztFTN)f@)E?hDaVDI!?=*%0{yIrkcxrURxL;B!*$+QwN1Tr%8U{9f;M*!3c_R_cco+ z)7)M<5`};705Yw1C|}Uf4iC}*=bXevlm--!6Y0wP*k*+x>9%~eov$s7W-^$t_DxF+)zz&K!}hnFEj$#wyTU*}1g3kbXxt#sFv(nB!0D`f>3v z7%+f*x?H?1qzwZ8aN4Ssdo9WA3s&u^%GtNy(_|JV7$(C$QGSCdyr1a@-P*}pD5n#0 zS;Y_@vITaoR|EmcY!&9Bf*=*lof370=~)2Z*+c#KGKxfj3co#L@U=@$YMxt~XsvUscdd0hJNJmm z2KXXpPZlb$0nQ_1*DoGpk1iz^C4vN{9p#r4R~ELCg&rOlY+ng&e1e{;BDxa|vz}2W zo^A7RA|iGCPJa5jKu~ApZXJU?_YRK=@JrH*jv7D!M7e>EBg|SgY%rIX3(!ZVz}{sn z4*a2aacg&4qo`spovBZdB^NE^Wy#&1nw*E+^G=B;v(eWyI>pPnw!(h?9a+3unq4#TPO(9@1 zvDc3sp{CIA5YD8m@YXJS6ts$%wt3t1QEBxQTs~ad(GA_spAq-OR3sOb@EnF!MW)49 z8}q6F``mmg79uLF-yS)*y4a;-?vd3{1G)}%6Loo!hTnF_ID^prl45F{m|J=gF&lXR zeI)~Cb3PrZJj3f)EM2KIL(j%2`l=~oCM{-J@%SQ{x5n zX=661C#VAJ7cd;sEDE-~dGd4M%36t_TBmCL9XW5@rsX`d(;eq3TUHQ-OZm=gSu;by zI`_~p?q;P!i11_6XP}yqDx?$CW?pqTu{xj5KGAnfRS9<+!&|kOZS4Om)*b@eIhTyP z{U&OS{zv00!q|=9@m`ju7Ji<9|p!oh)W%5 zvEl>T5b_~Gs0&xivDgT*-O4&#Q<$SaS&)yTe{U_QUQ;Q4N}u=bAkNbDzUQg1C)mWr zS8}GQUHHTX}Qt?u`=kQsf>kQbp$0Gq|2lkAL&eZ zK_Z}Ty!w{rSob$fdfwXCyXEGEtILh;rh1So27lR%Sv=;?f-(`{^&Y1qcK*!T;6o;n zM*x=++!}7J^_)!|^qwc3gIWvvR?NeiHG{Y;3O_U8T9+{t!Gm#nVmUZW9A^ip$letY zX_Y1QUTg6#nJ+9rMtjZlDJv&Hj7;Dvvhiq?vNdECuwpvbEqNTjyh%?K7|RBz8$E zp}V|8)=ZU`B?#BiEmh(UTI^Nt24k2BO777GDB7fk&`{WIYIwXtg5G1T=8wM}brZRIJt*s#!iUt38hz)T&&Ca|q zhF~ST^G=@CNF8!z`psPP2~}A0LZa>IXZKk#lg z((D_6UfY@0S?~`QF|nkNi2{5sn7hYXWCFp0(qHusc8)PLAQS7Qm>SM5K(!N{m+6%O zP5mxhex5DmRbf^?F86fPCV#N3OF!14=Y4(Cm6wQhWX)OTJ5Ha&i!VBbk&GKNS><>1 zSH#0Q_X0{Aix*g-Q9ZeouQ#~3El;Zb3a(#7#~)C;Qir8 zqb%e^+*{@fnmjmV?QZR;*cl5*JzqBvKDWI{sn_pgV&-TQii>(JXEz7beU_Jx_fsGC zh!V8_!OBBnY4$E5mUB_sQOw5D9J)I;*iJc>0;ERa#idlW&*t=NH_HZ|2`=ZF!7i=y z1vR?`LIZX{S|bIYGP7@GKx3$yhyzVTvQC!VnyyNNV!16;wXV-W4?eM7V*ew@sejW@ zEx)l;r&1>l&sF;Q zRr-M3JaG~8ukDC?ruq2Cl=o!2z2qYXz0=PQr_pEr?#WF-C|=vML!28s*}x9_Xsx(W z&Z;F;OQLp=7O4;#!^aad<=%r*!6ao$xYi0rt~BE-lpEXqKDgS3bbm9hr)I1wU=wZ* zUTWpxxXM##k(9t+`chKmorafHC-&I5devoNaYgBL!q=EbyWy8n8B?z>lHa%u&S)-R zIPX%Y0#MV`N}?+B{hC{6#`wd#h0S^P!g?vLhO;eD?EvIM?oq$6A0xhWE5G&5xXmGc zR8eSuj@G(j4ZnBOpb5|ray7=P-ESUQ8=2+8aqC2oj@#uQ--!4#8aGC@n2mM39-$sY zlNqw9CjF`anc(3zqI}aFE5<^wr=@i7Pa7_K-4>Gj0@niV%t7&Q*7i^Es5kUw39Irh<(%8O>UG$U%+gLf* z;Ea8n=Ospyrt0DXgL{ZAgYnAM!x*S`2RU{YN8sipQ>G z@{Lg12@zck^ZP-W!%DVVtO}qB#4l0rN7KLuOz&4golEZNjV|7>tfHw8yTZ7(h_d%z zR=rkjD{jnp4QLy2*5QlxLNcbIXseOgj11h(p3&0N$k~39bm+p^Ncv7 z+c>e#!5mBq6sKfUuUZGeVL*>aalU`To!T5H&23ji=)uG~{c^9$xmfyT(FE4dv6m$+ z;WVeCav!GWcEd1h*pVVS+x9tu+>uwxZd_Rzt~uZ zdnC9D{#=_Us=KCdaK{KWiO7or+PkEVXB7s6<^=uEK^=#?jr1$vP}xM=1f z8;u^X;L3-C&MNJW+b^-qi_)AC*3OM4%Ei#TqQ3BhR1RbhLtd}* zk%wLDmi3UR{Y=7CFZBc5p)Tui*&1-hd8tJGGL2aF9v zgDjElHRJ7VV0mpjv{mlR-WlUAIhPo>r*bSXkj*2XYAR+MxKX{_Un{!FCKenKQPeq; zGNhnMZ1(`Rz*paUC_pK66Z76nxN+kb(27eaol<6m{4}ytMW$;e0Tl2L@VPpy2NEB$ zeH7c0tdPAf6@F#yAqlM}7M4IMSf$`L8UbkzafXDVu*6tX+ zQ)W?Apb+6K>`gieI`q3 z!3{uu#+>=B5Bk*bQ+>rlVu>ibA-o&&cM83O~QhX5k zHa|mWt?0Bc0k<0eTf*eA>cak4X>r4jnnypX3Y&FqmPackWJY!&?eT z7JHBakFtdcBe|*QYJY;i@&qu*MNhwc8@;6f@Lk5;clSgsyR2!v>`h8+x1aT;b=ZYa z-*5_3=-#XZm|d~Rr#E5-(9`5VAAOxIj`DO+Y#{{V9_EFmKAgWsbIiuCMxigQ6&zt@ z-3FYd0=*1^Dx?orAGF|>eskKjBjvr`Be)8Y0wXV%3HULT67LaBNf#vH@}!Fra~d}^g(2IA_1s);gvUY!t#`=>3O))n| zf*}%om&Plxp_UT&i8!)qTZ!hijYxvn&1i**0+tyrE4E%j#fRXtenr$+F2 z=MOmyx|eW4j?x!I+!tYK43GHwl7dJfV8%1M@pNpq6|u)HoAB~x7etUc-NnK41HxHH zAY91^cyX=useO0W98IOkYGuK|j4DI?6F(uEsMpAu?Zx@)m7F5WVr&0IJ+J;m((^pK zYSA#uCmzpFnIekpfNSF<0^#Exw+7n#Z^8#>4n*f$Zbz9|Co9Q|n1V>O4D~%*En9v< zPHzJs(Bd}xFfokWi$g1q_axT$T`oXl6**n65#A@)BToiy2ZH^~Hm=z%JsheM|G-(E z;uf936?}un5UPmuTKV@Dnfk4&)8vBKl;N#YS@R3W` zWj<|Lttx)%tgggU5@mp zp~Gq$>9RH3YdeBo)s@vH3z6Hc^HmcDy^qH5x{5-<%;`qgn{LSUTn$^cHallDw{ihw zNp~cMKgT&SNCD75681gftj8JzTUS+FP!=KWO!yQo@h;r*qkLZ3+CYW)-jX=aNpb4e5^PJK8UX>ekAnbgyXnG@gnQkvSPkrBD#6vr-;75Dx+WAbg^!yf=K5teEdD z6*lWbqSBb8mBZMCJ}Phvxc!3Y4)U#YDNZE^YHQy+@)0alS6^3z%wIaeDF}famRq9* z1|Gzt!9QGN%UAE-9R(F!;j14{p3z_ot%6Bhl`>Jcny>kgTYY%6o7AGitd7~tSZlbm z5;bX$-eBV|urztf?H);{uThC$YsVYkRx{?>DxrvAB<* zWyok_;W;CGfUy(a$ZAt=L{p|(oRNRc6~@b+I|DubN1p9~-3q=@-bTp6BIeq(Hi#7^ zo}Z4|g(WiCBVThGG+v?=|EWzKcsvrEQ(4@`KGS)kx)4+%i;iq0(2##|g>laVy~}-} z#;ZWynGTmIu$PW8q0W9~=8C7wV^87{yGVQ?#7xV;pq0?&2H}3^X5OEsxEW-gfQxR| zdB6T0&csb|FX^1Hb@uU9yBt^&IJWLLJqfW&Q+oP}=CqnpU14tK^dvtbxzyHGH*77g z2UMA6kbP*IoOwd>(phSwspv4zEn`5a0&3r?unr;HHb9^?&9RcjH~kYQW^&T%kMPeO zFNfLkjzS3)kw)6m1!$m8i{*K>YAtj6bwtI8n>L5F&$2=^Ya&{v(!$5YAx$8Aj-I@L zi4gHm+Ir|wojaBxsCSdn62<0tsA-a8(QeHOx2hqE-Rs|pTOY#$lAPL8&j=X#bBFjg z+{PDGV9Ln>(FXH4zgQ<`@FKh$=KEM1c89yG)Fi@H~`jtbz>ltL22} zK%*B1W){73askHM?e01K^#7Tcci6R;@i?}_|01cG`G2kmEnsD3{jbfXrA)2NpDd_BS)gM8c6tCK zBO4oZq5#Qr-|{~>(*gz{Q)??|-)RyCWd-p!&wog$O+X+UdoBhBa|eAr0~2dH3q5^$ zV^fg6wY53DsWpS6sny>N&Y7VN%m3MpoatYBmj8|Yzsfu(J3TvqgM|@5!ph17<(G|} zgc-m{&%(mQ3LRo&qvvGgWM=swoD({=aJp&d&UAO@+Eu7R)dN-g=~$Z5y{s%;Yu|3w-~OysoZ3>cskjH24I%9bFjS zTm>;^eX`%1E1huIo!k-FI=I3+h+%G!ts9JhQz*z@GxPOMnSJp_ffE6ARJOR*-juwt z9=tR(k_%Q0MfC_gJRG!ZX?AFRYtgN1(j+^dcp z7oVAg-z-4B%|@a@0^8#`Ju`B%g$Eu=sX&_tz@NVn$0Kp=Q0I0}kJK0%W*a{c({aeE z^%ChP=l00p#1L)oe94N||B%teTx^L`nfM7WQm@g_Dz@l*XwlHBD4J2?#B99;H*zHk z0z)mvyI1>J`LsdaPWq63U*{N6QFFQE zo0H31+cS`S6oY(t%rT{*Q#EOJUO=(tSbH>!R<_7~C+TLjHw{biD)Mgg_nu^S=F-o? zba!N{t0f!wC`_r`*QO#-DGtF{h@syws7xBOM0|eZw9J*6*tVODd_ZF^&c-ZmF{_%D zqKH>tFxf^}WUwRjO!?1V^glUKIXz>eXa8t#?Opke@$XHTa9^?VF!vifGvZWlVN#!B*h zQbG&ut8tJ#cN%;C1TFFZr^^4I$)eU)e`boZkUTpb=-4v~X(K~ZJwa>dzZ^Lm6Eg`r zC-bwfcy^xlB%06x=vowQI|l<0^r7mXQD|>~zdn*M zJ-1VRrf+0r_}tFq&;S3{*W_6}_Rkm{p!_ktVURR6gwp*(R{LMz*r9m-4Ngc8q-SAm z{7eao;BP{27*tG+oQ&+`?2Ld$P+tDl_g?|wKYjIvLD37(4S)=y7SNRuHZrg_gs$>mbDrZJC93&1n7j{#}P^2L+5Bo@Q@&3S%qD$EFKY@2^PS<(`J^F?yV=nFcU9Xf|N$ z9_*Z)DN4Rk1n=q9>s9qwim47%ThtDVN5!hRNsU|n;<+Hk;eh_C;w>fmhYDu~?@gkV zr`?n&k*N$no1avAZJsjSm2;TbR?Bds4?LfxCKksB8W*c0fiO!X1ooy@>f$>!F8PNY zx2Oc^tl0S9P)*~=wE}F?8)9o5%4Triu`9wI%4^EA78@=$l~!`MsQaxz zz?v;@`6ueaPk^9)vP+1&erc(O(q8Fzg`e1!kGn~gyDQ*U*O!SCftJ}mFe2`S3n+SO z2zFT00w1K{g9NU>ljVNC^A0WlrTL3DqGYDq7r=s9L}ZHZ!7AD!62GgR?eR-}S?s|Lv=jgx4SR$$lWi zGLHNb{`!{kWkbU2^-d0OZzl^l-+*kL(S^|X$~U$AC0+)q3^_%e{*xKCf8h@Sdx)~l zuTGm+PE5t!U-K5)$<#>Iz%Q!mv5=!)0vqs!f8@7QvkMfs!*g~L322E-lh1sfr_3d9 zY~jtdK?f>^+mWQtr$<7YihbuP?_kDN@}5hw#Q5~tv4W2Y;i~E@db4Etcqk_%_w`<) z6jNritsll0UyHu>DgSnxdN|4q0;EiWxL?dM4ET{4jQZ71!iZpF^2PK=tzGItdSaAQ z^osZL#Cq_HkdpT?i?6IxvqVwOrs3Kg$I9q4E!>>mF`N1 zPi{d*R&f{Mlp%AW-4;FiYz7tN&?N4`O4?6*OGv##hI3qT50TI*OOr?lsuiEQzpWK9 zo~0&}n0${AAmG#AH!Mi3`I-09Y;!VZC?F{~Y98)wm_`{c7v0Ls26ow`(3?PUAv(sd zi`NwiD_o$Do6$WHo$jYOOtT@i;Gq%JD$6KhVOWxPq>-9@?EF5j^cp|{$`n!w+KS9b zqDnC%G(J3O#>YIS?W+TLllmiSBXdTxAG)r;}mDnOb7o@ z?%0tL_Zqh_6m1k#^%r_{U`pXNbF+mY`FDs+p=^~w5f?+0B++*_j}V7P|p;9vtul+^8Lf$Ymq>FrovGbL(_7WPkbJO+ z&$kXcFcr2&Y;#sZt(m&Kyi6rorIB!}l+ZhR#*wC9JgqRpP877zuF@f7U9!|EY6U-W zh_csF7=kM_=x!t?z6<7>I)~O2P^(8S*i<`%(hQ2l71llbgQu$m1u~rV`65)R z$6}(OFrj?$L9=@E9SwbZ_Q%$WH$fl&7Zs`BytEe6IMZLN=6?q2jU5|`#IxjOFcQmG zlLZTiOXz!6qNn7XcMGN)%932f`F|5MCK@oE7NDDyr3VRS*%UTXi&j8Z<8FQ7P0S6H z{6EmRTp-XfmW(cA`}cBWmf4s(%U6oo_qn91eE%FTLeou|m-FciWFmi2zn9&dIzwUI zPaMSi?E^$TOG8%shwupQ_WkP_i1wo{zjjs01i82dmb}Z{9Gog#I-#^5sSY>Z(18g{ zUG|s`_imNbA`r|_tPW$L9s&2R;fuRVX!v6`#z1hlggY%!wh%IS8ykMehBSw4)En&bGqj)_L}P$Y{; z1~BRjcYkqtCCZhp_erV?Cl4mOqY$TGT8tClw)(|}MHfcsnUor_xA+LTB}zoK0|!4b z8o#E`FC3}wWkR;OFO};16R4%yg@3b2X;623SVf%})hh4P>Up$jRm52#5AB8K24ANg z3M=spwJBzdk-fSseYq7r@OWqDw zGuomb(;_mCU%vj^+ZC4m2~wF*{<`0U`X77xL&?E|eqVLG6>`+jIxksj`8%?Y3B!(X z-1C1AYB1>SQCPHmFNrU|Kp#%sU5;=ey*h?iAsyi7ferCz1InIC_XMr0|= zf-u1*wY8v7%=n98X+yfHWyh@d!*uYG1mgI|onRg*Pib-!J0{D?=7O;4IMb`0#=XO7 zPT$pUyWf^yH+Cr4Y_|PSd5H29e^PPX=TAIW;um?c?Gv`Xb@=SM)i~~wS#ehP77t!l zt89ug;{h1%jFe4Wz{`Xg|9@!3vb-iXqMJ#V2>sJ zVOM$j_RU&z&|DFiqkHh-p^iAedfGdPY&0`OV>R9JY*LfQg=W<2u zDt_y)w<{4u9OvFEtW%bE_2o8QsgKr8_14AF9p^%j2j>$F!LY~Fb)dHutE=9gH-f;LPEPqM)RkDV z{}j|w4emS%x40z=gul7;Eq`r2;tqMM#qlAD3HibzjZ4i_cmCVhGGhS@#Zadr9qL$R ziyxMlT2kBz`KNAD(@r&M^YWF?3!LxAvT|_unLOzUf1O z+HVj;!^d=)5Ea&@$^|Jb{05z8sQXim>&_evT>SE~^mpk8Yr7V&lY73(C<_nClBY*8 zhqYw!S`kE3>^{H;|L@VTs>8(-lNV^#sl19}p4&GRcNYG(6>1UV$lcpj?90mwezW}< z!Y}kfLnSH#ZV*-QdoaY;rP%l~v7h=-GK?uUJi%LPJ+mmnvT#^ZP%JF{ z^_p1d-k0>H2Uyl%gmL_iH1OIVBrL}wB9c&8@Gf-6G!G0$PAU(u@Jc4OtMMW%-?9_*X$B{mu5OLhrlRglHAyYQM?QWGZ}#k6!sMVR#3O z_0(hQVOi?__Jt>#p_?6E%fq|EJN}PLz2$@lRgc{NvXEQNJRkBRxM@R1I)Yc z=|N<#eMpE&$Vif!y+`V~y#v2Er&(cI_~%0~-{x~0T2Bc5kHW4z5UTb4e~U`$q9|7y zMx)SW%*;7w7TH3mWG~A!7`tmXW6K({r7R^%wk#1Pw;M%>A}U!@wk+imLYA^*`@To_ z+xJ$#<(z+Jo_U_ZceKC(LRgu*U^V_W#usDUY)s;58 zuO_Ch*TQ()<%M?;P<_7b`Id`2w0WtSxfX#cwZXW2PT?_fkeKNJ|2=#rUDK7d&E!pu zP@~>9_dN5<SP_q^_}hHOo=>NwRi>gu)6 z*)RM#GX8rH>&5S_Zq*C*s+;y{b(VVYBP)58<;?qAZ}r{O>oa1sI)0SDzE5F}XY@5j zviO-~aT=kHIs8hPGRJzUcroCXq_X4P7`34bjnceoaZ?lpfp|`<+@$dmAN&Su8iwR} zVRo-WL(2xcSFgLE6tjgRiwRzgg|6{#k-`-6a;JWRa>m?1UwhEJ<-%wmrIUvO;hWUt zSI?j13AO*rqcmA0=2(yX$x8-ZLLXEw-+9xvE+|5Ln3m*QeQ&o&{h-T=T9{pO>&t6# zPsq7PVJNyY#|ekC8XBaS6?rILFOZuZ?Yl}hlH%WzR@8T7VD+HBNQRJ6W=Wd$QXZ{i znz}Gb48-T+X8W*7xVWYrJTmVgD~s&8lWv?@2Sm=;%kbJ}8O&B}TYh3MSCQ2)$I?6R z)?^&BWzVuhoTb=H+`0?Inq5L8?(fA}8!RE*n>!U%u;@`sK{%uDQo8n;&uOa5w6>Y-N;V=7T=|%lQ&6?ixwM*xE3q z*X@Y(%8d!!v&i6L+_{($apn9aj8GYE?}H6HVtirxzBFrb@goCz*`K&lrrz~dM5H|{ zmy+De>Pi)q<@ReoUNK+he&^QGiz}Y(x!At5ukvf!>*pq#H=Uqa4!jeJwfyDC?W*+1 zS*5crE5#L^Tiospwuq|dM?^)`$hZ&JP)JzjXc)66a@u2C)Fa)W?$ zIio7N%4fzn(WOaATSigop3lfkL8&29vC^)ZVpIh?JY^N=wVccjvSNH#FfdQ*J#p9F z@zJLp;^QeC65jE$rRgf2u%+ZD!OP1xywBq51zy-#O>Sw+I&jKrW7rPqf9~k9j z{43=`qRVCt(c0MJy85euD?J(w&LI~r8XYT$^_DxsL2nzprS&DE_El}E8LL?IveaU< z=8)x5(}XUC_E(&4Nr{tggk`}!8)GBZkhZ$lgag~|Uch+Iw_NBItmsYN--6K`3>I2< zhd1&dHk^3cK6D`**=xW~s!~ znM+Oiyo&6p#FSJ^z-K;Ny0f5wvFyMazg7-D;tsbw>n;iq8w%E8#p7{WUYveNDL-Rf z_WW+iO*MR6107qWFEZ_9smc*qa{Gxw(tWHqI#IP(zhG95#MtZI!zlP~wA`_g827Px zZg^xld|U6k>lPS{POxLOeU4woHj8~i&BA{a?C?2xb`I~iI9*kE;AqS?hsljIG zoMU?L%lYOzFf*wuqcSU8zPC%W_14I2fX}SVlTKcfrOcgYE2v&Ay5n4PK6R6`;D_1y zX0ePd{i*j=jo-z6!t9AppNUV?43qK~m?N%aY8J)z$(J?X*C?HQ{87<;A}`3xqq;Zo zK>pOG&ut#j zsaq(M+`Y(IuZXME@t2=#i2s$rDsnn6py2(^cb%8-m`kRgtWqx+nJ$_WsVr;sxKq<%_`UU znRU)aaV>4(Ho@1vR4%f;l;rz|CM3eZO|Axf?ggK(eGc;@$2AM73pIi7GLvlERV&#ty$e8W+Mc(8p8Y9=S7yTO-x5 z(|xgJCep1Byg!PX7xZ@fE!5-;+BLPdMDQ>_1w2_9686q+OjqK_a&7x1@F<_6M)V$L zcmyZErNtj?o0|LaqhWbc5BL4SE3s>p9<*i4URAohdTZ#_b6L;d6olD{e@-iYB5TH; z=s9|2%qF|a;EIhRB3i6!o%FzMvaTR1=3`{e)o|l|%0V3)#<@5j$0$Aw39gNRB_#U& z!g+@yl$AuA1y5Hhb~$l%_$#kpzj^EJU&l@;J$+|NP??ZSO2+TC92Xb8$d?qqIv!a# z7g=EIuyCKHeb>~Vlf$cOF+qbpv)|Tj$Zg8^uP3w%^_54xEzj&&R}fpL#<^W+@9W?# zvD=%Zigz`a?YERjn8z3&h6y2A^k(Vb6oNuDi`32qhLr5$koxqr1LtwKD_uVJc6UzD zx#e`>17I$WwtHpKpQKCfSw9|53+dbliw>oeM0?XEZ#4k~2hrtZm-4l|Esb))R76tRahzxW z>Uy2rrL8}sV8oy9UytHXYBnq@4+l54s|UbOz;kV;vkg}E;M(zht&*SH4dvCW4ZyH&>ED&f$h5{Ht=PEIvjSf zbwyD@P)i3E+rt+tc@Us)Hd5TOIw)(y!Nm?M`5!YNH1Ko-3`b|Qq5!tymNi1rKspRV z-~ucfL}dVIkxoJYg@Xi>NDLHeqZ4UlJc&vpkg+5RU>H!SG$P=8KzJe*m|$Td2~Q-f znUVkxgaX(TL_AtN7z+W_$H-N$awgxM?flB3mc1o1qPwUXj%StNTz-*HWq@wqqTY>LR5Mze!@uEH(m(#E@c#lVAtLRccqJp@ znM){v@6eq8nWT|D>q8q&wkU6AkkbfzL?f?Fit^DKT z0XNRgN(Xv}Iz7CjEj(zkdqR7CdCjqu$m?1TBZ4(WV|3x#>Ni?h5tZZ~z8xE^rZjXl zKJF`#%X`bMqqj69Ky`L;`fP%nPAR|J*Bm+}9rZLpYPjZ@?9BjYKMP{l>$}`#voGa* zR`%d8Y`Ch17`S>K1KEsj?-G5>=)VZ+*FR5-uh|Q@`k6 z+K!1TQEO7$lx>KO3XAj)@gF&_iM0{9Y+$W!T4}$dg-1$B%z4iEi+y9!9QSlAApZ@697+$oAB*GHynguZL!~ED7cRvSeL(L5LhPw zi2qJTAP`Y@z;`kj^klFhzLOCM2pYxrGSJTd2Y(1i;Cnp;p@1TOFQbsjKaGJlq961~ z1OUeWAcKfh_{VV}pa{qc-;Dv0NN9)uK?Z}0{2+s1>W?*mAO!j`76>9EKl+0~{eG-7 z;GhBS*7sw;M8ZE~VS4~(mW>BD=<)^*r@#z@w*`GyS3scviJ|O|wf6vY`=is&VlzG1 TYm*MhYzWM~ckdw`b?$!yqkUoV literal 0 HcmV?d00001 diff --git a/Assets/OtherAssets/DEV/PAID/vInspector/Read me.pdf.meta b/Assets/OtherAssets/DEV/PAID/vInspector/Read me.pdf.meta new file mode 100644 index 0000000..f6094d5 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vInspector/Read me.pdf.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 5bc65026e2b5e458b838d06e4ef39409 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vInspector/VInspector.asmdef b/Assets/OtherAssets/DEV/PAID/vInspector/VInspector.asmdef new file mode 100644 index 0000000..5fe6cf9 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vInspector/VInspector.asmdef @@ -0,0 +1,14 @@ +{ + "name": "VInspector", + "rootNamespace": "", + "references": [], + "includePlatforms": [], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} \ No newline at end of file diff --git a/Assets/OtherAssets/DEV/PAID/vInspector/VInspector.asmdef.meta b/Assets/OtherAssets/DEV/PAID/vInspector/VInspector.asmdef.meta new file mode 100644 index 0000000..a272da7 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vInspector/VInspector.asmdef.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: de13f8b1675b449e6a6af1cc0f7842ec +AssemblyDefinitionImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vInspector/VInspector.cs b/Assets/OtherAssets/DEV/PAID/vInspector/VInspector.cs new file mode 100644 index 0000000..35b71d8 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vInspector/VInspector.cs @@ -0,0 +1,1574 @@ +#if UNITY_EDITOR +using UnityEngine; +using UnityEngine.UIElements; +using UnityEditor.UIElements; +using System.Collections; +using System.Collections.Generic; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using UnityEditor.SceneManagement; +using UnityEngine.SceneManagement; +using System.Reflection; +using System.Linq; +using System.Text.RegularExpressions; +using Type = System.Type; +using static VInspector.VInspectorState; +using static VInspector.VInspectorData; +using static VInspector.Libs.VUtils; +using static VInspector.Libs.VGUI; +// using static VTools.VDebug; + + +namespace VInspector +{ + public static class VInspector + { + + static void UpdateNavbars() // update + { + void updateNavbar(EditorWindow window) + { + if (!window) return; + if (!window.hasFocus) return; + + + var hasNavbar = navbars_byWindow.ContainsKey(window); + var shouldHaveNavbar = VInspectorMenu.navigationBarEnabled && !window.GetMemberValue("isLocked"); + + + if (!hasNavbar && shouldHaveNavbar) + createNavbar(window); + + if (hasNavbar && !shouldHaveNavbar) + destroyNavbar(window); + + } + + void createNavbar(EditorWindow window) + { + var navbar = new IMGUIContainer(); + + navbar.name = "vInspector-navbar"; + + navbar.style.width = Length.Percent(100); + navbar.style.height = 28; + + var navbarGui = new VInspectorNavbar(window); + navbar.onGUIHandler = () => navbarGui.OnGUI(navbar.contentRect); + + navbar.style.position = Position.Absolute; + + window.rootVisualElement.Add(navbar); + + + + var navbarSpacer = new VisualElement(); + + navbarSpacer.name = "vInspector-navbar-spacer"; + + navbar.style.width = Length.Percent(100); + navbarSpacer.style.height = 28 + 1; + + window.rootVisualElement.Insert(0, navbarSpacer); + + + + navbars_byWindow[window] = navbar; + navbarSpacers_byWindow[window] = navbarSpacer; + + } + void destroyNavbar(EditorWindow window) + { + var navbar = window.rootVisualElement.Q("vInspector-navbar"); + var navbarSpacer = window.rootVisualElement.Q("vInspector-navbar-spacer"); + + navbar.RemoveFromHierarchy(); + navbarSpacer.RemoveFromHierarchy(); + + + navbars_byWindow.Remove(window); + navbarSpacers_byWindow.Remove(window); + + } + + + foreach (var inspector in allInspectors) + updateNavbar(inspector); + + } + + static Dictionary navbars_byWindow = new(); + static Dictionary navbarSpacers_byWindow = new(); + + + + + static void PasteButton_OnGUI(EditorWindow window, Rect rect) + { + if (!goEditors_byWindow.TryGetValue(window, out var editor)) return; + + var isActive = VInspectorClipboard.CanComponentsBePastedTo(editor.targets.Cast()); + var copiedDatas = VInspectorClipboard.instance.copiedComponetDatas; + var text = copiedDatas.Count > 1 ? $"Paste {copiedDatas.Count} components" : "Paste Component"; + + void pasteButton_active() + { + if (!isActive) return; + + if (!GUI.Button(rect, text)) return; + + foreach (var target in editor.targets) + foreach (var data in copiedDatas) + VInspectorClipboard.PasteComponentAsNew(data, target as GameObject); + + if (!curEvent.holdingAlt) + VInspectorClipboard.ClearCopiedDatas(); + + } + void pasteButton_inactive() + { + if (isActive) return; + + SetGUIEnabled(false); + + GUI.Button(rect, text); + + ResetGUIEnabled(); + + } + void cancelButton() + { + if (!rect.IsHovered()) return; + + var buttonRect = rect.SetWidthFromRight(rect.height).MoveX(-1); + var iconSize = 12; + var colorNormal = Greyscale(rect.IsHovered() ? .7f : .6f) * (isActive ? 1 : .9f); + var colorHovered = Greyscale(isDarkTheme ? 1f : .25f) * (isActive ? 1 : .9f); + var colorPressed = Greyscale(isDarkTheme ? .7f : .65f) * (isActive ? 1 : .9f); + + + if (!IconButton(buttonRect, "CrossIcon", iconSize, colorNormal, colorHovered, colorPressed)) return; + + VInspectorClipboard.ClearCopiedDatas(); + + } + void escHint() + { + if (!rect.SetWidthFromRight(rect.height).MoveX(-1).IsHovered()) return; + + var textRect = rect.SetWidthFromRight(39).MoveY(-.5f); + var fontSize = 10; + var color = Greyscale(isDarkTheme ? .9f : 1f) * (isActive ? 1 : .9f); + + + SetLabelFontSize(fontSize); + SetGUIColor(color); + + GUI.Label(textRect, "Esc"); + + ResetGUIColor(); + ResetLabelStyle(); + + } + + + if (!curEvent.isRepaint) + cancelButton(); + + pasteButton_active(); + pasteButton_inactive(); + escHint(); + + if (curEvent.isRepaint) + cancelButton(); + } + + static void UpdatePasteButtons() // update and selectionChanged + { + void updateButton(EditorWindow window) + { + if (!window) return; + if (!window.hasFocus) return; + + + var hasCopiedComponents = VInspectorClipboard.instance.copiedComponetDatas.Any(); + var inspectingGameObjects = window.GetType() == t_InspectorWindow ? window.InvokeMethod("GetInspectedObjects")?.All(r => r is GameObject) == true : + window.GetType() == t_PropertyEditor ? propertyEditorsInspectingGameObjects.Contains(window) : true; + + var hasButton = pasteButtons_byWindow.ContainsKey(window); + var shouldHaveButton = hasCopiedComponents && inspectingGameObjects; + + + if (!hasButton && shouldHaveButton) + createButton(window); + + if (hasButton && !shouldHaveButton) + destroyButton(window); + + } + + void createButton(EditorWindow window) + { + var addComponentButton = window.rootVisualElement.Q(className: "unity-inspector-add-component-button"); + + if (addComponentButton == null) return; + + + var buttonHolder = new VisualElement(); + + buttonHolder.name = "vInspector-paste-component-button-holder"; + + buttonHolder.style.flexDirection = FlexDirection.Row; + buttonHolder.style.justifyContent = Justify.Center; + + + var button = new IMGUIContainer(); + + button.style.width = 230f; + button.style.height = 25f; + button.style.marginLeft = 2f; + button.style.marginRight = 2f; + button.style.marginTop = -3f; + button.style.marginBottom = 15f; + + + button.onGUIHandler = () => PasteButton_OnGUI(window, button.contentRect); + + + + addComponentButton.parent.Add(buttonHolder); + + buttonHolder.Add(button); + + + + pasteButtons_byWindow[window] = button; ; + + } + void destroyButton(EditorWindow window) + { + var buttonHolder = pasteButtons_byWindow[window].parent; + + buttonHolder.RemoveFromHierarchy(); + + + pasteButtons_byWindow.Remove(window); + + } + + + foreach (var inspector in allInspectors) + updateButton(inspector); + + foreach (var propertyEditor in propertyEditorsInspectingGameObjects) + updateButton(propertyEditor); + + } + + static Dictionary pasteButtons_byWindow = new(); + + + + + static void FillCollections(Editor editor) // finishedDefaultHeaderGUI + { + if (editor.GetMemberValue("propertyViewer") is not EditorWindow window) return; + if (editor.target is not GameObject) return; + + goEditors_byWindow[window] = editor; + + + if (window.GetType() != t_PropertyEditor) return; + if (propertyEditorsInspectingGameObjects.Contains(window)) return; + + propertyEditorsInspectingGameObjects.Add(window); + + } + + static List propertyEditorsInspectingGameObjects = new(); + static Dictionary goEditors_byWindow = new(); + + + + + + + + + + + + + + + + static void ComponentShortcuts() // globalEventHandler + { + if (EditorWindow.mouseOverWindow is not EditorWindow hoveredWindow) return; + if (!hoveredWindow) return; + if (hoveredWindow.GetType() != t_InspectorWindow && hoveredWindow.GetType() != t_PropertyEditor) return; + if (!curEvent.isKeyDown) return; + if (curEvent.keyCode == KeyCode.None) return; + + + void expandOrCollapseHovered() + { + if (curEvent.holdingAnyModifierKey) return; + if (!curEvent.isKeyDown || curEvent.keyCode != KeyCode.E) return; + if (Tools.viewTool == ViewTool.FPS) return; + if (!VInspectorMenu.toggleExpandedEnabled) return; + if (hoveredComponentHeader == null) return; + + + + ToggleComponentExpanded(hoveredComponent, hoveredWindow); + + + + hoveredWindow.Repaint(); + + curEvent.Use(); + + + + if (!Application.unityVersion.Contains("2022")) return; + + var curTransformTool = Tools.current; + + toCallNextUpdate += () => Tools.current = curTransformTool; + + // E shortcut changes transform tool in 2022 + // here we undo this + + } + void expandOrCollapseAll() + { + if (curEvent.modifiers != (EventModifiers.Shift | EventModifiers.Command) && curEvent.modifiers != (EventModifiers.Shift | EventModifiers.Control)) return; + if (!curEvent.isKeyDown || curEvent.keyCode != KeyCode.E) return; + if (!VInspectorMenu.collapseEverythingEnabled) return; + + + ToggleAllComponentsExpanded(hoveredWindow); + + + hoveredWindow.Repaint(); + + curEvent.Use(); + + } + void collapseEverythingElse() + { + if (curEvent.modifiers != EventModifiers.Shift) return; + if (!curEvent.isKeyDown || curEvent.keyCode != KeyCode.E) return; + if (!VInspectorMenu.collapseEverythingElseEnabled) return; + if (hoveredComponentHeader == null) return; + + + + CollapseOtherComponents(hoveredComponent, hoveredWindow); + + + + hoveredWindow.Repaint(); + + curEvent.Use(); + + } + void toggleActive() + { + if (curEvent.holdingAnyModifierKey) return; + if (!curEvent.isKeyDown || curEvent.keyCode != KeyCode.A) return; + if (Tools.viewTool == ViewTool.FPS) return; + if (!VInspectorMenu.toggleActiveEnabled) return; + if (hoveredComponentHeader == null) return; + + if (EditorUtility.GetObjectEnabled(hoveredComponent) == -1) return; + + + var components = hoveredComponentHeader.editingMultiselection ? hoveredComponentHeader.multiselectedComponents : new List { hoveredComponent }; + + var anyComponentsEnabled = components.Any(r => EditorUtility.GetObjectEnabled(r) == 1); + + + foreach (var r in components) + r.RecordUndo(); + + foreach (var r in components) + EditorUtility.SetObjectEnabled(r, !anyComponentsEnabled); + + + + hoveredWindow.Repaint(); + + curEvent.Use(); + + } + void delete() + { + if (curEvent.holdingAnyModifierKey) return; + if (!curEvent.isKeyDown || curEvent.keyCode != KeyCode.X) return; + if (!VInspectorMenu.deleteEnabled) return; + if (hoveredComponentHeader == null) return; + + + + Component requiredByComponent = null; + + foreach (var otherComponent in hoveredComponent.gameObject.GetComponents()) + if (otherComponent.GetType().GetCustomAttributes().Any(r => (r.m_Type0 ?? r.m_Type1 ?? r.m_Type2)?.IsAssignableFrom(hoveredComponent.GetType()) ?? false)) + requiredByComponent = otherComponent; + + if (requiredByComponent != null && hoveredComponent is not Transform) + Debug.Log($"Can't delete {hoveredComponent.GetType().Name.Decamelcase()} because it is required by {requiredByComponent.GetType().Name.Decamelcase()}"); + + + + if (requiredByComponent == null && hoveredComponent is not Transform) + if (VInspectorMenu.componentAnimationsEnabled) + DeleteComponent_withAnimation(hoveredWindow, hoveredComponentHeader.editingMultiselection ? hoveredComponentHeader.multiselectedComponents : new List { hoveredComponent }); + else + DeleteComponent_withoutAnimation(hoveredComponentHeader.editingMultiselection ? hoveredComponentHeader.multiselectedComponents : new List { hoveredComponent }); + + + + + hoveredWindow.Repaint(); + + curEvent.Use(); + + + + if (!Application.unityVersion.Contains("2022")) return; + + var curPivotRotation = Tools.pivotRotation; + + toCallNextUpdate += () => Tools.pivotRotation = curPivotRotation; + + // X shortcut changes Tools.pivotRotation in 2022 + // here we undo this + + } + + void clearCopiedDatas() + { + if (curEvent.modifiers != EventModifiers.None) return; + if (!curEvent.isKeyDown) return; + if (curEvent.keyCode != KeyCode.Escape) return; + + + VInspectorClipboard.ClearCopiedDatas(); + + + hoveredWindow.Repaint(); + + curEvent.Use(); + + } + void moveBack() + { + if (!curEvent.isMouseDown) return; + if (curEvent.mouseButton != 3) return; + if (hoveredWindow.GetType() != t_InspectorWindow) return; + + if (!VInspectorSelectionHistory.instance.prevStates.Any()) return; + + VInspectorSelectionHistory.instance.MoveBack(); + + } + void moveForward() + { + if (!curEvent.isMouseDown) return; + if (curEvent.mouseButton != 4) return; + if (hoveredWindow.GetType() != t_InspectorWindow) return; + + if (!VInspectorSelectionHistory.instance.nextStates.Any()) return; + + VInspectorSelectionHistory.instance.MoveForward(); + } + + expandOrCollapseHovered(); + expandOrCollapseAll(); + collapseEverythingElse(); + toggleActive(); + delete(); + + clearCopiedDatas(); + moveBack(); + moveForward(); + + } + + public static VInspectorComponentHeader hoveredComponentHeader; + public static Component hoveredComponent => hoveredComponentHeader?.component; + + + + static void UpdateComponentAnimations() // update + { + void set_deltaTime() + { + deltaTime = (float)(EditorApplication.timeSinceStartup - lastLayoutTime); + + // if (deltaTime > .05f) + // deltaTime = .0166f; + + lastLayoutTime = EditorApplication.timeSinceStartup; + + } + void updateExpandAnimationsQueue() + { + if (!queuedExpandAnimations.Any()) { lastActivatedQueuedAnimation = null; return; } + + void unqueue(ExpandAnimation animation) + { + queuedExpandAnimations.Remove(animation); + + animation.Start(); + + lastActivatedQueuedAnimation = animation; + + } + + if (lastActivatedQueuedAnimation == null) + unqueue(queuedExpandAnimations.First()); + + else if (!lastActivatedQueuedAnimation.expandedInspectorHeightUnknown) + if ((lastActivatedQueuedAnimation.currentInspectorHeight - lastActivatedQueuedAnimation.targetInspectorHeight).Abs() < expandAnimation_unqueueAtDistance) + unqueue(queuedExpandAnimations.First()); + + } + void updateExpandAnimations() + { + foreach (var animation in activeExpandAnimations_byComponent.Values.ToList()) + animation.Update(); + + } + void updateDeleteAnimations() + { + foreach (var animation in activeDeleteAnimations.ToList()) + animation.Update(); + + } + + + toCallNextUpdate?.Invoke(); + toCallNextUpdate = null; + + set_deltaTime(); + updateExpandAnimationsQueue(); + updateExpandAnimations(); + updateDeleteAnimations(); + + } + + static float expandAnimation_lerpSpeed => 12; + static float expandAnimation_speedLimit => 4000; + static float expandAnimation_unqueueAtDistance => 90; + + static float deleteAnimation_lerpSpeed => 10; + static float deleteAnimation_speedLimit => 3000; + + static System.Action toCallNextUpdate; + + static float deltaTime; + static double lastLayoutTime; + + static Dictionary activeExpandAnimations_byComponent = new(); + static List queuedExpandAnimations = new(); + static ExpandAnimation lastActivatedQueuedAnimation; + + static List activeDeleteAnimations = new(); + + + class ExpandAnimation + { + public void Start() + { + void expand() + { + if (targetExpandedState != true) return; + + SetComponentExpanded_withoutAnimation(inspectorWindow, component, true); + + + } + void findInspectorElement() + { + var editorsList = inspectorWindow.rootVisualElement.Q(className: "unity-inspector-editors-list"); + + foreach (var someEditorElement in editorsList.Children()) + if (someEditorElement.Children().FirstOrDefault(r => r is InspectorElement) is InspectorElement someInspectorElement) + if (someInspectorElement.GetFieldValue("m_Editor") is Editor editor) + if (editor.target == component) + { + inspectorElement = someInspectorElement; + break; + + } + + } + void detachInspectorElement() + { + if (inspectorElement == null) return; + if (targetExpandedState != true) return; + + inspectorElement.style.position = Position.Absolute; + inspectorElement.style.visibility = Visibility.Hidden; + + // needed to read inspectorElement height without it affecting layout + // reattached in UpdateAnimation + + } + void createMaskElement() + { + if (inspectorElement == null) return; + + + maskElement = new IMGUIContainer(); + + maskElement.name = "vInspector-mask-for-expand-animation"; + + (maskElement as IMGUIContainer).onGUIHandler = () => new Rect(0, 0, 1232, 1232).Draw(GUIColors.windowBackground); + + inspectorElement.parent.Add(maskElement); + + } + void set_expandedInspectorHeight() + { + if (targetExpandedState == true) + expandedInspectorHeightUnknown = true; + else + expandedInspectorHeight = inspectorElement.layout.height; + + } + void set_currentInspectorHeight() + { + if (targetExpandedState == true) + currentInspectorHeight = collapsedInspectorHeight; + else + currentInspectorHeight = expandedInspectorHeight; + + } + + + expand(); + findInspectorElement(); + detachInspectorElement(); + createMaskElement(); + set_expandedInspectorHeight(); + set_currentInspectorHeight(); + + activeExpandAnimations_byComponent[component] = this; + + + } + public void Finish() + { + void collapse() + { + if (targetExpandedState != false) return; + + SetComponentExpanded_withoutAnimation(inspectorWindow, component, false); + + } + void resetInspectorElementStyle() + { + inspectorElement.style.maxHeight = StyleKeyword.Null; + inspectorElement.style.marginBottom = 0; + + } + void removeMaskElement() + { + maskElement.RemoveFromHierarchy(); + } + + + collapse(); + + toCallNextUpdate += resetInspectorElementStyle; + toCallNextUpdate += removeMaskElement; + + activeExpandAnimations_byComponent.Remove(component); + + } + + public void Update() + { + void set_expandedInspectorHeight() + { + if (!expandedInspectorHeightUnknown) return; + if (inspectorElement.layout.height == 0) return; + + expandedInspectorHeight = inspectorElement.layout.height; + expandedInspectorHeightUnknown = false; + + reattachInspectorElement(); + + } + void reattachInspectorElement() + { + inspectorElement.style.position = Position.Relative; + inspectorElement.style.visibility = Visibility.Visible; + + } + void lerp() + { + if (expandedInspectorHeightUnknown) return; + + + MathUtil.SmoothDamp(ref currentInspectorHeight, targetInspectorHeight, expandAnimation_lerpSpeed, ref currentInspectorHeightDerivative, deltaTime, expandAnimation_speedLimit); + + } + void modifyInspectorElementStyle() + { + if (expandedInspectorHeightUnknown) return; + + inspectorElement.style.maxHeight = currentInspectorHeight.Max(0); + inspectorElement.style.marginBottom = currentInspectorHeight.Min(0); + + } + void finish() + { + if ((currentInspectorHeight - targetInspectorHeight).Abs() > .5f) return; + + Finish(); + + } + + set_expandedInspectorHeight(); + lerp(); + modifyInspectorElementStyle(); + finish(); + + } + + + + public EditorWindow inspectorWindow; + + public Component component; + + public VisualElement inspectorElement; + public VisualElement maskElement; + + public float expandedInspectorHeight; + public bool expandedInspectorHeightUnknown; + + public float collapsedInspectorHeight => -7; + + public float currentInspectorHeight; + public float currentInspectorHeightDerivative; + + public float targetInspectorHeight => targetExpandedState == true ? expandedInspectorHeight : collapsedInspectorHeight; + + public bool targetExpandedState; + + } + + class DeleteAnimation + { + public void Start() + { + void findEditorElement() + { + var editorsList = inspectorWindow.rootVisualElement.Q(className: "unity-inspector-editors-list"); + + foreach (var someEditorElement in editorsList.Children()) + if (someEditorElement.Children().FirstOrDefault(r => r is InspectorElement) is InspectorElement someInspectorElement) + if (someInspectorElement.GetFieldValue("m_Editor") is Editor editor) + if (editor.target == component) + { + editorElement = someEditorElement; + break; + } + + } + void createSpacerElement() + { + spacerElement = new IMGUIContainer(); + + spacerElement.name = "vInspector-spacer-for-delete-animation"; + + (spacerElement as IMGUIContainer).onGUIHandler = () => new Rect(0, 0, 1232, 1).Draw(Greyscale(.1f)); + + editorElement.parent.Insert(editorElement.parent.IndexOf(editorElement), spacerElement); + + spacerElement.style.height = editorElement.layout.height; + + currentSpacerHeight = editorElement.layout.height; + + + } + + findEditorElement(); + createSpacerElement(); + + activeDeleteAnimations.Add(this); + + } + public void Finish() + { + spacerElement.RemoveFromHierarchy(); + + activeDeleteAnimations.Remove(this); + + } + + public void Update() + { + void lerp() + { + MathUtil.SmoothDamp(ref currentSpacerHeight, 0, deleteAnimation_lerpSpeed, ref currentSpacerHeightDerivative, deltaTime, deleteAnimation_speedLimit); + } + void modifySpacerElement() + { + spacerElement.style.height = currentSpacerHeight; + } + void finish() + { + if (currentSpacerHeight > .5f) return; + + Finish(); + + } + + lerp(); + modifySpacerElement(); + finish(); + + } + + + + public EditorWindow inspectorWindow; + + public Component component; + + public VisualElement editorElement; + public VisualElement spacerElement; + + public float currentSpacerHeight; + public float currentSpacerHeightDerivative; + + } + + + + public static void ToggleComponentExpanded(Component component, EditorWindow inspectorWindow) + { + if (VInspectorMenu.componentAnimationsEnabled) + SetComponentExpanded_withAnimation(inspectorWindow, component, newExpandedState: !GetCompnentExpanded(inspectorWindow, component), queueAnimation: false); + else + SetComponentExpanded_withoutAnimation(inspectorWindow, component, newExpandedState: !GetCompnentExpanded(inspectorWindow, component)); + + } + public static void ToggleAllComponentsExpanded(EditorWindow inspectorWindow) + { + var firstEditor = inspectorWindow.GetMemberValue("m_Tracker").activeEditors.First(); + + var allComponents = inspectorWindow.GetMemberValue("m_Tracker").activeEditors.Where(r => r.target is Component && r.targets.Length == firstEditor.targets.Length && r.target is not ParticleSystemRenderer) + .Select(r => r.target as Component); + var anyComponentsExpanded = allComponents.Any(r => GetCompnentExpanded(inspectorWindow, r)); + + + queuedExpandAnimations.Clear(); + lastActivatedQueuedAnimation = null; + + foreach (var component in !anyComponentsExpanded ? allComponents : allComponents.Reverse()) + if (VInspectorMenu.componentAnimationsEnabled) + SetComponentExpanded_withAnimation(inspectorWindow, component, newExpandedState: !anyComponentsExpanded, queueAnimation: true); + else + SetComponentExpanded_withoutAnimation(inspectorWindow, component, newExpandedState: !anyComponentsExpanded); + + } + public static void CollapseOtherComponents(Component component, EditorWindow inspectorWindow) + { + var firstEditor = inspectorWindow.GetMemberValue("m_Tracker").activeEditors.First(); + + var allComponents = inspectorWindow.GetMemberValue("m_Tracker").activeEditors.Where(r => r.target is Component && r.targets.Length == firstEditor.targets.Length && r.target is not ParticleSystemRenderer) + .Select(r => r.target as Component); + foreach (var someComponent in allComponents) + if (someComponent != component) + if (VInspectorMenu.componentAnimationsEnabled) + SetComponentExpanded_withAnimation(inspectorWindow, someComponent, newExpandedState: false, queueAnimation: true); + else + SetComponentExpanded_withoutAnimation(inspectorWindow, someComponent, newExpandedState: false); + + if (VInspectorMenu.componentAnimationsEnabled) + SetComponentExpanded_withAnimation(inspectorWindow, component, newExpandedState: true, queueAnimation: false); + else + SetComponentExpanded_withoutAnimation(inspectorWindow, component, newExpandedState: true); + + } + + static bool GetCompnentExpanded(EditorWindow hoveredWindow, Component component) + { + if (activeExpandAnimations_byComponent.TryGetValue(component, out var animation)) + if (animation.targetExpandedState == false) + return false; + + var tracker = hoveredWindow.GetMemberValue("m_Tracker"); + + var editorIndex = tracker.activeEditors.ToList().IndexOfFirst(r => r.target == component); + + if (!editorIndex.IsInRangeOf(tracker.activeEditors)) return false; + + return tracker.GetVisible(editorIndex) == 1; + + } + + public static void SetComponentExpanded_withAnimation(EditorWindow inspectorWindow, Component component, bool newExpandedState, bool queueAnimation) + { + if (activeExpandAnimations_byComponent.TryGetValue(component, out var activeAnimation)) { activeAnimation.targetExpandedState = newExpandedState; return; }; + + + var tracker = inspectorWindow.GetMemberValue("m_Tracker"); + + var visibleExpandedState = tracker.GetVisible(tracker.activeEditors.ToList().IndexOfFirst(r => r.target == component)) == 1; + + if (visibleExpandedState == newExpandedState) return; + + + + var animation = new ExpandAnimation(); + + animation.inspectorWindow = inspectorWindow; + animation.component = component; + animation.targetExpandedState = newExpandedState; + + + + if (queueAnimation) + queuedExpandAnimations.Add(animation); + else + animation.Start(); + + } + public static void SetComponentExpanded_withoutAnimation(EditorWindow inspectorWindow, Component component, bool newExpandedState) + { + // sets saved state which applies to all components of same type + UnityEditorInternal.InternalEditorUtility.SetIsInspectorExpanded(component, newExpandedState); + + + // sets visible state that lives as long as selection is unchanged + var tracker = inspectorWindow.GetMemberValue("m_Tracker"); + + var editorIndex = tracker.activeEditors.ToList().IndexOfFirst(r => r.target == component); + + if (!editorIndex.IsInRangeOf(tracker.activeEditors)) return; + + tracker.SetVisible(editorIndex, newExpandedState ? 1 : 0); + + } + + + public static void DeleteComponent_withAnimation(EditorWindow inspectorWindow, List multiselectedComponents) + { + var animation = new DeleteAnimation(); + + animation.inspectorWindow = inspectorWindow; + animation.component = multiselectedComponents.First(); + + animation.Start(); + + DeleteComponent_withoutAnimation(multiselectedComponents); + + } + public static void DeleteComponent_withoutAnimation(List multiselectedComponents) + { + foreach (var r in multiselectedComponents) + Undo.DestroyObjectImmediate(r); + + } + + + + + + + + + + + + + + + + static void UpdateComponentHeaders(Editor editor) // finishedDefaultHeaderGUI + { + if (!curEvent.isLayout) return; + if (editor.GetType() != t_GameObjectInspector) return; + if (editor.target is not GameObject gameObject) return; + // if (editor.GetMemberValue("propertyViewer") is not EditorWindow window) return; + // if (window.GetMemberValue("m_Tracker") is not ActiveEditorTracker tracker) return; + + + var components = gameObject.GetComponents().Where(r => r); + + // if (editor.targets.Length > 1) + // components = tracker.activeEditors.Where(r => r.target && r.target is Component && r.targets.Length == editor.targets.Length).Select(r => r.targets.First() as Component).ToArray(); + + void clearHeadersOnReorder() + { + var curOrderHash = components.Aggregate(17, (hash, element) => hash * 31 + (element?.GetHashCode() ?? 0)); + + if (curOrderHash != componentOrderHashes_byEditor.GetValueOrDefault(editor)) + componentHeaders_byComponent.Clear(); + + componentOrderHashes_byEditor[editor] = curOrderHash; + + } + void createHeader(Component component) + { + if (!component) return; + if (componentHeaders_byComponent.ContainsKey(component)) return; + + componentHeaders_byComponent[component] = new VInspectorComponentHeader(component, editor); + + } + + + clearHeadersOnReorder(); + + foreach (var component in components) + createHeader(component); + + foreach (var component in components) + componentHeaders_byComponent[component].Update(); + + } + + static Dictionary componentHeaders_byComponent = new(); + + static Dictionary componentOrderHashes_byEditor = new(); + + + + + + public static void UpdateHeaderButtons(Editor _) + { + + var buttons = typeof(EditorGUIUtility).GetMemberValue("s_EditorHeaderItemsMethods"); + + if (buttons == null) return; // will be checked again next gui frame + + + + var t_HeaderItemDelegate = typeof(EditorGUIUtility).GetNestedType("HeaderItemDelegate", maxBindingFlags); + + helpButton ??= buttons.Cast().FirstOrDefault(r => r.Method.Name == "HelpIconButton"); + presetsButton ??= buttons.Cast().FirstOrDefault(r => r.Method.Name == "DrawPresetButton"); + copyPasteButton ??= typeof(VInspector).GetMethod(nameof(CopyPasteButton), maxBindingFlags).CreateDelegate(t_HeaderItemDelegate); + playmodeSaveButton ??= typeof(VInspector).GetMethod(nameof(PlaymodeSaveButton), maxBindingFlags).CreateDelegate(t_HeaderItemDelegate); + + + + + buttons.Remove(helpButton); + buttons.Remove(presetsButton); + buttons.Remove(copyPasteButton); + buttons.Remove(playmodeSaveButton); + + + if (VInspectorMenu.playmodeSaveButtonEnabled) + buttons.Insert(0, playmodeSaveButton); + + if (VInspectorMenu.copyPasteButtonsEnabled) + buttons.Insert(0, copyPasteButton); + + if (!VInspectorMenu.hideHelpButtonEnabled && helpButton != null) + buttons.Insert(0, helpButton); + + if (!VInspectorMenu.hidePresetsButtonEnabled && presetsButton != null) + buttons.Insert(0, presetsButton); + + + + + Editor.finishedDefaultHeaderGUI -= UpdateHeaderButtons; + + } + + static System.Delegate helpButton; + static System.Delegate presetsButton; + static System.Delegate copyPasteButton; + static System.Delegate playmodeSaveButton; + + + static bool CopyPasteButton(Rect buttonRect, Object[] targets) + { + if (targets.First() is not Component component) return false; + + + var copiedData = VInspectorClipboard.instance.copiedComponetDatas.FirstOrDefault(r => r.sourceComponent == component); + var pastableData = VInspectorClipboard.instance.copiedComponetDatas.FirstOrDefault(r => r.sourceComponent.GetType() == component.GetType()); + + var isCopied = copiedData != null; + var canValuesBePasted = !isCopied && pastableData != null; + + GUI.Label(buttonRect, new GUIContent("", canValuesBePasted ? "Paste values" : isCopied ? "Copied" : "Copy component")); + + + + var iconName = canValuesBePasted ? "Paste values" : isCopied ? "Copied" : "Copy"; + var iconSize = 16; + var color = Greyscale(isDarkTheme ? .78f : .49f); + var colorHovered = Greyscale(isDarkTheme ? 1f : .2f); + var colorPressed = Greyscale(isDarkTheme ? .8f : .6f); + var colorDisabled = Greyscale(.52f); + + + + + var disabled = targets.Length > 1; + + if (disabled) { IconButton(buttonRect, iconName, iconSize, colorDisabled, colorDisabled, colorDisabled); return true; } + + + + if (!IconButton(buttonRect, iconName, iconSize, color, colorHovered, colorPressed)) return true; + + if (canValuesBePasted) + VInspectorClipboard.PasteComponentValues(pastableData, component); + else + VInspectorClipboard.CopyComponent(component); + + return true; + + } + + static bool PlaymodeSaveButton(Rect buttonRect, Object[] targets) + { + + void tryApplyingFailedToSaveDatas() + { + if (Application.isPlaying) return; + if (!VInspectorClipboard.instance.failedToSaveComponentDatas.Any()) return; + + foreach (var data in VInspectorClipboard.instance.failedToSaveComponentDatas.ToList()) + foreach (var target in targets) + if (target is Component component_) + if (target.GetGlobalID().UnpackForPrefab() == data.globalId) + { + VInspectorClipboard.ApplyComponentData(data, component_); + VInspectorClipboard.instance.failedToSaveComponentDatas.Remove(data); + } + + + // applies saved component datas that couldn't be applied on playmode exit + // such datas correspond to: + // - prefabs (because prefabs produce different (unpacked) globalId in playmode) + // - objects from scenes that weren't loaded before playmode (because datas can only be applied to loaded object) + // + // this solution applies datas once respective components become visible in inspector + // a "proper" solution would involve looping through all objects in a scene once it's opened + + } + + tryApplyingFailedToSaveDatas(); + + + + if (!Application.isPlaying) return false; + if (targets.First() is not Component component) return false; + + + var savedData = VInspectorClipboard.instance.savedComponentDatas.FirstOrDefault(r => r.sourceComponent == component); + + var isSaved = savedData != null; + + GUI.Label(buttonRect, new GUIContent("", isSaved ? "Saved" : "Save in play mode")); + + + + var iconName = isSaved ? "Saved" : "Save"; + var iconSize = 16; + var color = Greyscale(isDarkTheme ? .8f : .46f); + var colorHovered = Greyscale(isDarkTheme ? 1f : .2f); + var colorPressed = Greyscale(isDarkTheme ? .8f : .6f); + + + + + if (!IconButton(buttonRect, iconName, iconSize, color, colorHovered, colorPressed)) return true; + + if (targets.Length > 1) + foreach (var target in targets) + VInspectorClipboard.SaveComponent(target as Component); + else + VInspectorClipboard.SaveComponent(component); + + + + return true; + + } + + + + + + + + + + + + + + + + static void LoadSceneBookmarkObjects() // update + { + if (!data) return; + + + var scenesToLoadFor = unloadedSceneBookmarks_sceneGuids.Select(r => EditorSceneManager.GetSceneByPath(r.ToPath())) + .Where(r => r.isLoaded); + if (!scenesToLoadFor.Any()) return; + + + + foreach (var scene in scenesToLoadFor) + { + var bookmarksFromThisScene = data.bookmarks.Where(r => r.globalId.guid == scene.path.ToGuid()).ToList(); + + var objectsForTheseBookmarks = bookmarksFromThisScene.Select(r => !Application.isPlaying ? r.globalId + : r.globalId.UnpackForPrefab()).GetObjects(); + + for (int i = 0; i < bookmarksFromThisScene.Count; i++) + if (objectsForTheseBookmarks[i]) + bookmarksFromThisScene[i]._obj = objectsForTheseBookmarks[i]; + else + bookmarksFromThisScene[i].failedToLoadSceneObject = true; + + } + + unloadedSceneBookmarks_sceneGuids.Clear(); + + + foreach (var inspector in allInspectors) + inspector.Repaint(); + + } + + public static HashSet unloadedSceneBookmarks_sceneGuids = new(); + + + + + static void StashBookmarkObjects() // on playmode enter before awake + { + stashedBookmarkObjects_byBookmark.Clear(); + + foreach (var bookmark in data.bookmarks) + stashedBookmarkObjects_byBookmark[bookmark] = bookmark._obj; + + } + static void UnstashBookmarkObjects() // on playmode exit + { + foreach (var bookmark in data.bookmarks) + if (stashedBookmarkObjects_byBookmark.TryGetValue(bookmark, out var stashedObject)) + if (stashedObject != null) + bookmark._obj = stashedObject; + + } + + static Dictionary stashedBookmarkObjects_byBookmark = new(); + + + [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)] + static void OnPlaymodeEnter_beforeAwake() + { + if (!data) return; + + StashBookmarkObjects(); + + } + static void OnPlaymodeExit(PlayModeStateChange state) + { + if (state != PlayModeStateChange.EnteredEditMode) return; + if (!data) return; + + + UnstashBookmarkObjects(); + + // scene objects can get recreated in playmode if the scene was reloaded + // in this case their respective bookmarks will be updated in OnSceneLoaded_inPlaymode to reference the recreated versions + // so we ensure that after playmode bookmarks reference the same objects as they did before playmode + + + + + foreach (var bookmark in data.bookmarks) + if (bookmark.globalId.guid == "00000000000000000000000000000000") + if (bookmark._obj is GameObject gameObject) + { + bookmark.globalId = new GlobalID(bookmark.globalId.ToString().Replace("00000000000000000000000000000000", gameObject.scene.path.ToGuid())); + data.Dirty(); + } + + // objects from DontDestroyOnLoad that were bookmarked in playmode have globalIds with blank scene guids + // we fix this after playmode, when scene guids become available + + } + + + + + + + + + + + + + + + + public static bool HasVInspectorAttribtues(Type type) + { + if (typesWithVInspectorAttributes != null) return typesWithVInspectorAttributes.Contains(type); + + + typesWithVInspectorAttributes = new(); + + typesWithVInspectorAttributes.UnionWith(TypeCache.GetFieldsWithAttribute().Select(r => r.DeclaringType)); + typesWithVInspectorAttributes.UnionWith(TypeCache.GetFieldsWithAttribute().Select(r => r.DeclaringType)); + + typesWithVInspectorAttributes.UnionWith(TypeCache.GetFieldsWithAttribute().Select(r => r.DeclaringType)); + typesWithVInspectorAttributes.UnionWith(TypeCache.GetFieldsWithAttribute().Select(r => r.DeclaringType)); + typesWithVInspectorAttributes.UnionWith(TypeCache.GetFieldsWithAttribute().Select(r => r.DeclaringType)); + typesWithVInspectorAttributes.UnionWith(TypeCache.GetFieldsWithAttribute().Select(r => r.DeclaringType)); + + typesWithVInspectorAttributes.UnionWith(TypeCache.GetFieldsWithAttribute().Select(r => r.DeclaringType)); + typesWithVInspectorAttributes.UnionWith(TypeCache.GetMethodsWithAttribute().Select(r => r.DeclaringType)); + + typesWithVInspectorAttributes.UnionWith(TypeCache.GetMethodsWithAttribute().Select(r => r.DeclaringType)); + + foreach (var r in typesWithVInspectorAttributes.ToHashSet()) + typesWithVInspectorAttributes.UnionWith(TypeCache.GetTypesDerivedFrom(r)); + + + + return typesWithVInspectorAttributes.Contains(type); + + } + + static HashSet typesWithVInspectorAttributes = null; + + + + public static bool HasUITKOnlyDrawers(SerializedObject serializedObject) + { + if (serializedObject.targetObject == null) return false; + + + var targetType = serializedObject.targetObject.GetType(); + + if (uitkUsage_byType.ContainsKey(targetType)) return uitkUsage_byType[targetType]; + + + + + var maxSearchDepth = 1; + + var curProperty = serializedObject.GetIterator(); + + while (curProperty.NextVisible(enterChildren: curProperty.depth < maxSearchDepth)) + { + var handler = typeof(Editor).Assembly.GetType("UnityEditor.ScriptAttributeUtility").InvokeMethod("GetHandler", curProperty); + + var propertyDrawer = handler.GetPropertyValue("propertyDrawer"); + + if (propertyDrawer == null) continue; + + + var hasUITKimplementation = propertyDrawer.CreatePropertyGUI(curProperty.Copy()) != null; + var hasIMGUIimplementation = propertyDrawer.GetType().GetMethod("OnGUI", new[] { typeof(Rect), typeof(SerializedProperty), typeof(GUIContent) }).DeclaringType == propertyDrawer.GetType(); + + if (hasUITKimplementation && !hasIMGUIimplementation) + return uitkUsage_byType[targetType] = true; + + } + + + + + return uitkUsage_byType[targetType] = false; + + } + + static Dictionary uitkUsage_byType = new(); + + + + static void OnUndoRedo() + { + if (!valueChangedCallbacks_byUndoPosition.Any()) return; + + if (valueChangedCallbacks_byUndoPosition.TryGetValue(EditorUtils.GetCurrendUndoGroupIndex(), out var callback)) + callback.Invoke(); + + } + + public static Dictionary valueChangedCallbacks_byUndoPosition = new(); + + + + + + + + + + + + + + + + [InitializeOnLoadMethod] + static void Init() + { + if (VInspectorMenu.pluginDisabled) return; + + void subscribe() + { + + Editor.finishedDefaultHeaderGUI -= UpdateComponentHeaders; + Editor.finishedDefaultHeaderGUI += UpdateComponentHeaders; + + + Editor.finishedDefaultHeaderGUI -= FillCollections; + Editor.finishedDefaultHeaderGUI += FillCollections; + + + EditorApplication.update -= UpdatePasteButtons; + EditorApplication.update += UpdatePasteButtons; + + Selection.selectionChanged -= UpdatePasteButtons; + Selection.selectionChanged += UpdatePasteButtons; + + + EditorApplication.update -= UpdateNavbars; + EditorApplication.update += UpdateNavbars; + + EditorApplication.update -= UpdateComponentAnimations; + EditorApplication.update += UpdateComponentAnimations; + + Editor.finishedDefaultHeaderGUI -= UpdateHeaderButtons; + Editor.finishedDefaultHeaderGUI += UpdateHeaderButtons; + + + Undo.undoRedoPerformed -= OnUndoRedo; + Undo.undoRedoPerformed += OnUndoRedo; + + + EditorApplication.update -= LoadSceneBookmarkObjects; + EditorApplication.update += LoadSceneBookmarkObjects; + + + + + var globalEventHandler = typeof(EditorApplication).GetFieldValue("globalEventHandler"); + typeof(EditorApplication).SetFieldValue("globalEventHandler", ComponentShortcuts + (globalEventHandler - ComponentShortcuts)); + + + EditorApplication.quitting -= VInspectorState.Save; + EditorApplication.quitting += VInspectorState.Save; + + EditorApplication.playModeStateChanged -= OnPlaymodeExit; + EditorApplication.playModeStateChanged += OnPlaymodeExit; + + EditorApplication.playModeStateChanged -= VInspectorClipboard.OnPlaymodeStateChanged; + EditorApplication.playModeStateChanged += VInspectorClipboard.OnPlaymodeStateChanged; + + + } + void loadData() + { + data = AssetDatabase.LoadAssetAtPath(ProjectPrefs.GetString("vInspector-lastKnownDataPath")); + + + if (data) return; + + data = AssetDatabase.FindAssets("t:VInspectorData").Select(guid => AssetDatabase.LoadAssetAtPath(guid.ToPath())).FirstOrDefault(); + + + if (!data) return; + + ProjectPrefs.SetString("vInspector-lastKnownDataPath", data.GetPath()); + + } + void loadDataDelayed() + { + if (data) return; + + EditorApplication.delayCall += () => EditorApplication.delayCall += loadData; + + // AssetDatabase isn't up to date at this point (it gets updated after InitializeOnLoadMethod) + // and if current AssetDatabase state doesn't contain the data - it won't be loaded during Init() + // so here we schedule an additional, delayed attempt to load the data + // this addresses reports of data loss when trying to load it on a new machine + + } + void removeDeletedBookmarks() + { + if (!data) return; + + + var toRemove = data.bookmarks.Where(r => r.isDeleted); + + if (!toRemove.Any()) return; + + + foreach (var r in toRemove.ToList()) + data.bookmarks.Remove(r); + + data.Dirty(); + + + // delayed to give bookmarks a chance to load in update + + } + + subscribe(); + loadData(); + loadDataDelayed(); + + EditorApplication.delayCall += () => EditorApplication.delayCall += () => removeDeletedBookmarks(); + + } + + public static VInspectorData data; + + + + + static IEnumerable allInspectors => _allInspectors ??= t_InspectorWindow.GetFieldValue("m_AllInspectors").Cast().Where(r => r.GetType() == t_InspectorWindow); + static IEnumerable _allInspectors; + + static Type t_InspectorWindow = typeof(Editor).Assembly.GetType("UnityEditor.InspectorWindow"); + static Type t_PropertyEditor = typeof(Editor).Assembly.GetType("UnityEditor.PropertyEditor"); + static Type t_GameObjectInspector = typeof(Editor).Assembly.GetType("UnityEditor.GameObjectInspector"); + + static Type t_VHierarchy = Type.GetType("VHierarchy.VHierarchy") ?? Type.GetType("VHierarchy.VHierarchy, VHierarchy, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null"); + + public static MethodInfo mi_VHierarchy_GetIconName = t_VHierarchy?.GetMethod("GetIconName_forVInspector", maxBindingFlags); + + + + + + + + const string version = "2.0.14"; + + + } +} +#endif \ No newline at end of file diff --git a/Assets/OtherAssets/DEV/PAID/vInspector/VInspector.cs.meta b/Assets/OtherAssets/DEV/PAID/vInspector/VInspector.cs.meta new file mode 100644 index 0000000..8421816 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vInspector/VInspector.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cb0378825d0df482cb66e646e57e5153 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vInspector/VInspectorAttributes.cs b/Assets/OtherAssets/DEV/PAID/vInspector/VInspectorAttributes.cs new file mode 100644 index 0000000..af77b4f --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vInspector/VInspectorAttributes.cs @@ -0,0 +1,139 @@ +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using System; +using UnityEngine; +// using static VTools.VDebug; +#if UNITY_EDITOR +using static VInspector.Libs.VUtils; +#endif + + +namespace VInspector +{ + + public class FoldoutAttribute : Attribute + { + public string name; + + public FoldoutAttribute(string name) => this.name = name; + + } + public class EndFoldoutAttribute : Attribute { } + + + + public class TabAttribute : Attribute + { + public string name; + + public TabAttribute(string name) => this.name = name; + + } + public class EndTabAttribute : Attribute { } + + + + public class ButtonAttribute : Attribute + { + public string name = ""; + public int size = 30; + public int space = 0; + public string color = "Grey"; + + public ButtonAttribute() => this.name = ""; + public ButtonAttribute(string name) => this.name = name; + + } + + + + public class VariantsAttribute : PropertyAttribute + { + public object[] variants; + + public VariantsAttribute(params object[] variants) => this.variants = variants; + + } + + + + public class MinMaxSliderAttribute : PropertyAttribute + { + public float min; + public float max; + + public MinMaxSliderAttribute(float min, float max) { this.min = min; this.max = max; } + + } + + + + public abstract class IfAttribute : Attribute + { + public string variableName; + public object variableValue; + +#if UNITY_EDITOR + public bool Evaluate(object target) + { + if (target.GetType().GetFieldInfo(variableName) == null && + target.GetType().GetPropertyInfo(variableName) == null) + return false; + + var curValue = target.GetMemberValue(variableName); + + return object.Equals(curValue, variableValue); + + } +#endif + + public IfAttribute(string boolName) { this.variableName = boolName; this.variableValue = true; } + public IfAttribute(string variableName, object variableValue) { this.variableName = variableName; this.variableValue = variableValue; } + + } + public class EndIfAttribute : Attribute { } + + public class HideIfAttribute : IfAttribute + { + public HideIfAttribute(string boolName) : base(boolName) { } + public HideIfAttribute(string variableName, object variableValue) : base(variableName, variableValue) { } + } + public class ShowIfAttribute : IfAttribute + { + public ShowIfAttribute(string boolName) : base(boolName) { } + public ShowIfAttribute(string variableName, object variableValue) : base(variableName, variableValue) { } + } + public class EnableIfAttribute : IfAttribute + { + public EnableIfAttribute(string boolName) : base(boolName) { } + public EnableIfAttribute(string variableName, object variableValue) : base(variableName, variableValue) { } + } + public class DisableIfAttribute : IfAttribute + { + public DisableIfAttribute(string boolName) : base(boolName) { } + public DisableIfAttribute(string variableName, object variableValue) : base(variableName, variableValue) { } + } + + + + public class ReadOnlyAttribute : Attribute { } + + + + public class ShowInInspectorAttribute : Attribute { } + + + [AttributeUsage(AttributeTargets.Method)] + public class OnValueChangedAttribute : Attribute + { + public string[] variableOrGroupNames; + + public OnValueChangedAttribute(string name) => this.variableOrGroupNames = new[] { name }; + public OnValueChangedAttribute(params string[] names) => this.variableOrGroupNames = names; + + } + + + +} \ No newline at end of file diff --git a/Assets/OtherAssets/DEV/PAID/vInspector/VInspectorAttributes.cs.meta b/Assets/OtherAssets/DEV/PAID/vInspector/VInspectorAttributes.cs.meta new file mode 100644 index 0000000..216da3d --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vInspector/VInspectorAttributes.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c8d83838ee14b4445a0abac264bf08d8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vInspector/VInspectorClipboard.cs b/Assets/OtherAssets/DEV/PAID/vInspector/VInspectorClipboard.cs new file mode 100644 index 0000000..9af4e9e --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vInspector/VInspectorClipboard.cs @@ -0,0 +1,231 @@ +#if UNITY_EDITOR +using UnityEngine; +using UnityEngine.UIElements; +using System.Collections; +using System.Collections.Generic; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.Linq; +using System.Text.RegularExpressions; +using Type = System.Type; +using static VInspector.Libs.VUtils; +using static VInspector.Libs.VGUI; +// using static VTools.VDebug; + + +namespace VInspector +{ + public class VInspectorClipboard : ScriptableSingleton + { + public static void CopyComponent(Component component) + { + instance.RecordUndo(); + + if (instance.copiedComponetDatas.FirstOrDefault(r => r.sourceComponent == component) is ComponentData alreadyCopiedData) + { + instance.discardedComponentDatas.Add(alreadyCopiedData); + instance.copiedComponetDatas.Remove(alreadyCopiedData); + } + else + instance.copiedComponetDatas.Add(GetComponentData(component)); + + instance.Dirty(); + + } + public static void PasteComponentValues(ComponentData data, Component component) + { + component.RecordUndo(); + + ApplyComponentData(data, component); + + component.Dirty(); + + + + instance.RecordUndo(); + + instance.copiedComponetDatas.Remove(data); + instance.discardedComponentDatas.Add(data); + + instance.Dirty(); + + } + public static void PasteComponentAsNew(ComponentData data, GameObject gameObject) + { + var addedComponent = Undo.AddComponent(gameObject, data.sourceComponent.GetType()); + + ApplyComponentData(data, addedComponent); + + } + + public static void ClearCopiedDatas() + { + instance.RecordUndo(); + + instance.discardedComponentDatas.AddRange(instance.copiedComponetDatas); + instance.copiedComponetDatas.Clear(); + + instance.Dirty(); + + } + + + + public static bool CanComponentsBePastedTo(IEnumerable targetGos) + { + if (!targetGos.Any()) return false; + + foreach (var copiedData in instance.copiedComponetDatas) + if (nonDuplicableComponentTypes.Contains(copiedData.sourceComponent.GetType())) + if (targetGos.Any(r => r.TryGetComponent(copiedData.sourceComponent.GetType(), out _))) + return false; + + return true; + + } + + static Type[] nonDuplicableComponentTypes = new[] + { + typeof(Transform), + typeof(RectTransform), + typeof(MeshFilter), + typeof(MeshRenderer), + typeof(SkinnedMeshRenderer), + typeof(Camera), + typeof(AudioListener), + typeof(Rigidbody), + typeof(Rigidbody2D), + typeof(Light), + typeof(Canvas), + typeof(Animation), + typeof(Animator), + typeof(AudioSource), + typeof(ParticleSystem), + typeof(TrailRenderer), + typeof(LineRenderer), + typeof(LensFlare), + typeof(Projector), + typeof(AudioReverbZone), + typeof(AudioEchoFilter), + typeof(Terrain), + typeof(TerrainCollider), + + }; + + + + [SerializeReference] public List copiedComponetDatas = new(); + + [SerializeReference] public List discardedComponentDatas = new(); // removed datas get stashed here so they can be restored on undo/redo + + + + + + + + public static void SaveComponent(Component component) + { + instance.RecordUndo(); + + if (instance.savedComponentDatas.FirstOrDefault(r => r.sourceComponent == component) is ComponentData alreadySavedData) + { + instance.discardedComponentDatas.Add(alreadySavedData); + instance.savedComponentDatas.Remove(alreadySavedData); + } + else + instance.savedComponentDatas.Add(GetComponentData(component, saveGlobalId: true)); + + instance.Dirty(); + + } + + public static void OnPlaymodeStateChanged(PlayModeStateChange state) + { + if (state == PlayModeStateChange.EnteredPlayMode) + instance.failedToSaveComponentDatas.Clear(); + + + if (state != PlayModeStateChange.EnteredEditMode) return; + + foreach (var data in instance.savedComponentDatas) + if (EditorUtility.InstanceIDToObject(data.sourceComponent.GetInstanceID()) is Component sourceComponent) + ApplyComponentData(data, sourceComponent); + else if (data.globalId.GetObject() is Component sourceComponent_) + ApplyComponentData(data, sourceComponent_); + else + instance.failedToSaveComponentDatas.Add(data); + + instance.savedComponentDatas.Clear(); + + } + + [SerializeReference] public List savedComponentDatas = new(); + [SerializeReference] public List failedToSaveComponentDatas = new(); + + + + + + + + public static ComponentData GetComponentData(Component component, bool saveGlobalId = false) + { + var data = new ComponentData(); + + data.sourceComponent = component; + + if (saveGlobalId) + data.globalId = component.GetGlobalID(); + + + + var property = new SerializedObject(component).GetIterator(); + + if (!property.Next(true)) return data; + + + do data.serializedPropertyValues_byPath[property.propertyPath] = property.GetBoxedValue(); + while (property.NextVisible(true)); + + return data; + + } + public static void ApplyComponentData(ComponentData componentData, Component targetComponent) + { + foreach (var key in componentData.serializedPropertyValues_byPath.Keys.ToList()) + if (componentData.serializedPropertyValues_byPath[key] is Object unityObject && !unityObject) // sometimes object references become null after playmode in unity 6, so we have to restore them by instanceId + componentData.serializedPropertyValues_byPath[key] = EditorUtility.InstanceIDToObject(unityObject.GetInstanceID()); + + foreach (var kvp in componentData.serializedPropertyValues_byPath) + { + var so = new SerializedObject(targetComponent); + var property = so.FindProperty(kvp.Key); + + so.Update(); + + property.SetBoxedValue(kvp.Value); + + so.ApplyModifiedProperties(); + + targetComponent.Dirty(); + + } + + } + + [System.Serializable] + public class ComponentData + { + public Component sourceComponent; + public Dictionary serializedPropertyValues_byPath = new(); + + public GlobalID globalId; + + } + + } + +} +#endif \ No newline at end of file diff --git a/Assets/OtherAssets/DEV/PAID/vInspector/VInspectorClipboard.cs.meta b/Assets/OtherAssets/DEV/PAID/vInspector/VInspectorClipboard.cs.meta new file mode 100644 index 0000000..bdadaa5 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vInspector/VInspectorClipboard.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2e946bdceafc44945ad0c659f550d6d5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vInspector/VInspectorComponentHeader.cs b/Assets/OtherAssets/DEV/PAID/vInspector/VInspectorComponentHeader.cs new file mode 100644 index 0000000..700b6ea --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vInspector/VInspectorComponentHeader.cs @@ -0,0 +1,463 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Linq; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UIElements; +using UnityEditor.UIElements; +using System.Reflection; +using UnityEditor; +using static VInspector.Libs.VUtils; +using static VInspector.Libs.VGUI; +// using static VTools.VDebug; + + + +namespace VInspector +{ + public class VInspectorComponentHeader + { + + void OnGUI() + { + void masks() + { + if (!curEvent.isRepaint) return; + + + Color backgroundColor; + + void set_backgroundColor() + { + var hovered = EditorGUIUtility.isProSkin ? Greyscale(.28f) : Greyscale(.84f); + var normal = EditorGUIUtility.isProSkin ? Greyscale(.244f) : Greyscale(.8f); + + backgroundColor = normal; + + if (headerRect.IsHovered() && !mousePressedOnBackground && EditorWindow.mouseOverWindow == window) + backgroundColor = hovered; + + } + void set_buttonMaskSize() + { + buttonsList ??= typeof(EditorGUIUtility).GetMemberValue("s_EditorHeaderItemsMethods"); + + var buttonsCount = 1 + buttonsList.Count; + + if (VInspectorMenu.playmodeSaveButtonEnabled && !Application.isPlaying) + buttonsCount--; + + + buttonMaskRect = headerRect.AddHeightFromMid(-2).SetXMax(headerRect.xMax - 3).SetWidthFromRight(buttonsCount * 20); + + } + + void hideArrow() + { + if (headerRect.IsHovered()) return; + if (!VInspectorMenu.minimalModeEnabled) return; + + headerRect.SetWidth(17).MoveX(-2).AddHeightFromMid(-2).AddWidthFromRight(-4).Draw(backgroundColor); + + } + void hideButtons() + { + if (headerRect.IsHovered()) return; + if (!VInspectorMenu.minimalModeEnabled) return; + + buttonMaskRect.Draw(backgroundColor); + + } + void hideScriptText() + { + if (component is not MonoBehaviour) return; + if (!VInspectorMenu.minimalModeEnabled) return; + if (imguiContainer.onGUIHandler.Method.DeclaringType.FullName.StartsWith("Sisus")) return; + + + var name = component.GetType().Name.Decamelcase(); + + var rect = headerRect.AddHeightFromMid(-2).SetWidth(60).MoveX(name.GetLabelWidth(fontSize: 12, isBold: true) + 60 - 3); + rect = rect.MoveX(-2); + + rect.xMax = rect.xMax.Min(buttonMaskRect.x).Max(rect.xMin); + + + rect.Draw(backgroundColor); + + } + void tintScriptIcon() + { + if (component is not MonoBehaviour) return; + if (!mousePressedOnScriptIcon) return; + + var iconRect = headerRect.MoveX(18).SetWidth(22); + + iconRect.Draw(backgroundColor.SetAlpha(EditorGUIUtility.isProSkin ? .3f : .45f)); + + } + void nameCurtain() + { + var rect = headerRect.AddHeightFromMid(-2).SetXMax(buttonMaskRect.x + 2).SetWidthFromRight(18); + + rect.DrawCurtainLeft(backgroundColor); + + } + + + set_backgroundColor(); + set_buttonMaskSize(); + + hideArrow(); + hideButtons(); + hideScriptText(); + tintScriptIcon(); + nameCurtain(); + + } + + void scriptIconClicks() + { + if (component is not MonoBehaviour) return; + if (curEvent.mouseButton != 0) return; + if (!VInspectorMenu.hideScriptFieldEnabled) return; + + var iconRect = headerRect.MoveX(18).SetWidth(22); + + + void mosueDown() + { + if (!curEvent.isMouseDown) return; + if (!iconRect.IsHovered()) return; + + + mousePressedOnScriptIcon = true; + mousePressedOnScriptIcon_initPos = curEvent.mousePosition; + + + var script = MonoScript.FromMonoBehaviour(component as MonoBehaviour); + + // if (curEvent.holdingAlt) + + + if (curEvent.clickCount == 2) + AssetDatabase.OpenAsset(script); + + + curEvent.Use(); + + } + void mouseUp() + { + if (!curEvent.isMouseUp) return; + if (!mousePressedOnScriptIcon) return; + + var script = MonoScript.FromMonoBehaviour(component as MonoBehaviour); + + if (curEvent.clickCount == 1) + EditorUtils.PingObject(script); + + mousePressedOnScriptIcon = false; + + window.Repaint(); + + curEvent.Use(); + + } + void startDrag() + { + if (!curEvent.isMouseDrag) return; + if (!mousePressedOnScriptIcon) return; + if ((mousePressedOnScriptIcon_initPos - curEvent.mousePosition).magnitude < 2) return; + + DragAndDrop.PrepareStartDrag(); + DragAndDrop.objectReferences = new[] { component }; + DragAndDrop.StartDrag(component.ToString()); + + mousePressedOnScriptIcon = false; + mousePressedOnBackground = false; + + } + + + mosueDown(); + mouseUp(); + startDrag(); + + } + + void expandWithAnimation() + { + if (!mousePressedOnBackground) return; + if (!curEvent.isMouseUp) return; + if (curEvent.mouseButton != 0) return; + if (headerRect.SetWidth(16).MoveX(40).IsHovered()) return; // enabled toggle + if (buttonMaskRect.IsHovered()) return; // right buttons + + + if (curEvent.holdingShift) + VInspector.CollapseOtherComponents(component, window); + + else if (VInspectorMenu.componentAnimationsEnabled) + VInspector.ToggleComponentExpanded(component, window); + + else return; + + + mousePressedOnBackground = false; + + curEvent.Use(); + + EditorGUIUtility.hotControl = 0; + + } + void createComponentWindow() + { + if (!mousePressedOnBackground) return; + if (!curEvent.isMouseDrag) return; + if (!curEvent.holdingAlt) return; + if (curEvent.mousePosition.DistanceTo(mousePressedOnBackground_initPos) < 2) return; + if (!VInspectorMenu.componentWindowsEnabled) return; + + if (VInspectorComponentWindow.draggedInstance != null) return; + + + + var position = EditorGUIUtility.GUIToScreenPoint(headerRect.position + (curEvent.mousePosition - mousePressedOnBackground_initPos)); + + VInspectorComponentWindow.CreateDraggedInstance(component, position, headerRect.width); + + + + EditorGUIUtility.hotControl = 0; + + mousePressedOnBackground = false; + + + + } + + void set_mousePressedOnBackground() + { + if (curEvent.isMouseDown && !buttonMaskRect.IsHovered()) + { + mousePressedOnBackground = true; + mousePressedOnBackground_initPos = curEvent.mousePosition; + } + + if (curEvent.isMouseUp || curEvent.isDragUpdate) + mousePressedOnBackground = false; + + if (!imguiContainer.contentRect.IsHovered()) + mousePressedOnBackground = false; + + } + void set_hoveredComponentHeader() + { + if (!curEvent.isRepaint) return; + + if (component is Transform) + VInspector.hoveredComponentHeader = null; + + if (headerRect.IsHovered()) + VInspector.hoveredComponentHeader = this; + + } + + void defaultHeaderGUI() + { + void initOffsets() + { + if (!VInspectorMenu.minimalModeEnabled) return; + if (headerContentStyle != null) return; + + headerContentStyle = typeof(EditorStyles).GetMemberValue("inspectorTitlebar"); + headerFoldoutStyle = typeof(EditorStyles).GetMemberValue("titlebarFoldout"); + + headerContentStyle_defaultLeftPadding = headerContentStyle.padding.left; + headerFoldoutStyle_defaultLeftMargin = headerFoldoutStyle.margin.left; + + } + void setAdjustedOffsets() + { + if (!VInspectorMenu.minimalModeEnabled) return; + + headerContentStyle.padding.left = headerContentStyle_defaultLeftPadding - 2; + headerFoldoutStyle.margin.left = headerFoldoutStyle_defaultLeftMargin - 1; + + } + void setDefaultOffsets() + { + if (!VInspectorMenu.minimalModeEnabled) return; + + headerContentStyle.padding.left = headerContentStyle_defaultLeftPadding; + headerFoldoutStyle.margin.left = headerFoldoutStyle_defaultLeftMargin; + + } + + initOffsets(); + setAdjustedOffsets(); + defaultHeaderGUIAction.Invoke(); + setDefaultOffsets(); + + } + + void preventKeyboardFocus() + { + if (!curEvent.isUsed) return; + if (!headerRect.IsHovered()) return; + if (imguiContainer.onGUIHandler.Method.DeclaringType.FullName.StartsWith("Sisus")) return; + + GUIUtility.keyboardControl = 0; + + // removes that annoying blue highlight after clicking on header + + } + + + + if (curEvent.isRepaint) + defaultHeaderGUI(); + + masks(); + + scriptIconClicks(); + expandWithAnimation(); + createComponentWindow(); + + set_mousePressedOnBackground(); + set_hoveredComponentHeader(); + + if (!curEvent.isRepaint) + defaultHeaderGUI(); + + preventKeyboardFocus(); + + } + + static IList buttonsList; + static Rect buttonMaskRect; + + public bool mousePressedOnBackground; + public bool mousePressedOnScriptIcon; + public Vector2 mousePressedOnBackground_initPos; + public Vector2 mousePressedOnScriptIcon_initPos; + + public Rect headerRect + { + get + { + var contentRect = imguiContainer.contentRect; + + if (contentRect.height == 42) // with extra lines like "Multi-object editing not supported" + return contentRect.SetHeight(22); + else + return contentRect.SetHeightFromBottom(22); // fixes offset on transform header in 6000 + } + } + + static GUIStyle headerContentStyle; + static GUIStyle headerFoldoutStyle; + + static int headerContentStyle_defaultLeftPadding; + static int headerFoldoutStyle_defaultLeftMargin; + + public Vector2 mouseDownPos; + + + + + + + public void Update() + { + if (imguiContainer is VisualElement v && v.panel == null) { imguiContainer.onGUIHandler = defaultHeaderGUIAction; imguiContainer = null; } + if (imguiContainer?.onGUIHandler.Method.DeclaringType == typeof(VInspectorComponentHeader)) return; + if (imguiContainer?.onGUIHandler.Method.DeclaringType.FullName.StartsWith("Sisus") == true) return; + if (typeof(ScriptableObject).IsAssignableFrom(component.GetType())) return; + if (editor.GetPropertyValue("propertyViewer") is not EditorWindow window) return; + + + this.window = window; + + void fixWrongWindow() + { + if (Application.unityVersion != "2022.3.26f1" && Application.unityVersion != "2023.2.20f1") return; + + if (!window.hasFocus) + window = window.GetMemberValue("m_Parent")?.GetMemberValue>("m_Panes")?.FirstOrDefault(r => r.hasFocus) ?? window; + + // in some versions wrong inspector may be returned by propertyViewer when there are multiple inspectors + // also the same instance of an editor may be used on all inspectors + // here we fix it for cases when multiple inspectors are in the same dock area + + } + void findHeader(VisualElement element) + { + if (element == null) return; + + if (element.GetType().Name == "EditorElement") + { + IMGUIContainer curHeaderImguiContainer = null; + + foreach (var child in element.Children()) + { + curHeaderImguiContainer ??= new[] { child as IMGUIContainer }.FirstOrDefault(r => r != null && r.name.EndsWith("Header")); + + if (curHeaderImguiContainer is null) continue; + if (child is not InspectorElement) continue; + if (child.GetFieldValue("m_Editor") is not Editor editor) continue; + if (editor.target != component) continue; + + imguiContainer = curHeaderImguiContainer; + + if (editingMultiselection = editor.targets.Count() > 1) + multiselectedComponents = editor.targets.Cast().ToList(); + + return; + + } + + } + + foreach (var r in element.Children()) + if (imguiContainer == null) + findHeader(r); + + } + void setupGUICallbacks() + { + if (imguiContainer == null) return; + + defaultHeaderGUIAction = imguiContainer.onGUIHandler; + imguiContainer.onGUIHandler = OnGUI; + } + + fixWrongWindow(); + findHeader(window.rootVisualElement); + setupGUICallbacks(); + + } + + public bool editingMultiselection; + public List multiselectedComponents; + + IMGUIContainer imguiContainer; + System.Action defaultHeaderGUIAction; + + EditorWindow window; + + + + + + + + public VInspectorComponentHeader(Component component, Editor editor) { this.component = component; this.editor = editor; } + + public Editor editor; + public Component component; + + } +} +#endif \ No newline at end of file diff --git a/Assets/OtherAssets/DEV/PAID/vInspector/VInspectorComponentHeader.cs.meta b/Assets/OtherAssets/DEV/PAID/vInspector/VInspectorComponentHeader.cs.meta new file mode 100644 index 0000000..24d0da1 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vInspector/VInspectorComponentHeader.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 69daed4ccfde7447ba633e320119de35 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vInspector/VInspectorComponentWindow.cs b/Assets/OtherAssets/DEV/PAID/vInspector/VInspectorComponentWindow.cs new file mode 100644 index 0000000..06f21bf --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vInspector/VInspectorComponentWindow.cs @@ -0,0 +1,498 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.Linq; +using UnityEditor.UIElements; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using Type = System.Type; +using static VInspector.VInspector; +using static VInspector.Libs.VUtils; +using static VInspector.Libs.VGUI; +// using static VTools.VDebug; + + +namespace VInspector +{ + public class VInspectorComponentWindow : EditorWindow + { + + void OnGUI() + { + if (!component) component = EditorUtility.InstanceIDToObject(componentIid) as Component; + if (!component) { Close(); return; } + if (!editor) { Init(component); skipHeightUpdate = true; } + + + void background() + { + position.SetPos(0, 0).Draw(GUIColors.windowBackground); + } + void header() + { + var headerRect = ExpandWidthLabelRect(18).Resize(-1).AddWidthFromMid(6); + var closeButtonRect = headerRect.SetWidthFromRight(16).SetHeightFromMid(16).Move(-4, 0); + + var backgroundColor = isDarkTheme ? Greyscale(.25f) : GUIColors.windowBackground; + + void startDragging() + { + if (isResizingVertically) return; + if (isResizingHorizontally) return; + if (isDragged) return; + if (!curEvent.isMouseDrag) return; + if (!headerRect.IsHovered()) return; + + + draggedInstance = this; + + dragStartMousePos = curEvent.mousePosition_screenSpace; + dragStartWindowPos = position.position; + + } + void updateDragging() + { + if (!isDragged) return; + + + var draggedPosition = dragStartWindowPos + curEvent.mousePosition_screenSpace - dragStartMousePos; + + if (!curEvent.isRepaint) + position = position.SetPos(draggedPosition); + + + EditorGUIUtility.hotControl = EditorGUIUtility.GetControlID(FocusType.Passive); + + } + void stopDragging() + { + if (!isDragged) return; + if (!curEvent.isMouseMove && !curEvent.isMouseUp) return; + + + draggedInstance = null; + + EditorGUIUtility.hotControl = 0; + + } + + void background() + { + headerRect.Draw(backgroundColor); + + headerRect.SetHeightFromBottom(1).Draw(isDarkTheme ? Greyscale(.2f) : Greyscale(.7f)); + + } + void icon() + { + var iconRect = headerRect.SetWidth(20).MoveX(14).MoveY(-1); + + if (!componentIcons_byType.ContainsKey(component.GetType())) + componentIcons_byType[component.GetType()] = EditorGUIUtility.ObjectContent(component, component.GetType()).image; + + GUI.Label(iconRect, componentIcons_byType[component.GetType()]); + + } + void toggle() + { + var toggleRect = headerRect.MoveX(36).SetSize(20, 20); + + + var pi_enabled = component.GetType().GetProperty("enabled") ?? + component.GetType().BaseType?.GetProperty("enabled") ?? + component.GetType().BaseType?.BaseType?.GetProperty("enabled") ?? + component.GetType().BaseType?.BaseType?.BaseType?.GetProperty("enabled"); + + + if (pi_enabled == null) return; + + var enabled = (bool)pi_enabled.GetValue(component); + + + if (GUI.Toggle(toggleRect, enabled, "") == enabled) return; + + component.RecordUndo(); + pi_enabled.SetValue(component, !enabled); + + } + void name() + { + var nameRect = headerRect.MoveX(54).MoveY(-1); + + + var s = new GUIContent(EditorGUIUtility.ObjectContent(component, component.GetType())).text; + s = s.Substring(s.LastIndexOf('(') + 1); + s = s.Substring(0, s.Length - 1); + + if (instances.Any(r => r.component.GetType() == component.GetType() && r.component != component)) + s += " - " + component.gameObject.name; + + + SetLabelBold(); + + GUI.Label(nameRect, s); + + ResetLabelStyle(); + + } + void nameCurtain() + { + var flatColorRect = headerRect.SetX(closeButtonRect.x + 3).SetXMax(headerRect.xMax); + var gradientRect = headerRect.SetXMax(flatColorRect.x).SetWidthFromRight(30); + + flatColorRect.Draw(backgroundColor); + gradientRect.DrawCurtainLeft(backgroundColor); + + } + void closeButton() + { + var iconName = "CrossIcon"; + var iconSize = 14; + var color = isDarkTheme ? Greyscale(.65f) : Greyscale(.35f); + var colorHovered = isDarkTheme ? Greyscale(.9f) : color; + var colorPressed = color; + + + if (!IconButton(closeButtonRect, iconName, iconSize, color, colorHovered, colorPressed)) return; + + Close(); + + EditorGUIUtility.ExitGUI(); + + } + void rightClick() + { + if (!curEvent.isMouseDown) return; + if (curEvent.mouseButton != 1) return; + if (!headerRect.IsHovered()) return; + + typeof(EditorUtility).InvokeMethod("DisplayObjectContextMenu", Rect.zero.SetPos(curEvent.mousePosition), component, 0); + + } + + startDragging(); + updateDragging(); + stopDragging(); + + background(); + icon(); + toggle(); + name(); + nameCurtain(); + closeButton(); + rightClick(); + + } + void body_imgui() + { + if (useUITK) return; + + + EditorGUIUtility.labelWidth = (this.position.width * .4f).Max(120); + + + scrollPosition = EditorGUILayout.BeginScrollView(Vector2.up * scrollPosition).y; + BeginIndent(17); + + + editor?.OnInspectorGUI(); + + updateHeight_imgui(); + + + EndIndent(1); + EditorGUILayout.EndScrollView(); + + + EditorGUIUtility.labelWidth = 0; + + } + void outline() + { + if (Application.platform == RuntimePlatform.OSXEditor) return; + + position.SetPos(0, 0).DrawOutline(Greyscale(.1f)); + + } + + void updateHeight_imgui() + { + if (useUITK) return; + + + ExpandWidthLabelRect(height: -5); + + if (!curEvent.isRepaint) return; + if (isResizingVertically) return; + + + targetHeight = lastRect.y + 30; + + position = position.SetHeight(targetHeight.Min(maxHeight)); + + + prevHeight = position.height; + + } + void updateHeight_uitk() + { + if (!useUITK) return; + if (!curEvent.isRepaint) return; + if (skipHeightUpdate) { skipHeightUpdate = false; return; } // crashses otherwise + + + var lastElement = inspectorElement[inspectorElement.childCount - 1]; + + targetHeight = lastElement.contentRect.yMax + 33; + + position = position.SetHeight(targetHeight); + + } + + void horizontalResize() + { + var showingScrollbar = targetHeight > maxHeight; + + var resizeArea = this.position.SetPos(0, 0).SetWidthFromRight(showingScrollbar ? 3 : 5).AddHeightFromBottom(-20); + + void startResize() + { + if (isDragged) return; + if (isResizingHorizontally) return; + if (!curEvent.isMouseDown && !curEvent.isMouseDrag) return; + if (!resizeArea.IsHovered()) return; + + isResizingHorizontally = true; + + resizeStartMousePos = curEvent.mousePosition_screenSpace; + resizeStartWindowSize = this.position.size; + + } + void updateResize() + { + if (!isResizingHorizontally) return; + + + var resizedWidth = resizeStartWindowSize.x + curEvent.mousePosition_screenSpace.x - resizeStartMousePos.x; + + var width = resizedWidth.Max(300); + + if (!curEvent.isRepaint) + position = position.SetWidth(width); + + + EditorGUIUtility.hotControl = EditorGUIUtility.GetControlID(FocusType.Passive); + // GUI.focused + + } + void stopResize() + { + if (!isResizingHorizontally) return; + if (!curEvent.isMouseUp) return; + + isResizingHorizontally = false; + + EditorGUIUtility.hotControl = 0; + + } + + + EditorGUIUtility.AddCursorRect(resizeArea, MouseCursor.ResizeHorizontal); + + startResize(); + updateResize(); + stopResize(); + + } + void verticalResize() + { + var resizeArea = this.position.SetPos(0, 0).SetHeightFromBottom(5); + + void startResize() + { + if (isDragged) return; + if (isResizingVertically) return; + if (!curEvent.isMouseDown && !curEvent.isMouseDrag) return; + if (!resizeArea.IsHovered()) return; + + isResizingVertically = true; + + resizeStartMousePos = curEvent.mousePosition_screenSpace; + resizeStartWindowSize = this.position.size; + + } + void updateResize() + { + if (!isResizingVertically) return; + + + var resizedHeight = resizeStartWindowSize.y + curEvent.mousePosition_screenSpace.y - resizeStartMousePos.y; + + var height = resizedHeight.Min(targetHeight).Max(50); + + if (!curEvent.isRepaint) + position = position.SetHeight(height); + + maxHeight = height; + + + EditorGUIUtility.hotControl = EditorGUIUtility.GetControlID(FocusType.Passive); + + } + void stopResize() + { + if (!isResizingVertically) return; + if (!curEvent.isMouseUp) return; + + isResizingVertically = false; + + EditorGUIUtility.hotControl = 0; + + } + + + EditorGUIUtility.AddCursorRect(resizeArea, MouseCursor.ResizeVertical); + + startResize(); + updateResize(); + stopResize(); + + } + + + + background(); + header(); + + + horizontalResize(); + verticalResize(); + + + Space(3); + body_imgui(); + outline(); + + Space(7); + + updateHeight_uitk(); + + + if (isDragged) + Repaint(); + + EditorApplication.delayCall -= Repaint; + EditorApplication.delayCall += Repaint; + + } + + public bool isDragged => draggedInstance == this; + public Vector2 dragStartMousePos; + public Vector2 dragStartWindowPos; + + public bool isResizingHorizontally; + public bool isResizingVertically; + public Vector2 resizeStartMousePos; + public Vector2 resizeStartWindowSize; + + public float scrollPosition; + + bool skipHeightUpdate; + + public float targetHeight; + public float maxHeight; + public float prevHeight; + + static Dictionary componentIcons_byType = new(); + + + + + + + + + public void Init(Component component) + { + if (editor) + editor.DestroyImmediate(); + + this.component = component; + this.editor = Editor.CreateEditor(component); + + this.componentIid = component.GetInstanceID(); + + hasCustomUITKEditor = editor.GetType().GetMethod("CreateInspectorGUI", maxBindingFlags) != null; + + if (!instances.Contains(this)) + instances.Add(this); + + + + if (!useUITK) return; + + inspectorElement = new InspectorElement(editor.serializedObject); + + inspectorElement.style.marginTop = 23; + + this.rootVisualElement.Add(inspectorElement); + + } + + void OnDestroy() + { + editor?.DestroyImmediate(); + + if (instances.Contains(this)) + instances.Remove(this); + + } + + public Component component; + public Editor editor; + public InspectorElement inspectorElement; + + public int componentIid; + + bool useUITK => editor.target is MonoBehaviour && (HasUITKOnlyDrawers(editor.serializedObject) || hasCustomUITKEditor); + bool hasCustomUITKEditor; + + public static List instances = new(); + + + + + + public static void CreateDraggedInstance(Component component, Vector2 windowPosition, float windowWidth) + { + draggedInstance = ScriptableObject.CreateInstance(); + + draggedInstance.ShowPopup(); + draggedInstance.Init(component); + draggedInstance.Focus(); + + + draggedInstance.wantsMouseMove = true; + + // draggedInstance.minSize = new Vector2(300, 50); // will make window resizeable on mac, but not on windows + draggedInstance.maxHeight = EditorGUIUtility.GetMainWindowPosition().height * .7f; + + + draggedInstance.position = Rect.zero.SetPos(windowPosition).SetWidth(windowWidth).SetHeight(200); + draggedInstance.prevHeight = draggedInstance.position.height; + + draggedInstance.dragStartMousePos = curEvent.mousePosition_screenSpace; + draggedInstance.dragStartWindowPos = windowPosition; + + } + + public static VInspectorComponentWindow draggedInstance; + + } +} +#endif \ No newline at end of file diff --git a/Assets/OtherAssets/DEV/PAID/vInspector/VInspectorComponentWindow.cs.meta b/Assets/OtherAssets/DEV/PAID/vInspector/VInspectorComponentWindow.cs.meta new file mode 100644 index 0000000..81b6ecf --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vInspector/VInspectorComponentWindow.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 295e0a9b2039246c59fbe00423b2723e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vInspector/VInspectorData.cs b/Assets/OtherAssets/DEV/PAID/vInspector/VInspectorData.cs new file mode 100644 index 0000000..a453150 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vInspector/VInspectorData.cs @@ -0,0 +1,156 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.Serialization; +using UnityEditor; +using UnityEditor.ShortcutManagement; +using System.Reflection; +using System.Linq; +using UnityEngine.UIElements; +using UnityEngine.SceneManagement; +using UnityEditor.SceneManagement; +using Type = System.Type; +using static VInspector.VInspectorState; +using static VInspector.Libs.VUtils; +using static VInspector.Libs.VGUI; +// using static VTools.VDebug; + + +namespace VInspector +{ + public class VInspectorData : ScriptableObject + { + + public List bookmarks = new(); + + [System.Serializable] + public class Bookmark + { + + public Object obj + { + get + { + if (isSceneGameObject && _obj == null) + { + if (!failedToLoadSceneObject) // to prevent continuous GlobalID.GetObjects() calls if object is deleted + VInspector.unloadedSceneBookmarks_sceneGuids.Add(globalId.guid); + + return null; + } + + return _obj ??= globalId.GetObject(); + + } + + } + public Object _obj; + + [System.NonSerialized] + public bool failedToLoadSceneObject; + + + + public bool isLoadable => obj != null; + + public bool isDeleted + { + get + { + if (!isSceneGameObject) + return !isLoadable; + + if (isLoadable) + return false; + + if (!AssetDatabase.LoadAssetAtPath(globalId.guid.ToPath())) + return true; + + for (int i = 0; i < EditorSceneManager.sceneCount; i++) + if (EditorSceneManager.GetSceneAt(i).path == globalId.guid.ToPath()) + return true; + + return false; + + } + } + + public string assetPath => globalId.guid.ToPath(); + + public Type type => Type.GetType(_typeString) ?? typeof(DefaultAsset); + + public string name + { + get + { + if (!obj) return "Can't load object"; + + if (assetPath.GetExtension() == ".cs") + return obj.name.Decamelcase(); + else + return obj.name; + + } + } + + + + public Bookmark(Object o) + { + globalId = o.GetGlobalID(); + + isSceneGameObject = o is GameObject go && go.scene.rootCount != 0; + isAsset = !isSceneGameObject; + + _typeString = o.GetType().AssemblyQualifiedName; + + _obj = o; + + } + + public GlobalID globalId; + + public bool isSceneGameObject; + public bool isAsset; + + public string _typeString; + + } + + + + + + + + + + + [CustomEditor(typeof(VInspectorData))] + class Editor : UnityEditor.Editor + { + public override void OnInspectorGUI() + { + var style = new GUIStyle(EditorStyles.label) { wordWrap = true }; + + + SetGUIEnabled(false); + BeginIndent(0); + + Space(10); + EditorGUILayout.LabelField("This file stores bookmarks from vInspector's navigation bar", style); + + EndIndent(10); + ResetGUIEnabled(); + + // Space(15); + // base.OnInspectorGUI(); + + } + } + + + } +} +#endif \ No newline at end of file diff --git a/Assets/OtherAssets/DEV/PAID/vInspector/VInspectorData.cs.meta b/Assets/OtherAssets/DEV/PAID/vInspector/VInspectorData.cs.meta new file mode 100644 index 0000000..601e74b --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vInspector/VInspectorData.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b7915df4cd4da490ab52fc8fa9ee9127 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vInspector/VInspectorDrawers.cs b/Assets/OtherAssets/DEV/PAID/vInspector/VInspectorDrawers.cs new file mode 100644 index 0000000..14e2ea9 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vInspector/VInspectorDrawers.cs @@ -0,0 +1,429 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Linq; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UIElements; +using UnityEditor.UIElements; +using System.Reflection; +using UnityEditor; +using UnityEditorInternal; +using Type = System.Type; +using Attribute = System.Attribute; +using static VInspector.VInspectorState; +using static VInspector.Libs.VUtils; +using static VInspector.Libs.VGUI; +// using static VTools.VDebug; + + +namespace VInspector +{ + [CustomPropertyDrawer(typeof(SerializedDictionary<,>), true)] + public class SerializedDictionaryDrawer : PropertyDrawer + { + public override void OnGUI(Rect rect, SerializedProperty prop, GUIContent label) + { + var indentedRect = EditorGUI.IndentedRect(rect); + + void header() + { + var headerRect = indentedRect.SetHeight(EditorGUIUtility.singleLineHeight); + + void foldout() + { + var fullHeaderRect = headerRect.MoveX(3).AddWidthFromRight(17); + + if (fullHeaderRect.IsHovered()) + fullHeaderRect.Draw(Greyscale(1, .07f)); + + SetGUIColor(Color.clear); + SetGUIEnabled(true); + + if (GUI.Button(fullHeaderRect.AddWidth(-50), "")) + prop.isExpanded = !prop.isExpanded; + + ResetGUIColor(); + ResetGUIEnabled(); + + + + var triangleRect = rect.SetHeight(EditorGUIUtility.singleLineHeight); + + SetGUIEnabled(true); + + EditorGUI.Foldout(triangleRect, prop.isExpanded, ""); + + ResetGUIEnabled(); + + + } + void label() + { + SetLabelBold(); + SetLabelFontSize(12); + SetGUIColor(Greyscale(.9f)); + SetGUIEnabled(true); + + GUI.Label(headerRect, prop.displayName); + + ResetGUIEnabled(); + ResetGUIColor(); + ResetLabelStyle(); + + } + void count() + { + kvpsProp_byProp[prop].arraySize = EditorGUI.DelayedIntField(headerRect.SetWidthFromRight(48 + EditorGUI.indentLevel * 15), kvpsProp_byProp[prop].arraySize); + } + void repeatedKeysWarning() + { + if (!curEvent.isRepaint) return; + + + var hasRepeatedKeys = false; + var hasNullKeys = false; + + for (int i = 0; i < kvpsProp_byProp[prop].arraySize; i++) + { + hasRepeatedKeys |= kvpsProp_byProp[prop].GetArrayElementAtIndex(i).FindPropertyRelative("isKeyRepeated").boolValue; + hasNullKeys |= kvpsProp_byProp[prop].GetArrayElementAtIndex(i).FindPropertyRelative("isKeyNull").boolValue; + } + + if (!hasRepeatedKeys && !hasNullKeys) return; + + + + var warningTextRect = headerRect.AddWidthFromRight(-prop.displayName.GetLabelWidth(isBold: true)); + var warningIconRect = warningTextRect.SetHeightFromMid(20).SetWidth(20); + + var warningText = (hasRepeatedKeys && hasNullKeys) ? "Repeated and null keys" + : hasRepeatedKeys ? "Repeated keys" + : hasNullKeys ? "Null keys" : ""; + + + + GUI.Label(warningIconRect, EditorGUIUtility.IconContent("Warning")); + + + SetGUIColor(new Color(1, .9f, .03f) * 1.1f); + + GUI.Label(warningTextRect.MoveX(16), warningText); + + ResetGUIColor(); + + } + + foldout(); + label(); + count(); + repeatedKeysWarning(); + + } + void list_() + { + if (!prop.isExpanded) return; + + SetupList(prop); + + lists_byProp[prop].DoList(indentedRect.AddHeightFromBottom(-EditorGUIUtility.singleLineHeight - 3)); + } + + + SetupProps(prop); + + header(); + list_(); + + } + + public override float GetPropertyHeight(SerializedProperty prop, GUIContent label) + { + SetupProps(prop); + + var height = EditorGUIUtility.singleLineHeight; + + if (prop.isExpanded) + { + SetupList(prop); + height += lists_byProp[prop].GetHeight() + 3; + } + + return height; + } + + float GetListElementHeight(int index, SerializedProperty prop) + { + var kvpProp = kvpsProp_byProp[prop].GetArrayElementAtIndex(index); + var keyProp = kvpProp.FindPropertyRelative("Key"); + var valueProp = kvpProp.FindPropertyRelative("Value"); + + float propHeight(SerializedProperty prop) + { + // var height = typeof(Editor).Assembly.GetType("UnityEditor.ScriptAttributeUtility").InvokeMethod("GetHandler", prop).InvokeMethod("GetHeight", prop, GUIContent.none, true); + var height = EditorGUI.GetPropertyHeight(prop); + + if (!IsSingleLine(prop) && prop.type != "EventReference") + height -= 10; + + return height; + + } + + return Mathf.Max(propHeight(keyProp), propHeight(valueProp)); + + } + + void DrawListElement(Rect rect, int index, bool isActive, bool isFocused, SerializedProperty prop) + { + Rect keyRect; + Rect valueRect; + Rect dividerRect; + + var kvpProp = kvpsProp_byProp[prop].GetArrayElementAtIndex(index); + var keyProp = kvpProp.FindPropertyRelative("Key"); + var valueProp = kvpProp.FindPropertyRelative("Value"); + + void drawProp(Rect rect, SerializedProperty prop) + { + if (IsSingleLine(prop)) { EditorGUI.PropertyField(rect.SetHeight(EditorGUIUtility.singleLineHeight), prop, GUIContent.none); return; } + + + prop.isExpanded = true; + + GUI.BeginGroup(rect); + + if (prop.type == "EventReference") // don't hide first line for FMOD EventReference + { + EditorGUIUtility.labelWidth = 1; + EditorGUI.PropertyField(rect.SetPos(0, 0), prop, GUIContent.none); + EditorGUIUtility.labelWidth = 0; + } + else + EditorGUI.PropertyField(rect.SetPos(0, -20), prop, true); + + GUI.EndGroup(); + + } + + void rects() + { + var dividerWidh = 6f; + + var dividerPos = dividerPosProp.floatValue.Clamp(.2f, .8f); + + var fullRect = rect.AddWidthFromRight(-1).AddHeightFromMid(-2); + + keyRect = fullRect.SetWidth(fullRect.width * dividerPos - dividerWidh / 2); + valueRect = fullRect.SetWidthFromRight(fullRect.width * (1 - dividerPos) - dividerWidh / 2); + dividerRect = fullRect.MoveX(fullRect.width * dividerPos - dividerWidh / 2).SetWidth(dividerWidh).Resize(-1); + + } + void key() + { + drawProp(keyRect, keyProp); + + } + void warning() + { + var isKeyRepeated = kvpProp.FindPropertyRelative("isKeyRepeated").boolValue; + var isKeyNull = kvpProp.FindPropertyRelative("isKeyNull").boolValue; + + if (!isKeyRepeated && !isKeyNull) return; + + + var warningRect = keyRect.SetWidthFromRight(20).SetHeight(20).MoveY(-1); + + if (kvpProp.FindPropertyRelative("Key").propertyType == SerializedPropertyType.ObjectReference) + warningRect = warningRect.MoveX(-17); + + + GUI.Label(warningRect, EditorGUIUtility.IconContent("Warning")); + + } + void value() + { + drawProp(valueRect, valueProp); + } + void divider() + { + EditorGUIUtility.AddCursorRect(dividerRect, MouseCursor.ResizeHorizontal); + + if (!rect.IsHovered()) return; + + if (dividerRect.IsHovered()) + { + if (curEvent.isMouseDown) + isDividerDragged = true; + + if (curEvent.isMouseUp || curEvent.isMouseMove || curEvent.isMouseLeaveWindow) + isDividerDragged = false; + } + + if (isDividerDragged && curEvent.isMouseDrag) + dividerPosProp.floatValue += curEvent.mouseDelta.x / rect.width; + + } + + rects(); + key(); + warning(); + value(); + divider(); + + } + + void DrawDictionaryIsEmpty(Rect rect) => GUI.Label(rect, "Dictionary is empty"); + + + + IEnumerable GetChildren(SerializedProperty prop, bool enterVisibleGrandchildren) + { + var startPath = prop.propertyPath; + + var enterVisibleChildren = true; + + while (prop.NextVisible(enterVisibleChildren) && prop.propertyPath.StartsWith(startPath)) + { + yield return prop; + enterVisibleChildren = enterVisibleGrandchildren; + } + + } + + bool IsSingleLine(SerializedProperty prop) => prop.propertyType != SerializedPropertyType.Generic || !prop.hasVisibleChildren; + + + + public void SetupList(SerializedProperty prop) + { + if (lists_byProp.ContainsKey(prop)) return; + + SetupProps(prop); + + lists_byProp[prop] = new ReorderableList(kvpsProp_byProp[prop].serializedObject, kvpsProp_byProp[prop], true, false, true, true); + lists_byProp[prop].drawElementCallback = (q, w, e, r) => DrawListElement(q, w, e, r, prop); + lists_byProp[prop].elementHeightCallback = (q) => GetListElementHeight(q, prop); + lists_byProp[prop].drawNoneElementCallback = DrawDictionaryIsEmpty; + + } + + Dictionary lists_byProp = new(); + // ReorderableList list; + + bool isDividerDragged; + + + public void SetupProps(SerializedProperty prop) + { + if (kvpsProp_byProp.ContainsKey(prop)) return; + + kvpsProp_byProp[prop] = prop.FindPropertyRelative("serializedKvps"); + + this.dividerPosProp = prop.FindPropertyRelative("dividerPos"); + + + } + + Dictionary kvpsProp_byProp = new(); + // SerializedProperty kvpsProp; + + SerializedProperty dividerPosProp; + + } + + + [CustomPropertyDrawer(typeof(VariantsAttribute))] + public class VariantsDrawer : PropertyDrawer + { + public override void OnGUI(Rect rect, SerializedProperty prop, GUIContent label) + { + var variants = ((VariantsAttribute)attribute).variants; + + + EditorGUI.BeginProperty(rect, label, prop); + + var iCur = prop.hasMultipleDifferentValues ? -1 : variants.ToList().IndexOf(prop.GetBoxedValue()); + + var iNew = EditorGUI.IntPopup(rect, label.text, iCur, variants.Select(r => r.ToString()).ToArray(), Enumerable.Range(0, variants.Length).ToArray()); + + if (iNew != -1) + prop.SetBoxedValue(variants[iNew]); + else if (!prop.hasMultipleDifferentValues) + prop.SetBoxedValue(variants[0]); + + EditorGUI.EndProperty(); + + } + } + + + [CustomPropertyDrawer(typeof(MinMaxSliderAttribute))] + public class MinMaxSliderDrawer : PropertyDrawer + { + public override void OnGUI(Rect rect, SerializedProperty prop, GUIContent label) + { + var fieldWidth = 52; + + var controlsRect = rect.AddWidthFromRight(-EditorGUIUtility.labelWidth); + + var minFieldRect = controlsRect.SetWidth(fieldWidth).AddWidthFromRight(-2 + EditorGUI.indentLevel * 15); + var maxFieldRect = controlsRect.SetWidthFromRight(fieldWidth).AddWidthFromRight(-2 + EditorGUI.indentLevel * 15); + var sliderRect = controlsRect.AddWidthFromMid(-fieldWidth * 2 - 4).AddWidthFromRight(-2 + EditorGUI.indentLevel * 15); + + + var isInt = prop.propertyType == SerializedPropertyType.Vector2Int; + + var min = isInt ? prop.vector2IntValue.x : prop.vector2Value.x; + var max = isInt ? prop.vector2IntValue.y : prop.vector2Value.y; + + var minLimit = ((MinMaxSliderAttribute)attribute).min; + var maxLimit = ((MinMaxSliderAttribute)attribute).max; + + + + + EditorGUI.PrefixLabel(rect, label); + + + EditorGUI.BeginProperty(rect, label, prop); + + if (sliderRect.width > 14) + { + EditorGUI.BeginChangeCheck(); + + EditorGUI.MinMaxSlider(sliderRect, ref min, ref max, minLimit, maxLimit); + + if (EditorGUI.EndChangeCheck()) + { + var abs = (maxLimit - minLimit).Abs(); + + var decimals = abs > 190 ? + 0 : abs > 19 ? + 1 : abs > 1.9f ? + 2 : + 3; + + min = (float)System.Math.Round(min, decimals); + max = (float)System.Math.Round(max, decimals); + + // same rounding logic as for [Range] + } + + } + + min = EditorGUI.DelayedFloatField(minFieldRect, min).Max(minLimit).Min(maxLimit); + max = EditorGUI.DelayedFloatField(maxFieldRect, max).Max(min).Max(minLimit).Min(maxLimit); + + if (isInt) + prop.vector2IntValue = new Vector2Int(min.RoundToInt(), max.RoundToInt()); + else + prop.vector2Value = new Vector2(min, max); + + EditorGUI.EndProperty(); + + + } + } + +} +#endif \ No newline at end of file diff --git a/Assets/OtherAssets/DEV/PAID/vInspector/VInspectorDrawers.cs.meta b/Assets/OtherAssets/DEV/PAID/vInspector/VInspectorDrawers.cs.meta new file mode 100644 index 0000000..8544e32 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vInspector/VInspectorDrawers.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 481331f9e0bfe494f98ee0fc8ebfeb84 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/OtherAssets/DEV/PAID/vInspector/VInspectorEditor.cs b/Assets/OtherAssets/DEV/PAID/vInspector/VInspectorEditor.cs new file mode 100644 index 0000000..ef0d347 --- /dev/null +++ b/Assets/OtherAssets/DEV/PAID/vInspector/VInspectorEditor.cs @@ -0,0 +1,2105 @@ +#if UNITY_EDITOR +using System.Collections; +using System.Linq; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UIElements; +using UnityEditor.UIElements; +using System.Reflection; +using UnityEditor; +using Type = System.Type; +using Attribute = System.Attribute; +using static VInspector.VInspector; +using static VInspector.VInspectorState; +using static VInspector.Libs.VUtils; +using static VInspector.Libs.VGUI; +// using static VTools.VDebug; + + +namespace VInspector +{ + class VInspectorEditor + { + + public void OnGUI() + { + var drawingTabPath = ""; + var drawingFoldoutPath = ""; + var hideField = false; + var disableField = false; + var noVariablesShown = true; + var selectedTabPath = rootTab.GetSelectedTabPath(); + + + + void drawMember(MemberInfo memberInfo) + { + + void ifs() + { + var endIfAttribute = memberInfo.GetCustomAttributeCached(); + + if (endIfAttribute != null) hideField = disableField = false; + + + var ifAttribute = memberInfo.GetCustomAttributeCached(); + + if (ifAttribute is HideIfAttribute) hideField = ifAttribute.Evaluate(target); + if (ifAttribute is ShowIfAttribute) hideField = !ifAttribute.Evaluate(target); + if (ifAttribute is DisableIfAttribute) disableField = ifAttribute.Evaluate(target); + if (ifAttribute is EnableIfAttribute) disableField = !ifAttribute.Evaluate(target); + + } + void tabs() + { + void drawSubtabs(Tab tab) + { + if (!tab.subtabs.Any()) return; + + + Space(noVariablesShown ? 2 : 6); + + var selName = TabsMultiRow(tab.selectedSubtab.name, false, 24, tab.subtabs.Select(r => r.name).ToArray()); + + Space(5); + + + if (selName != tab.selectedSubtab.name) + { + tab.selectedSubtabIndex = tab.subtabs.IndexOfFirst(r => r.name == selName); + + selectedTabPath = rootTab.GetSelectedTabPath(); + + } + + + GUI.backgroundColor = Color.white; + + tab.subtabsDrawn = true; + + } + + void endTab() + { + if (memberInfo.GetCustomAttributeCached() is not EndTabAttribute endTabAttribute) return; + + drawingTabPath = ""; + drawingFoldoutPath = ""; + hideField = false; + disableField = false; + + } + void beginTab() + { + if (memberInfo.GetCustomAttributeCached() is not TabAttribute tabAttribute) return; + + drawingTabPath = tabAttribute.name; + drawingFoldoutPath = ""; + hideField = false; + disableField = false; + + } + void ensureNeededTabsDrawn() + { + if (!selectedTabPath.StartsWith(drawingTabPath)) return; + + + var curTab = rootTab; + + foreach (var name in drawingTabPath.Split('/').Where(r => r != "")) + { + if (!curTab.subtabsDrawn) + drawSubtabs(curTab); + + curTab = curTab.subtabs.Find(r => r.name == name); + + } + + } + + + endTab(); + beginTab(); + ensureNeededTabsDrawn(); + + } + void foldouts() + { + bool drawFoldout(string name, bool isExpanded, Foldout foldout) + { + var controlRect = EditorGUILayout.GetControlRect(); + var fullRect = controlRect.AddWidthFromRight(-15 * EditorGUI.indentLevel).AddWidthFromRight(18).AddWidth(3); + + var controlId = EditorGUIUtility.GetControlID(FocusType.Passive); + var isPressed = EditorGUIUtility.hotControl == controlId; + + void name_() + { + var labelRect = controlRect.AddWidthFromRight(-15 * EditorGUI.indentLevel); + + + SetLabelBold(); + SetLabelFontSize(12); + + GUI.Label(labelRect, name); + + ResetLabelStyle(); + + + } + void triangle() + { + // return; + // if (!curEvent.isRepaint) return; + + var unityFoldoutRect = controlRect.MoveX(-.5f); + + isExpanded = EditorGUI.Foldout(unityFoldoutRect, isExpanded, ""); + + } + void highlight() + { + var hoveredColor = Greyscale(1, .06f); + var pressedColor = Greyscale(1, .04f); + + + fullRect.MarkInteractive(); + + if (isPressed) + fullRect.Draw(pressedColor); + + else if (fullRect.IsHovered()) + fullRect.Draw(hoveredColor); + + } + + void mouseDown() + { + if (!curEvent.isMouseDown) return; + if (!fullRect.IsHovered()) return; + + EditorGUIUtility.hotControl = controlId; + + curEvent.Use(); + + } + void mouseUp() + { + if (!curEvent.isMouseUp) return; + if (!isPressed) return; + + if (fullRect.IsHovered()) + isExpanded = !isExpanded; + + EditorGUIUtility.hotControl = 0; + + EditorGUIUtility.keyboardControl = 0; + + curEvent.Use(); + + } + + + // background(); + name_(); + triangle(); + highlight(); + + mouseDown(); + mouseUp(); + + + return isExpanded; + + + } + void drawButtons(Foldout foldout) + { + var noButtonsToShow = true; + + Space(10); + + foreach (var button in foldout.buttons) + drawButton(button, ref noButtonsToShow); + + + if (noButtonsToShow) + Space(-10); + else + Space(5); + + } + + void beginFoldout(string name) + { + if (!rootFoldout.IsSubfoldoutContentVisible(drawingFoldoutPath)) return; + + + + updateIndentLevel(); + + drawingFoldoutPath = drawingFoldoutPath.CombinePath(name); + + + + var foldout = rootFoldout.GetSubfoldout(drawingFoldoutPath); + + foldout.isExpanded = drawFoldout(foldout.name, foldout.isExpanded, foldout); + + } + void endFoldout() + { + var foldout = rootFoldout.GetSubfoldout(drawingFoldoutPath); + + if (foldout.isExpanded) + drawButtons(foldout); + + drawingFoldoutPath = drawingFoldoutPath.HasParentPath() ? drawingFoldoutPath.GetParentPath() : ""; + + } + + + + var newFoldoutPath = drawingFoldoutPath; + + if (memberInfo.GetCustomAttributeCached() is not null) + newFoldoutPath = ""; + + if (memberInfo.GetCustomAttributeCached() is FoldoutAttribute foldoutAttribute) + newFoldoutPath = foldoutAttribute.name; + + if (newFoldoutPath == drawingFoldoutPath) return; + + + + + var drawingPathNames = drawingFoldoutPath.Split('/').Where(r => r != "").ToList(); + + var newPathNames = newFoldoutPath.Split('/').Where(r => r != "").ToList(); + + var sharedPathNames = new List(); + + for (int i = 0; i < drawingPathNames.Count && i < newPathNames.Count; i++) + if (drawingPathNames[i] == newPathNames[i]) + sharedPathNames.Add(drawingPathNames[i]); + else break; + + + + + for (int i = drawingPathNames.Count; i > sharedPathNames.Count; i--) + endFoldout(); + + for (int i = sharedPathNames.Count; i < newPathNames.Count; i++) + beginFoldout(newPathNames[i]); + + } + + void updateIndentLevel() + { + var prev = EditorGUI.indentLevel; + + EditorGUI.indentLevel = baseIndentLevel + drawingFoldoutPath.Split('/').Where(r => r != "").Count(); + + if (prev > EditorGUI.indentLevel) + Space(7); + } + + void field() + { + var fieldInfo = memberInfo as FieldInfo; + var propertyInfo = memberInfo as PropertyInfo; + + var isSerialized = serializedProperties_byMemberInfos.TryGetValue(memberInfo, out var serializedProeprty); + var isNestedEditor = isSerialized && HasVInspectorAttribtues(fieldInfo.FieldType); + var isResettable = isSerialized && VInspectorResettableVariables.IsResettable(fieldInfo); + var isReadOnly = Attribute.IsDefined(memberInfo, typeof(ReadOnlyAttribute)) || memberInfo is PropertyInfo && !propertyInfo.CanWrite; + + void serialized_default() + { + if (!isSerialized) return; + if (isNestedEditor) return; + if (isResettable) return; + if (Attribute.IsDefined(memberInfo, typeof(ButtonAttribute))) return; + + EditorGUILayout.PropertyField(serializedProeprty, true); + + } + void serialized_resettable() + { + if (!isSerialized) return; + if (isNestedEditor) return; + if (!isResettable) return; + + + var lastControlId = typeof(EditorGUIUtility).GetFieldValue("s_LastControlID"); + + + + if (!curEvent.isRepaint) + if (fieldRects_byLastControlId.ContainsKey(lastControlId)) + VInspectorResettableVariables.ResetButtonGUI(fieldRects_byLastControlId[lastControlId], serializedProeprty, fieldInfo, targets); + + + + EditorGUILayout.PropertyField(serializedProeprty, true); + + if (curEvent.isRepaint) + fieldRects_byLastControlId[lastControlId] = lastRect.AddWidthFromRight(-EditorGUIUtility.labelWidth - 2).SetHeightFromBottom(EditorGUIUtility.singleLineHeight); + + + + if (curEvent.isRepaint) + VInspectorResettableVariables.ResetButtonGUI(fieldRects_byLastControlId[lastControlId], serializedProeprty, fieldInfo, targets); + + + + } + void serialized_nestedEditor() + { + if (!isSerialized) return; + if (!isNestedEditor) return; + + EditorGUILayout.PropertyField(serializedProeprty, false); + + + if (!serializedProeprty.isExpanded) return; + + if (!nestedEditors_byPropertyPath.TryGetValue(serializedProeprty.propertyPath, out var nestedEditor)) + nestedEditor = nestedEditors_byPropertyPath[serializedProeprty.propertyPath] = new VInspectorEditor(rootPropertyGetter: () => serializedProeprty, + targetsGetter: () => targets.Select(r => fieldInfo.GetValue(r))); + nestedEditor.baseIndentLevel = EditorGUI.indentLevel + 1; + nestedEditor.OnGUI(); + + } + void nonSerialized_field() + { + if (isSerialized) return; + if (!Attribute.IsDefined(memberInfo, typeof(ShowInInspectorAttribute))) return; + if (memberInfo is not FieldInfo fieldInfo) return; + + + var type = fieldInfo.FieldType; + var name = fieldInfo.Name.FormatVariableName(); + + var curValue = fieldInfo.GetValue(fieldInfo.IsStatic ? null : target); + + var newValue = curValue; + + if (type == typeof(int)) newValue = EditorGUILayout.IntField(name, (int)curValue); + else if (type == typeof(float)) newValue = EditorGUILayout.FloatField(name, (float)curValue); + else if (type == typeof(double)) newValue = EditorGUILayout.DoubleField(name, (float)curValue); + else if (type == typeof(string)) newValue = EditorGUILayout.TextField(name, (string)curValue); + else if (type == typeof(bool)) newValue = EditorGUILayout.Toggle(name, (bool)curValue); + else if (type == typeof(Vector2)) newValue = EditorGUILayout.Vector2Field(name, (Vector2)curValue); + else if (type == typeof(Vector3)) newValue = EditorGUILayout.Vector3Field(name, (Vector3)curValue); + else if (type == typeof(Vector4)) newValue = EditorGUILayout.Vector4Field(name, (Vector4)curValue); + else if (type == typeof(Color)) newValue = EditorGUILayout.ColorField(name, (Color)curValue); + else if (type == typeof(Rect)) newValue = EditorGUILayout.RectField(name, (Rect)curValue); + else if (type == typeof(RectInt)) newValue = EditorGUILayout.RectIntField(name, (RectInt)curValue); + else if (type == typeof(Bounds)) newValue = EditorGUILayout.BoundsField(name, (Bounds)curValue); + else if (type == typeof(BoundsInt)) newValue = EditorGUILayout.BoundsIntField(name, (BoundsInt)curValue); + else if (type == typeof(Vector2Int)) newValue = EditorGUILayout.Vector2IntField(name, (Vector2Int)curValue); + else if (type == typeof(Vector3Int)) newValue = EditorGUILayout.Vector3IntField(name, (Vector3Int)curValue); + else if (type.IsEnum) newValue = EditorGUILayout.EnumPopup(name, (System.Enum)curValue); + else if (typeof(Object).IsAssignableFrom(type)) newValue = EditorGUILayout.ObjectField(name, (Object)curValue, type, true); + else EditorGUILayout.TextField(name, curValue?.ToString()); + + + fieldInfo.SetValue(fieldInfo.IsStatic ? null : target, newValue); + + noVariablesShown = false; + + } + void nonSerialized_property() + { + if (isSerialized) return; + if (!Attribute.IsDefined(memberInfo, typeof(ShowInInspectorAttribute))) return; + if (memberInfo is not PropertyInfo propertyInfo) return; + + + var type = propertyInfo.PropertyType; + var name = propertyInfo.Name.FormatVariableName(); + + var curValue = propertyInfo.GetValue(propertyInfo.GetAccessors(true).First().IsStatic ? null : target); + + var newValue = curValue; + + if (type == typeof(int)) newValue = EditorGUILayout.IntField(name, (int)curValue); + else if (type == typeof(float)) newValue = EditorGUILayout.FloatField(name, (float)curValue); + else if (type == typeof(double)) newValue = EditorGUILayout.DoubleField(name, (float)curValue); + else if (type == typeof(string)) newValue = EditorGUILayout.TextField(name, (string)curValue); + else if (type == typeof(bool)) newValue = EditorGUILayout.Toggle(name, (bool)curValue); + else if (type == typeof(Vector2)) newValue = EditorGUILayout.Vector2Field(name, (Vector2)curValue); + else if (type == typeof(Vector3)) newValue = EditorGUILayout.Vector3Field(name, (Vector3)curValue); + else if (type == typeof(Vector4)) newValue = EditorGUILayout.Vector4Field(name, (Vector4)curValue); + else if (type == typeof(Color)) newValue = EditorGUILayout.ColorField(name, (Color)curValue); + else if (type == typeof(Rect)) newValue = EditorGUILayout.RectField(name, (Rect)curValue); + else if (type == typeof(RectInt)) newValue = EditorGUILayout.RectIntField(name, (RectInt)curValue); + else if (type == typeof(Bounds)) newValue = EditorGUILayout.BoundsField(name, (Bounds)curValue); + else if (type == typeof(BoundsInt)) newValue = EditorGUILayout.BoundsIntField(name, (BoundsInt)curValue); + else if (type == typeof(Vector2Int)) newValue = EditorGUILayout.Vector2IntField(name, (Vector2Int)curValue); + else if (type == typeof(Vector3Int)) newValue = EditorGUILayout.Vector3IntField(name, (Vector3Int)curValue); + else if (type.IsEnum) newValue = EditorGUILayout.EnumPopup(name, (System.Enum)curValue); + else if (typeof(Object).IsAssignableFrom(type)) newValue = EditorGUILayout.ObjectField(name, (Object)curValue, type, true); + else EditorGUILayout.TextField(name, curValue?.ToString()); + + + if (propertyInfo.CanWrite && !Attribute.IsDefined(propertyInfo, typeof(ReadOnlyAttribute))) + if (!object.Equals(newValue, curValue)) + propertyInfo.SetValue(propertyInfo.GetAccessors(true).First().IsStatic ? null : target, newValue); + + noVariablesShown = false; + + } + + void invokeValueChangedCallbacks() + { + if (memberInfo is not FieldInfo fieldInfo) return; + + if (!valueChangedCallbacks_byFieldInfo.TryGetValue(fieldInfo, out var methodInfos)) + if (!valueChangedCallbacks_byGroupPath.TryGetValue(drawingTabPath, out methodInfos)) + if (!valueChangedCallbacks_byGroupPath.TryGetValue(drawingFoldoutPath, out methodInfos)) return; + + + void invokeCallbacks() => methodInfos.ForEach(r => targets.ForEach(rr => r.Invoke(rr, null))); + + // invokeCallbacks(); + AbstractEditor.toCallAfterModifyingSO += invokeCallbacks; + + valueChangedCallbacks_byUndoPosition[EditorUtils.GetCurrendUndoGroupIndex()] = invokeCallbacks; + + } + + + SetGUIEnabled(!isReadOnly && !disableField); + + EditorGUI.BeginChangeCheck(); + + serialized_default(); + serialized_resettable(); + serialized_nestedEditor(); + nonSerialized_field(); + nonSerialized_property(); + + if (EditorGUI.EndChangeCheck()) + invokeValueChangedCallbacks(); + + ResetGUIEnabled(); + + } + + + + + + ifs(); + + if (hideField) return; + + GUI.enabled = !disableField; + + + + + tabs(); + + if (selectedTabPath != drawingTabPath && !selectedTabPath.StartsWith(drawingTabPath + "/") && drawingTabPath != "") return; + + noVariablesShown = false; + + + + + foldouts(); + + if (!rootFoldout.IsSubfoldoutContentVisible(drawingFoldoutPath)) return; + + + + + + updateIndentLevel(); + + field(); + + } + + void drawButton(Button button, ref bool noButtonsShown) + { + if (button.tabAttribute != null && !selectedTabPath.StartsWith(button.tabAttribute.name)) return; + + if (button.ifAttribute is HideIfAttribute && button.ifAttribute.Evaluate(target)) return; + if (button.ifAttribute is ShowIfAttribute && !button.ifAttribute.Evaluate(target)) return; + + + + var prevGuiEnabled = GUI.enabled; + if (button.ifAttribute is DisableIfAttribute && button.ifAttribute.Evaluate(target)) GUI.enabled = false; + if (button.ifAttribute is EnableIfAttribute && !button.ifAttribute.Evaluate(target)) GUI.enabled = false; + + + + + Rect buttonRect; + Color color = Color.white; + + void set_buttonRect() + { + GUILayout.BeginHorizontal(); + + GUILayout.Space(EditorGUI.indentLevel * 15); + + buttonRect = ExpandWidthLabelRect(height: button.size); + + GUILayout.EndHorizontal(); + + } + void set_color() + { + if (button.color.ToLower() == "grey" || button.color.ToLower() == "gray") return; + + + var hue = 0f; + var saturation = .6f; + var lightness = isDarkTheme ? .57f : .64f; + + if (button.color.ToLower() == "red") hue = 0; + else if (button.color.ToLower() == "orange") hue = .08f; + else if (button.color.ToLower() == "yellow") hue = .13f; + else if (button.color.ToLower() == "green") { hue = .32f; saturation = .49f; lightness = isDarkTheme ? .56f : .6f; } + else if (button.color.ToLower() == "blue") hue = .55f; + else if (button.color.ToLower() == "pink") hue = .94f; + else return; + + + + color = ColorUtils.HSLToRGB(hue, saturation, lightness); + + color *= 2f; + color.a = 1; + + } + + void argumentsBackground() + { + if (!button.parameterInfos.Any()) return; + if (!button.isExpanded) return; + if (!curEvent.isRepaint) return; + + + var backgroundColor = Greyscale(isDarkTheme ? .27f : .83f); + var outlineColor = Greyscale(isDarkTheme ? .15f : .65f); + var cornerRadius = 3; + + var backgroundRect = buttonRect.AddHeight(button.parameterInfos.Count * (EditorGUIUtility.singleLineHeight + 2) + 8); + + backgroundRect.DrawRounded(outlineColor, cornerRadius); + backgroundRect.Resize(1).DrawRounded(backgroundColor, cornerRadius - 1); + + } + void buttonItself() + { + var prevBackgroundColor = GUI.backgroundColor; + GUI.backgroundColor = button.isPressed() ? GUIColors.pressedButtonBackground : color; + + var clicked = GUI.Button(buttonRect, button.name); + + GUI.backgroundColor = prevBackgroundColor; + + + + if (!clicked) return; + + foreach (var targetObject in rootProperty.serializedObject.targetObjects) + targetObject.RecordUndo(); + + foreach (var target in targets) + button.action(target); + + foreach (var targetObject in rootProperty.serializedObject.targetObjects) + targetObject.Dirty(); + + } + void expandButton() + { + if (!button.parameterInfos.Any()) return; + + var expandButtonRect = buttonRect.SetWidth(24).MoveX(1); + + var colorNormal = Greyscale(isDarkTheme ? (buttonRect.IsHovered() ? .85f : .8f) : .7f); + var colorHovered = Greyscale(isDarkTheme ? 10f : 0f, 10f); + var colorPressed = Greyscale(.85f); + + var iconSize = 12; + + + if (!IconButton(expandButtonRect, button.isExpanded ? "d_IN_foldout_act_on" : "d_IN_foldout_act", iconSize, colorNormal, colorHovered, colorPressed)) return; + + button.isExpanded = !button.isExpanded; + + GUIUtility.keyboardControl = 0; + + } + void parameters() + { + if (!button.isExpanded) return; + if (!button.parameterInfos.Any()) return; + + void parameter(int i) + { + var type = button.parameterInfos[i].ParameterType; + var name = button.parameterInfos[i].Name.FormatVariableName(); + + var curValue = button.GetParameterValue(i); + + var newValue = curValue; + + if (type == typeof(int)) newValue = EditorGUILayout.IntField(name, (int)curValue); + else if (type == typeof(float)) newValue = EditorGUILayout.FloatField(name, (float)curValue); + else if (type == typeof(double)) newValue = EditorGUILayout.DoubleField(name, (float)curValue); + else if (type == typeof(string)) newValue = EditorGUILayout.TextField(name, (string)curValue); + else if (type == typeof(bool)) newValue = EditorGUILayout.Toggle(name, (bool)curValue); + else if (type == typeof(Vector2)) newValue = EditorGUILayout.Vector2Field(name, (Vector2)curValue); + else if (type == typeof(Vector3)) newValue = EditorGUILayout.Vector3Field(name, (Vector3)curValue); + else if (type == typeof(Vector4)) newValue = EditorGUILayout.Vector4Field(name, (Vector4)curValue); + else if (type == typeof(Color)) newValue = EditorGUILayout.ColorField(name, (Color)curValue); + else if (type == typeof(Rect)) newValue = EditorGUILayout.RectField(name, (Rect)curValue); + else if (type == typeof(RectInt)) newValue = EditorGUILayout.RectIntField(name, (RectInt)curValue); + else if (type == typeof(Bounds)) newValue = EditorGUILayout.BoundsField(name, (Bounds)curValue); + else if (type == typeof(BoundsInt)) newValue = EditorGUILayout.BoundsIntField(name, (BoundsInt)curValue); + else if (type == typeof(Vector2Int)) newValue = EditorGUILayout.Vector2IntField(name, (Vector2Int)curValue); + else if (type == typeof(Vector3Int)) newValue = EditorGUILayout.Vector3IntField(name, (Vector3Int)curValue); + else if (type.IsEnum) newValue = EditorGUILayout.EnumPopup(name, (System.Enum)curValue); + else if (typeof(Object).IsAssignableFrom(type)) newValue = EditorGUILayout.ObjectField(name, (Object)curValue, type, true); + else EditorGUILayout.PrefixLabel(name); + + + button.SetParameterValue(i, newValue); + + } + + + + BeginIndent(7); + Space(1); + + for (int i = 0; i < button.parameterInfos.Count; i++) + parameter(i); + + Space(11); + EndIndent(5); + + + } + + + GUILayout.Space(button.space - 2); + + set_buttonRect(); + set_color(); + argumentsBackground(); + + if (!curEvent.isRepaint) + expandButton(); + + buttonItself(); + + if (curEvent.isRepaint) + expandButton(); + + parameters(); + + if (button.isExpanded) + Space(6); + + + + + GUI.enabled = prevGuiEnabled; + + noButtonsShown = false; + + } + + + void scriptField() + { + if (scriptFieldProperty == null) return; + if (VInspectorMenu.hideScriptFieldEnabled) return; + + using (new EditorGUI.DisabledScope(true)) + EditorGUILayout.PropertyField(scriptFieldProperty); + + } + void topMargin() + { + if (scriptFieldProperty == null) return; + if (!VInspectorMenu.hideScriptFieldEnabled) return; + + Space(2); + + } + + void members() + { + EditorGUI.indentLevel = baseIndentLevel; + + foreach (var memberInfo in drawableMemberLists_byTargetType[targetType]) + drawMember(memberInfo); + + EditorGUI.indentLevel = baseIndentLevel; + + + } + void noVariablesToShow() + { + if (!noVariablesShown) return; + + using (new EditorGUI.DisabledScope(true)) + GUILayout.Label("No variables to show"); + + + } + void endUnendedFoldouts() + { + if (drawingFoldoutPath == "") return; + + void drawButtons(Foldout foldout) + { + var noButtonsToShow = true; + + Space(10); + + foreach (var button in foldout.buttons) + drawButton(button, ref noButtonsToShow); + + + if (noButtonsToShow) + Space(-10); + else + Space(5); + + } + + while (drawingFoldoutPath != "") + { + var foldout = rootFoldout.GetSubfoldout(drawingFoldoutPath); + + EditorGUI.indentLevel = baseIndentLevel + drawingFoldoutPath.Split('/').Where(r => r != "").Count(); + + if (rootFoldout.IsSubfoldoutContentVisible(drawingFoldoutPath)) + drawButtons(foldout); + + drawingFoldoutPath = drawingFoldoutPath.HasParentPath() ? drawingFoldoutPath.GetParentPath() : ""; + + } + + EditorGUI.indentLevel = 0; + + } + + void buttons() + { + GUI.enabled = true; + + var noButtonsToShow = true; + + foreach (var button in this.buttons) + drawButton(button, ref noButtonsToShow); + + + if (noButtonsToShow) + Space(-17); + + } + + + + + rootTab.ResetSubtabsDrawn(); + + scriptField(); + topMargin(); + + members(); + noVariablesToShow(); + endUnendedFoldouts(); + + Space(16); + buttons(); + + Space(4); + + } + + public int baseIndentLevel; + + public SerializedProperty rootProperty => rootPropertyGetter.Invoke(); + public IEnumerable targets => targetsGetter.Invoke(); + public object target => targets.FirstOrDefault(); + + public Type targetType => _targetType ??= target?.GetType(); + public Type _targetType; + + static Dictionary fieldRects_byLastControlId = new(); + + + + + + + + public VInspectorEditor(System.Func rootPropertyGetter, System.Func> targetsGetter) + { + this.rootPropertyGetter = rootPropertyGetter; + this.targetsGetter = targetsGetter; + + + void createTabs() + { + void setupTab(Tab tab, IEnumerable allSubtabPaths) + { + void refreshSubtabs() + { + var names = allSubtabPaths.Select(r => r.Split('/').First()).ToList(); + + foreach (var name in names) + if (!tab.subtabs.Any(r => r.name == name)) + tab.subtabs.Add(new Tab() { name = name }); + + foreach (var subtab in tab.subtabs.ToList()) + if (!names.Any(r => r == subtab.name)) + tab.subtabs.Remove(subtab); + + tab.subtabs.SortBy(r => names.IndexOf(r.name)); + + } + void setupSubtabs() + { + foreach (var subtab in tab.subtabs) + setupTab(subtab, allSubtabPaths.Where(r => r.StartsWith(subtab.name + "/")).Select(r => r.Remove(subtab.name + "/")).ToList()); + } + + refreshSubtabs(); + setupSubtabs(); + + } + + void findAttributes() + { + if (tabAttributes_byTargetType.ContainsKey(targetType)) return; + + var attributes = TypeCache.GetFieldsWithAttribute() + .Where(r => r.DeclaringType.IsAssignableFrom(targetType)) + .OrderBy(r => r.MetadataToken) + .Select(r => r.GetCustomAttributeCached()); + + tabAttributes_byTargetType[targetType] = attributes.ToList(); + + } + void createTabs() + { + rootTab = new Tab() { isRootTab = true }; + + var allTabPaths = tabAttributes_byTargetType[targetType].Select(r => r.name); + + setupTab(rootTab, allTabPaths); + + } + + + findAttributes(); + createTabs(); + + } + void createFoldouts() + { + void setupFoldout(Foldout foldout, IEnumerable allSubfoldoutPaths) + { + void refreshSubfoldouts() + { + var names = allSubfoldoutPaths.Select(r => r.Split('/').First()).ToList(); + + foreach (var name in names) + if (foldout.subfoldouts.Find(r => r.name == name) == null) + foldout.subfoldouts.Add(new Foldout() { name = name }); + + foreach (var subtab in foldout.subfoldouts.ToList()) + if (names.Find(r => r == subtab.name) == null) + foldout.subfoldouts.Remove(subtab); + + foldout.subfoldouts.SortBy(r => names.IndexOf(r.name)); + + } + void setupSubfoldouts() + { + foreach (var subtab in foldout.subfoldouts) + setupFoldout(subtab, allSubfoldoutPaths.Where(r => r.StartsWith(subtab.name + "/")).Select(r => r.Remove(subtab.name + "/")).ToList()); + } + + refreshSubfoldouts(); + setupSubfoldouts(); + + } + + void findAttributes() + { + if (foldoutAttributes_byTargetType.ContainsKey(targetType)) return; + + var attributes = TypeCache.GetFieldsWithAttribute() + .Where(r => r.DeclaringType.IsAssignableFrom(targetType)) + .OrderBy(r => r.MetadataToken) + .Select(r => r.GetCustomAttributeCached()); + + foldoutAttributes_byTargetType[targetType] = attributes.ToList(); + + } + void createFoldouts() + { + rootFoldout = new Foldout() { isRootFoldout = true }; + + var allFoldoutPaths = foldoutAttributes_byTargetType[targetType].Select(r => r.name); + + setupFoldout(rootFoldout, allFoldoutPaths); + + } + + + findAttributes(); + createFoldouts(); + + } + void createButtons() + { + void createButton(MemberInfo member, ButtonAttribute buttonAttribute) + { + var button = new Button(); + + button.size = buttonAttribute.size; + button.space = buttonAttribute.space; + button.color = buttonAttribute.color; + + + if (member.GetCustomAttributeCached() is TabAttribute tabAttribute) + button.tabAttribute = tabAttribute; + + if (member.GetCustomAttributeCached() is FoldoutAttribute foldoutAttribute) + button.foldoutAttribute = foldoutAttribute; + + if (member.GetCustomAttributeCached() is IfAttribute ifAttribute) + button.ifAttribute = ifAttribute; + + + if (member is FieldInfo field && field.FieldType == typeof(bool)) + { + var fieldTarget = field.IsStatic ? null : target; + + button.action = (o) => field.SetValue(o, !(bool)field.GetValue(o)); + button.name = buttonAttribute.name != "" ? buttonAttribute.name : field.Name.FormatVariableName(false); + button.isPressed = () => (bool)field.GetValue(fieldTarget); + + } + + if (member is MethodInfo method) + { + if (method.DeclaringType.IsGenericType) + method = targetType.GetMethodInfo(method.Name, method.GetParameters().Select(r => r.ParameterType).ToArray()) ?? method; // fixes wrong method when targetType inherits from method.DeclaringType and method.DeclaringType is generic + + if (!method.GetParameters().Any()) + { + var methodTarget = method.IsStatic ? null : target; + + button.action = (methodTarget) => method.Invoke(methodTarget, null); + button.name = buttonAttribute.name != "" ? buttonAttribute.name : method.Name.FormatVariableName(false); + button.isPressed = () => false; + + } + else + { + var methodTarget = method.IsStatic ? null : target; + + button.action = (methodTarget) => method.Invoke(methodTarget, Enumerable.Range(0, button.parameterInfos.Count).Select(i => button.GetParameterValue(i)).ToArray()); + button.name = buttonAttribute.name != "" ? buttonAttribute.name : method.Name.FormatVariableName(false); + button.isPressed = () => false; + + button.parameterInfos = method.GetParameters().ToList(); + + } + + } + + + + if (button.action != null) + if (button.foldoutAttribute != null && rootFoldout.GetSubfoldout(button.foldoutAttribute.name) is Foldout foldout) + foldout.buttons.Add(button); + else + this.buttons.Add(button); + + } + + void findFields() + { + if (fieldsWithButtonAttributes_byTargetType.ContainsKey(targetType)) return; + + var fields = TypeCache.GetFieldsWithAttribute() + .Where(r => r.DeclaringType.IsAssignableFrom(targetType)) + .OrderBy(r => r.MetadataToken); + + fieldsWithButtonAttributes_byTargetType[targetType] = fields.ToList(); + + } + void findMethods() + { + if (methodsWithButtonAttributes_byTargetType.ContainsKey(targetType)) return; + + var methods = TypeCache.GetMethodsWithAttribute() + .Where(r => r.DeclaringType.IsAssignableFrom(targetType) || (targetType.BaseType.IsGenericType && targetType.BaseType.GetGenericTypeDefinition() == r.DeclaringType)) + .OrderBy(r => r.MetadataToken); + + methodsWithButtonAttributes_byTargetType[targetType] = methods.ToList(); + + } + void createButtons() + { + this.buttons = new List