Skip to content

Instantly share code, notes, and snippets.

@liveasnotes
Last active November 26, 2022 07:17
Show Gist options
  • Select an option

  • Save liveasnotes/2a0a1cc75fabbebaba38164f24747918 to your computer and use it in GitHub Desktop.

Select an option

Save liveasnotes/2a0a1cc75fabbebaba38164f24747918 to your computer and use it in GitHub Desktop.
unity editor extension: Hierarchy Objects Alphabetical Sorter
// Copyright 2022-2023 liveanotes
// Released under the MIT license.
// see https://opensource.org/licenses/MIT
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.Linq;
namespace liveasnotes.Unity.EditorExtension.HOAS
{
[InitializeOnLoad]
static class Preferences
{
const string _PREF_KEY_LIVEASNOTES_HIERARCHY_OBJECTS_ALPHABETICAL_SORTING_AUTOMATED = "liveasnotes_HierarchyObjectsAlphabeticalSorting_Automated";
// cf. https://forum.unity.com/threads/editorprefs-pre-defined-magic-strings.414017/
public static bool isSiblingIndexAlphabeticalSortingAutomated
{
get => EditorPrefs.GetBool(_PREF_KEY_LIVEASNOTES_HIERARCHY_OBJECTS_ALPHABETICAL_SORTING_AUTOMATED);
set
{
EditorPrefs.SetBool(_PREF_KEY_LIVEASNOTES_HIERARCHY_OBJECTS_ALPHABETICAL_SORTING_AUTOMATED, value);
if (isSiblingIndexAlphabeticalSortingAutomated)
{
HierarchyObjectsAlphabeticalSorter.SortAlphabetically(Resources.FindObjectsOfTypeAll<Transform>(), true);
}
}
}
[SettingsProvider]
static SettingsProvider CreatePreferencesView()
{
SettingsProvider provider = new SettingsProvider("Preferences/あ_liveasnotes.HOAS", SettingsScope.User)
// The character string that is following "Preferences/" determines the order in the item list.
{
label = "liveasnotes.HOAS",
guiHandler = (searchText) =>
{
isSiblingIndexAlphabeticalSortingAutomated = EditorGUILayout.ToggleLeft(" Automate HOAS(Hierarchy Objects Alphabetical Sorter).", isSiblingIndexAlphabeticalSortingAutomated);
GUILayout.Space(10);
EditorGUILayout.LabelField("--- README ---");
EditorGUILayout.HelpBox("Recommend: DISABLE the toggle of Preferences > General > Enable Alphanumeric Sorting if you have enabled the toggle; otherwise, you can not recognize the result of this automated sorting feature.", MessageType.Warning);
EditorGUILayout.HelpBox("Manual sorting (default shortcut keys: Alt+L and Shift+Alt+L) supports the undo feature of Unity Editor.", MessageType.Info);
EditorGUILayout.HelpBox("Automated sorting does NOT support the undo feature to prevent user confusion.", MessageType.Info);
EditorGUILayout.HelpBox("Automated sorting does NOT handle the items of canvas to prevent unexpected modification of layer sorting order for UI.", MessageType.Info);
},
keywords = new HashSet<string>(new[] { "Hierarchy", "Object", "Alphabet", "Sort", "Order" })
};
return provider;
}
}
[InitializeOnLoad]
public class HierarchyObjectsAlphabeticalSorter
{
// cf. https://sleepygamersmemo.blogspot.com/2018/08/unity-sort-hierarchy.html
static bool isProcessOngoing = false;
static bool hasSiblingIndexChanged = false;
static HierarchyObjectsAlphabeticalSorter()
{
EditorApplication.hierarchyChanged += OnHierarchyChanged;
}
static void OnHierarchyChanged()
{
if (Preferences.isSiblingIndexAlphabeticalSortingAutomated)
{
if (hasSiblingIndexChanged)
{
hasSiblingIndexChanged = false;
}
else if (!isProcessOngoing)
{
SortAlphabetically(Resources.FindObjectsOfTypeAll<Transform>());
}
}
}
public static void SortAlphabetically(Transform[] _transforms, bool _isCalledManually = false, bool _isCanvasIgnored = true)
{
isProcessOngoing = true;
foreach (IGrouping<Transform, Transform> siblings in _transforms.GroupBy(_obj => _obj.parent))
{
if (_isCanvasIgnored && siblings.Key && siblings.Key.TryGetComponent(out Canvas canvas))
// `siblings.Key` -> Avoid Null Reference Exception caused by that the root object has null parent.
// `*.TryGetComponent(out Canvas *)` -> Check whether it is canvas to avoid sorting its item.
{
continue;
}
else
{
int i = 0;
foreach (Transform t in siblings.OrderBy(_obj => _obj.name))
{
if (_isCalledManually)
{
Undo.RecordObject(t, "Manual Sort");
Undo.SetTransformParent(t, t.parent, "Manual Sort");
}
t.SetSiblingIndex(i);
i++;
}
}
}
hasSiblingIndexChanged = true;
// - The all above modifications with SetSiblingIndex() are stacked on `hierarchyChanged`
// and the event seems to be called just once after exiting `EditorApplication.update`.
// - This flag is set for a workaround to skip the process
// that causes an endless loop of modification.
isProcessOngoing = false;
}
[MenuItem("Edit / Sort Alphabetically &L")]
// Alt + L
public static void SortManually()
{
SortAlphabetically(Selection.transforms, true);
}
[MenuItem("Edit / Sort Alphabetically (also Canvas Children) #&L")]
// Shift + Alt + L
public static void ForceSortManually()
{
SortAlphabetically(Selection.transforms, true, false);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment