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,12 @@
namespace Module.NavigationTool.Editor.Toolbar
{
public interface IToolbarProjectSettings
{
string Title { get; }
bool IsSettingsDirty { get; }
void Initialize(ToolbarProjectSettings projectSettings);
void Draw();
void SetSettingsValue();
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a45b77fff4244ae4a1271208dcb3bc9c
timeCreated: 1693423025

View file

@ -0,0 +1,113 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using UnityEngine;
namespace Module.NavigationTool.Editor.Toolbar
{
[Serializable]
public sealed class ToolbarProjectSettings
{
[SerializeField]
private List<string> keys = new();
[SerializeField]
private List<string> values = new();
public const string RELATIVE_PATH = "ProjectSettings/ToolbarProjectSettings.asset";
public void Load()
{
try
{
string path = GetAbsolutePath();
if (File.Exists(path))
{
string json = File.ReadAllText(path);
JsonUtility.FromJsonOverwrite(json, this);
}
else
{
keys.Clear();
values.Clear();
}
}
catch (Exception e)
{
Debug.LogException(e);
}
}
public void Save()
{
try
{
string path = GetAbsolutePath();
string json = JsonUtility.ToJson(this, true);
File.WriteAllText(path, json, Encoding.Unicode);
}
catch (Exception e)
{
Debug.LogException(e);
}
}
public T GetValueAs<T>() where T : new()
{
try
{
string key = typeof(T).FullName;
int index = keys.IndexOf(key);
if (index == -1)
{
var t = new T();
keys.Add(key);
values.Add(JsonUtility.ToJson(t));
return t;
}
string json = values[index];
return JsonUtility.FromJson<T>(json);
}
catch (Exception e)
{
Debug.LogException(e);
return new T();
}
}
public void SetValue<T>(T value) where T : new()
{
try
{
string key = typeof(T).FullName;
string json = JsonUtility.ToJson(value);
int index = keys.IndexOf(key);
if (index != -1)
{
values[index] = json;
}
else
{
keys.Add(key);
values.Add(json);
}
}
catch (Exception e)
{
Debug.LogException(e);
}
}
private string GetAbsolutePath()
{
string path = Application.dataPath;
path = path.Substring(0, Application.dataPath.Length - 6);
path += RELATIVE_PATH;
return path;
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a5fbb83f9c104775ae07b33244e133d2
timeCreated: 1693466538

View file

@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace Module.NavigationTool.Editor.Toolbar
{
internal static class ToolbarProjectSettingsProvider
{
private static Styles STYLES;
private static IToolbarProjectSettings[] SETTINGS;
private static ToolbarProjectSettings PROJECT_SETTINGS;
private const string PREF_FOLDOUT_STATE = "ToolbarFoldout_{0}";
[SettingsProvider]
public static SettingsProvider GetProvider()
{
Initialize();
var keywords = new List<string> { "Toolbar" };
for (var i = 0; i < SETTINGS.Length; i++)
{
keywords.Add(SETTINGS[i].Title);
}
return new SettingsProvider("Module/Toolbar", SettingsScope.Project)
{
label = "Toolbar",
keywords = keywords.ToArray(),
guiHandler = OnGui
};
}
private static void Initialize()
{
if (STYLES == null)
STYLES = new Styles();
if (PROJECT_SETTINGS == null)
{
PROJECT_SETTINGS = new ToolbarProjectSettings();
PROJECT_SETTINGS.Load();
}
if (SETTINGS == null)
SETTINGS = ToolbarSettingsUtility.GetAllProjectSettings(PROJECT_SETTINGS);
}
private static void OnGui(string searchContext)
{
Initialize();
STYLES.Initialize(GUI.skin);
var isSettingsDirty = false;
EditorGUILayout.BeginVertical(STYLES.settingsGroup);
{
for (var i = 0; i < SETTINGS.Length; i++)
{
IToolbarProjectSettings settings = SETTINGS[i];
try
{
bool foldoutState = GetFoldoutState(settings);
bool newFoldoutState = EditorGUILayout.BeginFoldoutHeaderGroup(foldoutState, settings.Title, EditorStyles.foldoutHeader);
if (newFoldoutState)
{
EditorGUI.indentLevel++;
settings.Draw();
if (settings.IsSettingsDirty)
{
isSettingsDirty = true;
settings.SetSettingsValue();
}
EditorGUI.indentLevel--;
}
EditorGUILayout.EndFoldoutHeaderGroup();
EditorGUILayout.Space();
if (newFoldoutState != foldoutState)
SetFoldoutState(settings, newFoldoutState);
}
catch (Exception e)
{
EditorGUILayout.LabelField(e.Message);
}
}
if (isSettingsDirty)
PROJECT_SETTINGS.Save();
}
EditorGUILayout.EndVertical();
}
private static bool GetFoldoutState(IToolbarProjectSettings settings)
{
string key = string.Format(PREF_FOLDOUT_STATE, settings.GetType().FullName);
return EditorPrefs.GetBool(key, true);
}
private static void SetFoldoutState(IToolbarProjectSettings settings, bool foldout)
{
string key = string.Format(PREF_FOLDOUT_STATE, settings.GetType().FullName);
EditorPrefs.SetBool(key, foldout);
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 7e21927d181c41f1acdbecc9f5fc3e26
timeCreated: 1693423048

View file

@ -36,7 +36,7 @@ namespace Module.NavigationTool.Editor.Toolbar
if (STYLES == null)
STYLES = new Styles();
if (SETTINGS == null)
SETTINGS = ToolbarSettingsUtility.GetAllSettings();
SETTINGS = ToolbarSettingsUtility.GetAllUserSettings();
}
private static void OnGui(string searchContext)

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: cf9c741582d646d5ab02533554680715
timeCreated: 1693390703

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

View file

@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
namespace Module.NavigationTool.Editor.Toolbar
{
internal sealed class ToolbarScenePickerProjectSettings : IToolbarProjectSettings
{
public string Title => "Scene";
public bool IsSettingsDirty { get; private set; }
private StringReordableListDrawer assetLabelList;
private SceneGroupReordableListDrawer sceneGroupList;
private ToolbarProjectSettings projectSettings;
private Settings settings;
public void Initialize(ToolbarProjectSettings projectSettings)
{
this.projectSettings = projectSettings;
settings = projectSettings.GetValueAs<Settings>();
assetLabelList = new StringReordableListDrawer(settings.labels, "Scene sorting by Asset label");
assetLabelList.onChanged += ToolScenePicker.SetAsDirty;
sceneGroupList = new SceneGroupReordableListDrawer(settings.sceneGroups.groups, "Scene groups");
sceneGroupList.onChanged += ToolScenePicker.SetAsDirty;
}
public void Draw()
{
assetLabelList.DoLayoutList();
sceneGroupList.DoLayoutList();
IsSettingsDirty = assetLabelList.IsDirty || sceneGroupList.IsDirty;
}
public void SetSettingsValue()
{
projectSettings.SetValue(settings);
}
[Serializable]
public sealed class Settings
{
public List<string> labels = new();
public SceneGroupArray sceneGroups = new();
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3dd46bde8e5b4cca9c95d051862f5817
timeCreated: 1693466453

View file

@ -1,9 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
using UnityEditor;
namespace Module.NavigationTool.Editor.Toolbar
{
@ -12,7 +7,6 @@ namespace Module.NavigationTool.Editor.Toolbar
public string Title => "Scene";
private const string PREF_IS_SCENE_ENABLED = "ToolbarSettings_IsSceneEnabled";
private const string PREF_SCENE_ASSET_LABELS = "ToolbarSettings_SceneAssetLabels";
private const string PREF_SCENE_PICKER_LOAD_SET_AS_ADDITIVE_KEY = "ToolbarSettings_ScenePickerLoadSetAsAdditive";
public static bool IsSceneEnabled
@ -21,78 +15,19 @@ namespace Module.NavigationTool.Editor.Toolbar
private set => EditorPrefs.SetBool(PREF_IS_SCENE_ENABLED, value);
}
public static List<string> SceneAssetLabels
{
get => EditorPrefs.GetString(PREF_SCENE_ASSET_LABELS).Split(',').ToList();
private set => EditorPrefs.SetString(PREF_SCENE_ASSET_LABELS, string.Join(",", value));
}
public static bool IsScenePickerSetAsAdditive
{
get => EditorPrefs.GetBool(PREF_SCENE_PICKER_LOAD_SET_AS_ADDITIVE_KEY, false);
set => EditorPrefs.SetBool(PREF_SCENE_PICKER_LOAD_SET_AS_ADDITIVE_KEY, value);
}
private ReorderableList assetLabelList;
private List<string> assetLabels;
public void Initialize()
{
assetLabels = SceneAssetLabels.ToList();
assetLabelList = new ReorderableList(assetLabels, typeof(string), true, true, true, true);
assetLabelList.drawElementCallback += OnAssetLabelListDrawElement;
assetLabelList.drawHeaderCallback += OnAssetLabelListDrawHeader;
assetLabelList.onAddCallback += OnAssetLabelListAddedElement;
assetLabelList.onRemoveCallback += OnAssetLabelListRemovedElement;
assetLabelList.onReorderCallback += OnAssetLabelListReorder;
}
public void Draw()
{
IsSceneEnabled = EditorGUILayout.Toggle("Enable Scene picker", IsSceneEnabled);
if (assetLabelList == null)
return;
assetLabelList.DoLayoutList();
SceneAssetLabels = assetLabels;
}
private static void OnAssetLabelListDrawHeader(Rect rect)
{
EditorGUI.LabelField(rect, "Scene sorting by Asset label");
}
private void OnAssetLabelListDrawElement(Rect rect, int index, bool isActive, bool isFocused)
{
rect.height -= 2;
rect.y += 1;
string text = assetLabels[index];
string temp = EditorGUI.TextField(rect, text);
if (string.Equals(text, temp, StringComparison.Ordinal))
return;
assetLabels[index] = temp;
ToolScenePicker.SetAsDirty();
}
private void OnAssetLabelListAddedElement(ReorderableList list)
{
assetLabels.Add(string.Empty);
ToolScenePicker.SetAsDirty();
}
private void OnAssetLabelListRemovedElement(ReorderableList list)
{
assetLabels.RemoveAt(list.index);
ToolScenePicker.SetAsDirty();
}
private void OnAssetLabelListReorder(ReorderableList list)
{
ToolScenePicker.SetAsDirty();
}
}
}

View file

@ -1,7 +1,4 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.CompilerServices;
using System.Text;
using JetBrains.Annotations;
using UnityEditor;
@ -19,17 +16,9 @@ namespace Module.NavigationTool.Editor.Toolbar
public override EToolbarPlacement Placement => EToolbarPlacement.Right;
public override int Priority => (int)EToolbarPriority.Medium;
private static readonly List<int> SELECTED_INDICES = new List<int>();
private static readonly StringBuilder STRING_BUILDER = new StringBuilder();
private static readonly ScenePickerList SCENE_LIST = new ScenePickerList();
private static string SCENE_LABEL = string.Empty;
private static string[] SCENE_NAMES = new string[0];
private static string[] SCENE_SHORT_NAMES = new string[0];
private static string[] SCENE_PATHS = new string[0];
private static readonly GUIContent LABEL_SCENE_ADDITIVE = new GUIContent(string.Empty, "Additive scene loading (toogle to Single)");
private static readonly GUIContent LABEL_SCENE_SINGLE = new GUIContent(string.Empty, "Single scene loading (toggle to Additive)");
private static readonly GUIContent LABEL_SCENE_CREATE = new GUIContent(string.Empty, "Create a new scene");
private const float BUTTON_WIDTH = 24.0f;
private static bool IS_DIRTY = true;
private static void Initialize()
@ -37,115 +26,11 @@ namespace Module.NavigationTool.Editor.Toolbar
if (!IS_DIRTY)
return;
SELECTED_INDICES.Clear();
var listNames = new List<string>();
var listShortNames = new List<string>();
var listPaths = new List<string>();
InitializeBuildSettingsScenes(listNames, listShortNames, listPaths);
InitializeRemainingScenes(listNames, listShortNames, listPaths);
for (var i = 0; i < SceneManager.sceneCount; i++)
{
Scene scene = SceneManager.GetSceneAt(i);
if (string.IsNullOrEmpty(scene.path))
continue;
int index = listPaths.IndexOf(scene.path);
if (index != -1)
SELECTED_INDICES.Add(index);
}
SCENE_NAMES = listNames.ToArray();
SCENE_SHORT_NAMES = listShortNames.ToArray();
SCENE_PATHS = listPaths.ToArray();
SCENE_LIST.Refresh();
RefreshSceneLabel();
IS_DIRTY = false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void InitializeBuildSettingsScenes(List<string> listNames, List<string> listShortNames, List<string> listPaths)
{
EditorBuildSettingsScene[] scenes = EditorBuildSettings.scenes;
for (int i = 0, added = 0; i < scenes.Length; i++)
{
string path = scenes[i].path;
if (string.IsNullOrEmpty(path))
continue;
if (AssetDatabase.LoadAssetAtPath<SceneAsset>(path) == null)
continue;
listNames.Add(Path.GetFileNameWithoutExtension(path));
listShortNames.Add(listNames[added]);
listPaths.Add(path);
added++;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void InitializeRemainingScenes(List<string> listNames, List<string> listShortNames, List<string> listPaths)
{
List<string> sortByAssetLabels = ToolbarScenePickerSettings.SceneAssetLabels;
var assetLabelToScenes = new SceneSortElement[sortByAssetLabels.Count + 1];
string[] guids = AssetDatabase.FindAssets("t:scene");
for (var i = 0; i < guids.Length; i++)
{
string path = AssetDatabase.GUIDToAssetPath(guids[i]);
if (listPaths.Contains(path) || !path.StartsWith("Assets"))
continue;
var scene = AssetDatabase.LoadAssetAtPath<SceneAsset>(path);
if (scene == null)
continue;
string sceneName = path.Substring(7, path.Length - 13)
.Replace('/', '\\');
string[] assetLabels = AssetDatabase.GetLabels(scene);
int index = -1;
for (var j = 0; j < assetLabels.Length; j++)
{
index = sortByAssetLabels.IndexOf(assetLabels[j]);
if (index != -1)
break;
}
if (index == -1)
index = sortByAssetLabels.Count;
if (assetLabelToScenes[index] == null)
assetLabelToScenes[index] = new SceneSortElement();
assetLabelToScenes[index].names.Add(sceneName);
assetLabelToScenes[index].shortNames.Add(Path.GetFileName(sceneName));
assetLabelToScenes[index].paths.Add(path);
}
for (var i = 0; i < assetLabelToScenes.Length; i++)
{
SceneSortElement e = assetLabelToScenes[i];
if (e == null)
continue;
listNames.Add(string.Empty);
listShortNames.Add(string.Empty);
listPaths.Add(string.Empty);
listNames.AddRange(e.names);
listShortNames.AddRange(e.shortNames);
listPaths.AddRange(e.paths);
}
}
public override void Update()
{
Initialize();
@ -155,9 +40,9 @@ namespace Module.NavigationTool.Editor.Toolbar
{
Initialize();
var rect0 = new Rect(rect.x, rect.y, rect.width - BUTTON_WIDTH * 2.0f, rect.height);
var rect1 = new Rect(rect0.xMax, rect.y, BUTTON_WIDTH, rect.height);
var rect2 = new Rect(rect1.xMax, rect.y, BUTTON_WIDTH, rect.height);
var rect0 = new Rect(rect.x, rect.y, rect.width - Styles.BUTTON_WIDTH * 2.0f, rect.height);
var rect1 = new Rect(rect0.xMax, rect.y, Styles.BUTTON_WIDTH, rect.height);
var rect2 = new Rect(rect1.xMax, rect.y, Styles.BUTTON_WIDTH, rect.height);
DrawSceneDropDown(rect0);
DrawSceneLoadToggle(rect1);
DrawSceneAddButton(rect2);
@ -177,12 +62,12 @@ namespace Module.NavigationTool.Editor.Toolbar
if (tempIsAdditive)
{
GUI.Label(rect, styles.iconSceneAdditive, styles.labelCenter);
GUI.Label(rect, LABEL_SCENE_ADDITIVE, styles.labelCenter);
GUI.Label(rect, Styles.LABEL_SCENE_ADDITIVE, styles.labelCenter);
}
else
{
GUI.Label(rect, styles.iconSceneSingle, styles.labelCenter);
GUI.Label(rect, LABEL_SCENE_SINGLE, styles.labelCenter);
GUI.Label(rect, Styles.LABEL_SCENE_SINGLE, styles.labelCenter);
}
if (tempIsAdditive != isScenePickerSetAsAdditive)
@ -211,17 +96,17 @@ namespace Module.NavigationTool.Editor.Toolbar
}
}
GUI.Label(rect, LABEL_SCENE_CREATE, styles.labelCenter);
GUI.Label(rect, Styles.LABEL_SCENE_CREATE, styles.labelCenter);
}
private static void ShowDropDown(Rect rect)
{
var menu = new GenericMenu();
for (var i = 0; i < SCENE_NAMES.Length; i++)
for (var i = 0; i < SCENE_LIST.labels.Length; i++)
{
int sceneIndex = i;
menu.AddItem(new GUIContent(SCENE_NAMES[i]), SELECTED_INDICES.Contains(i), () => SelectScene(sceneIndex));
menu.AddItem(SCENE_LIST.labels[i], SCENE_LIST.selected.Contains(i), () => SelectScene(sceneIndex));
}
menu.DropDown(rect);
@ -231,28 +116,57 @@ namespace Module.NavigationTool.Editor.Toolbar
{
try
{
string path = SCENE_PATHS[index];
Scene scene = SceneManager.GetSceneByPath(path);
ScenePickerList.SceneElement scenes = SCENE_LIST.scenes[index];
bool isScenePickerSetAsAdditive = ToolbarScenePickerSettings.IsScenePickerSetAsAdditive;
for (var i = 0; i < scenes.paths.Count; i++)
{
string path = scenes.paths[i];
Scene scene = SceneManager.GetSceneByPath(path);
if (scenes.isGroup)
{
if (isScenePickerSetAsAdditive || i != 0)
{
EditorSceneManager.OpenScene(path, OpenSceneMode.Additive);
int sceneIndex = SCENE_LIST.IndexOfPath(path, false);
if (sceneIndex != -1)
SCENE_LIST.selected.Add(sceneIndex);
}
else
{
EditorSceneManager.OpenScene(path, OpenSceneMode.Single);
int sceneIndex = SCENE_LIST.IndexOfPath(path, false);
SCENE_LIST.selected.Clear();
if (sceneIndex != -1)
SCENE_LIST.selected.Add(index);
}
}
else
{
if (scene.isLoaded)
{
if (SceneManager.sceneCount == 1)
return;
EditorSceneManager.CloseScene(scene, true);
SELECTED_INDICES.Remove(index);
SCENE_LIST.selected.Remove(index);
}
else if (isScenePickerSetAsAdditive)
{
EditorSceneManager.OpenScene(SCENE_PATHS[index], OpenSceneMode.Additive);
SELECTED_INDICES.Add(index);
EditorSceneManager.OpenScene(path, OpenSceneMode.Additive);
SCENE_LIST.selected.Add(index);
}
else
{
EditorSceneManager.OpenScene(SCENE_PATHS[index], OpenSceneMode.Single);
SELECTED_INDICES.Clear();
SELECTED_INDICES.Add(index);
EditorSceneManager.OpenScene(path, OpenSceneMode.Single);
SCENE_LIST.selected.Clear();
SCENE_LIST.selected.Add(index);
}
}
}
RefreshSceneLabel();
@ -267,14 +181,14 @@ namespace Module.NavigationTool.Editor.Toolbar
{
STRING_BUILDER.Clear();
if (SELECTED_INDICES.Count != 0)
if (SCENE_LIST.selected.Count != 0)
{
for (var i = 0; i < SELECTED_INDICES.Count; i++)
for (var i = 0; i < SCENE_LIST.selected.Count; i++)
{
if (i > 0)
STRING_BUILDER.Append(", ");
STRING_BUILDER.Append(SCENE_SHORT_NAMES[SELECTED_INDICES[i]]);
STRING_BUILDER.Append(SCENE_LIST.scenes[SCENE_LIST.selected[i]].shortname);
}
}
else
@ -287,7 +201,7 @@ namespace Module.NavigationTool.Editor.Toolbar
public override float CalculateWidth()
{
return 100.0f + BUTTON_WIDTH * 2.0f;
return 100.0f + Styles.BUTTON_WIDTH * 2.0f;
}
public static void SetAsDirty()
@ -295,11 +209,12 @@ namespace Module.NavigationTool.Editor.Toolbar
IS_DIRTY = true;
}
private sealed class SceneSortElement
private static class Styles
{
public readonly List<string> names = new List<string>();
public readonly List<string> shortNames = new List<string>();
public readonly List<string> paths = new List<string>();
public static readonly GUIContent LABEL_SCENE_ADDITIVE = new GUIContent(string.Empty, "Additive scene loading (toogle to Single)");
public static readonly GUIContent LABEL_SCENE_SINGLE = new GUIContent(string.Empty, "Single scene loading (toggle to Additive)");
public static readonly GUIContent LABEL_SCENE_CREATE = new GUIContent(string.Empty, "Create a new scene");
public const float BUTTON_WIDTH = 24.0f;
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: cf2b6dd510df4e818247e29339e154bc
timeCreated: 1693333131

View file

@ -0,0 +1,76 @@
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
namespace Module.NavigationTool.Editor.Toolbar
{
internal abstract class AbstractReordableListDrawer<T>
{
public readonly List<T> list;
private readonly ReorderableList reorderableList;
public event Action onChanged;
public bool IsDirty { get; private set; }
protected AbstractReordableListDrawer(List<T> list, string header)
{
this.list = list;
reorderableList = new ReorderableList(list, typeof(string), true, true, true, true);
reorderableList.drawElementCallback += (rect, index, isActive, isFocused) =>
{
EditorGUI.BeginChangeCheck();
OnDrawElement(rect, index, isActive, isFocused);
if (EditorGUI.EndChangeCheck())
IsDirty = true;
};
reorderableList.drawHeaderCallback += rect => EditorGUI.LabelField(rect, header);
reorderableList.onAddCallback += _ =>
{
OnAddElementToList();
InvokeChanged();
};
reorderableList.onRemoveCallback += rl =>
{
list.RemoveAt(rl.index);
OnRemovedElementFromList(rl.index);
InvokeChanged();
};
reorderableList.onReorderCallback += _ => InvokeChanged();;
reorderableList.elementHeightCallback += OnElementHeight;
}
public void DoList(Rect rect)
{
IsDirty = false;
reorderableList.DoList(rect);
}
public void DoLayoutList()
{
IsDirty = false;
reorderableList.DoLayoutList();
}
protected abstract void OnDrawElement(Rect rect, int index, bool isActive, bool isFocused);
protected abstract void OnAddElementToList();
protected virtual void OnRemovedElementFromList(int index)
{
}
protected virtual float OnElementHeight(int index)
{
return EditorGUIUtility.singleLineHeight;
}
protected void InvokeChanged()
{
onChanged?.Invoke();
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ad769ac53dd3476b82e6a2b62117c2d3
timeCreated: 1693332886

View file

@ -0,0 +1,81 @@
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace Module.NavigationTool.Editor.Toolbar
{
internal sealed class SceneGroupReordableListDrawer : AbstractReordableListDrawer<SceneGroup>
{
private readonly List<StringReordableListDrawer> elements = new List<StringReordableListDrawer>();
public SceneGroupReordableListDrawer(List<SceneGroup> list, string header)
: base(list, header)
{
for (var i = 0; i < list.Count; i++)
{
elements.Add(new StringReordableListDrawer(list[i].filters, "Filters"));
}
}
protected override void OnDrawElement(Rect rect, int index, bool isActive, bool isFocused)
{
rect.height -= 2;
rect.y += 1;
SceneGroup group = list[index];
var rectName = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight);
var rectType = new Rect(rect.x, rectName.yMax + 2f, rect.width, EditorGUIUtility.singleLineHeight);
var rectOptional = new Rect(rect.x, rectType.yMax + 2f, rect.width - 80f, EditorGUIUtility.singleLineHeight);
var rectOptionalBtn = new Rect(rectOptional.xMax, rectType.yMax + 2f, 80f, EditorGUIUtility.singleLineHeight);
var rectLabels = new Rect(rect.x, rectOptional.yMax + 2f, rect.width, rect.height - (rectName.y - rectOptional.yMax) - 4f);
EditorGUI.BeginChangeCheck();
group.name = EditorGUI.TextField(rectName, "Name", group.name);
group.optionalMainScenePath = EditorGUI.TextField(rectOptional, "Optional Main Scene Path", group.optionalMainScenePath);
if (GUI.Button(rectOptionalBtn, "Find"))
{
string mainScenePath = EditorUtility.OpenFilePanel("Scene", "Assets", "unity");
if (!string.IsNullOrEmpty(mainScenePath))
{
mainScenePath = mainScenePath.Substring(Application.dataPath.Length - 6);
group.optionalMainScenePath = mainScenePath;
}
}
group.filterType = (ESceneGroupFilterType)EditorGUI.EnumPopup(rectType, "Filter Type", group.filterType);
elements[index].DoList(rectLabels);
if (EditorGUI.EndChangeCheck())
InvokeChanged();
}
protected override void OnAddElementToList()
{
var filters = new List<string>();
list.Add(new SceneGroup
{
name = "New Scene Group",
filters = filters
});
elements.Add(new StringReordableListDrawer(filters, "Filters"));
}
protected override void OnRemovedElementFromList(int index)
{
elements.RemoveAt(index);
}
protected override float OnElementHeight(int index)
{
int labelCount = list[index].filters.Count;
return base.OnElementHeight(index) * 3f
+ Mathf.Max(0, labelCount - 1) * (EditorGUIUtility.singleLineHeight + 2f)
+ 78f;
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4923054c7113444ab1b696d769cdae00
timeCreated: 1693333828

View file

@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
namespace Module.NavigationTool.Editor.Toolbar
{
internal sealed class StringReordableListDrawer : AbstractReordableListDrawer<string>
{
public StringReordableListDrawer(List<string> list, string header)
: base(list, header)
{
}
protected override void OnDrawElement(Rect rect, int index, bool isActive, bool isFocused)
{
rect.height -= 2;
rect.y += 1;
string text = list[index];
string temp = EditorGUI.TextField(rect, text);
if (string.Equals(text, temp, StringComparison.Ordinal))
return;
list[index] = temp;
InvokeChanged();
}
protected override void OnAddElementToList()
{
list.Add(string.Empty);
}
}
}

View file

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e4b2250a66d548f1aacb916c7b5b6204
timeCreated: 1693333142

View file

@ -7,7 +7,7 @@ namespace Module.NavigationTool.Editor.Toolbar
{
internal static class ToolbarSettingsUtility
{
public static IToolbarSettings[] GetAllSettings()
public static IToolbarSettings[] GetAllUserSettings()
{
var list = new List<IToolbarSettings>(8);
@ -43,5 +43,42 @@ namespace Module.NavigationTool.Editor.Toolbar
return list.ToArray();
}
public static IToolbarProjectSettings[] GetAllProjectSettings(ToolbarProjectSettings settings)
{
var list = new List<IToolbarProjectSettings>(8);
try
{
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
Type iType = typeof(IToolbarProjectSettings);
for (var i = 0; i < assemblies.Length; i++)
{
Assembly assembly = assemblies[i];
Type[] types = assembly.GetTypes();
for (var j = 0; j < types.Length; j++)
{
Type type = types[j];
if (type.IsInterface || type.IsAbstract || !iType.IsAssignableFrom(type))
continue;
var toolbar = (IToolbarProjectSettings)FormatterServices.GetUninitializedObject(type);
toolbar.Initialize(settings);
list.Add(toolbar);
}
}
list.Sort((s0, s1) => string.Compare(s0.Title, s1.Title, StringComparison.Ordinal));
}
catch (Exception)
{
// Fail silently
}
return list.ToArray();
}
}
}