Scene picker: Added option to add groups of scenes to open

This commit is contained in:
Anders Ejlersen 2023-08-31 17:41:44 +02:00
parent 41604b98e7
commit a86f76904e
28 changed files with 1052 additions and 225 deletions

View file

@ -0,0 +1,9 @@
namespace Module.NavigationTool.Editor.Toolbar
{
public enum ESceneGroupFilterType
{
AssetLabels,
NameContains,
PathContains
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6cf395ccc24c46b18597cfbf6d4e32fd
timeCreated: 1693390754

View file

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
namespace Module.NavigationTool.Editor.Toolbar
{
[Serializable]
public sealed class SceneGroup
{
public string name;
public ESceneGroupFilterType filterType;
public string optionalMainScenePath;
public List<string> filters = new();
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4b0f8e524da24c2e8d0d6c3d0a446059
timeCreated: 1693390715

View file

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
namespace Module.NavigationTool.Editor.Toolbar
{
[Serializable]
public sealed class SceneGroupArray
{
public List<SceneGroup> groups = new();
public int Count => groups.Count;
public SceneGroup this[int index] => groups[index];
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 59bece5e9d5f4b539434937529afb0e7
timeCreated: 1693390710

View file

@ -0,0 +1,389 @@
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);
FetchAllScenesFromLabels(assets, settings.labels);
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 FetchAllScenesFromLabels(List<WorkingSetScene> assets, List<string> labels)
{
List<WorkingSetScene> filteredScenes = GetAllWorkingSetScenesWithLabels(assets, labels);
filteredScenes = FilterToUniques(filteredScenes);
filteredScenes = FilterAllExcept(filteredScenes, 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 void FetchAllScenesFromSceneGroups(List<WorkingSetScene> assets, SceneGroupArray sceneGroups)
{
if (sceneGroups.Count != 0)
scenes.Add(new SceneElement{ includeAsSelectable = false });
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 (!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);
}
}
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;
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d336bd910c0049c8ab47f94d9924baf3
timeCreated: 1693405779