Community Made Tools

Have you made any useful utilities with Odin?

Login and submit your creations here

Prefab Override Drawer

Authored by Maciej
Shared 08-10-2020

This drawer brings back prefab value override drawing features from default Unity drawing - every overriden value and all their parent classes have bolded label and blue line next to it.

Works with Odin 2.1.x and 3.x

May reduce editor's performance, if you have many, big classes nested into other classes, because script have to go throguh every child value trying to find overrides every time it is redrawn.

GitHub Gist

PrefabOverrideDrawer.cs - just put it in Editor folder

using Sirenix.OdinInspector.Editor;
using Sirenix.Utilities;
using Sirenix.Utilities.Editor;
using UnityEditor;
using UnityEngine;

[DrawerPriority(0,10000,0)]
public sealed class PrefabOverrideDrawer<T> : OdinValueDrawer<T>
{
	private static readonly Color OVERRIDE_MARGIN_COLOR = new Color(0.003921569f, 0.6f, 0.9215686f, 0.75f);
	
	protected override bool CanDrawValueProperty(InspectorProperty property)
	{
#if ODIN_INSPECTOR_3
		return !property.IsTreeRoot;
#else
		return base.CanDrawValueProperty(property);
#endif
	}

	/// <summary>
	/// Draws the property.
	/// </summary>
	protected override void DrawPropertyLayout(GUIContent label)
	{
		bool draw = Event.current.type == EventType.Repaint;

#if ODIN_INSPECTOR_3
		draw = draw && GlobalConfig<GeneralDrawerConfig>.Instance.ShowPrefabModifiedValueBar;
#endif
		
		bool overriden = false;
		bool childOrCollectionOverriden = false;

		if (draw)
		{
			var valueOverriden = Property.ValueEntry != null && Property.ValueEntry.ValueChangedFromPrefab;
			var childValueOverriden = false;
			var collectionChanged = false;
			
			if (!valueOverriden)
			{
				collectionChanged = Property.ValueEntry != null && (Property.ValueEntry.ListLengthChangedFromPrefab ||
				                                                    Property.ValueEntry.DictionaryChangedFromPrefab);
				
				if (!collectionChanged)
					childValueOverriden = ChildValueOverriden(Property);
			}
			
			overriden = childOrCollectionOverriden = childValueOverriden || collectionChanged;
			
#if !ODIN_INSPECTOR_3
			overriden = overriden || valueOverriden;
#endif

			if (overriden)
				GUIHelper.BeginLayoutMeasuring();
		}
		
		if (draw && childOrCollectionOverriden)
			GUIHelper.PushIsBoldLabel(true);
		
		CallNextDrawer(label);
		
		if (draw && childOrCollectionOverriden)
			GUIHelper.PopIsBoldLabel();

		if (!draw || !overriden)
			return;
		
		var rect = GUIHelper.EndLayoutMeasuring();

		var partOfCollection = Property.Parent != null && Property.Parent.ChildResolver is ICollectionResolver;
		
		if (partOfCollection)
			rect = GUIHelper.GetCurrentLayoutRect();

		GUIHelper.IndentRect(ref rect);

		GUIHelper.PushGUIEnabled(true);

		if (!partOfCollection && childOrCollectionOverriden)
			rect.height = EditorGUIUtility.singleLineHeight;
		
		rect.width = 2;
		rect.x -= 2;
		
		SirenixEditorGUI.DrawSolidRect(rect, OVERRIDE_MARGIN_COLOR);
		
		GUIHelper.PopGUIEnabled();
	}

	private static bool ChildValueOverriden(InspectorProperty property)
	{
		foreach (var child in property.Children)
		{
			if (child.ValueEntry != null && (child.ValueEntry.ValueChangedFromPrefab || 
			                                 child.ValueEntry.ListLengthChangedFromPrefab ||
			                                 child.ValueEntry.DictionaryChangedFromPrefab))
			{
				return true;
			}

			if (ChildValueOverriden(child))
				return true;
		}

		return false;
	}
}