module-navigation-tool/Editor/Toolbar/Tools/ScenePickerObjects/ScenePickerList.cs
Anders Ejlersen dd80d46ebb Scene Picker: Modified scene groups to contain LoadAsGroup, SortAsGroup and SortInSubmenuAsGroup
### Changed
-  Scene groups to contain three different types of behaviours for grouping:
   - Load As Group: Groups all scenes into a single entry
   - Sort As Group: Sorts all scenes into entries close to each other in the dropdown
   - Sort In Submenu As Group: Sorts all scenes into entries in a submenu

### Removed
- Removed "Scene sorting by Asset label" groups
2025-03-29 11:12:03 +01:00

410 lines
14 KiB
C#

using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace Module.NavigationTool.Editor.Toolbar
{
internal sealed class ScenePickerList
{
public GUIContent[] labels = new GUIContent[0];
public readonly List<SceneElement> scenes = new List<SceneElement>();
public readonly List<int> selected = new List<int>();
public void Refresh()
{
scenes.Clear();
selected.Clear();
var projectSettings = new ToolbarProjectSettings();
projectSettings.Load();
var settings = projectSettings.GetValueAs<ToolbarScenePickerProjectSettings.Settings>();
List<WorkingSetScene> assets = GetWorkingSet();
FetchAllScenesFromBuildSettings(assets);
FetchAllScenesFromSceneGroups(assets, settings.sceneGroups);
FetchAllRemainingScenes(assets);
FetchAllLabels();
FetchAllSelectedIndices();
}
private void FetchAllScenesFromBuildSettings(List<WorkingSetScene> assets)
{
for (var i = 0; i < assets.Count; i++)
{
WorkingSetScene asset = assets[i];
if (!asset.inBuildSettings)
continue;
scenes.Add(new SceneElement
{
name = asset.name,
shortname = asset.shortname,
paths = { asset.path }
});
}
}
private void FetchAllScenesFromSceneGroups(List<WorkingSetScene> assets, SceneGroupArray sceneGroups)
{
for (var i = 0; i < sceneGroups.Count; i++)
{
SceneGroup sceneGroup = sceneGroups[i];
string name = sceneGroup.name;
List<WorkingSetScene> filteredScenes;
if (sceneGroup.filterType == ESceneGroupFilterType.AssetLabels)
{
filteredScenes = GetAllWorkingSetScenesWithLabels(assets, sceneGroup.filters);
}
else if (sceneGroup.filterType == ESceneGroupFilterType.NameContains)
{
filteredScenes = GetAllWorkingSetScenesWithNameContaining(assets, sceneGroup.filters);
}
else
{
filteredScenes = GetAllWorkingSetScenesWithPathContaining(assets, sceneGroup.filters);
name = sceneGroup.name.Replace("/", "\\");
}
if (sceneGroup.behaviourType == ESceneGroupBehaviourType.LoadAsGroup)
{
if (sceneGroups.Count != 0)
scenes.Add(new SceneElement{ includeAsSelectable = false });
if (!string.IsNullOrEmpty(sceneGroup.optionalMainScenePath))
SetWorkingSceneAsFirst(filteredScenes, sceneGroup.optionalMainScenePath);
filteredScenes = FilterToUniques(filteredScenes);
var scene = new SceneElement
{
name = name,
shortname = name,
isGroup = true
};
for (var j = 0; j < filteredScenes.Count; j++)
{
scene.paths.Add(filteredScenes[j].path);
}
scene.name += $" ({scene.paths.Count})";
scenes.Add(scene);
}
else if (sceneGroup.behaviourType == ESceneGroupBehaviourType.SortAsGroup)
{
filteredScenes = FilterToUniques(filteredScenes);
filteredScenes = FilterAllExcept(filteredScenes, scenes);
if (filteredScenes.Count != 0)
scenes.Add(new SceneElement{ includeAsSelectable = false });
for (var j = 0; j < filteredScenes.Count; j++)
{
scenes.Add(new SceneElement
{
name = filteredScenes[j].name,
shortname = filteredScenes[j].shortname,
paths = { filteredScenes[j].path }
});
}
}
else if (sceneGroup.behaviourType == ESceneGroupBehaviourType.SortInSubmenuAsGroup)
{
if (string.IsNullOrEmpty(sceneGroup.subMenuPath))
continue;
filteredScenes = FilterToUniques(filteredScenes);
filteredScenes = FilterAllExcept(filteredScenes, scenes);
if (filteredScenes.Count != 0)
scenes.Add(new SceneElement{ includeAsSelectable = false });
for (var j = 0; j < filteredScenes.Count; j++)
{
scenes.Add(new SceneElement
{
name = $"{sceneGroup.subMenuPath}/{filteredScenes[j].name}",
shortname = filteredScenes[j].shortname,
paths = { filteredScenes[j].path }
});
}
}
}
}
private void FetchAllRemainingScenes(List<WorkingSetScene> assets)
{
List<WorkingSetScene> filteredScenes = FilterAllExcept(assets, scenes);
if (filteredScenes.Count != 0)
scenes.Add(new SceneElement{ includeAsSelectable = false });
for (var i = 0; i < filteredScenes.Count; i++)
{
scenes.Add(new SceneElement
{
name = filteredScenes[i].name,
shortname = filteredScenes[i].shortname,
paths = { filteredScenes[i].path }
});
}
}
private static List<WorkingSetScene> FilterToUniques(List<WorkingSetScene> list)
{
var uniques = new List<WorkingSetScene>(list.Count);
for (var i = 0; i < list.Count; i++)
{
if (!uniques.Contains(list[i]))
uniques.Add(list[i]);
}
return uniques;
}
private static List<WorkingSetScene> FilterAllExcept(List<WorkingSetScene> list, List<SceneElement> except, bool includeGroups = false)
{
var filtered = new List<WorkingSetScene>(list.Count);
for (var i = 0; i < list.Count; i++)
{
var contains = false;
for (var j = 0; j < except.Count; j++)
{
if (except[j].isGroup && !includeGroups)
continue;
if (!except[j].paths.Contains(list[i].path))
continue;
contains = true;
break;
}
if (!contains)
filtered.Add(list[i]);
}
return filtered;
}
private void FetchAllLabels()
{
labels = new GUIContent[scenes.Count];
for (var i = 0; i < scenes.Count; i++)
{
labels[i] = new GUIContent(scenes[i].name);
}
}
private void FetchAllSelectedIndices()
{
for (var i = 0; i < SceneManager.sceneCount; i++)
{
Scene scene = SceneManager.GetSceneAt(i);
if (string.IsNullOrEmpty(scene.path))
continue;
int index = IndexOfPath(scenes, scene.path, false);
if (index != -1)
selected.Add(index);
}
}
private static List<WorkingSetScene> GetAllWorkingSetScenesWithLabels(List<WorkingSetScene> assets, List<string> labels)
{
var list = new List<WorkingSetScene>();
for (var i = 0; i < labels.Count; i++)
{
list.AddRange(GetAllWorkingSetScenesWithLabel(assets, labels[i]));
}
return list;
}
private static List<WorkingSetScene> GetAllWorkingSetScenesWithLabel(List<WorkingSetScene> assets, string label)
{
var list = new List<WorkingSetScene>();
for (var i = 0; i < assets.Count; i++)
{
if (assets[i].labels.Contains(label))
list.Add(assets[i]);
}
return list;
}
private static List<WorkingSetScene> GetAllWorkingSetScenesWithNameContaining(List<WorkingSetScene> assets, List<string> filters)
{
var list = new List<WorkingSetScene>();
for (var i = 0; i < assets.Count; i++)
{
for (var j = 0; j < filters.Count; j++)
{
if (!assets[i].shortname.Contains(filters[j]))
continue;
list.Add(assets[i]);
break;
}
}
return list;
}
private static List<WorkingSetScene> GetAllWorkingSetScenesWithPathContaining(List<WorkingSetScene> assets, List<string> filters)
{
var list = new List<WorkingSetScene>();
for (var i = 0; i < assets.Count; i++)
{
for (var j = 0; j < filters.Count; j++)
{
if (!PathContains(assets[i].path, filters[j]))
continue;
list.Add(assets[i]);
break;
}
}
return list;
}
private static bool IsValidScene(string path)
{
if (string.IsNullOrEmpty(path))
return false;
if (!path.StartsWith("Assets"))
return false;
return AssetDatabase.LoadAssetAtPath<SceneAsset>(path) != null;
}
public int IndexOfPath(string path, bool includeGroups)
{
return IndexOfPath(scenes, path, includeGroups);
}
private static int IndexOfPath(List<SceneElement> list, string path, bool includeGroups)
{
for (var i = 0; i < list.Count; i++)
{
if (!includeGroups && list[i].isGroup)
continue;
if (!list[i].includeAsSelectable)
continue;
if (list[i].paths.IndexOf(path) != -1)
return i;
}
return -1;
}
private static bool PathContains(string path, string subpath)
{
path = path.Replace("\\", "/");
subpath = subpath.Replace("\\", "/");
return path.Contains(subpath);
}
private static bool PathEquals(string path, string subpath)
{
path = path.Replace("\\", "/");
subpath = subpath.Replace("\\", "/");
return path.Equals(subpath);
}
private static void SetWorkingSceneAsFirst(List<WorkingSetScene> scenes, string path)
{
int index = -1;
for (var i = 0; i < scenes.Count; i++)
{
if (!PathEquals(scenes[i].path, path))
continue;
index = i;
break;
}
if (index == -1)
return;
WorkingSetScene temp = scenes[0];
scenes[0] = scenes[index];
scenes[index] = temp;
}
private static List<WorkingSetScene> GetWorkingSet()
{
var workingSet = new List<WorkingSetScene>();
string[] guids = AssetDatabase.FindAssets("t:scene");
EditorBuildSettingsScene[] buildSettingsScenes = EditorBuildSettings.scenes;
for (var i = 0; i < guids.Length; i++)
{
string path = AssetDatabase.GUIDToAssetPath(guids[i]);
if (!IsValidScene(path))
continue;
var asset = AssetDatabase.LoadAssetAtPath<SceneAsset>(path);
string sceneName = path.Substring(7, path.Length - 13).Replace('/', '\\');
var inBuildSettings = false;
for (var j = 0; j < buildSettingsScenes.Length; j++)
{
if (!buildSettingsScenes[j].path.Equals(path))
continue;
inBuildSettings = true;
break;
}
workingSet.Add(new WorkingSetScene
{
path = path,
name = sceneName,
shortname = Path.GetFileNameWithoutExtension(path),
labels = AssetDatabase.GetLabels(asset),
inBuildSettings = inBuildSettings
});
}
return workingSet;
}
public sealed class SceneElement
{
public string name;
public string shortname;
public bool includeAsSelectable = true;
public bool isGroup;
public readonly List<string> paths = new List<string>();
}
private sealed class WorkingSetScene
{
public string path;
public string name;
public string shortname;
public string[] labels;
public bool inBuildSettings;
}
}
}