Skip to content

Instantly share code, notes, and snippets.

@restush
Last active September 30, 2024 11:37

Revisions

  1. restush revised this gist Sep 30, 2024. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion SceneListEditorWindow.cs
    Original file line number Diff line number Diff line change
    @@ -87,7 +87,7 @@ private void CreateGUI()
    showAlternatingRowBackgrounds = AlternatingRowBackground.All,
    reorderable = true,
    reorderMode = ListViewReorderMode.Animated,

    makeNoneElement = () => new VisualElement()

    };
    pinnedView.style.unityTextAlign = TextAnchor.MiddleCenter;
  2. restush revised this gist Sep 30, 2024. 1 changed file with 20 additions and 1 deletion.
    21 changes: 20 additions & 1 deletion SceneListEditorWindow.cs
    Original file line number Diff line number Diff line change
    @@ -145,7 +145,26 @@ private void CreateGUI()
    void Bind(VisualElement v, int index, bool isPinnedList)
    {
    var i = index;
    var item = isPinnedList ? pinnedList[i] : dataList[i];
    var item = (HorizontalItemData)default;
    if(isPinnedList)
    {
    // check valid pinnedList index to prevent out of range
    if(pinnedList.Count > i)
    {
    item = pinnedList[i];
    }
    }
    else
    {
    if (dataList.Count > i)
    {
    item = dataList[i];
    }
    }

    if (item == null)
    return;

    var sceneButton = v.Q<Button>("SceneButton");
    sceneButton.text = item.SceneName;

  3. restush revised this gist Aug 24, 2024. 1 changed file with 0 additions and 1 deletion.
    1 change: 0 additions & 1 deletion SceneListEditorWindow.cs
    Original file line number Diff line number Diff line change
    @@ -73,7 +73,6 @@ static void CreateMenu()

    private void CreateGUI()
    {
    Debug.Log("CreateGUI");
    Load();

    pinnedList = dataList.FindAll(x => x.Pinned);
  4. restush revised this gist Aug 24, 2024. 1 changed file with 14 additions and 13 deletions.
    27 changes: 14 additions & 13 deletions SceneListEditorWindow.cs
    Original file line number Diff line number Diff line change
    @@ -66,12 +66,14 @@ static void CreateMenu()
    {
    var window = GetWindow<SceneListEditorWindow>();
    window.titleContent = new GUIContent("Scene List & Pins");
    window.Focus();
    }



    private void CreateGUI()
    {
    Debug.Log("CreateGUI");
    Load();

    pinnedList = dataList.FindAll(x => x.Pinned);
    @@ -141,9 +143,9 @@ private void CreateGUI()
    }
    }

    void Bind(VisualElement v, int i, bool isPinnedList)
    void Bind(VisualElement v, int index, bool isPinnedList)
    {

    var i = index;
    var item = isPinnedList ? pinnedList[i] : dataList[i];
    var sceneButton = v.Q<Button>("SceneButton");
    sceneButton.text = item.SceneName;
    @@ -183,8 +185,11 @@ void Bind(VisualElement v, int i, bool isPinnedList)
    ShowVisualElement(header);
    }

    pinnedView.Rebuild();
    unpinnedView.Rebuild();
    pinnedView.RefreshItem(i);
    unpinnedView.RefreshItem(i);

    //pinnedView.Rebuild();
    //unpinnedView.Rebuild();

    Save();

    @@ -199,6 +204,10 @@ void Bind(VisualElement v, int i, bool isPinnedList)

    private void Load()
    {
    // clear to prevent duplicate
    dataList.Clear();
    pinnedList.Clear();

    var sceneAssets = FindAssetsOfType<SceneAsset>();

    string currentFilePath = AssetDatabase.GetAssetPath(MonoScript.FromScriptableObject(this));
    @@ -340,13 +349,5 @@ private T[] FindAssetsOfType<T>() where T : UnityEngine.Object
    }

    return list.ToArray();
    }

    private void OnProjectChange()
    {
    dataList.Clear();
    pinnedList.Clear();
    rootVisualElement.Clear();
    CreateGUI();
    }
    }
    }
  5. restush revised this gist Aug 23, 2024. 1 changed file with 13 additions and 6 deletions.
    19 changes: 13 additions & 6 deletions SceneListEditorWindow.cs
    Original file line number Diff line number Diff line change
    @@ -95,9 +95,6 @@ private void CreateGUI()
    // callback on reorder
    pinnedView.itemIndexChanged += (oldIndex, newIndex) =>
    {
    pinnedList[newIndex].PinnedIndex = newIndex;
    pinnedList[oldIndex].PinnedIndex = oldIndex;

    Save();
    };
    pinnedView.selectionType = SelectionType.None;
    @@ -112,9 +109,6 @@ private void CreateGUI()
    ,
    };




    unpinnedView.selectionType = SelectionType.None;
    unpinnedView.style.flexGrow = 1;

    @@ -320,6 +314,11 @@ private void UnsubscribeToButton(Button button)
    private void Save()
    {
    StateData stateData = new StateData();
    for (int i = 0; i < pinnedList.Count; i++)
    {
    pinnedList[i].PinnedIndex = i;
    }

    stateData.pinnedList = pinnedList;
    string json = JsonUtility.ToJson(stateData);
    File.WriteAllText(jsonFilePath, json);
    @@ -342,4 +341,12 @@ private T[] FindAssetsOfType<T>() where T : UnityEngine.Object

    return list.ToArray();
    }

    private void OnProjectChange()
    {
    dataList.Clear();
    pinnedList.Clear();
    rootVisualElement.Clear();
    CreateGUI();
    }
    }
  6. restush revised this gist Aug 23, 2024. 2 changed files with 214 additions and 193 deletions.
    405 changes: 213 additions & 192 deletions SceneListEditorWindow.cs
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,5 @@
    using System.Collections;
    /// https://gist.github.com/restush/849b5662dd82b7eb68ee78950e1a8033

    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    @@ -11,13 +12,36 @@

    public class SceneListEditorWindow : EditorWindow
    {

    private class UserData
    {
    public Dictionary<string, System.Action> _callbacks = new();

    public void AddCallback(string key, System.Action callback)
    {
    if (_callbacks.ContainsKey(key))
    _callbacks[key] = callback;
    else
    _callbacks.Add(key, callback);
    }

    public void RemoveCallback(string key, Button button)
    {
    if (_callbacks.ContainsKey(key))
    {
    button.clicked -= _callbacks[key];
    _callbacks.Remove(key);
    }
    }
    }

    [System.Serializable]
    private class HorizontalItemData
    {
    public string SceneName;
    public string GUID;
    public bool Pinned;
    public int Index;
    public int PinnedIndex;
    }

    private List<HorizontalItemData> dataList = new();
    @@ -44,139 +68,21 @@ static void CreateMenu()
    window.titleContent = new GUIContent("Scene List & Pins");
    }

    T[] FindAssetsOfType<T>() where T : UnityEngine.Object
    {
    string[] guids = AssetDatabase.FindAssets($"t:{typeof(T).Name}");
    List<T> list = new List<T>();
    foreach (string guid in guids)
    {
    string assetPath = AssetDatabase.GUIDToAssetPath(guid);
    T asset = AssetDatabase.LoadAssetAtPath<T>(assetPath);

    if (asset != null)
    {
    list.Add(asset);
    }
    }

    return list.ToArray();
    }

    private void CreateGUI()
    {

    // find all scenes in the project
    var sceneAssets = FindAssetsOfType<SceneAsset>();

    string currentFilePath = AssetDatabase.GetAssetPath(MonoScript.FromScriptableObject(this));
    string currentDirectory = Path.GetDirectoryName(currentFilePath);
    jsonFilePath = Path.Combine(currentDirectory, "FindMissingEditor-JSON.json");

    foreach (var sceneAsset in sceneAssets)
    {
    string path = AssetDatabase.GetAssetPath(sceneAsset);

    dataList.Add(new HorizontalItemData() { SceneName = sceneAsset.name, GUID = AssetDatabase.AssetPathToGUID(path), Pinned = false });
    }

    if (File.Exists(jsonFilePath))
    {
    string json = File.ReadAllText(jsonFilePath);
    StateData stateData = JsonUtility.FromJson<StateData>(json);

    // use except to remove pinned scene from dataList
    if (stateData != null && stateData.pinnedList != null)
    {
    var count = dataList.Count;
    for (int i = 0; i < count; i++)
    {

    for (int j = 0; j < stateData.pinnedList.Count; j++)
    {
    if (dataList[i].GUID == stateData.pinnedList[j].GUID)
    {
    dataList[i].Pinned = true;
    dataList[i].Index = stateData.pinnedList[j].Index;
    break;
    }
    }

    }
    }




    }



    Load();

    pinnedList = dataList.FindAll(x => x.Pinned);
    dataList.RemoveAll(x => x.Pinned);
    pinnedList = pinnedList.OrderBy(x => x.Index).ToList();


    VisualElement CreateItem()
    {
    var horizontalItem = new VisualElement() { name = "HorizontalItem" };
    var sceneButton = new Button() { name = "SceneButton" };
    sceneButton.style.minWidth = 210;
    sceneButton.style.flexGrow = 1;
    sceneButton.style.unityTextAlign = TextAnchor.MiddleLeft;
    var pinButton = new Button() { name = "Pin", text = "Pin" };
    var findButton = new Button() { name = "Find", text = "Find" };
    horizontalItem.Add(sceneButton);
    horizontalItem.Add(pinButton);
    horizontalItem.Add(findButton);
    horizontalItem.style.flexDirection = FlexDirection.Row;
    pinButton.style.maxWidth = 70;
    findButton.style.maxWidth = 70;

    return horizontalItem;
    }
    pinnedList = pinnedList.OrderBy(x => x.PinnedIndex).ToList();

    pinnedView = new ListView()
    {
    itemsSource = pinnedList,
    makeItem = () => CreateItem(),
    bindItem = (v, index) =>
    {
    int i = index;
    var item = pinnedList[i];
    var sceneButton = v.Q<Button>("SceneButton");
    sceneButton.text = item.SceneName;

    if (sceneButton.userData != null)
    sceneButton.clicked -= sceneButton.userData as System.Action;
    System.Action onClickScene = () => EditorSceneManager.OpenScene(AssetDatabase.GUIDToAssetPath(item.GUID));
    sceneButton.userData = onClickScene;
    sceneButton.clicked += onClickScene;

    var pinButton = v.Q<Button>("Pin");
    item.Pinned = true;
    pinButton.text = "Unpin";

    System.Action action = () =>
    {
    if (pinButton.userData != null)
    pinButton.clicked -= pinButton.userData as System.Action;
    item.Pinned = false;
    dataList.Add(item);
    pinnedList.Remove(item);
    pinnedView.style.minHeight = PinnedMinHeight();
    pinnedView.Rebuild();
    unpinnedView.Rebuild();
    };

    if (pinButton.userData != null)
    pinButton.clicked -= pinButton.userData as System.Action;
    pinButton.userData = action;
    pinButton.clicked += action;

    var findButton = v.Q<Button>("Find");
    findButton.clicked += () => EditorGUIUtility.PingObject(AssetDatabase.LoadAssetAtPath<SceneAsset>(AssetDatabase.GUIDToAssetPath(item.GUID)));
    },
    bindItem = (v, i) => Bind(v, i, true),
    showAlternatingRowBackgrounds = AlternatingRowBackground.All,
    reorderable = true,
    reorderMode = ListViewReorderMode.Animated,
    @@ -189,8 +95,10 @@ VisualElement CreateItem()
    // callback on reorder
    pinnedView.itemIndexChanged += (oldIndex, newIndex) =>
    {
    pinnedList[newIndex].Index = newIndex;
    pinnedList[oldIndex].Index = oldIndex;
    pinnedList[newIndex].PinnedIndex = newIndex;
    pinnedList[oldIndex].PinnedIndex = oldIndex;

    Save();
    };
    pinnedView.selectionType = SelectionType.None;

    @@ -200,55 +108,12 @@ VisualElement CreateItem()

    itemsSource = dataList,
    makeItem = () => CreateItem(),
    bindItem = (v, i) =>
    {
    var item = dataList[i];
    var sceneButton = v.Q<Button>("SceneButton");
    sceneButton.text = item.SceneName;

    if (sceneButton.userData != null)
    sceneButton.clicked -= sceneButton.userData as System.Action;
    System.Action onClickScene = () => EditorSceneManager.OpenScene(AssetDatabase.GUIDToAssetPath(item.GUID));
    sceneButton.userData = onClickScene;
    sceneButton.clicked += onClickScene;

    var pinButton = v.Q<Button>("Pin");
    item.Pinned = false;
    pinButton.text = "Pin";

    System.Action action = () =>
    {
    if (pinButton.userData != null)
    pinButton.clicked -= pinButton.userData as System.Action;
    item.Index = pinnedList.Count;
    pinnedList.Add(item);
    dataList.Remove(item);

    pinnedView.style.minHeight = PinnedMinHeight();
    // show pinned view
    pinnedView.style.display = DisplayStyle.Flex;
    pinnedView.style.visibility = Visibility.Visible;
    pinnedView.pickingMode = PickingMode.Position;

    header.style.display = DisplayStyle.Flex;
    header.style.visibility = Visibility.Visible;
    header.pickingMode = PickingMode.Position;

    bindItem = (v, i) => Bind(v, i, false)
    ,
    };

    pinnedView.Rebuild();
    unpinnedView.Rebuild();

    };

    if (pinButton.userData != null)
    pinButton.clicked -= pinButton.userData as System.Action;
    pinButton.userData = action;
    pinButton.clicked += action;

    var findButton = v.Q<Button>("Find");
    findButton.clicked += () => EditorGUIUtility.PingObject(AssetDatabase.LoadAssetAtPath<SceneAsset>(AssetDatabase.GUIDToAssetPath(item.GUID)));
    },
    };

    unpinnedView.selectionType = SelectionType.None;
    unpinnedView.style.flexGrow = 1;
    @@ -275,50 +140,206 @@ VisualElement CreateItem()
    root.Add(unpinnedView);


    int PinnedMinHeight()
    if (pinnedList.Count == 0)
    {
    if (pinnedView != null) HideVisualElement(pinnedView);
    if (header != null) HideVisualElement(header);
    }
    }

    void Bind(VisualElement v, int i, bool isPinnedList)
    {

    var item = isPinnedList ? pinnedList[i] : dataList[i];
    var sceneButton = v.Q<Button>("SceneButton");
    sceneButton.text = item.SceneName;

    System.Action onClickScene = () => EditorSceneManager.OpenScene(AssetDatabase.GUIDToAssetPath(item.GUID));
    SubscribeToButton(sceneButton, onClickScene);

    var pinButton = v.Q<Button>("Pin");
    item.Pinned = !isPinnedList;
    pinButton.text = isPinnedList ? "Unpin" : "Pin";

    System.Action clicked = () =>
    {
    if (pinnedList.Count > 0)
    Debug.Log("Clicked " + item.SceneName);
    UnsubscribeToButton(pinButton);
    item.Pinned = !isPinnedList;

    if (!isPinnedList)
    {
    if (pinnedView != null)
    pinnedView.style.marginBottom = 10;
    if (unpinnedView != null)
    unpinnedView.style.marginTop = 10;
    item.PinnedIndex = pinnedList.Count;
    pinnedList.Add(item);
    dataList.Remove(item);
    }
    else
    {
    pinnedList.Remove(item);
    dataList.Insert(0, item);
    }



    if (pinnedList.Count == 0)
    pinnedView.style.minHeight = PinnedMinHeight();

    if (!isPinnedList) // if isPinnedList = false, show header and pinned view
    {
    if (pinnedView != null)
    {
    pinnedView.style.display = DisplayStyle.None;
    pinnedView.style.visibility = Visibility.Hidden;
    pinnedView.pickingMode = PickingMode.Ignore;
    }
    ShowVisualElement(pinnedView);
    ShowVisualElement(header);
    }

    pinnedView.Rebuild();
    unpinnedView.Rebuild();

    Save();

    };

    SubscribeToButton(pinButton, clicked);

    if (header != null)
    var findButton = v.Q<Button>("Find");
    System.Action clickedFind = () => EditorGUIUtility.PingObject(AssetDatabase.LoadAssetAtPath<SceneAsset>(AssetDatabase.GUIDToAssetPath(item.GUID)));
    SubscribeToButton(findButton, clickedFind);
    }

    private void Load()
    {
    var sceneAssets = FindAssetsOfType<SceneAsset>();

    string currentFilePath = AssetDatabase.GetAssetPath(MonoScript.FromScriptableObject(this));
    string currentDirectory = Path.GetDirectoryName(currentFilePath);
    jsonFilePath = Path.Combine(currentDirectory, "SceneList-JSON.json");

    foreach (var sceneAsset in sceneAssets)
    {
    string path = AssetDatabase.GetAssetPath(sceneAsset);

    dataList.Add(new HorizontalItemData() { SceneName = sceneAsset.name, GUID = AssetDatabase.AssetPathToGUID(path), Pinned = false });
    }

    if (File.Exists(jsonFilePath))
    {
    string json = File.ReadAllText(jsonFilePath);
    StateData stateData = JsonUtility.FromJson<StateData>(json);

    // use except to remove pinned scene from dataList
    if (stateData != null && stateData.pinnedList != null && stateData.pinnedList.Count > 0)
    {
    var count = dataList.Count;
    for (int i = 0; i < count; i++)
    {
    header.style.display = DisplayStyle.None;
    header.style.visibility = Visibility.Hidden;
    header.pickingMode = PickingMode.Ignore;

    for (int j = 0; j < stateData.pinnedList.Count; j++)
    {
    if (dataList[i].GUID == stateData.pinnedList[j].GUID)
    {
    dataList[i].Pinned = true;
    dataList[i].PinnedIndex = stateData.pinnedList[j].PinnedIndex;
    break;
    }
    }

    }
    }

    const int maxItem = 10;
    int heightIncrease = 22;
    return Mathf.Clamp(pinnedList.Count * heightIncrease, 0, maxItem * heightIncrease);
    }
    }

    private VisualElement CreateItem()
    {
    var horizontalItem = new VisualElement() { name = "HorizontalItem" };
    var sceneButton = new Button() { name = "SceneButton" };
    sceneButton.style.minWidth = 210;
    sceneButton.style.flexGrow = 1;
    sceneButton.style.unityTextAlign = TextAnchor.MiddleLeft;
    var pinButton = new Button() { name = "Pin", text = "Pin" };
    var findButton = new Button() { name = "Find", text = "Find" };
    horizontalItem.Add(sceneButton);
    horizontalItem.Add(pinButton);
    horizontalItem.Add(findButton);
    horizontalItem.style.flexDirection = FlexDirection.Row;
    pinButton.style.maxWidth = 70;
    findButton.style.maxWidth = 70;

    return horizontalItem;
    }

    private int PinnedMinHeight()
    {
    if (pinnedList.Count > 0)
    {
    if (pinnedView != null) pinnedView.style.marginBottom = 10;
    if (unpinnedView != null) unpinnedView.style.marginTop = 10;
    }


    if (pinnedList.Count == 0)
    {
    if (pinnedView != null) HideVisualElement(pinnedView);
    if (header != null) HideVisualElement(header);
    }

    const int maxItem = 10;
    int heightIncrease = 22;
    return Mathf.Clamp(pinnedList.Count * heightIncrease, 0, maxItem * heightIncrease);
    }

    private void ShowVisualElement(VisualElement visualElement)
    {
    visualElement.style.display = DisplayStyle.Flex;
    visualElement.style.visibility = Visibility.Visible;
    visualElement.pickingMode = PickingMode.Position;

    }

    private void HideVisualElement(VisualElement visualElement)
    {
    visualElement.style.display = DisplayStyle.None;
    visualElement.style.visibility = Visibility.Hidden;
    visualElement.pickingMode = PickingMode.Ignore;
    }

    private void SubscribeToButton(Button button, System.Action clicked)
    {
    UnsubscribeToButton(button);

    if (button.userData == null) button.userData = new UserData();

    (button.userData as UserData).AddCallback(button.text, clicked);
    button.clicked += clicked;
    }

    private void UnsubscribeToButton(Button button)
    {
    if (button.userData == null) return;
    (button.userData as UserData).RemoveCallback(button.text, button);
    }

    private void OnDestroy()

    private void Save()
    {
    StateData stateData = new StateData();
    stateData.pinnedList = pinnedList;
    string json = JsonUtility.ToJson(stateData);
    File.WriteAllText(jsonFilePath, json);
    }

    private T[] FindAssetsOfType<T>() where T : UnityEngine.Object
    {
    string[] guids = AssetDatabase.FindAssets($"t:{typeof(T).Name}");
    List<T> list = new List<T>();
    foreach (string guid in guids)
    {
    string assetPath = AssetDatabase.GUIDToAssetPath(guid);
    T asset = AssetDatabase.LoadAssetAtPath<T>(assetPath);

    if (asset != null)
    {
    list.Add(asset);
    }
    }

    return list.ToArray();
    }
    }
    2 changes: 1 addition & 1 deletion _readme.md
    Original file line number Diff line number Diff line change
    @@ -1,5 +1,5 @@
    <p align="center">
    <img src="https://gist.github.com/user-attachments/assets/61027782-fd43-42b9-ab88-65d3f8d5a047" alt="image"/>
    <img src="https://gist.github.com/user-attachments/assets/1467ff9b-d7a5-434d-bba8-80ae1849aa1b" alt="image" style="max-width: 100%; height: auto;"/>
    </p>

    ##### How to open: On Unity menu bar -> Tools -> Scene List and Pins
  7. restush created this gist Aug 22, 2024.
    324 changes: 324 additions & 0 deletions SceneListEditorWindow.cs
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,324 @@
    using System.Collections;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using UnityEditor;
    using UnityEditor.SceneManagement;
    using UnityEngine;
    using UnityEngine.UIElements;



    public class SceneListEditorWindow : EditorWindow
    {
    [System.Serializable]
    private class HorizontalItemData
    {
    public string SceneName;
    public string GUID;
    public bool Pinned;
    public int Index;
    }

    private List<HorizontalItemData> dataList = new();
    private List<HorizontalItemData> pinnedList = new();

    [System.Serializable]
    private class StateData
    {
    public List<HorizontalItemData> pinnedList;
    }

    [SerializeField] VisualTreeAsset tree;
    [SerializeField] VisualTreeAsset treeButton;
    private ListView pinnedView;
    private ListView unpinnedView;
    private string jsonFilePath;
    private Label header;
    private Label header2;

    [MenuItem("Tools/Scene List and Pins")]
    static void CreateMenu()
    {
    var window = GetWindow<SceneListEditorWindow>();
    window.titleContent = new GUIContent("Scene List & Pins");
    }

    T[] FindAssetsOfType<T>() where T : UnityEngine.Object
    {
    string[] guids = AssetDatabase.FindAssets($"t:{typeof(T).Name}");
    List<T> list = new List<T>();
    foreach (string guid in guids)
    {
    string assetPath = AssetDatabase.GUIDToAssetPath(guid);
    T asset = AssetDatabase.LoadAssetAtPath<T>(assetPath);

    if (asset != null)
    {
    list.Add(asset);
    }
    }

    return list.ToArray();
    }

    private void CreateGUI()
    {

    // find all scenes in the project
    var sceneAssets = FindAssetsOfType<SceneAsset>();

    string currentFilePath = AssetDatabase.GetAssetPath(MonoScript.FromScriptableObject(this));
    string currentDirectory = Path.GetDirectoryName(currentFilePath);
    jsonFilePath = Path.Combine(currentDirectory, "FindMissingEditor-JSON.json");

    foreach (var sceneAsset in sceneAssets)
    {
    string path = AssetDatabase.GetAssetPath(sceneAsset);

    dataList.Add(new HorizontalItemData() { SceneName = sceneAsset.name, GUID = AssetDatabase.AssetPathToGUID(path), Pinned = false });
    }

    if (File.Exists(jsonFilePath))
    {
    string json = File.ReadAllText(jsonFilePath);
    StateData stateData = JsonUtility.FromJson<StateData>(json);

    // use except to remove pinned scene from dataList
    if (stateData != null && stateData.pinnedList != null)
    {
    var count = dataList.Count;
    for (int i = 0; i < count; i++)
    {

    for (int j = 0; j < stateData.pinnedList.Count; j++)
    {
    if (dataList[i].GUID == stateData.pinnedList[j].GUID)
    {
    dataList[i].Pinned = true;
    dataList[i].Index = stateData.pinnedList[j].Index;
    break;
    }
    }

    }
    }




    }




    pinnedList = dataList.FindAll(x => x.Pinned);
    dataList.RemoveAll(x => x.Pinned);
    pinnedList = pinnedList.OrderBy(x => x.Index).ToList();


    VisualElement CreateItem()
    {
    var horizontalItem = new VisualElement() { name = "HorizontalItem" };
    var sceneButton = new Button() { name = "SceneButton" };
    sceneButton.style.minWidth = 210;
    sceneButton.style.flexGrow = 1;
    sceneButton.style.unityTextAlign = TextAnchor.MiddleLeft;
    var pinButton = new Button() { name = "Pin", text = "Pin" };
    var findButton = new Button() { name = "Find", text = "Find" };
    horizontalItem.Add(sceneButton);
    horizontalItem.Add(pinButton);
    horizontalItem.Add(findButton);
    horizontalItem.style.flexDirection = FlexDirection.Row;
    pinButton.style.maxWidth = 70;
    findButton.style.maxWidth = 70;

    return horizontalItem;
    }

    pinnedView = new ListView()
    {
    itemsSource = pinnedList,
    makeItem = () => CreateItem(),
    bindItem = (v, index) =>
    {
    int i = index;
    var item = pinnedList[i];
    var sceneButton = v.Q<Button>("SceneButton");
    sceneButton.text = item.SceneName;

    if (sceneButton.userData != null)
    sceneButton.clicked -= sceneButton.userData as System.Action;
    System.Action onClickScene = () => EditorSceneManager.OpenScene(AssetDatabase.GUIDToAssetPath(item.GUID));
    sceneButton.userData = onClickScene;
    sceneButton.clicked += onClickScene;

    var pinButton = v.Q<Button>("Pin");
    item.Pinned = true;
    pinButton.text = "Unpin";

    System.Action action = () =>
    {
    if (pinButton.userData != null)
    pinButton.clicked -= pinButton.userData as System.Action;
    item.Pinned = false;
    dataList.Add(item);
    pinnedList.Remove(item);
    pinnedView.style.minHeight = PinnedMinHeight();
    pinnedView.Rebuild();
    unpinnedView.Rebuild();
    };

    if (pinButton.userData != null)
    pinButton.clicked -= pinButton.userData as System.Action;
    pinButton.userData = action;
    pinButton.clicked += action;

    var findButton = v.Q<Button>("Find");
    findButton.clicked += () => EditorGUIUtility.PingObject(AssetDatabase.LoadAssetAtPath<SceneAsset>(AssetDatabase.GUIDToAssetPath(item.GUID)));
    },
    showAlternatingRowBackgrounds = AlternatingRowBackground.All,
    reorderable = true,
    reorderMode = ListViewReorderMode.Animated,


    };
    pinnedView.style.unityTextAlign = TextAnchor.MiddleCenter;
    pinnedView.style.flexGrow = 1;
    pinnedView.style.minHeight = PinnedMinHeight();
    // callback on reorder
    pinnedView.itemIndexChanged += (oldIndex, newIndex) =>
    {
    pinnedList[newIndex].Index = newIndex;
    pinnedList[oldIndex].Index = oldIndex;
    };
    pinnedView.selectionType = SelectionType.None;


    unpinnedView = new ListView()
    {

    itemsSource = dataList,
    makeItem = () => CreateItem(),
    bindItem = (v, i) =>
    {
    var item = dataList[i];
    var sceneButton = v.Q<Button>("SceneButton");
    sceneButton.text = item.SceneName;

    if (sceneButton.userData != null)
    sceneButton.clicked -= sceneButton.userData as System.Action;
    System.Action onClickScene = () => EditorSceneManager.OpenScene(AssetDatabase.GUIDToAssetPath(item.GUID));
    sceneButton.userData = onClickScene;
    sceneButton.clicked += onClickScene;

    var pinButton = v.Q<Button>("Pin");
    item.Pinned = false;
    pinButton.text = "Pin";

    System.Action action = () =>
    {
    if (pinButton.userData != null)
    pinButton.clicked -= pinButton.userData as System.Action;
    item.Index = pinnedList.Count;
    pinnedList.Add(item);
    dataList.Remove(item);

    pinnedView.style.minHeight = PinnedMinHeight();
    // show pinned view
    pinnedView.style.display = DisplayStyle.Flex;
    pinnedView.style.visibility = Visibility.Visible;
    pinnedView.pickingMode = PickingMode.Position;

    header.style.display = DisplayStyle.Flex;
    header.style.visibility = Visibility.Visible;
    header.pickingMode = PickingMode.Position;


    pinnedView.Rebuild();
    unpinnedView.Rebuild();

    };

    if (pinButton.userData != null)
    pinButton.clicked -= pinButton.userData as System.Action;
    pinButton.userData = action;
    pinButton.clicked += action;

    var findButton = v.Q<Button>("Find");
    findButton.clicked += () => EditorGUIUtility.PingObject(AssetDatabase.LoadAssetAtPath<SceneAsset>(AssetDatabase.GUIDToAssetPath(item.GUID)));
    },
    };

    unpinnedView.selectionType = SelectionType.None;
    unpinnedView.style.flexGrow = 1;


    var root = new VisualElement() { name = "Root" };
    rootVisualElement.Add(root);

    header = new Label("Pinned Scenes") { name = "Header" };
    header.style.unityFontStyleAndWeight = FontStyle.Bold;
    header.style.fontSize = 18;
    header.style.unityTextAlign = TextAnchor.MiddleCenter;
    header.style.marginBottom = 2;
    root.Add(header);
    root.Add(pinnedView);


    header2 = new Label("Unpinned Scenes") { name = "Header" };
    header2.style.unityFontStyleAndWeight = FontStyle.Bold;
    header2.style.fontSize = 18;
    header2.style.unityTextAlign = TextAnchor.MiddleCenter;
    header2.style.marginBottom = 2;
    root.Add(header2);
    root.Add(unpinnedView);


    int PinnedMinHeight()
    {
    if (pinnedList.Count > 0)
    {
    if (pinnedView != null)
    pinnedView.style.marginBottom = 10;
    if (unpinnedView != null)
    unpinnedView.style.marginTop = 10;
    }


    if (pinnedList.Count == 0)
    {
    if (pinnedView != null)
    {
    pinnedView.style.display = DisplayStyle.None;
    pinnedView.style.visibility = Visibility.Hidden;
    pinnedView.pickingMode = PickingMode.Ignore;
    }

    if (header != null)
    {
    header.style.display = DisplayStyle.None;
    header.style.visibility = Visibility.Hidden;
    header.pickingMode = PickingMode.Ignore;
    }
    }

    const int maxItem = 10;
    int heightIncrease = 22;
    return Mathf.Clamp(pinnedList.Count * heightIncrease, 0, maxItem * heightIncrease);
    }



    }

    private void OnDestroy()
    {
    StateData stateData = new StateData();
    stateData.pinnedList = pinnedList;
    string json = JsonUtility.ToJson(stateData);
    File.WriteAllText(jsonFilePath, json);
    }


    }
    5 changes: 5 additions & 0 deletions _readme.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,5 @@
    <p align="center">
    <img src="https://gist.github.com/user-attachments/assets/61027782-fd43-42b9-ab88-65d3f8d5a047" alt="image"/>
    </p>

    ##### How to open: On Unity menu bar -> Tools -> Scene List and Pins