NMD83
November 24, 2019, 10:06pm
1
Pretty straight forward…
**On Windows folders are sorted together at the top of the file list before their sibling files in the same folder.
I have found the Finder setting to accomplish this, which works for Finder, but it has no effect on how Unity operates**: Sort Folders and Files on Mac Just Like Windows With a Single Setting Switch
I also found a forum post from 2013 with an extension that is designed to fix this. However despite fixing the missing “&” symbols in the script, it still does not seem to work: https://forum.unity.com/threads/project-window-extension-script-folders-sorting-double-click-expand-collapse.180186/
I ALSO found a Unity Answers question on this from 2015, to which the first response came from me, today, 4.5 years later: https://answers.unity.com/questions/991484/project-view-folder-grouping-in-osx.html
REALLY hoping someone can help me out here because it’s driving me insane.
i’m also looking for the same thing. this folder-file sorting is killing my production speed.
Sorry to raise this post, but had to tweak some of the code to get it to work, thought I would share.
thank you @WeslomPo for the original!
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEditor;
using System.Reflection;
[InitializeOnLoad]
public static class ProjectBrowserExtension
{
private const string k_UnityEditorProjectBrowserAssemblyName = "UnityEditor.ProjectBrowser";
private const string k_ProjectBrowsersFieldName = "s_ProjectBrowsers";
private const string k_AssetTreeFieldName = "m_AssetTree";
private const string k_ListAreaFieldName = "m_ListArea";
private const string k_DataFieldName = "data";
private const string k_FoldersFirstFieldName = "foldersFirst";
private static readonly object s_BoolTrue = true;
static ProjectBrowserExtension()
{
EditorApplication.projectChanged += OnChanged;
EditorApplication.playModeStateChanged += OnPlayMode;
EditorApplication.projectWindowItemOnGUI += OnFirstTime;
}
private static void OnFirstTime(string guid, Rect _)
{
EditorApplication.projectWindowItemOnGUI -= OnFirstTime;
Refresh();
}
private static void OnChanged() => Refresh();
private static void OnPlayMode(PlayModeStateChange obj) => Refresh();
/// <summary>
/// foreach browser in UnityEditor.ProjectBrowser.s_ProjectBrowsers
/// browser.m_AssetTree.data.foldersFirst = true
/// browser.m_ListArea.foldersFirst = true
/// </summary>
private static void Refresh()
{
Assembly assembly = Assembly.GetAssembly(typeof(UnityEditor.Editor));
Type projectBrowser = assembly.GetType(k_UnityEditorProjectBrowserAssemblyName);
FieldInfo field = projectBrowser.GetField(k_ProjectBrowsersFieldName, BindingFlags.Static | BindingFlags.NonPublic);
if (field == null)
return;
IEnumerable list = (IEnumerable) field.GetValue(projectBrowser);
foreach (object pb in list)
SetFolderFirstForProjectWindow(pb);
}
private static void SetFolderFirstForProjectWindow(object pb)
{
IEnumerable<FieldInfo> members = pb.GetType().GetRuntimeFields();
int maxMembersSought = 2;
foreach (FieldInfo member in members)
{
switch (member.Name)
{
// One column
case k_AssetTreeFieldName:
SetOneColumnFolderFirst(pb, member);
maxMembersSought--;
break;
// Two column
case k_ListAreaFieldName:
SetTwoColumnFolderFirst(pb, member);
maxMembersSought--;
break;
}
if (maxMembersSought == 0)
break;
}
}
private static void SetTwoColumnFolderFirst(object pb, FieldInfo listAreaField)
{
if (listAreaField == null)
return;
object listArea = listAreaField.GetValue(pb);
// safety check
if (listArea == null)
return;
PropertyInfo folderFirst = listArea.GetType().GetProperties().Single(x => x.Name == k_FoldersFirstFieldName);
folderFirst.SetValue(listArea, s_BoolTrue);
}
private static void SetOneColumnFolderFirst(object pb, FieldInfo assetTreeField)
{
if (assetTreeField == null)
return;
object assetTree = assetTreeField.GetValue(pb);
// Fix: as we are looping all members, it's possible to end up in a case where one member is seen first,
// this will be null.
if (assetTree == null)
return;
PropertyInfo data = assetTree.GetType().GetRuntimeProperties().Single(x => x.Name == k_DataFieldName);
// AssetsTreeViewDataSource
object dataSource = data.GetValue(assetTree);
// safety check
if (dataSource == null)
return;
PropertyInfo folderFirst = dataSource.GetType().GetProperties().Single(x => x.Name == k_FoldersFirstFieldName);
folderFirst.SetValue(dataSource, s_BoolTrue);
}
}
,Sorry to raise this post, but had to tweak some of the code to get it to work, thought I would share.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEditor;
using System.Reflection;
[InitializeOnLoad]
public static class ProjectBrowserExtension
{
private const string k_UnityEditorProjectBrowserAssemblyName = "UnityEditor.ProjectBrowser";
private const string k_ProjectBrowsersFieldName = "s_ProjectBrowsers";
private const string k_AssetTreeFieldName = "m_AssetTree";
private const string k_ListAreaFieldName = "m_ListArea";
private const string k_DataFieldName = "data";
private const string k_FoldersFirstFieldName = "foldersFirst";
private static readonly object s_BoolTrue = true;
static ProjectBrowserExtension()
{
EditorApplication.projectChanged += OnChanged;
EditorApplication.playModeStateChanged += OnPlayMode;
EditorApplication.projectWindowItemOnGUI += OnFirstTime;
}
private static void OnFirstTime(string guid, Rect _)
{
EditorApplication.projectWindowItemOnGUI -= OnFirstTime;
Refresh();
}
private static void OnChanged() => Refresh();
private static void OnPlayMode(PlayModeStateChange obj) => Refresh();
/// <summary>
/// foreach browser in UnityEditor.ProjectBrowser.s_ProjectBrowsers
/// browser.m_AssetTree.data.foldersFirst = true
/// browser.m_ListArea.foldersFirst = true
/// </summary>
private static void Refresh()
{
Assembly assembly = Assembly.GetAssembly(typeof(UnityEditor.Editor));
Type projectBrowser = assembly.GetType(k_UnityEditorProjectBrowserAssemblyName);
FieldInfo field = projectBrowser.GetField(k_ProjectBrowsersFieldName, BindingFlags.Static | BindingFlags.NonPublic);
if (field == null)
return;
IEnumerable list = (IEnumerable) field.GetValue(projectBrowser);
foreach (object pb in list)
SetFolderFirstForProjectWindow(pb);
}
private static void SetFolderFirstForProjectWindow(object pb)
{
IEnumerable<FieldInfo> members = pb.GetType().GetRuntimeFields();
int maxMembersSought = 2;
foreach (FieldInfo member in members)
{
switch (member.Name)
{
// One column
case k_AssetTreeFieldName:
SetOneColumnFolderFirst(pb, member);
maxMembersSought--;
break;
// Two column
case k_ListAreaFieldName:
SetTwoColumnFolderFirst(pb, member);
maxMembersSought--;
break;
}
if (maxMembersSought == 0)
break;
}
}
private static void SetTwoColumnFolderFirst(object pb, FieldInfo listAreaField)
{
if (listAreaField == null)
return;
object listArea = listAreaField.GetValue(pb);
// safety check
if (listArea == null)
return;
PropertyInfo folderFirst = listArea.GetType().GetProperties().Single(x => x.Name == k_FoldersFirstFieldName);
folderFirst.SetValue(listArea, s_BoolTrue);
}
private static void SetOneColumnFolderFirst(object pb, FieldInfo assetTreeField)
{
if (assetTreeField == null)
return;
object assetTree = assetTreeField.GetValue(pb);
// Fix: as we are looping all members, it's possible to end up in a case where one member is seen first,
// this will be null.
if (assetTree == null)
return;
PropertyInfo data = assetTree.GetType().GetRuntimeProperties().Single(x => x.Name == k_DataFieldName);
// AssetsTreeViewDataSource
object dataSource = data.GetValue(assetTree);
// safety check
if (dataSource == null)
return;
PropertyInfo folderFirst = dataSource.GetType().GetProperties().Single(x => x.Name == k_FoldersFirstFieldName);
folderFirst.SetValue(dataSource, s_BoolTrue);
}
}
It’s 2023 and we still don’t have a setting for this.
Hi @NMD83 ! How are you?
So, problem lay here
// not actually code
// foreach browser in UnityEditor.ProjectBrowser.s_ProjectBrowsers
// browser.m_AssetTree.data.foldersFirst = true
// browser.m_ListArea.foldersFirst = true
[InitializeOnLoad]
public static class ProjectBrowserExtension {
static ProjectBrowserExtension()
{
EditorApplication.projectChanged += OnChanged;
EditorApplication.playModeStateChanged += OnPlayMode;
EditorApplication.projectWindowItemOnGUI += OnFirstTime;
}
private static void OnFirstTime(string guid, Rect selectionrect)
{
EditorApplication.projectWindowItemOnGUI -= OnFirstTime;
Refresh();
}
private static void OnChanged() => Refresh();
private static void OnPlayMode(PlayModeStateChange obj) => Refresh();
/// <summary>
/// По сути, присваиваем значение определенной переменной:
/// foreach browser in UnityEditor.ProjectBrowser.s_ProjectBrowsers
/// browser.m_AssetTree.data.foldersFirst = true
/// browser.m_ListArea.foldersFirst = true
/// </summary>
private static void Refresh()
{
var assembly = Assembly.GetAssembly(typeof(Editor));
var projectBrowser = assembly.GetType("UnityEditor.ProjectBrowser");
var field = projectBrowser.GetField("s_ProjectBrowsers", BindingFlags.Static | BindingFlags.NonPublic);
if (field == null)
return;
var list = (IEnumerable) field.GetValue(projectBrowser);
foreach (var pb in list)
SetFolderFirstForProjectWindow(pb);
}
private static void SetFolderFirstForProjectWindow(object pb)
{
var members = pb.GetType().GetRuntimeFields();
foreach (var member in members)
switch (member.Name)
{
// One column
case "m_AssetTree":
SetOneColumnFolderFirst(pb, member);
break;
// Two column
case "m_ListArea":
SetTwoColumnFolderFirst(pb, member);
break;
}
}
private static void SetTwoColumnFolderFirst(object pb, FieldInfo listAreaField)
{
if (listAreaField == null)
return;
var listArea = listAreaField.GetValue(pb);
var folderFirst = listArea.GetType().GetProperties().Single(x => x.Name == "foldersFirst");
folderFirst.SetValue(listArea, true);
}
private static void SetOneColumnFolderFirst(object pb, FieldInfo assetTreeField)
{
if (assetTreeField == null)
return;
var assetTree = assetTreeField.GetValue(pb);
var data = assetTree.GetType().GetRuntimeProperties().Single(x => x.Name == "data");
// AssetsTreeViewDataSource
var dataSource = data.GetValue(assetTree);
var folderFirst = dataSource.GetType().GetProperties().Single(x => x.Name == "foldersFirst");
folderFirst.SetValue(dataSource, true);
}
}
Check my answer here: https://forum.unity.com/threads/project-window-extension-script-folders-sorting-double-click-expand-collapse.180186/#post-8437088
…
"The script above was breaking every time anything reloaded and you’d need to click something inside the project folder for it to sort.
So instead i just directly modified UnityEngine.CoreModule.dll using dnSpy (running on windows 11 in parallels)
UnityEditor → ProjectBrowser → GetShouldShowFoldersFirst() → right click → Edit IL instructions → replace instructions with “ldc.i4.1”, “ret” (meaning ‘return true;’).
Save, replace the dll. Have windows style sorting forever without any scripts. Works with Unity 2021.3.9f1 for me."