using System; using System.Collections.Generic; using UnityEditor.U2D.Common; using UnityEditor.UIElements; using UnityEngine; using UnityEngine.U2D.Animation; using UnityEngine.UIElements; namespace UnityEditor.U2D.Animation.SceneOverlays { class SpriteResolverSelector : VisualElement { static class Styles { public const string spriteResolverNameLabel = SpriteSwapOverlay.rootStyle + "__resolver-name-label"; public const string categoryAndLabelNameContainer = SpriteSwapOverlay.rootStyle + "__category-and-label-name-container"; public const string selector = SpriteSwapOverlay.rootStyle + "__selector"; public const string descriptionLabel = SpriteSwapOverlay.rootStyle + "__label-description"; } Label m_SpriteResolverLabel; INavigableElement m_CategoryContainer; INavigableElement m_LabelContainer; INavigableElement m_CurrentSelection; Label m_LabelNameLabel; string m_Category = string.Empty; string m_Label = string.Empty; List m_AvailableCategories; List> m_AvailableLabels; SerializedObject m_SerializedResolver; SerializedProperty m_SpriteHashProperty; SpriteResolver spriteResolver => (SpriteResolver)m_SerializedResolver?.targetObject; public SpriteResolverSelector(INavigableElement categoryContainer, INavigableElement labelContainer) { focusable = true; AddToClassList(Styles.selector); m_SpriteResolverLabel = new Label(); m_SpriteResolverLabel.AddToClassList(Styles.spriteResolverNameLabel); Add(m_SpriteResolverLabel); var categoryAndLabelNameHolder = new VisualElement(); categoryAndLabelNameHolder.AddToClassList(Styles.categoryAndLabelNameContainer); Add(categoryAndLabelNameHolder); m_CategoryContainer = categoryContainer; m_CategoryContainer.onSelectionChange += OnCategorySelected; var categoryContainerVisual = m_CategoryContainer.visualElement; categoryContainerVisual.RegisterCallback(OnFocusIn); categoryContainerVisual.RegisterCallback(OnFocusOut); categoryAndLabelNameHolder.Add(categoryContainerVisual); m_LabelNameLabel = new Label(); m_LabelNameLabel.AddToClassList(Styles.descriptionLabel); categoryAndLabelNameHolder.Add(m_LabelNameLabel); m_LabelContainer = labelContainer; m_LabelContainer.onSelectionChange += OnLabelSelected; var labelContainerVisual = m_LabelContainer.visualElement; labelContainerVisual.RegisterCallback(OnFocusIn); labelContainerVisual.RegisterCallback(OnFocusOut); Add(labelContainerVisual); RegisterCallback(OnKeyDown); RegisterCallback(OnDetachFromPanel); } public void Select() { m_LabelContainer.visualElement.Focus(); } public void SceneUpdate() { UpdateAnimationColor(); } void OnDetachFromPanel(DetachFromPanelEvent evt) { SetSpriteResolver(null); } public void SetSpriteResolver(SpriteResolver newSpriteResolver) { this.Unbind(); if (newSpriteResolver == null) return; m_SerializedResolver = new SerializedObject(newSpriteResolver); m_SpriteHashProperty = m_SerializedResolver.FindProperty(SpriteResolver.spriteHashPropertyName); this.TrackPropertyValue(m_SpriteHashProperty, OnResolvedSprite); ReadCategoryAndLabelFromSelection(); } void OnResolvedSprite(SerializedProperty serializedProperty) { ReadCategoryAndLabelFromSelection(); } void ReadCategoryAndLabelFromSelection() { var resolver = spriteResolver; if (resolver == null) return; m_Category = resolver.GetCategory() ?? string.Empty; m_Label = resolver.GetLabel() ?? string.Empty; UpdateVisuals(); } void UpdateVisuals() { var resolver = spriteResolver; if (resolver == null) return; m_SpriteResolverLabel.text = m_SpriteResolverLabel.tooltip = resolver.name; m_AvailableCategories = GetAvailableCategories(resolver) ?? new List(); m_AvailableLabels = new List>(); if (resolver.spriteLibrary != null) { foreach (var labelName in GetAvailableLabels(resolver, m_Category)) m_AvailableLabels.Add(new Tuple(labelName, resolver.spriteLibrary.GetSprite(m_Category, labelName))); } m_CategoryContainer.SetItems(m_AvailableCategories); m_CategoryContainer.Select(m_AvailableCategories.IndexOf(m_Category)); m_LabelContainer.SetItems(m_AvailableLabels); m_LabelContainer.Select(m_AvailableLabels.FindIndex(label => label.Item1 == m_Label)); m_Label = !string.IsNullOrWhiteSpace(m_Label) ? m_Label : TextContent.emptyCategory; m_LabelNameLabel.text = m_LabelNameLabel.tooltip = m_Label; m_LabelNameLabel.SetEnabled(m_AvailableLabels.Count > 0); if (m_LabelContainer.itemCount == 0) m_CurrentSelection = m_CategoryContainer; UpdateAnimationColor(); } void OnCategorySelected(int newSelection) { var resolver = spriteResolver; if (resolver == null) return; var categoryName = (string)m_CategoryContainer.GetItem(newSelection); if (categoryName == null || categoryName == m_Category) return; var availableLabels = resolver.spriteLibrary != null ? resolver.spriteLibrary.GetEntryNames(categoryName) : null; var labelList = availableLabels != null ? new List(availableLabels) : new List(); var labelName = string.Empty; if (labelList.Count > 0) labelName = labelList.Contains(m_Label) ? m_Label : labelList[0]; m_SpriteHashProperty.intValue = SpriteLibrary.GetHashForCategoryAndEntry(categoryName, labelName); m_SerializedResolver.ApplyModifiedProperties(); } void OnLabelSelected(int newSelection) { var resolver = spriteResolver; if (resolver == null) return; var (labelName, _) = (Tuple)m_LabelContainer.GetItem(newSelection); if (string.IsNullOrWhiteSpace(labelName) || labelName == m_Label) return; m_SpriteHashProperty.intValue = SpriteLibrary.GetHashForCategoryAndEntry(m_Category, labelName); m_SerializedResolver.ApplyModifiedProperties(); m_LabelNameLabel.text = m_LabelNameLabel.tooltip = labelName; } void OnKeyDown(KeyDownEvent evt) { if (m_CurrentSelection == null) return; switch (evt.keyCode) { case KeyCode.LeftArrow: var previousIndex = m_CurrentSelection.selectedIndex - 1; if (previousIndex < 0) previousIndex += m_CurrentSelection.itemCount; m_CurrentSelection.Select(previousIndex); evt.StopPropagation(); break; case KeyCode.RightArrow: var nextIndex = m_CurrentSelection.selectedIndex + 1; if (nextIndex >= m_CurrentSelection.itemCount) nextIndex = 0; m_CurrentSelection?.Select(nextIndex); evt.StopPropagation(); break; case KeyCode.DownArrow: case KeyCode.UpArrow: evt.StopPropagation(); break; } } void OnFocusIn(FocusInEvent evt) { var navigable = (INavigableElement)evt.currentTarget; if (navigable != null) m_CurrentSelection = navigable; } void OnFocusOut(FocusOutEvent evt) { var navigable = (INavigableElement)evt.currentTarget; if (navigable != null && m_CurrentSelection == navigable) m_CurrentSelection = null; } static List GetAvailableCategories(SpriteResolver spriteResolver) { if (spriteResolver == null || spriteResolver.spriteLibrary == null) return new List(); var availableCategories = spriteResolver.spriteLibrary.categoryNames; return availableCategories != null ? new List(availableCategories) : new List(); } static List GetAvailableLabels(SpriteResolver spriteResolver, string categoryName) { if (spriteResolver == null || spriteResolver.spriteLibrary == null || string.IsNullOrEmpty(categoryName)) return new List(); var availableLabels = spriteResolver.spriteLibrary.GetEntryNames(categoryName); return availableLabels != null ? new List(availableLabels) : new List(); } void UpdateAnimationColor() { var spriteResolverObject = spriteResolver; var animationState = PropertyAnimationState.NotAnimated; if (spriteResolverObject != null && m_SpriteHashProperty != null) { if (InternalEditorBridge.IsAnimated(spriteResolverObject, m_SpriteHashProperty)) { if (InternalEditorBridge.InAnimationRecording()) animationState = PropertyAnimationState.Recording; else if (InternalEditorBridge.IsCandidate(spriteResolverObject, m_SpriteHashProperty)) animationState = PropertyAnimationState.Candidate; else animationState = PropertyAnimationState.Animated; } } (m_LabelContainer as LabelContainer)?.SetAnimationState(animationState); } } }