using System.Collections.Generic; using System.IO; using System.Runtime.CompilerServices; using System.Text; using JetBrains.Annotations; using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.SceneManagement; namespace Module.NavigationTool.Editor.Toolbar { [UsedImplicitly] internal sealed class ToolScenePicker : AbstractToolbarDrawer { public override bool Visible => ToolbarScenePickerSettings.IsSceneEnabled; public override bool Enabled => !EditorApplication.isPlaying && !EditorApplication.isPlayingOrWillChangePlaymode; public override EToolbarPlacement Placement => EToolbarPlacement.Right; public override int Priority => (int)EToolbarPriority.Medium; private static readonly List SELECTED_INDICES = new List(); private static readonly StringBuilder STRING_BUILDER = new StringBuilder(); 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() { if (!IS_DIRTY) return; SELECTED_INDICES.Clear(); var listNames = new List(); var listShortNames = new List(); var listPaths = new List(); InitializeBuildSettingsScenes(listNames, listShortNames, listPaths); InitializeRemainingScenes(listNames, listShortNames, listPaths); for (var i = 0; i < SceneManager.sceneCount; i++) { Scene scene = SceneManager.GetSceneAt(i); int index = listPaths.IndexOf(scene.path); if (index != -1) SELECTED_INDICES.Add(listPaths.IndexOf(scene.path)); } SCENE_NAMES = listNames.ToArray(); SCENE_SHORT_NAMES = listShortNames.ToArray(); SCENE_PATHS = listPaths.ToArray(); RefreshSceneLabel(); IS_DIRTY = false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void InitializeBuildSettingsScenes(List listNames, List listShortNames, List listPaths) { EditorBuildSettingsScene[] scenes = EditorBuildSettings.scenes; for (var i = 0; i < scenes.Length; i++) { if (string.IsNullOrEmpty(scenes[i].path)) continue; listNames.Add(Path.GetFileNameWithoutExtension(scenes[i].path)); listShortNames.Add(listNames[i]); listPaths.Add(scenes[i].path); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void InitializeRemainingScenes(List listNames, List listShortNames, List listPaths) { List 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(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(); } protected override void Draw(Rect rect) { 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); DrawSceneDropDown(rect0); DrawSceneLoadToggle(rect1); DrawSceneAddButton(rect2); } private void DrawSceneDropDown(Rect rect) { if (GUI.Button(rect, SCENE_LABEL, styles.popup)) ShowDropDown(rect); } private void DrawSceneLoadToggle(Rect rect) { bool isScenePickerSetAsAdditive = ToolbarScenePickerSettings.IsScenePickerSetAsAdditive; bool tempIsAdditive = EditorGUI.Toggle(rect, isScenePickerSetAsAdditive, styles.button); if (tempIsAdditive) { GUI.Label(rect, styles.iconSceneAdditive, styles.labelCenter); GUI.Label(rect, LABEL_SCENE_ADDITIVE, styles.labelCenter); } else { GUI.Label(rect, styles.iconSceneSingle, styles.labelCenter); GUI.Label(rect, LABEL_SCENE_SINGLE, styles.labelCenter); } if (tempIsAdditive != isScenePickerSetAsAdditive) ToolbarScenePickerSettings.IsScenePickerSetAsAdditive = tempIsAdditive; } private void DrawSceneAddButton(Rect rect) { if (GUI.Button(rect, styles.iconPlusSmall, styles.button)) { if (!EditorUtility.DisplayDialog("Create new scene", "Are you sure that you want to create a new scene?", "Yes", "No")) return; Scene scene = EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Additive); if (EditorUtility.DisplayDialog("Create new scene", "Save scene to disk?", "Yes", "No")) EditorSceneManager.SaveScene(scene); } GUI.Label(rect, LABEL_SCENE_CREATE, styles.labelCenter); } private static void ShowDropDown(Rect rect) { var menu = new GenericMenu(); for (var i = 0; i < SCENE_NAMES.Length; i++) { int sceneIndex = i; menu.AddItem(new GUIContent(SCENE_NAMES[i]), SELECTED_INDICES.Contains(i), () => SelectScene(sceneIndex)); } menu.DropDown(rect); } private static void SelectScene(int index) { string path = SCENE_PATHS[index]; Scene scene = SceneManager.GetSceneByPath(path); bool isScenePickerSetAsAdditive = ToolbarScenePickerSettings.IsScenePickerSetAsAdditive; if (scene.isLoaded) { if (SceneManager.sceneCount == 1) return; EditorSceneManager.CloseScene(scene, true); SELECTED_INDICES.Remove(index); } else if (isScenePickerSetAsAdditive) { EditorSceneManager.OpenScene(SCENE_PATHS[index], OpenSceneMode.Additive); SELECTED_INDICES.Add(index); } else { EditorSceneManager.OpenScene(SCENE_PATHS[index], OpenSceneMode.Single); SELECTED_INDICES.Clear(); SELECTED_INDICES.Add(index); } RefreshSceneLabel(); } private static void RefreshSceneLabel() { STRING_BUILDER.Clear(); for (var i = 0; i < SELECTED_INDICES.Count; i++) { if (i > 0) STRING_BUILDER.Append(", "); STRING_BUILDER.Append(SCENE_SHORT_NAMES[SELECTED_INDICES[i]]); } SCENE_LABEL = STRING_BUILDER.ToString(); } public override float CalculateWidth() { return 100.0f + BUTTON_WIDTH * 2.0f; } public static void SetAsDirty() { IS_DIRTY = true; } private sealed class SceneSortElement { public readonly List names = new List(); public readonly List shortNames = new List(); public readonly List paths = new List(); } } }