#if ADDRESSABLES && UI_ELEMENTS using UnityEngine; using UnityEngine.UIElements; using UnityEngine.AddressableAssets; using UnityEngine.ResourceManagement.AsyncOperations; using System.Collections.Generic; using System; using System.IO; #if UNITY_EDITOR using UnityEditor; #endif namespace NEG.Utils.UiToolkits { public class MultiSelectChips : VisualElement { public event Action OnTryingToRemoveItem; public event Action OnTryingToAddItem; public string LabelText { get => label.text; set { if (!string.IsNullOrEmpty(value)) { if (label == null) { InitLabel(); } label.text = value; } else if (label != null) { label.RemoveFromHierarchy(); label = null; } } } public ICollection ItemsSource { get => itemsSource; set { itemsSource = value; UpdateItems(); } } private Label label; private AsyncOperationHandle uxmlHandle, itemUxmlHandle; private VisualTreeAsset itemPrefab; private ICollection itemsSource; private readonly List spawnedItems = new(); private VisualElement realItemsParent; public new class UxmlFactory : UxmlFactory { } public new class UxmlTraits : VisualElement.UxmlTraits { private readonly UxmlStringAttributeDescription label; public UxmlTraits() { label = new() { name = "label" }; } public override void Init(VisualElement ve, IUxmlAttributes bag, CreationContext cc) { base.Init(ve, bag, cc); ((MultiSelectChips)ve).LabelText = label.GetValueFromBag(bag, cc); } } public MultiSelectChips() : base() { #if UNITY_EDITOR string path = AssetDatabase.GUIDToAssetPath(AssetDatabase.FindAssets($"t:Script {nameof(MultiSelectChips)}")[0]); path = path.Remove(path.LastIndexOf('/')); SetVisuals(AssetDatabase.LoadAssetAtPath($"{path}/MultiSelectChips.uxml")); itemPrefab = AssetDatabase.LoadAssetAtPath($"{path}/MultiSelectChipItem.uxml"); #else uxmlHandle = Addressables.LoadAssetAsync("NegUiToolkits/MultiSelectChips.uxml"); itemUxmlHandle = Addressables.LoadAssetAsync("NegUiToolkits/MultiSelectChipItem.uxml"); uxmlHandle.Completed += OnUxmlLoaded; itemUxmlHandle.Completed += OnItemUxmlLoaded; #endif } public void UpdateItems() { if (itemPrefab == null || itemsSource == null || realItemsParent == null) return; List itemsToDestroy = new List(spawnedItems); itemsToDestroy.RemoveAll((x) => itemsSource.Contains(x.ChipItem)); foreach (var item in itemsToDestroy) { realItemsParent.Remove(item.VisualElement); spawnedItems.Remove(item); } List itemsToAdd = new(itemsSource); foreach (var item in spawnedItems) { if (itemsToAdd.Contains(item.ChipItem)) itemsToAdd.Remove(item.ChipItem); } foreach (var item in itemsToAdd) { VisualElement chipItemVisuals = itemPrefab.Instantiate(); realItemsParent.Insert(0, chipItemVisuals); spawnedItems.Add(new MultiSelectChipItem(chipItemVisuals, item, this)); } } public void TryRemoveItem(IMultiSelectChipItem item) { OnTryingToRemoveItem?.Invoke(item); } private void InitLabel() { label = new Label() { pickingMode = PickingMode.Ignore }; Insert(0, label); } private void OnUxmlLoaded(AsyncOperationHandle operation) { if (operation.Status == AsyncOperationStatus.Succeeded) { SetVisuals(operation.Result); Addressables.Release(uxmlHandle); UpdateItems(); } else { Debug.LogError($"Asset failed to load."); } } private void OnItemUxmlLoaded(AsyncOperationHandle operation) { if (operation.Status == AsyncOperationStatus.Succeeded) { itemPrefab = operation.Result; Addressables.Release(itemUxmlHandle); UpdateItems(); } else { Debug.LogError($"Asset failed to load."); } } private void SetVisuals(VisualTreeAsset treeAsset) { realItemsParent = treeAsset.Instantiate(); Add(realItemsParent); var button = realItemsParent.Q