Community Made Tools

Have you made any useful utilities with Odin?

Login and submit your creations here

Tag attribute

Authored by Maciej
Shared 11-11-2021

Attribute that displays string, List and string[] as dropdown with tags.

Gist version is easier and faster to modify, so use it as it may have newer version than here

Gist

TagAttribute.cs

using System; [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public class TagAttribute : Attribute { }

TagAttributeDrawer.cs

using System.Collections.Generic; using System.Linq; using System.Reflection; using Sirenix.OdinInspector; using Sirenix.OdinInspector.Editor; using UnityEditor; using UnityEngine; public class TagStringDrawer : OdinAttributeDrawer<TagAttribute, string> { private GUIContent m_buttonContent = new GUIContent(); protected override void Initialize() => UpdateButtonContent(); private void UpdateButtonContent() { m_buttonContent.text = ValueEntry.SmartValue; } protected override void DrawPropertyLayout(GUIContent label) { var rect = EditorGUILayout.GetControlRect(label != null); if (label == null) rect = EditorGUI.IndentedRect(rect); else rect = EditorGUI.PrefixLabel(rect, label); if (!EditorGUI.DropdownButton(rect, m_buttonContent, FocusType.Passive)) return; var selector = new GenericSelector<string>(UnityEditorInternal.InternalEditorUtility.tags); selector.SetSelection(ValueEntry.SmartValue); selector.ShowInPopup(rect.position); selector.SelectionChanged += x => { ValueEntry.Property.Tree.DelayAction(() => { ValueEntry.SmartValue = x.FirstOrDefault(); UpdateButtonContent(); }); }; } } public abstract class TagStringListBaseDrawer<T> : OdinAttributeDrawer<TagAttribute, T> where T : IList<string> { private GUIContent m_buttonContent = new GUIContent(); protected override void Initialize() => UpdateButtonContent(); private void UpdateButtonContent() { m_buttonContent.text = m_buttonContent.tooltip = string.Join(", ", ValueEntry.SmartValue); } protected override void DrawPropertyLayout(GUIContent label) { var rect = EditorGUILayout.GetControlRect(label != null); if (label == null) rect = EditorGUI.IndentedRect(rect); else rect = EditorGUI.PrefixLabel(rect, label); if (!EditorGUI.DropdownButton(rect, m_buttonContent, FocusType.Passive)) return; var selector = new TagSelector(UnityEditorInternal.InternalEditorUtility.tags); rect.y += rect.height; selector.SetSelection(ValueEntry.SmartValue); selector.ShowInPopup(rect.position); selector.SelectionChanged += x => { ValueEntry.Property.Tree.DelayAction(() => { UpdateValue(x); UpdateButtonContent(); }); }; } protected abstract void UpdateValue(IEnumerable<string> x); } [DrawerPriority(1)] [DontApplyToListElements] public class TagStringArrayDrawer : TagStringListBaseDrawer<string[]> { protected override void UpdateValue(IEnumerable<string> x) => ValueEntry.SmartValue = x.ToArray(); } [DrawerPriority(1)] [DontApplyToListElements] public class TagStringListDrawer : TagStringListBaseDrawer<List<string>> { protected override void UpdateValue(IEnumerable<string> x) => ValueEntry.SmartValue = x.ToList(); } public class TagSelector : GenericSelector<string> { private FieldInfo m_requestCheckboxUpdate; public TagSelector(string[] tags) : base(tags) { CheckboxToggle = true; m_requestCheckboxUpdate = typeof(GenericSelector<string>).GetField("requestCheckboxUpdate", BindingFlags.NonPublic | BindingFlags.Instance); } protected override void DrawSelectionTree() { base.DrawSelectionTree(); EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("None")) { SetSelection(new List<string>()); m_requestCheckboxUpdate.SetValue(this, true); TriggerSelectionChanged(); } if (GUILayout.Button("All")) { SetSelection(UnityEditorInternal.InternalEditorUtility.tags); m_requestCheckboxUpdate.SetValue(this, true); TriggerSelectionChanged(); } EditorGUILayout.EndHorizontal(); } }