├── Editor ├── Scripts │ ├── AssetItemViewActionManipulator.cs.meta │ ├── AssetHandle.cs.meta │ ├── AssetCategory.cs.meta │ ├── AssetItemView.cs.meta │ ├── UrlEditWindow.cs.meta │ ├── MenuItemSelectWindow.cs.meta │ ├── AssetQuickAccessLocalCache.cs.meta │ ├── DragAndDropInManipulator.cs.meta │ ├── AssetCategory.cs │ ├── AssetQuickAccessWindow.cs.meta │ ├── DragAndDropInManipulator.cs │ ├── AssetItemViewActionManipulator.cs │ ├── AssetQuickAccessLocalCache.cs │ ├── MenuItemSelectWindow.cs │ ├── UrlEditWindow.cs │ ├── AssetItemView.cs │ ├── AssetHandle.cs │ └── AssetQuickAccessWindow.cs ├── Scripts.meta ├── GBG.AssetQuickAccess.Editor.asmdef.meta └── GBG.AssetQuickAccess.Editor.asmdef ├── Documents~ └── imgs │ └── img_sample_asset_quick_access_window.png ├── LICENSE.meta ├── README.md.meta ├── README_CN.md.meta ├── package.json.meta ├── Editor.meta ├── package.json ├── LICENSE ├── .gitignore ├── README_CN.md └── README.md /Editor/Scripts/AssetItemViewActionManipulator.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5b1ed5c5729e4651a9687176f66fb200 3 | timeCreated: 1721629852 -------------------------------------------------------------------------------- /Documents~/imgs/img_sample_asset_quick_access_window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SolarianZ/UnityAssetQuickAccessTool/main/Documents~/imgs/img_sample_asset_quick_access_window.png -------------------------------------------------------------------------------- /LICENSE.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bd0c26f4df2b6374981ffd6f4cc7183d 3 | DefaultImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /README.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: be1985cf74a3fb241b4fe6e25cb24673 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /README_CN.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2e3e35aecb8749e40b5f1bb3fae7e303 3 | TextScriptImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /package.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: aaa5bf70b2b99374fb7fd240dbc0301a 3 | PackageManifestImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: aa04e3372499ac64da6c58bb25ba31fa 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/Scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 7ab5f00a6026e824b8fd61de7b91b2f8 3 | folderAsset: yes 4 | DefaultImporter: 5 | externalObjects: {} 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Editor/GBG.AssetQuickAccess.Editor.asmdef.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ec50623cda89aa44eba9fddb76812c59 3 | AssemblyDefinitionImporter: 4 | externalObjects: {} 5 | userData: 6 | assetBundleName: 7 | assetBundleVariant: 8 | -------------------------------------------------------------------------------- /Editor/Scripts/AssetHandle.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 582bc6cc7fa665f46b7697b488d73fe2 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Scripts/AssetCategory.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: be594413c35229948b7a863b51311562 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Scripts/AssetItemView.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: cfeb45524bfb4f548b24303e5560fee4 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Scripts/UrlEditWindow.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e5b433482303b604093c16fa5eb5ceda 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Scripts/MenuItemSelectWindow.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1b300aeb10fd3e843bb35337016c06ea 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Scripts/AssetQuickAccessLocalCache.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0d2357233d19e6444a266846b97cdb8f 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Scripts/DragAndDropInManipulator.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 828d947f4e82cb44e89e442bbc18cb8c 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: [] 7 | executionOrder: 0 8 | icon: {instanceID: 0} 9 | userData: 10 | assetBundleName: 11 | assetBundleVariant: 12 | -------------------------------------------------------------------------------- /Editor/Scripts/AssetCategory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace GBG.AssetQuickAccess.Editor 4 | { 5 | [Flags] 6 | public enum AssetCategory 7 | { 8 | None = 0, 9 | ProjectAsset = 1, 10 | SceneObject = 2, 11 | ExternalFile = 4, 12 | Url = 8, 13 | MenuItem = 16, 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Editor/Scripts/AssetQuickAccessWindow.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3c2975ebd3ab8e444b5d7551e391666a 3 | MonoImporter: 4 | externalObjects: {} 5 | serializedVersion: 2 6 | defaultReferences: 7 | - m_ViewDataDictionary: {instanceID: 0} 8 | - testTex: {fileID: 2800000, guid: 062061b21fd6ee1409a9cac0dbb9aff0, type: 3} 9 | executionOrder: 0 10 | icon: {instanceID: 0} 11 | userData: 12 | assetBundleName: 13 | assetBundleVariant: 14 | -------------------------------------------------------------------------------- /Editor/GBG.AssetQuickAccess.Editor.asmdef: -------------------------------------------------------------------------------- 1 | { 2 | "name": "GBG.AssetQuickAccess.Editor", 3 | "rootNamespace": "GBG.AssetQuickAccess.Editor", 4 | "references": [], 5 | "includePlatforms": [ 6 | "Editor" 7 | ], 8 | "excludePlatforms": [], 9 | "allowUnsafeCode": false, 10 | "overrideReferences": false, 11 | "precompiledReferences": [], 12 | "autoReferenced": true, 13 | "defineConstraints": [], 14 | "versionDefines": [], 15 | "noEngineReferences": false 16 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "com.greenbamboogames.assetquickaccess", 3 | "version": "3.3.3", 4 | "displayName": "Asset Quick Access!", 5 | "description": "Pin frequently used objects to a separate editor window. An enhanced version of Unity's Favorites feature.", 6 | "unity": "2021.3", 7 | "documentationUrl": "https://github.com/SolarianZ/UnityAssetQuickAccessTool", 8 | "changelogUrl": "https://github.com/SolarianZ/UnityAssetQuickAccessTool/releases", 9 | "licensesUrl": "https://github.com/SolarianZ/UnityAssetQuickAccessTool/blob/main/LICENSE", 10 | "keywords": [ 11 | "Asset", 12 | "Favorite", 13 | "Quick Access", 14 | "Record" 15 | ], 16 | "author": { 17 | "name": "ZQY", 18 | "email": "vdergow@hotmail.com", 19 | "url": "https://github.com/SolarianZ" 20 | }, 21 | "type": "tool", 22 | "hideInEditor": false 23 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Qiuyu ZHANG 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # This .gitignore file should be placed at the root of your Unity project directory 2 | # 3 | # Get latest from https://github.com/github/gitignore/blob/master/Unity.gitignore 4 | # 5 | /[Ll]ibrary/ 6 | /[Tt]emp/ 7 | /[Oo]bj/ 8 | /[Bb]uild/ 9 | /[Bb]uilds/ 10 | /[Ll]ogs/ 11 | /[Mm]emoryCaptures/ 12 | 13 | # Asset meta data should only be ignored when the corresponding asset is also ignored 14 | !/[Aa]ssets/**/*.meta 15 | 16 | # Uncomment this line if you wish to ignore the asset store tools plugin 17 | # /[Aa]ssets/AssetStoreTools* 18 | 19 | # Autogenerated Jetbrains Rider plugin 20 | [Aa]ssets/Plugins/Editor/JetBrains* 21 | 22 | # Visual Studio cache directory 23 | .vs/ 24 | 25 | # Gradle cache directory 26 | .gradle/ 27 | 28 | # Autogenerated VS/MD/Consulo solution and project files 29 | ExportedObj/ 30 | .consulo/ 31 | *.csproj 32 | *.unityproj 33 | *.sln 34 | *.suo 35 | *.tmp 36 | *.user 37 | *.userprefs 38 | *.pidb 39 | *.booproj 40 | *.svd 41 | *.pdb 42 | *.mdb 43 | *.opendb 44 | *.VC.db 45 | 46 | # Unity3D generated meta files 47 | *.pidb.meta 48 | *.pdb.meta 49 | *.mdb.meta 50 | 51 | # Unity3D generated file on crash reports 52 | sysinfo.txt 53 | 54 | # Builds 55 | *.apk 56 | *.unitypackage 57 | 58 | # Crashlytics generated file 59 | crashlytics-build.properties 60 | 61 | -------------------------------------------------------------------------------- /Editor/Scripts/DragAndDropInManipulator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using UnityEditor; 4 | using UnityEngine.UIElements; 5 | using UObject = UnityEngine.Object; 6 | 7 | namespace GBG.AssetQuickAccess.Editor 8 | { 9 | public class DragAndDropInManipulator : PointerManipulator 10 | { 11 | public event Action, IList> OnDragAndDrop; 12 | 13 | 14 | public DragAndDropInManipulator(VisualElement target) 15 | { 16 | this.target = target; 17 | } 18 | 19 | public void RemoveSelfFromTarget() 20 | { 21 | target.RemoveManipulator(this); 22 | } 23 | 24 | 25 | protected override void RegisterCallbacksOnTarget() 26 | { 27 | target.RegisterCallback(OnDragUpdate); 28 | target.RegisterCallback(OnDragPerform); 29 | } 30 | 31 | protected override void UnregisterCallbacksFromTarget() 32 | { 33 | target.UnregisterCallback(OnDragUpdate); 34 | target.UnregisterCallback(OnDragPerform); 35 | } 36 | 37 | 38 | private void OnDragUpdate(DragUpdatedEvent _) 39 | { 40 | if (AssetItemView.DragGenericData.Equals(DragAndDrop.GetGenericData(AssetItemView.DragGenericData))) 41 | { 42 | return; 43 | } 44 | 45 | if (DragAndDrop.paths.Length > 0 || DragAndDrop.objectReferences.Length > 0) 46 | { 47 | DragAndDrop.visualMode = DragAndDropVisualMode.Generic; 48 | } 49 | } 50 | 51 | private void OnDragPerform(DragPerformEvent _) 52 | { 53 | if (AssetItemView.DragGenericData.Equals(DragAndDrop.GetGenericData(AssetItemView.DragGenericData))) 54 | { 55 | DragAndDrop.SetGenericData(AssetItemView.DragGenericData, null); 56 | return; 57 | } 58 | 59 | DragAndDrop.AcceptDrag(); 60 | // Sometimes the dragged assets are not included in DragAndDrop.objectReferences, for unknown reasons. 61 | OnDragAndDrop?.Invoke(DragAndDrop.objectReferences, DragAndDrop.paths); 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /README_CN.md: -------------------------------------------------------------------------------- 1 | # Unity资产快速访问工具 2 | 3 | [English](./README.md) 4 | 5 | 将常用的 **Unity对象、菜单项和外部文件/文件夹/网址** 固定到独立的编辑器窗口中。Unity Favorites功能的增强版。 6 | 7 | ![Asset Quick Access Window](./Documents~/imgs/img_sample_asset_quick_access_window.png) 8 | 9 | ## 功能 10 | 11 | - 记录常用对象,包括: 12 | - 项目资产 13 | - Scene中的对象和组件 14 | - 外部文件和文件夹 15 | - 网址(文本内容) 16 | - 菜单项(路径) 17 | - 按类别筛选已记录项目 18 | - 快速 定位 / 打开 已记录项目 19 | - 复制已记录项目路径 20 | - 复制已记录项目Guid 21 | - 复制已记录项目类型 22 | - 在文件夹中显示已记录项目 23 | 24 | ## 支持的Unity版本 25 | 26 | Unity 2021.3 或更新版本。 27 | 28 | Unity 2019.2 - Unity 2021.2请使用 [v1.4.3](https://github.com/SolarianZ/UnityAssetQuickAccessTool/releases/tag/v1.4.3) 版本。
29 | Unity 2017.4 - Unity 2019.1请使用 [v1.2.1](https://github.com/SolarianZ/UnityAssetQuickAccessTool/releases/tag/v1.2.1) 版本。 30 | 31 | ## 安装 32 | 33 | [![openupm](https://img.shields.io/npm/v/com.greenbamboogames.assetquickaccess?label=openupm®istry_uri=https://package.openupm.com)](https://openupm.com/packages/com.greenbamboogames.assetquickaccess/) 34 | 35 | 从 [OpenUPM](https://openupm.com/packages/com.greenbamboogames.assetquickaccess) 安装,或者直接将此仓库克隆到项目的Packages文件夹下。 36 | 37 | ## 如何使用 38 | 39 | 从菜单 **Tools/Bamboo/Asset Quick Access** 或者快捷键 `Ctrl Q` 打开资产快速访问工具窗口。 40 | 41 | - 将项目 **拖放** 到工具窗口中,可以记录该项目。 42 | - 在Unity对象上下文菜单中,选择 **Bamboo/Add to Asset Quick Access** ,可以记录该对象。 43 | - 使用 **鼠标左键单击** 已记录项目,可以在Editor中定位(Ping)此项目。 44 | - 若项目是Scene中的对象或组件,且Scene未打开,则改为定位其所在的SceneAsset; 45 | - 若项目是外部文件或文件夹,则什么都不做。 46 | - 使用 **鼠标左键双击** 已记录项目,可以打开/执行此项目。 47 | - 若项目是Scene中的对象或组件,且Scene未打开,则打开其所在的SceneAsset。 48 | - 使用 **鼠标右键单击** 已记录项目,可以显示项目操作菜单。 49 | - ~~在 **Find Asset** 输入框中输入资产的Guid或路径查找资产~~ (使用Unity内置的搜索功能(`Ctrl K`)替代)。 50 | - 使用窗口工具栏的类别按钮筛选项目 51 | - 使用窗口工具栏下拉菜单中的 **Add External File** 选项添加一个外部文件。 52 | - 使用窗口工具栏下拉菜单中的 **Add External Folder** 选项添加一个外部文件夹。 53 | - 使用窗口工具栏下拉菜单中的 **Add URL** 选项添加一个外部链接。 54 | - 使用窗口工具栏下拉菜单中的 **Remove All Items** 选项清除已记录的所有项目。 55 | 56 | 若要禁用快捷键,请在 **Edit > Project Settings > Player** 中添加 [脚本编译条件符号](https://docs.unity3d.com/Manual/CustomScriptingSymbols.html) `GBG_AQA_HOTKEY_OFF` 。也可以通过 [Shortcuts Manager](https://docs.unity3d.com/Manual/ShortcutsManager.html) 调整快捷键。 57 | 58 | 若要禁用Unity对象上下文菜单项,请在 **Edit > Project Settings > Player** 中添加 [脚本编译条件符号](https://docs.unity3d.com/Manual/CustomScriptingSymbols.html) `GBG_AQA_CONTEXT_MENU_OFF` 。 59 | 60 | ## 已知问题 61 | 62 | 1. 无法将工程文件夹(`Application.dataPath`)中的文件和文件夹拖拽到快速访问窗口,因为Unity没有为此类项目提供对外的拖拽回调。 63 | - 解决方案:使用工具栏下拉菜单中的添加外部项选项替代拖拽。 64 | 2. Play Mode中动态生产的对象被删除后,再次生成同等对象(类型、路径等均相同),快速访问工具无法将两者对应起来,会认为前者处于Missing状态。 -------------------------------------------------------------------------------- /Editor/Scripts/AssetItemViewActionManipulator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEditor; 3 | using UnityEngine; 4 | using UnityEngine.UIElements; 5 | using UObject = UnityEngine.Object; 6 | 7 | namespace GBG.AssetQuickAccess.Editor 8 | { 9 | public class AssetItemViewActionManipulator : PointerManipulator 10 | { 11 | internal AssetHandle AssetHandle => ((AssetItemView)target).AssetHandle; 12 | private bool _draggable; 13 | private int _clickCount; 14 | 15 | public Action Clicked; 16 | public Action DoubleClicked; 17 | public Action ContextClicked; 18 | 19 | 20 | protected override void RegisterCallbacksOnTarget() 21 | { 22 | target.RegisterCallback(OnContextClick); 23 | target.RegisterCallback(OnPointerDown); 24 | target.RegisterCallback(OnPointerUp); 25 | target.RegisterCallback(OnPointerMove); 26 | } 27 | 28 | protected override void UnregisterCallbacksFromTarget() 29 | { 30 | target.UnregisterCallback(OnContextClick); 31 | target.UnregisterCallback(OnPointerDown); 32 | target.UnregisterCallback(OnPointerUp); 33 | target.UnregisterCallback(OnPointerMove); 34 | } 35 | 36 | private void OnContextClick(ContextClickEvent evt) 37 | { 38 | evt.StopImmediatePropagation(); 39 | _draggable = false; 40 | _clickCount = 0; 41 | 42 | ContextClicked?.Invoke(evt.mousePosition); 43 | } 44 | 45 | private void OnPointerDown(PointerDownEvent evt) 46 | { 47 | if (evt.button == 0) // Left click 48 | { 49 | evt.StopImmediatePropagation(); 50 | _draggable = true; 51 | _clickCount = evt.clickCount; 52 | } 53 | } 54 | 55 | private void OnPointerUp(PointerUpEvent evt) 56 | { 57 | if (evt.button == 0) // Left click 58 | { 59 | int clickCount = _clickCount; 60 | _clickCount = 0; 61 | if (_draggable) 62 | { 63 | _draggable = false; 64 | evt.StopImmediatePropagation(); 65 | 66 | switch (clickCount) 67 | { 68 | case 1: // Click 69 | Clicked?.Invoke(); 70 | break; 71 | case 2: // Double click 72 | DoubleClicked?.Invoke(); 73 | break; 74 | } 75 | } 76 | } 77 | } 78 | 79 | private void OnPointerMove(PointerMoveEvent evt) 80 | { 81 | if (_draggable) 82 | { 83 | evt.StopImmediatePropagation(); 84 | _draggable = false; 85 | _clickCount = 0; 86 | 87 | // MEMO Unity Bug: https://issuetracker.unity3d.com/product/unity/issues/guid/UUM-76471 88 | // This DragAndDrop code causes the VisualElement to fail to receive the PointerUpEvent 89 | DragAndDrop.PrepareStartDrag(); 90 | DragAndDrop.SetGenericData(AssetItemView.DragGenericData, AssetItemView.DragGenericData); 91 | DragAndDrop.objectReferences = new UObject[] { AssetHandle.Asset }; 92 | DragAndDrop.paths = new string[] { AssetDatabase.GetAssetPath(AssetHandle.Asset) }; 93 | DragAndDrop.StartDrag(null); 94 | } 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Unity Asset Quick Access Tool. 2 | 3 | [中文](./README_CN.md) 4 | 5 | Pin frequently used **Unity objects, menu items and external files/folders/urls** to a separate editor window. An enhanced version of the Unity's Favorites feature. 6 | 7 | ![Asset Quick Access Window](./Documents~/imgs/img_sample_asset_quick_access_window.png) 8 | 9 | ## Features 10 | 11 | - Record frequently used objects, including: 12 | - Project assets 13 | - Scene objects and components 14 | - External files and folders 15 | - External URLs(text content) 16 | - Menu items(path) 17 | - Filter recorded items by category. 18 | - Quickly locate / open recorded items. 19 | - Copy the path of recorded items. 20 | - Copy the guid of recorded items. 21 | - Copy the type of recorded items. 22 | - Show recorded items in the folder. 23 | 24 | ## Supported Unity Version 25 | 26 | Unity 2021.3 and later. 27 | 28 | For Unity 2019.2 - Unity 2021.2, please use version [v1.4.3](https://github.com/SolarianZ/UnityAssetQuickAccessTool/releases/tag/v1.4.3).
29 | For Unity 2017.4 - Unity 2019.1, please use version [v1.2.1](https://github.com/SolarianZ/UnityAssetQuickAccessTool/releases/tag/v1.2.1). 30 | 31 | ## Installation 32 | 33 | [![openupm](https://img.shields.io/npm/v/com.greenbamboogames.assetquickaccess?label=openupm®istry_uri=https://package.openupm.com)](https://openupm.com/packages/com.greenbamboogames.assetquickaccess/) 34 | 35 | Install this package via [OpenUPM](https://openupm.com/packages/com.greenbamboogames.assetquickaccess), or clone this repository directly into the Packages folder of your project. 36 | 37 | ## How to use 38 | 39 | Open the Asset Quick Access window from the menu **Tools/Bamboo/Asset Quick Access** or the `Ctrl+Q` shortcut. 40 | 41 | - **Drag and drop** items into the window to record them. 42 | - In the Unity object context menu, selecting **Bamboo/Add to Asset Quick Access** can record that object. 43 | - **Left-click** on a recorded item to locate (ping) it in the Editor. 44 | - If the item is an object or component in a Scene and the Scene is not open, it will locate the containing SceneAsset instead. 45 | - If the item is an external file or folder, no action will be taken. 46 | - **Double-click** on a recorded item to open/execute it. 47 | - If the item is an object or component in a Scene and the Scene is not open, it will open the containing SceneAsset. 48 | - **Right-click** on a recorded item to display the operation menu. 49 | - ~~Enter the asset's guid or path in the **Find Asset** input field to find it~~ (Use Unity's builtin search(`Ctrl K`) instead). 50 | - Use the category buttons on the window's toolbar to filter items. 51 | - Use the **Add External File** option in the window toolbar's dropdown menu to add an external file. 52 | - Use the **Add External Folder** option in the window toolbar's dropdown menu to add an external folder. 53 | - Use the **Add URL** option in the window toolbar's dropdown menu to add an external url. 54 | - Use the **Remove All Items** option in the window toolbar's dropdown menu to clear all recorded items. 55 | 56 | To disable the shortcut, add the [scripting symbol](https://docs.unity3d.com/Manual/CustomScriptingSymbols.html) `GBG_AQA_HOTKEY_OFF` in **Edit > Project Settings > Player** . You can also adjust the shortcut through the [Shortcuts Manager](https://docs.unity3d.com/Manual/ShortcutsManager.html). 57 | 58 | To disable the Unity object context menu item, add the [scripting symbol](https://docs.unity3d.com/Manual/CustomScriptingSymbols.html) `GBG_AQA_CONTEXT_MENU_OFF` in **Edit > Project Settings > Player**. 59 | 60 | ## Known Issues 61 | 62 | 1. Files and folders from the project folder (`Application.dataPath`) cannot be dragged into the quick access window because Unity does not provide external drag-and-drop callbacks for such items. 63 | - **Solution**: Use the add external item options from the toolbar dropdown menu instead of dragging. 64 | 2. When objects dynamically generated in Play Mode are deleted and new equivalent objects (with the same type, path, etc.) are regenerated, the quick access tool is unable to associate the new equivalent objects with the previous ones, and will consider the previous objects to be in a "Missing" state. -------------------------------------------------------------------------------- /Editor/Scripts/AssetQuickAccessLocalCache.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Text; 4 | using UnityEditor; 5 | using UnityEngine; 6 | using UObject = UnityEngine.Object; 7 | 8 | namespace GBG.AssetQuickAccess.Editor 9 | { 10 | [FilePath("Library/com.greenbamboogames.assetquickaccess/LocalCache.asset", 11 | FilePathAttribute.Location.ProjectFolder)] 12 | internal class AssetQuickAccessLocalCache : ScriptableSingleton 13 | { 14 | public IList AssetHandles => _assetHandles; 15 | public AssetCategory SelectedCategories 16 | { 17 | get { return _selectedCategory; } 18 | set 19 | { 20 | if (_selectedCategory == value) return; 21 | _selectedCategory = value; 22 | ForceSave(); 23 | } 24 | } 25 | 26 | [SerializeField] 27 | private List _assetHandles = new List(); 28 | [SerializeField] 29 | private AssetCategory _selectedCategory = AssetCategory.None; 30 | 31 | 32 | public bool AddExternalPaths(IEnumerable paths, ref StringBuilder errorsBuilder, bool clearErrorsBuilder) 33 | { 34 | if (clearErrorsBuilder) 35 | { 36 | errorsBuilder?.Clear(); 37 | } 38 | 39 | bool added = false; 40 | foreach (string path in paths) 41 | { 42 | if (_assetHandles.Any(h => h.GetAssetPath() == path)) 43 | { 44 | errorsBuilder ??= new StringBuilder(); 45 | errorsBuilder.AppendLine("File or folder already exists."); 46 | continue; 47 | } 48 | 49 | AssetHandle handle = AssetHandle.CreateFromExternalFile(path, out string error); 50 | if (!string.IsNullOrEmpty(error)) 51 | { 52 | errorsBuilder ??= new StringBuilder(); 53 | errorsBuilder.AppendLine(error); 54 | } 55 | 56 | if (handle == null) 57 | { 58 | continue; 59 | } 60 | 61 | _assetHandles.Add(handle); 62 | added = true; 63 | } 64 | 65 | if (added) 66 | { 67 | ForceSave(); 68 | } 69 | 70 | return added; 71 | } 72 | 73 | public bool AddObjects(IEnumerable objects, ref StringBuilder errorsBuilder, bool clearErrorsBuilder) 74 | { 75 | if (clearErrorsBuilder) 76 | { 77 | errorsBuilder?.Clear(); 78 | } 79 | 80 | bool added = false; 81 | foreach (UObject obj in objects) 82 | { 83 | if (EditorUtility.IsPersistent(obj)) 84 | { 85 | if (_assetHandles.Any(h => h.Asset == obj)) 86 | { 87 | errorsBuilder ??= new StringBuilder(); 88 | errorsBuilder.AppendLine("Asset already exists."); 89 | continue; 90 | } 91 | 92 | AssetHandle handle = AssetHandle.CreateFromObject(obj, out string error); 93 | if (!string.IsNullOrEmpty(error)) 94 | { 95 | errorsBuilder ??= new StringBuilder(); 96 | errorsBuilder.AppendLine(error); 97 | } 98 | 99 | if (handle == null) 100 | { 101 | continue; 102 | } 103 | 104 | _assetHandles.Add(handle); 105 | added = true; 106 | } 107 | else 108 | { 109 | if (_assetHandles.Any(h => h.Asset == obj)) 110 | { 111 | errorsBuilder ??= new StringBuilder(); 112 | errorsBuilder.AppendLine("Object already exists."); 113 | continue; 114 | } 115 | 116 | AssetHandle handle = AssetHandle.CreateFromObject(obj, out string error); 117 | if (!string.IsNullOrEmpty(error)) 118 | { 119 | errorsBuilder ??= new StringBuilder(); 120 | errorsBuilder.AppendLine(error); 121 | } 122 | 123 | if (handle == null) 124 | { 125 | continue; 126 | } 127 | 128 | if (_assetHandles.Any(h => h.Guid == handle.Guid)) 129 | { 130 | continue; 131 | } 132 | 133 | _assetHandles.Add(handle); 134 | added = true; 135 | } 136 | } 137 | 138 | if (added) 139 | { 140 | ForceSave(); 141 | } 142 | 143 | return added; 144 | } 145 | 146 | public bool AddUrls(IEnumerable<(string url, string title)> urlInfos, ref StringBuilder errorsBuilder, bool clearErrorsBuilder) 147 | { 148 | if (clearErrorsBuilder) 149 | { 150 | errorsBuilder?.Clear(); 151 | } 152 | 153 | bool added = false; 154 | foreach ((string url, string title) urlInfo in urlInfos) 155 | { 156 | if (_assetHandles.Any(h => h.GetAssetPath() == urlInfo.url)) 157 | { 158 | errorsBuilder ??= new StringBuilder(); 159 | errorsBuilder.AppendLine("URL already exists."); 160 | continue; 161 | } 162 | 163 | AssetHandle handle = AssetHandle.CreateFromUrl(urlInfo.url, urlInfo.title, out string error); 164 | if (!string.IsNullOrEmpty(error)) 165 | { 166 | errorsBuilder ??= new StringBuilder(); 167 | errorsBuilder.AppendLine(error); 168 | } 169 | 170 | if (handle == null) 171 | { 172 | continue; 173 | } 174 | 175 | _assetHandles.Add(handle); 176 | added = true; 177 | } 178 | 179 | if (added) 180 | { 181 | ForceSave(); 182 | } 183 | 184 | return added; 185 | } 186 | 187 | public bool AddMenuItems(IEnumerable<(string menuPath, string title)> menuItemInfos, ref StringBuilder errorsBuilder, bool clearErrorsBuilder) 188 | { 189 | if (clearErrorsBuilder) 190 | { 191 | errorsBuilder?.Clear(); 192 | } 193 | 194 | bool added = false; 195 | foreach ((string menuPath, string title) menuItemInfo in menuItemInfos) 196 | { 197 | if (_assetHandles.Any(h => h.GetAssetPath() == menuItemInfo.menuPath)) 198 | { 199 | errorsBuilder ??= new StringBuilder(); 200 | errorsBuilder.AppendLine("Menu item already exists."); 201 | continue; 202 | } 203 | 204 | AssetHandle handle = AssetHandle.CreateFromMenuPath(menuItemInfo.menuPath, menuItemInfo.title, out string error); 205 | if (!string.IsNullOrEmpty(error)) 206 | { 207 | errorsBuilder ??= new StringBuilder(); 208 | errorsBuilder.AppendLine(error); 209 | } 210 | 211 | if (handle == null) 212 | { 213 | continue; 214 | } 215 | 216 | _assetHandles.Add(handle); 217 | added = true; 218 | } 219 | 220 | if (added) 221 | { 222 | ForceSave(); 223 | } 224 | 225 | return added; 226 | } 227 | 228 | public bool RemoveAsset(AssetHandle handle) 229 | { 230 | if (_assetHandles.Remove(handle)) 231 | { 232 | ForceSave(); 233 | return true; 234 | } 235 | 236 | return false; 237 | } 238 | 239 | public void RemoveAllAssets() 240 | { 241 | _assetHandles.Clear(); 242 | ForceSave(); 243 | 244 | Debug.Log("All asset quick access items removed."); 245 | } 246 | 247 | public void ForceSave() 248 | { 249 | Save(true); 250 | } 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /Editor/Scripts/MenuItemSelectWindow.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using UnityEditor; 6 | using UnityEngine; 7 | using UnityEngine.UIElements; 8 | 9 | namespace GBG.AssetQuickAccess.Editor 10 | { 11 | //[Serializable] 12 | //public class MenuItemInfo 13 | //{ 14 | // public string Path; 15 | // public bool HasValidation; 16 | // public int Priority; 17 | //} 18 | 19 | internal class MenuItemSelectWindow : EditorWindow 20 | { 21 | public delegate void SubmitHandler(string menuPath, string title); 22 | 23 | public static MenuItemSelectWindow Open(Vector2 upperCenterPosition, SubmitHandler onSubmit, bool showAsDropdown = true) 24 | { 25 | MenuItemSelectWindow window = CreateInstance(); 26 | window._onSubmit += onSubmit; 27 | 28 | float width = 400; 29 | float height = 300; 30 | if (showAsDropdown) 31 | { 32 | Rect position = new Rect(upperCenterPosition - new Vector2(width / 2, 0), default); 33 | window.ShowAsDropDown(position, new Vector2(width, height)); 34 | window.position = position; 35 | } 36 | else 37 | { 38 | window.Show(); 39 | } 40 | 41 | return window; 42 | } 43 | 44 | public static List GetAllMenuItemPaths() 45 | { 46 | List menuPaths = TypeCache.GetMethodsWithAttribute() 47 | .SelectMany(method => method.GetCustomAttributes()) 48 | .Where(attr => attr.validate == false) 49 | .Select(attr => RemoveShortcutSymbols(attr.menuItem)) 50 | .Distinct() 51 | .ToList(); 52 | //string[] menuPaths = AppDomain.CurrentDomain.GetAssemblies() 53 | // .SelectMany(asm => asm.GetTypes()) 54 | // .SelectMany(type => type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)) 55 | // .SelectMany(method => method.GetCustomAttributes()) 56 | // .Where(attr => attr.validate == false) 57 | // .Select(attr => attr.menuItem) 58 | // .Distinct() 59 | // .ToArray(); 60 | 61 | return menuPaths; 62 | } 63 | 64 | public static string RemoveShortcutSymbols(string menuPath) 65 | { 66 | // https://docs.unity3d.com/ScriptReference/MenuItem.html 67 | //%: Represents Ctrl on Windows and Linux. Cmd on macOS. 68 | //^: Represents Ctrl on Windows, Linux, and macOS. 69 | //#: Represents Shift. 70 | //&: Represents Alt. 71 | menuPath = menuPath.TrimEnd(); 72 | int lastIndexOfSpace = menuPath.LastIndexOf(' '); 73 | if (lastIndexOfSpace == -1) 74 | { 75 | return menuPath; 76 | } 77 | 78 | char firstShortcutChar = menuPath[lastIndexOfSpace + 1]; 79 | if (firstShortcutChar != '%' && 80 | firstShortcutChar != '^' && 81 | firstShortcutChar != '#' && 82 | firstShortcutChar != '&') 83 | { 84 | //Debug.LogError($"[Asset Quick Access] Failed to remove shortcut symbols from menu path: {menuPath}"); 85 | return menuPath; 86 | } 87 | 88 | menuPath = menuPath.Substring(0, lastIndexOfSpace).TrimEnd(); 89 | return menuPath; 90 | } 91 | 92 | //public static bool ExecuteMenuItem(MethodInfo menuMethodInfo, bool validate) 93 | //{ 94 | // if (menuMethodInfo.GetParameters().Length == 0) 95 | // { 96 | // var result = menuMethodInfo.Invoke(null, new object[0]); 97 | // return !validate || (bool)result; 98 | // } 99 | // 100 | // if (menuMethodInfo.GetParameters()[0].ParameterType == typeof(MenuCommand)) 101 | // { 102 | // var result = menuMethodInfo.Invoke(null, new[] { new MenuCommand(null) }); 103 | // return !validate || (bool)result; 104 | // } 105 | // 106 | // return false; 107 | //} 108 | 109 | 110 | private SubmitHandler _onSubmit; 111 | private TextField _menuPathField; 112 | private ListView _menuPathListView; 113 | private Label _statusLabel; 114 | private Button _addButton; 115 | 116 | private List _allMenuPaths = new List(); 117 | private List _filteredMenuPaths = new List(); 118 | 119 | 120 | private void OnEnable() 121 | { 122 | _allMenuPaths = GetAllMenuItemPaths(); 123 | _allMenuPaths.Sort(); 124 | _filteredMenuPaths.Clear(); 125 | _filteredMenuPaths.AddRange(_allMenuPaths); 126 | } 127 | 128 | private void CreateGUI() 129 | { 130 | rootVisualElement.style.paddingLeft = 4; 131 | rootVisualElement.style.paddingRight = 4; 132 | rootVisualElement.style.paddingTop = 4; 133 | rootVisualElement.style.paddingBottom = 4; 134 | rootVisualElement.RegisterCallback(HandleKeyUp); 135 | 136 | // Menu path field 137 | _menuPathField = new TextField 138 | { 139 | name = "MenuPathField", 140 | label = "Menu Item", 141 | }; 142 | _menuPathField.Q