2025-05-01 01:48:08 -07:00

344 lines
15 KiB
C#

using System;
using System.Collections.Generic;
using UnityEditor.AddressableAssets.Settings;
using UnityEditor.IMGUI.Controls;
using UnityEngine;
using UnityEngine.TestTools;
namespace UnityEditor.AddressableAssets.GUI
{
[InitializeOnLoad, ExcludeFromCoverage]
class GroupsPopupWindow : EditorWindow
{
private AddressableAssetGroup m_InitialSelection;
private bool m_AllowReadOnlyGroups;
private bool m_StayOpenAfterSelection;
private Action<AddressableAssetSettings, List<AddressableAssetEntry>, AddressableAssetGroup> m_Action;
private AddressableAssetSettings m_SettingsContext;
private List<AddressableAssetEntry> m_EntriesContext;
private GroupsPopupTreeView m_Tree;
private TreeViewState m_TreeState;
private SearchField m_SearchField;
private string m_CurrentName = string.Empty;
private Texture2D m_FolderTexture;
private bool m_ShouldClose;
private void ForceClose()
{
m_ShouldClose = true;
}
public void Initialize(AddressableAssetGroup initialSelection, bool allowReadOnlyGroups, bool stayOpenAfterSelection, Vector2 mousePosition,
Action<AddressableAssetSettings, List<AddressableAssetEntry>, AddressableAssetGroup> action, AddressableAssetSettings settingsContext, List<AddressableAssetEntry> entriesContext)
{
m_InitialSelection = initialSelection;
m_AllowReadOnlyGroups = allowReadOnlyGroups;
m_StayOpenAfterSelection = stayOpenAfterSelection;
Rect rect = position;
mousePosition = GUIUtility.GUIToScreenPoint(mousePosition);
rect.position = mousePosition;
position = rect;
m_Action = action;
m_SettingsContext = settingsContext;
m_EntriesContext = entriesContext;
m_SearchField = new SearchField();
m_SearchField.SetFocus();
m_SearchField.downOrUpArrowKeyPressed += () => { m_Tree.SetFocus(); };
if (m_Tree != null && m_InitialSelection != null)
m_Tree.SetInitialSelection(m_InitialSelection.Name);
m_FolderTexture = EditorGUIUtility.IconContent("Folder Icon").image as Texture2D;
m_ShouldClose = false;
}
private void OnLostFocus()
{
ForceClose();
}
private void OnGUI()
{
Rect rect = position;
int border = 4;
int topPadding = 2;
int searchHeight = 20;
var searchRect = new Rect(border, topPadding, rect.width - border * 2, searchHeight);
var remainTop = topPadding + searchHeight + border;
var remainingRect = new Rect(border, topPadding + searchHeight + border, rect.width - border * 2, rect.height - remainTop - border);
if (m_Tree == null)
{
if (m_TreeState == null)
m_TreeState = new TreeViewState();
m_Tree = new GroupsPopupTreeView(m_TreeState, this, m_AllowReadOnlyGroups);
m_Tree.Reload();
if (m_InitialSelection != null)
m_Tree.SetInitialSelection(m_InitialSelection.Name);
}
bool isKeyPressed = Event.current.type == EventType.KeyDown && Event.current.isKey;
bool isEnterKeyPressed = isKeyPressed && (Event.current.keyCode == KeyCode.KeypadEnter || Event.current.keyCode == KeyCode.Return);
bool isUpOrDownArrowPressed = isKeyPressed && (Event.current.keyCode == KeyCode.UpArrow || Event.current.keyCode == KeyCode.DownArrow);
if (isUpOrDownArrowPressed)
m_Tree.SetFocus();
m_CurrentName = m_SearchField.OnGUI(searchRect, m_CurrentName);
m_Tree.searchString = m_CurrentName;
m_Tree.IsEnterKeyPressed = isEnterKeyPressed;
m_Tree.OnGUI(remainingRect);
if (m_ShouldClose || isEnterKeyPressed)
{
GUIUtility.hotControl = 0;
Close();
}
}
internal class GroupsPopupTreeItem : TreeViewItem
{
internal AddressableAssetGroup m_Group;
public GroupsPopupTreeItem(int id, int depth, string displayName, AddressableAssetGroup group, Texture2D folderTexture)
: base(id, depth, displayName)
{
m_Group = group;
icon = folderTexture;
}
}
internal class GroupsPopupTreeView : TreeView
{
internal GroupsPopupWindow m_Popup;
internal bool IsEnterKeyPressed { get; set; }
internal bool m_ShowReadOnlyGroups;
public GroupsPopupTreeView(TreeViewState state, GroupsPopupWindow popup, bool showReadOnlyGroups)
: base(state)
{
m_Popup = popup;
m_ShowReadOnlyGroups = showReadOnlyGroups;
#if UNITY_2022_1_OR_NEWER
enableItemHovering = true;
#endif
}
public override void OnGUI(Rect rect)
{
base.OnGUI(rect);
if (IsEnterKeyPressed && HasFocus())
{
m_Popup.ForceClose();
}
}
protected override void DoubleClickedItem(int id)
{
var groupTreeItem = FindItem(id, rootItem) as GroupsPopupTreeItem;
if (groupTreeItem != null)
{
m_Popup.m_Action(m_Popup.m_SettingsContext, m_Popup.m_EntriesContext, groupTreeItem.m_Group);
}
m_Popup.ForceClose();
}
internal void SetInitialSelection(string assetString)
{
foreach (var child in rootItem.children)
{
if (child.displayName.IndexOf(assetString, StringComparison.OrdinalIgnoreCase) >= 0)
{
SetSelection(new List<int> { child.id });
return;
}
}
}
protected override void SelectionChanged(IList<int> selectedIds)
{
if (selectedIds != null && selectedIds.Count == 1)
{
var groupTreeItem = FindItem(selectedIds[0], rootItem) as GroupsPopupTreeItem;
if (groupTreeItem != null)
{
m_Popup.m_Action(m_Popup.m_SettingsContext, m_Popup.m_EntriesContext, groupTreeItem.m_Group);
}
if (m_Popup.m_StayOpenAfterSelection)
SetFocus();
else
m_Popup.ForceClose();
}
}
protected override bool CanMultiSelect(TreeViewItem item)
{
return false;
}
protected override IList<TreeViewItem> BuildRows(TreeViewItem root)
{
if (string.IsNullOrEmpty(searchString))
{
return base.BuildRows(root);
}
List<TreeViewItem> rows = new List<TreeViewItem>();
foreach (var child in rootItem.children)
{
if (child.displayName.IndexOf(searchString, StringComparison.OrdinalIgnoreCase) >= 0)
rows.Add(child);
}
return rows;
}
protected override TreeViewItem BuildRoot()
{
var root = new TreeViewItem(-1, -1);
var aaSettings = AddressableAssetSettingsDefaultObject.Settings;
if (aaSettings == null)
{
var message = "Use 'Window->Addressables' to initialize.";
root.AddChild(new GroupsPopupTreeItem(message.GetHashCode(), 0, message, null, m_Popup.m_FolderTexture));
}
else
{
foreach (AddressableAssetGroup group in aaSettings.groups)
{
if (!m_ShowReadOnlyGroups && group.ReadOnly)
continue;
var child = new GroupsPopupTreeItem(group.Guid.GetHashCode(), 0, group.name, group, m_Popup.m_FolderTexture);
root.AddChild(child);
}
}
return root;
}
}
}
class GroupsPopupUtility
{
static string s_GroupsDropdownControlName = nameof(AddressableAssetInspectorGUI) + ".GroupsPopupField";
static Texture s_GroupsCaretTexture = null;
static Texture s_FolderTexture = null;
internal static void DrawGroupsDropdown(GUIContent dropdownlabelContent, AddressableAssetGroup displayGroup, bool enabledDropdown, bool mixedValueDropdown, bool allowReadOnlyGroups,
Action<AddressableAssetSettings, List<AddressableAssetEntry>, AddressableAssetGroup> action, List<AddressableAssetEntry> entriesContext)
{
using (new EditorGUI.DisabledScope(!enabledDropdown))
{
GUILayout.Label(dropdownlabelContent);
if (mixedValueDropdown)
EditorGUI.showMixedValue = true;
UnityEngine.GUI.SetNextControlName(s_GroupsDropdownControlName);
float iconHeight = EditorGUIUtility.singleLineHeight - EditorGUIUtility.standardVerticalSpacing * 3;
Vector2 iconSize = EditorGUIUtility.GetIconSize();
EditorGUIUtility.SetIconSize(new Vector2(iconHeight, iconHeight));
if (s_FolderTexture == null)
{
s_FolderTexture = EditorGUIUtility.IconContent("Folder Icon").image;
}
GUIContent groupGUIContent = new GUIContent(displayGroup.Name, s_FolderTexture);
Rect groupFieldRect = GUILayoutUtility.GetRect(new GUIContent(), EditorStyles.objectField);
float newXPos = EditorGUIUtility.labelWidth + 20;
float newWidth = groupFieldRect.width + (groupFieldRect.x - newXPos);
groupFieldRect.x = newXPos;
groupFieldRect.width = newWidth;
EditorGUI.DropdownButton(groupFieldRect, groupGUIContent, FocusType.Keyboard, EditorStyles.objectField);
EditorGUIUtility.SetIconSize(new Vector2(iconSize.x, iconSize.y));
if (mixedValueDropdown)
EditorGUI.showMixedValue = false;
float pickerWidth = 12f;
Rect groupFieldRectNoPicker = new Rect(groupFieldRect);
groupFieldRectNoPicker.xMax = groupFieldRect.xMax - pickerWidth * 1.33f;
Rect pickerRect = new Rect(groupFieldRectNoPicker.xMax, groupFieldRectNoPicker.y, pickerWidth, groupFieldRectNoPicker.height);
bool isPickerPressed = Event.current.clickCount == 1 && pickerRect.Contains(Event.current.mousePosition);
DrawCaret(pickerRect);
if (enabledDropdown)
{
bool isEnterKeyPressed = Event.current.type == EventType.KeyDown && Event.current.isKey && (Event.current.keyCode == KeyCode.KeypadEnter || Event.current.keyCode == KeyCode.Return);
bool enterKeyRequestsPopup = isEnterKeyPressed && (s_GroupsDropdownControlName == UnityEngine.GUI.GetNameOfFocusedControl());
if (isPickerPressed || enterKeyRequestsPopup)
{
AddressableAssetGroup initialSelection = !mixedValueDropdown ? displayGroup : null;
EditorWindow.GetWindow<GroupsPopupWindow>(true, "Select Addressable Group").Initialize(initialSelection, allowReadOnlyGroups, true, Event.current.mousePosition, action, displayGroup.Settings, entriesContext);
}
bool isDragging = Event.current.type == EventType.DragUpdated && groupFieldRectNoPicker.Contains(Event.current.mousePosition);
bool isDropping = Event.current.type == EventType.DragPerform && groupFieldRectNoPicker.Contains(Event.current.mousePosition);
HandleDragAndDrop(isDragging, isDropping, allowReadOnlyGroups, action, displayGroup.Settings, entriesContext);
}
if (!mixedValueDropdown)
{
if (Event.current.clickCount == 1 && groupFieldRectNoPicker.Contains(Event.current.mousePosition))
{
UnityEngine.GUI.FocusControl(s_GroupsDropdownControlName);
AddressableAssetsWindow.Init();
var window = EditorWindow.GetWindow<AddressableAssetsWindow>();
window.SelectGroupInGroupEditor(displayGroup, false);
}
if (Event.current.clickCount == 2 && groupFieldRectNoPicker.Contains(Event.current.mousePosition))
{
AddressableAssetsWindow.Init();
var window = EditorWindow.GetWindow<AddressableAssetsWindow>();
window.SelectGroupInGroupEditor(displayGroup, true);
}
}
}
}
static void DrawCaret(Rect pickerRect)
{
if (s_GroupsCaretTexture == null)
{
s_GroupsCaretTexture = EditorGUIUtility.IconContent("d_pick").image;
}
UnityEngine.GUI.DrawTexture(pickerRect, s_GroupsCaretTexture, ScaleMode.ScaleToFit);
}
static void HandleDragAndDrop( bool isDragging, bool isDropping, bool allowReadOnlyGroups,
Action<AddressableAssetSettings, List<AddressableAssetEntry>, AddressableAssetGroup> action, AddressableAssetSettings settingsContext, List<AddressableAssetEntry> entriesContext)
{
var groupItems = DragAndDrop.GetGenericData("AssetEntryTreeViewItem") as List<AssetEntryTreeViewItem>;
if (isDragging)
{
bool singleItem = groupItems != null && groupItems.Count == 1;
AssetEntryTreeViewItem item = groupItems[0];
bool validGroup = item.IsGroup && allowReadOnlyGroups ? true : !item.group.ReadOnly;
DragAndDrop.visualMode = (singleItem && validGroup) ? DragAndDropVisualMode.Copy : DragAndDropVisualMode.Rejected;
}
else if (isDropping)
{
if (groupItems != null)
{
var group = groupItems[0].group;
action(settingsContext, entriesContext, group);
}
}
}
}
}