Skip to content

Instantly share code, notes, and snippets.

@My-Key
Last active December 3, 2022 14:42
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save My-Key/91ebe8dcb0e71655ecb576291b459e11 to your computer and use it in GitHub Desktop.
Save My-Key/91ebe8dcb0e71655ecb576291b459e11 to your computer and use it in GitHub Desktop.
ListAsString attribute for Odin - draws list elements as string, and when selected true gui for this element is drawn below list
using System;
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class ListAsStringAttribute : Attribute
{
public string StringGetter { get; }
public string SetSelectedFunction { get; }
/// <summary>
/// Display list elements as labels with text from `ToString` or from <paramref name="stringGetter"/>.
/// When item is selected its true drawer is drawn below list.
/// </summary>
/// <param name="stringGetter">Replace `ToString` with an action that takes an index as a parameter and returns a string.
/// Example: `string GetString(int index)`</param>
/// <param name="setSelectedFunction">Call provided method with an index as a parameter when selected.
/// Example: `SetSelected(int index)`</param>
public ListAsStringAttribute(string stringGetter = null, string setSelectedFunction = null)
{
StringGetter = stringGetter;
SetSelectedFunction = setSelectedFunction;
}
}
using Sirenix.OdinInspector.Editor;
using Sirenix.OdinInspector.Editor.ActionResolvers;
using Sirenix.OdinInspector.Editor.ValueResolvers;
using Sirenix.Utilities;
using Sirenix.Utilities.Editor;
using UnityEditor;
using UnityEngine;
using NamedValue = Sirenix.OdinInspector.Editor.ValueResolvers.NamedValue;
// Based on ListItemSelector
// https://www.odininspector.com/community-tools/561/listitemselector-attribute-easily-select-items-in-lists
[DrawerPriority(0,0,1)]
public class ListAsStringAttributeDrawer : OdinAttributeDrawer<ListAsStringAttribute>
{
private static readonly Color SELECTED_COLOR = new Color(0.301f, 0.563f, 1f, 0.4f);
private bool m_isListElement;
private PropertyContext<InspectorProperty> m_globalSelectedProperty;
private PropertyContext<InspectorProperty> m_globalSelectedPropertyTemp;
private InspectorProperty m_selectedProperty;
private PropertyContext<bool> m_drawingSelected;
private ValueResolver<string> m_stringResolver;
private ActionResolver m_selectedIndexSetter;
protected override void Initialize()
{
m_isListElement = Property.Parent != null && Property.Parent.ChildResolver is IOrderedCollectionResolver;
var listProperty = !m_isListElement ? Property : Property.Parent;
var baseMemberProperty = listProperty.FindParent(x =>
x.Info.PropertyType == PropertyType.Value, true);
m_globalSelectedProperty =
baseMemberProperty.Context.GetGlobal("SelectedProperty" + baseMemberProperty.GetHashCode(),
(InspectorProperty) null);
m_globalSelectedPropertyTemp =
baseMemberProperty.Context.GetGlobal("SelectedPropertyTemp" + baseMemberProperty.GetHashCode(),
(InspectorProperty) null);
m_drawingSelected =
baseMemberProperty.Context.GetGlobal("DrawingSelected" + baseMemberProperty.GetHashCode(),
false);
if (!m_drawingSelected.Value && !string.IsNullOrWhiteSpace(Attribute.StringGetter))
m_stringResolver = ValueResolver.Get<string>(Property, Attribute.StringGetter,
new NamedValue("index", typeof(int), 0));
if (!m_isListElement && !string.IsNullOrWhiteSpace(Attribute.SetSelectedFunction))
{
m_selectedIndexSetter = ActionResolver.Get(Property, Attribute.SetSelectedFunction,
new[] { new Sirenix.OdinInspector.Editor.ActionResolvers.NamedValue("index", typeof(int)) });
Select(-1);
}
}
protected override void DrawPropertyLayout(GUIContent label)
{
var eventType = Event.current.type;
if (m_isListElement)
{
var isSelected = m_globalSelectedProperty.Value == Property;
if (isSelected && m_drawingSelected.Value)
{
SirenixEditorGUI.BeginBox("Selected");
CallNextDrawer(label);
SirenixEditorGUI.EndBox();
}
else if (!m_drawingSelected.Value)
{
Property.PushDraw();
Property.AnimateVisibility = false;
var rect = GUIHelper.GetCurrentLayoutRect();
string str = null;
if (m_stringResolver != null && !m_stringResolver.HasError)
{
m_stringResolver.Context.NamedValues.Set("index", Property.Index);
str = m_stringResolver.GetValue();
}
// Copied from DisplayAsString
if (str == null)
str = Property.ValueEntry.WeakSmartValue?.ToString() ?? "Null";
GUIContent content = GUIHelper.TempContent(str);
var labelRect = EditorGUILayout.GetControlRect(false,
SirenixGUIStyles.MultiLineLabel.CalcHeight(content,
Property.LastDrawnValueRect.width - GUIHelper.BetterLabelWidth),
GUILayoutOptions.MinWidth(0.0f));
if (eventType == EventType.Repaint && isSelected)
EditorGUI.DrawRect(rect, SELECTED_COLOR);
if (eventType == EventType.MouseDown && rect.Contains(Event.current.mousePosition) &&
!isSelected)
{
m_globalSelectedProperty.Value = Property;
}
if (label == null)
GUI.Label(labelRect, content, SirenixGUIStyles.MultiLineLabel);
else
{
GUI.Label(EditorGUI.PrefixLabel(labelRect, label), content,
SirenixGUIStyles.MultiLineLabel);
}
Property.PopDraw();
}
}
else
{
if (m_selectedIndexSetter != null)
m_selectedIndexSetter.DrawError();
if (m_stringResolver != null && m_stringResolver.HasError)
m_stringResolver.DrawError();
CallNextDrawer(label);
if (Property.State.Expanded && m_selectedProperty != null && m_selectedProperty.Index < Property.Children.Count)
{
m_drawingSelected.Value = true;
Property.Children[m_selectedProperty.Index].Draw(null);
m_drawingSelected.Value = false;
}
if (eventType == EventType.MouseDown)
{
var sel = m_globalSelectedProperty.Value;
// Select
if (sel != null && sel != m_selectedProperty)
{
m_selectedProperty = sel;
Select(m_selectedProperty.Index);
}
}
// Deselect when destroyed or not expanded
if ((!Property.State.Expanded || m_selectedProperty != null &&
(m_selectedProperty.Index < Property.Children.Count &&
m_selectedProperty != Property.Children[m_selectedProperty.Index] ||
m_selectedProperty.Index >= Property.Children.Count)))
{
Select(m_selectedProperty != null ? m_selectedProperty.Index : -1);
m_selectedProperty = null;
m_globalSelectedProperty.Value = null;
m_globalSelectedPropertyTemp.Value = null;
}
}
}
private void Select(int index)
{
GUIHelper.RequestRepaint();
if (m_selectedIndexSetter != null && !m_selectedIndexSetter.HasError)
{
Property.Tree.DelayAction(() =>
{
m_selectedIndexSetter.Context.NamedValues.Set("index", index);
m_selectedIndexSetter.DoActionForAllSelectionIndices();
});
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment