mirror of
https://github.com/GrahamKracker/UnityExplorer.git
synced 2025-07-14 23:56:36 +08:00
Progress on ReflectionInspector, framework mostly done
This commit is contained in:
@ -12,8 +12,6 @@ namespace UnityExplorer.UI.Widgets
|
||||
{
|
||||
public class ButtonListSource<T> : IPoolDataSource<ButtonCell>
|
||||
{
|
||||
public int GetRealIndexOfTempIndex(int index) => throw new NotImplementedException("TODO");
|
||||
|
||||
internal ScrollPool<ButtonCell> ScrollPool;
|
||||
|
||||
public int ItemCount => currentEntries.Count;
|
||||
|
@ -1,219 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.Core.Search;
|
||||
using UnityExplorer.UI.Inspectors;
|
||||
using UnityExplorer.UI.Models;
|
||||
using UnityExplorer.UI.ObjectPool;
|
||||
using UnityExplorer.UI.Panels;
|
||||
using UnityExplorer.UI.Utility;
|
||||
using UnityExplorer.UI.Widgets.AutoComplete;
|
||||
|
||||
namespace UnityExplorer.UI.Widgets
|
||||
{
|
||||
public class ObjectSearch : UIModel
|
||||
{
|
||||
public ObjectExplorer Parent { get; }
|
||||
|
||||
public ObjectSearch(ObjectExplorer parent)
|
||||
{
|
||||
Parent = parent;
|
||||
}
|
||||
|
||||
private SearchContext m_context = SearchContext.UnityObject;
|
||||
private SceneFilter m_sceneFilter = SceneFilter.Any;
|
||||
private ChildFilter m_childFilter = ChildFilter.Any;
|
||||
|
||||
public ButtonListSource<object> dataHandler;
|
||||
|
||||
private ScrollPool<ButtonCell> resultsScrollPool;
|
||||
private List<object> currentResults = new List<object>();
|
||||
|
||||
public override GameObject UIRoot => uiRoot;
|
||||
private GameObject uiRoot;
|
||||
|
||||
private GameObject sceneFilterRow;
|
||||
private GameObject childFilterRow;
|
||||
private GameObject unityObjectClassRow;
|
||||
|
||||
private InputField nameInputField;
|
||||
private InputField classInputField;
|
||||
|
||||
private Text resultsLabel;
|
||||
|
||||
public List<object> GetEntries() => currentResults;
|
||||
|
||||
private void OnContextDropdownChanged(int value)
|
||||
{
|
||||
m_context = (SearchContext)value;
|
||||
|
||||
// show/hide other filters depending on what we just selected.
|
||||
bool shouldShowGoFilters = m_context == SearchContext.GameObject
|
||||
|| m_context == SearchContext.Component
|
||||
|| m_context == SearchContext.Custom;
|
||||
|
||||
sceneFilterRow.SetActive(shouldShowGoFilters);
|
||||
childFilterRow.SetActive(shouldShowGoFilters);
|
||||
|
||||
unityObjectClassRow.SetActive(m_context == SearchContext.Custom);
|
||||
}
|
||||
|
||||
private void OnSceneFilterDropChanged(int value) => m_sceneFilter = (SceneFilter)value;
|
||||
|
||||
private void OnChildFilterDropChanged(int value) => m_childFilter = (ChildFilter)value;
|
||||
|
||||
public void DoSearch()
|
||||
{
|
||||
cachedCellTexts.Clear();
|
||||
|
||||
if (m_context == SearchContext.Singleton)
|
||||
currentResults = SearchProvider.SingletonSearch(nameInputField.text);
|
||||
else if (m_context == SearchContext.StaticClass)
|
||||
currentResults = SearchProvider.StaticClassSearch(nameInputField.text);
|
||||
else
|
||||
{
|
||||
string compType = "";
|
||||
if (m_context == SearchContext.Custom)
|
||||
compType = classInputField.text;
|
||||
|
||||
currentResults = SearchProvider.UnityObjectSearch(nameInputField.text, compType, m_context, m_childFilter, m_sceneFilter);
|
||||
}
|
||||
|
||||
dataHandler.RefreshData();
|
||||
resultsScrollPool.RefreshCells(true);
|
||||
|
||||
resultsLabel.text = $"{currentResults.Count} results";
|
||||
}
|
||||
|
||||
// Cache the syntax-highlighted text for each search result to reduce allocs.
|
||||
private static readonly Dictionary<int, string> cachedCellTexts = new Dictionary<int, string>();
|
||||
|
||||
public void SetCell(ButtonCell cell, int index)
|
||||
{
|
||||
if (!cachedCellTexts.ContainsKey(index))
|
||||
{
|
||||
string text;
|
||||
if (m_context == SearchContext.StaticClass)
|
||||
text = SignatureHighlighter.HighlightTypeName(currentResults[index] as Type, true, true);
|
||||
else
|
||||
text = ToStringUtility.ToStringWithType(currentResults[index], currentResults[index]?.GetActualType());
|
||||
|
||||
cachedCellTexts.Add(index, text);
|
||||
}
|
||||
|
||||
cell.Button.ButtonText.text = cachedCellTexts[index];
|
||||
}
|
||||
|
||||
private void OnCellClicked(int dataIndex)
|
||||
{
|
||||
if (m_context == SearchContext.StaticClass)
|
||||
InspectorManager.InspectStatic(currentResults[dataIndex] as Type);
|
||||
else
|
||||
InspectorManager.Inspect(currentResults[dataIndex]);
|
||||
}
|
||||
|
||||
private bool ShouldDisplayCell(object arg1, string arg2) => true;
|
||||
|
||||
public override void ConstructUI(GameObject parent)
|
||||
{
|
||||
uiRoot = UIFactory.CreateVerticalGroup(parent, "ObjectSearch", true, true, true, true, 2, new Vector4(2, 2, 2, 2));
|
||||
UIFactory.SetLayoutElement(uiRoot, flexibleHeight: 9999);
|
||||
|
||||
// Search context row
|
||||
|
||||
var contextGroup = UIFactory.CreateHorizontalGroup(uiRoot, "SearchContextRow", false, true, true, true, 2, new Vector4(2, 2, 2, 2));
|
||||
UIFactory.SetLayoutElement(contextGroup, minHeight: 25, flexibleHeight: 0);
|
||||
|
||||
var contextLbl = UIFactory.CreateLabel(contextGroup, "SearchContextLabel", "Searching for:", TextAnchor.MiddleLeft);
|
||||
UIFactory.SetLayoutElement(contextLbl.gameObject, minWidth: 110, flexibleWidth: 0);
|
||||
|
||||
var contextDropObj = UIFactory.CreateDropdown(contextGroup, out Dropdown contextDrop, null, 14, OnContextDropdownChanged);
|
||||
foreach (var name in Enum.GetNames(typeof(SearchContext)))
|
||||
contextDrop.options.Add(new Dropdown.OptionData(name));
|
||||
UIFactory.SetLayoutElement(contextDropObj, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999);
|
||||
|
||||
// Unity class input
|
||||
|
||||
unityObjectClassRow = UIFactory.CreateHorizontalGroup(uiRoot, "UnityClassRow", false, true, true, true, 2, new Vector4(2, 2, 2, 2));
|
||||
UIFactory.SetLayoutElement(unityObjectClassRow, minHeight: 25, flexibleHeight: 0);
|
||||
|
||||
var unityClassLbl = UIFactory.CreateLabel(unityObjectClassRow, "UnityClassLabel", "Custom Type:", TextAnchor.MiddleLeft);
|
||||
UIFactory.SetLayoutElement(unityClassLbl.gameObject, minWidth: 110, flexibleWidth: 0);
|
||||
|
||||
var classInputObj = UIFactory.CreateInputField(unityObjectClassRow, "ClassInput", "...", out this.classInputField);
|
||||
UIFactory.SetLayoutElement(classInputObj, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999);
|
||||
|
||||
new TypeCompleter(typeof(UnityEngine.Object), classInputField);
|
||||
|
||||
unityObjectClassRow.SetActive(false);
|
||||
|
||||
// Child filter row
|
||||
|
||||
childFilterRow = UIFactory.CreateHorizontalGroup(uiRoot, "ChildFilterRow", false, true, true, true, 2, new Vector4(2, 2, 2, 2));
|
||||
UIFactory.SetLayoutElement(childFilterRow, minHeight: 25, flexibleHeight: 0);
|
||||
|
||||
var childLbl = UIFactory.CreateLabel(childFilterRow, "ChildLabel", "Child filter:", TextAnchor.MiddleLeft);
|
||||
UIFactory.SetLayoutElement(childLbl.gameObject, minWidth: 110, flexibleWidth: 0);
|
||||
|
||||
var childDropObj = UIFactory.CreateDropdown(childFilterRow, out Dropdown childDrop, null, 14, OnChildFilterDropChanged);
|
||||
foreach (var name in Enum.GetNames(typeof(ChildFilter)))
|
||||
childDrop.options.Add(new Dropdown.OptionData(name));
|
||||
UIFactory.SetLayoutElement(childDropObj, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999);
|
||||
|
||||
childFilterRow.SetActive(false);
|
||||
|
||||
// Scene filter row
|
||||
|
||||
sceneFilterRow = UIFactory.CreateHorizontalGroup(uiRoot, "SceneFilterRow", false, true, true, true, 2, new Vector4(2, 2, 2, 2));
|
||||
UIFactory.SetLayoutElement(sceneFilterRow, minHeight: 25, flexibleHeight: 0);
|
||||
|
||||
var sceneLbl = UIFactory.CreateLabel(sceneFilterRow, "SceneLabel", "Scene filter:", TextAnchor.MiddleLeft);
|
||||
UIFactory.SetLayoutElement(sceneLbl.gameObject, minWidth: 110, flexibleWidth: 0);
|
||||
|
||||
var sceneDropObj = UIFactory.CreateDropdown(sceneFilterRow, out Dropdown sceneDrop, null, 14, OnSceneFilterDropChanged);
|
||||
foreach (var name in Enum.GetNames(typeof(SceneFilter)))
|
||||
sceneDrop.options.Add(new Dropdown.OptionData(name));
|
||||
UIFactory.SetLayoutElement(sceneDropObj, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999);
|
||||
|
||||
sceneFilterRow.SetActive(false);
|
||||
|
||||
// Name filter input
|
||||
|
||||
var nameRow = UIFactory.CreateHorizontalGroup(uiRoot, "NameRow", true, true, true, true, 2, new Vector4(2, 2, 2, 2));
|
||||
UIFactory.SetLayoutElement(nameRow, minHeight: 25, flexibleHeight: 0);
|
||||
|
||||
var nameLbl = UIFactory.CreateLabel(nameRow, "NameFilterLabel", "Name contains:", TextAnchor.MiddleLeft);
|
||||
UIFactory.SetLayoutElement(nameLbl.gameObject, minWidth: 110, flexibleWidth: 0);
|
||||
|
||||
var nameInputObj = UIFactory.CreateInputField(nameRow, "NameFilterInput", "...", out this.nameInputField);
|
||||
UIFactory.SetLayoutElement(nameInputObj, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999);
|
||||
|
||||
// Search button
|
||||
|
||||
var searchButton = UIFactory.CreateButton(uiRoot, "SearchButton", "Search");
|
||||
UIFactory.SetLayoutElement(searchButton.Button.gameObject, minHeight: 25, flexibleHeight: 0);
|
||||
searchButton.OnClick += DoSearch;
|
||||
|
||||
// Results count label
|
||||
|
||||
var resultsCountRow = UIFactory.CreateHorizontalGroup(uiRoot, "ResultsCountRow", true, true, true, true);
|
||||
UIFactory.SetLayoutElement(resultsCountRow, minHeight: 25, flexibleHeight: 0);
|
||||
|
||||
resultsLabel = UIFactory.CreateLabel(resultsCountRow, "ResultsLabel", "0 results", TextAnchor.MiddleCenter);
|
||||
|
||||
// RESULTS SCROLL POOL
|
||||
|
||||
dataHandler = new ButtonListSource<object>(resultsScrollPool, GetEntries, SetCell, ShouldDisplayCell, OnCellClicked);
|
||||
resultsScrollPool = UIFactory.CreateScrollPool<ButtonCell>(uiRoot, "ResultsList", out GameObject scrollObj, out GameObject scrollContent);
|
||||
|
||||
//if (!Pool<ButtonCell>.PrototypeObject)
|
||||
// Pool<ButtonCell>.PrototypeObject = ButtonCell.CreatePrototypeCell(Pool<ButtonCell>.InactiveHolder).gameObject;
|
||||
|
||||
resultsScrollPool.Initialize(dataHandler);//, ButtonCell.CreatePrototypeCell(uiRoot));
|
||||
UIFactory.SetLayoutElement(scrollObj, flexibleHeight: 9999);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,297 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.Core;
|
||||
using UnityExplorer.UI.Models;
|
||||
using UnityExplorer.UI.Panels;
|
||||
|
||||
namespace UnityExplorer.UI.Widgets
|
||||
{
|
||||
public class SceneExplorer : UIModel
|
||||
{
|
||||
public ObjectExplorer Parent { get; }
|
||||
|
||||
public SceneExplorer(ObjectExplorer parent)
|
||||
{
|
||||
Parent = parent;
|
||||
|
||||
SceneHandler.OnInspectedSceneChanged += SceneHandler_OnInspectedSceneChanged;
|
||||
SceneHandler.OnLoadedScenesChanged += SceneHandler_OnLoadedScenesChanged;
|
||||
}
|
||||
|
||||
public override GameObject UIRoot => m_uiRoot;
|
||||
private GameObject m_uiRoot;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to automatically update per auto-update interval or not.
|
||||
/// </summary>
|
||||
public bool AutoUpdate = false;
|
||||
|
||||
public TransformTree Tree;
|
||||
private float timeOfLastUpdate = -1f;
|
||||
|
||||
private GameObject refreshRow;
|
||||
private Dropdown sceneDropdown;
|
||||
private readonly Dictionary<int, Dropdown.OptionData> sceneToDropdownOption = new Dictionary<int, Dropdown.OptionData>();
|
||||
|
||||
private IEnumerable<GameObject> GetRootEntries() => SceneHandler.CurrentRootObjects;
|
||||
|
||||
public void ForceUpdate()
|
||||
{
|
||||
ExpensiveUpdate();
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
if ((AutoUpdate || !SceneHandler.InspectingAssetScene) && Time.realtimeSinceStartup - timeOfLastUpdate >= 1f)
|
||||
{
|
||||
timeOfLastUpdate = Time.realtimeSinceStartup;
|
||||
ExpensiveUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
public void ExpensiveUpdate()
|
||||
{
|
||||
SceneHandler.Update();
|
||||
Tree.RefreshData(true);
|
||||
}
|
||||
|
||||
private void OnDropdownChanged(int value)
|
||||
{
|
||||
if (value < 0 || SceneHandler.LoadedScenes.Count <= value)
|
||||
return;
|
||||
|
||||
SceneHandler.SelectedScene = SceneHandler.LoadedScenes[value];
|
||||
SceneHandler.Update();
|
||||
Tree.RefreshData(true);
|
||||
OnSelectedSceneChanged(SceneHandler.SelectedScene.Value);
|
||||
}
|
||||
|
||||
private void SceneHandler_OnInspectedSceneChanged(Scene scene)
|
||||
{
|
||||
if (!sceneToDropdownOption.ContainsKey(scene.handle))
|
||||
PopulateSceneDropdown();
|
||||
|
||||
if (sceneToDropdownOption.ContainsKey(scene.handle))
|
||||
{
|
||||
var opt = sceneToDropdownOption[scene.handle];
|
||||
int idx = sceneDropdown.options.IndexOf(opt);
|
||||
if (sceneDropdown.value != idx)
|
||||
sceneDropdown.value = idx;
|
||||
else
|
||||
sceneDropdown.captionText.text = opt.text;
|
||||
}
|
||||
|
||||
OnSelectedSceneChanged(scene);
|
||||
}
|
||||
|
||||
private void OnSelectedSceneChanged(Scene scene)
|
||||
{
|
||||
if (refreshRow)
|
||||
refreshRow.SetActive(!scene.IsValid());
|
||||
}
|
||||
|
||||
private void SceneHandler_OnLoadedScenesChanged(ReadOnlyCollection<Scene> loadedScenes)
|
||||
{
|
||||
PopulateSceneDropdown();
|
||||
}
|
||||
|
||||
private void PopulateSceneDropdown()
|
||||
{
|
||||
sceneToDropdownOption.Clear();
|
||||
sceneDropdown.options.Clear();
|
||||
|
||||
foreach (var scene in SceneHandler.LoadedScenes)
|
||||
{
|
||||
string name = scene.name?.Trim();
|
||||
|
||||
if (!scene.IsValid())
|
||||
name = "HideAndDontSave";
|
||||
else if (string.IsNullOrEmpty(name))
|
||||
name = "<untitled>";
|
||||
|
||||
var option = new Dropdown.OptionData(name);
|
||||
sceneDropdown.options.Add(option);
|
||||
sceneToDropdownOption.Add(scene.handle, option);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnFilterInput(string input)
|
||||
{
|
||||
Tree.CurrentFilter = input;
|
||||
Tree.RefreshData(true, true);
|
||||
}
|
||||
|
||||
private void TryLoadScene(LoadSceneMode mode, Dropdown allSceneDrop)
|
||||
{
|
||||
var text = allSceneDrop.options[allSceneDrop.value].text;
|
||||
|
||||
if (text == DEFAULT_LOAD_TEXT)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
SceneManager.LoadScene(text, mode);
|
||||
allSceneDrop.value = 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExplorerCore.LogWarning($"Unable to load the Scene! {ex.ReflectionExToString()}");
|
||||
}
|
||||
}
|
||||
|
||||
public override void ConstructUI(GameObject content)
|
||||
{
|
||||
m_uiRoot = UIFactory.CreateUIObject("SceneExplorer", content);
|
||||
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(m_uiRoot, true, true, true, true, 0, 2, 2, 2, 2);
|
||||
UIFactory.SetLayoutElement(m_uiRoot, flexibleHeight: 9999);
|
||||
|
||||
// Tool bar (top area)
|
||||
|
||||
var toolbar = UIFactory.CreateVerticalGroup(m_uiRoot, "Toolbar", true, true, true, true, 2, new Vector4(2, 2, 2, 2),
|
||||
new Color(0.15f, 0.15f, 0.15f));
|
||||
|
||||
// Scene selector dropdown
|
||||
|
||||
var dropRow = UIFactory.CreateHorizontalGroup(toolbar, "DropdownRow", true, true, true, true, 5, default, new Color(1, 1, 1, 0));
|
||||
UIFactory.SetLayoutElement(dropRow, minHeight: 25, flexibleWidth: 9999);
|
||||
|
||||
var dropLabel = UIFactory.CreateLabel(dropRow, "SelectorLabel", "Scene:", TextAnchor.MiddleLeft, Color.cyan, false, 15);
|
||||
UIFactory.SetLayoutElement(dropLabel.gameObject, minHeight: 25, minWidth: 60, flexibleWidth: 0);
|
||||
|
||||
var dropdownObj = UIFactory.CreateDropdown(dropRow, out sceneDropdown, "<notset>", 13, OnDropdownChanged);
|
||||
UIFactory.SetLayoutElement(dropdownObj, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999);
|
||||
|
||||
SceneHandler.Update();
|
||||
PopulateSceneDropdown();
|
||||
sceneDropdown.captionText.text = sceneToDropdownOption.First().Value.text;
|
||||
|
||||
// Filter row
|
||||
|
||||
var filterRow = UIFactory.CreateHorizontalGroup(toolbar, "FilterGroup", true, true, true, true, 2, new Vector4(2, 2, 2, 2));
|
||||
UIFactory.SetLayoutElement(filterRow, minHeight: 25, flexibleHeight: 0);
|
||||
|
||||
//Filter input field
|
||||
var inputFieldObj = UIFactory.CreateInputField(filterRow, "FilterInput", "Search...", out InputField inputField, 13);
|
||||
inputField.targetGraphic.color = new Color(0.2f, 0.2f, 0.2f);
|
||||
RuntimeProvider.Instance.SetColorBlock(inputField, new Color(0.4f, 0.4f, 0.4f), new Color(0.2f, 0.2f, 0.2f), new Color(0.08f, 0.08f, 0.08f));
|
||||
UIFactory.SetLayoutElement(inputFieldObj, minHeight: 25);
|
||||
inputField.onValueChanged.AddListener(OnFilterInput);
|
||||
|
||||
// refresh row
|
||||
|
||||
refreshRow = UIFactory.CreateHorizontalGroup(toolbar, "RefreshGroup", true, true, true, true, 2, new Vector4(2, 2, 2, 2));
|
||||
UIFactory.SetLayoutElement(refreshRow, minHeight: 30, flexibleHeight: 0);
|
||||
|
||||
var refreshButton = UIFactory.CreateButton(refreshRow, "RefreshButton", "Update");
|
||||
UIFactory.SetLayoutElement(refreshButton.Button.gameObject, minWidth: 65, flexibleWidth: 0);
|
||||
refreshButton.OnClick += ForceUpdate;
|
||||
|
||||
var refreshToggle = UIFactory.CreateToggle(refreshRow, "RefreshToggle", out Toggle toggle, out Text text);
|
||||
UIFactory.SetLayoutElement(refreshToggle, flexibleWidth: 9999);
|
||||
text.text = "Auto-update (1 second)";
|
||||
text.alignment = TextAnchor.MiddleLeft;
|
||||
text.color = Color.white;
|
||||
text.fontSize = 12;
|
||||
toggle.isOn = false;
|
||||
toggle.onValueChanged.AddListener((bool val) => AutoUpdate = val);
|
||||
|
||||
refreshRow.SetActive(false);
|
||||
|
||||
// Transform Tree
|
||||
|
||||
var scrollPool = UIFactory.CreateScrollPool<TransformCell>(m_uiRoot, "TransformTree", out GameObject scrollObj,
|
||||
out GameObject scrollContent, new Color(0.11f, 0.11f, 0.11f));
|
||||
UIFactory.SetLayoutElement(scrollObj, flexibleHeight: 9999);
|
||||
UIFactory.SetLayoutElement(scrollContent, flexibleHeight: 9999);
|
||||
|
||||
Tree = new TransformTree(scrollPool) { GetRootEntriesMethod = GetRootEntries };
|
||||
Tree.Init();
|
||||
Tree.RefreshData(true, true);
|
||||
//scrollPool.Viewport.GetComponent<Mask>().enabled = false;
|
||||
//UIRoot.GetComponent<Mask>().enabled = false;
|
||||
|
||||
// Scene Loader
|
||||
|
||||
ConstructSceneLoader();
|
||||
}
|
||||
|
||||
private const string DEFAULT_LOAD_TEXT = "[Select a scene]";
|
||||
|
||||
private void ConstructSceneLoader()
|
||||
{
|
||||
// Scene Loader
|
||||
try
|
||||
{
|
||||
if (SceneHandler.WasAbleToGetScenesInBuild)
|
||||
{
|
||||
var sceneLoaderObj = UIFactory.CreateVerticalGroup(m_uiRoot, "SceneLoader", true, true, true, true);
|
||||
UIFactory.SetLayoutElement(sceneLoaderObj, minHeight: 25);
|
||||
//sceneLoaderObj.SetActive(false);
|
||||
|
||||
var loaderTitle = UIFactory.CreateLabel(sceneLoaderObj, "SceneLoaderLabel", "Scene Loader", TextAnchor.MiddleLeft, Color.white, true, 14);
|
||||
UIFactory.SetLayoutElement(loaderTitle.gameObject, minHeight: 25, flexibleHeight: 0);
|
||||
|
||||
var allSceneDropObj = UIFactory.CreateDropdown(sceneLoaderObj, out Dropdown allSceneDrop, "", 14, null);
|
||||
UIFactory.SetLayoutElement(allSceneDropObj, minHeight: 25, minWidth: 150, flexibleWidth: 0, flexibleHeight: 0);
|
||||
|
||||
allSceneDrop.options.Add(new Dropdown.OptionData(DEFAULT_LOAD_TEXT));
|
||||
|
||||
foreach (var scene in SceneHandler.AllSceneNames)
|
||||
allSceneDrop.options.Add(new Dropdown.OptionData(Path.GetFileNameWithoutExtension(scene)));
|
||||
|
||||
allSceneDrop.value = 1;
|
||||
allSceneDrop.value = 0;
|
||||
|
||||
var buttonRow = UIFactory.CreateHorizontalGroup(sceneLoaderObj, "LoadButtons", true, true, true, true, 4);
|
||||
|
||||
var loadButton = UIFactory.CreateButton(buttonRow, "LoadSceneButton", "Load (Single)", new Color(0.1f, 0.3f, 0.3f));
|
||||
UIFactory.SetLayoutElement(loadButton.Button.gameObject, minHeight: 25, minWidth: 150);
|
||||
loadButton.OnClick += () =>
|
||||
{
|
||||
TryLoadScene(LoadSceneMode.Single, allSceneDrop);
|
||||
};
|
||||
|
||||
var loadAdditiveButton = UIFactory.CreateButton(buttonRow, "LoadSceneButton", "Load (Additive)", new Color(0.1f, 0.3f, 0.3f));
|
||||
UIFactory.SetLayoutElement(loadAdditiveButton.Button.gameObject, minHeight: 25, minWidth: 150);
|
||||
loadAdditiveButton.OnClick += () =>
|
||||
{
|
||||
TryLoadScene(LoadSceneMode.Additive, allSceneDrop);
|
||||
};
|
||||
|
||||
var disabledColor = new Color(0.24f, 0.24f, 0.24f);
|
||||
RuntimeProvider.Instance.SetColorBlock(loadButton.Button, disabled: disabledColor);
|
||||
RuntimeProvider.Instance.SetColorBlock(loadAdditiveButton.Button, disabled: disabledColor);
|
||||
|
||||
loadButton.Button.interactable = false;
|
||||
loadAdditiveButton.Button.interactable = false;
|
||||
|
||||
allSceneDrop.onValueChanged.AddListener((int val) =>
|
||||
{
|
||||
var text = allSceneDrop.options[val].text;
|
||||
if (text == DEFAULT_LOAD_TEXT)
|
||||
{
|
||||
loadButton.Button.interactable = false;
|
||||
loadAdditiveButton.Button.interactable = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
loadButton.Button.interactable = true;
|
||||
loadAdditiveButton.Button.interactable = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExplorerCore.LogWarning($"Could not create the Scene Loader helper! {ex.ReflectionExToString()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -24,8 +24,7 @@ namespace UnityExplorer.UI.Widgets
|
||||
ScrollPool = scrollPool;
|
||||
}
|
||||
|
||||
// initialize with a reasonably sized pool, most caches will allocate a fair bit.
|
||||
private readonly List<DataViewInfo> heightCache = new List<DataViewInfo>(16384);
|
||||
private readonly List<DataViewInfo> heightCache = new List<DataViewInfo>();
|
||||
|
||||
public DataViewInfo this[int index]
|
||||
{
|
||||
@ -104,12 +103,8 @@ namespace UnityExplorer.UI.Widgets
|
||||
heightCache.RemoveAt(heightCache.Count - 1);
|
||||
|
||||
int idx = heightCache.Count;
|
||||
if (idx > 0)
|
||||
{
|
||||
while (rangeCache[rangeCache.Count - 1] == idx)
|
||||
rangeCache.RemoveAt(rangeCache.Count - 1);
|
||||
}
|
||||
|
||||
while (rangeCache.Count > 0 && rangeCache[rangeCache.Count - 1] == idx)
|
||||
rangeCache.RemoveAt(rangeCache.Count - 1);
|
||||
}
|
||||
|
||||
/// <summary>Get the data index at the specific position of the total height cache.</summary>
|
||||
@ -119,31 +114,29 @@ namespace UnityExplorer.UI.Widgets
|
||||
public int GetDataIndexAtPosition(float desiredHeight, out DataViewInfo cache)
|
||||
{
|
||||
cache = default;
|
||||
|
||||
if (!heightCache.Any())
|
||||
return 0;
|
||||
|
||||
int rangeIndex = GetRangeIndexOfPosition(desiredHeight);
|
||||
|
||||
if (rangeIndex < 0)
|
||||
return 0;
|
||||
if (rangeIndex >= rangeCache.Count)
|
||||
{
|
||||
ExplorerCore.LogWarning("RangeIndex < 0? " + rangeIndex);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (rangeCache.Count <= rangeIndex)
|
||||
{
|
||||
ExplorerCore.LogWarning("Want range index " + rangeIndex + " but count is " + rangeCache.Count);
|
||||
RebuildCache();
|
||||
rangeIndex = GetRangeIndexOfPosition(desiredHeight);
|
||||
if (rangeCache.Count <= rangeIndex)
|
||||
throw new Exception("Range index (" + rangeIndex + ") exceeded rangeCache count (" + rangeCache.Count + ")");
|
||||
ExplorerCore.Log("desiredHeight is " + desiredHeight + ", but our total height is " + totalHeight + ", clamping to data count");
|
||||
ExplorerCore.Log("highest data index: " + (ScrollPool.DataSource.ItemCount - 1) + ", rangeIndex was " + rangeIndex + ", actual range limit is " + (rangeCache.Count - 1));
|
||||
cache = heightCache[heightCache.Count - 1];
|
||||
return ScrollPool.DataSource.ItemCount - 1;
|
||||
}
|
||||
|
||||
int dataIndex = rangeCache[rangeIndex];
|
||||
cache = heightCache[dataIndex];
|
||||
|
||||
return dataIndex;
|
||||
}
|
||||
|
||||
/// <summary>Set a given data index with the specified value.</summary>
|
||||
public void SetIndex(int dataIndex, float height)
|
||||
public void SetIndex(int dataIndex, float height, bool inRebuild = false)
|
||||
{
|
||||
if (dataIndex >= ScrollPool.DataSource.ItemCount)
|
||||
{
|
||||
@ -166,7 +159,6 @@ namespace UnityExplorer.UI.Widgets
|
||||
var diff = height - prevHeight;
|
||||
if (diff != 0.0f)
|
||||
{
|
||||
// LogWarning("Height for data index " + dataIndex + " changed by " + diff);
|
||||
totalHeight += diff;
|
||||
cache.height = height;
|
||||
}
|
||||
@ -183,8 +175,16 @@ namespace UnityExplorer.UI.Widgets
|
||||
|
||||
if (rangeCache.Count <= rangeIndex)
|
||||
{
|
||||
RebuildCache();
|
||||
return;
|
||||
if (rangeCache[rangeCache.Count - 1] != dataIndex - 1)
|
||||
{
|
||||
// The previous index in the range cache is not the previous data index for the data we were given.
|
||||
// Need to rebuild.
|
||||
if (!inRebuild)
|
||||
RebuildCache();
|
||||
else
|
||||
throw new Exception($"DataHeightCache rebuild failed. Trying to set {rangeIndex} but current count is {rangeCache.Count}!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (spread != cache.normalizedSpread)
|
||||
@ -199,6 +199,7 @@ namespace UnityExplorer.UI.Widgets
|
||||
// In some rare cases we may not find our data index at the expected range index.
|
||||
// We can make some educated guesses and find the real index pretty quickly.
|
||||
int minStart = GetRangeIndexOfPosition(dataIndex * DefaultHeight);
|
||||
if (minStart < 0) minStart = 0;
|
||||
for (int i = minStart; i < rangeCache.Count; i++)
|
||||
{
|
||||
if (rangeCache[i] == dataIndex)
|
||||
@ -218,10 +219,8 @@ namespace UnityExplorer.UI.Widgets
|
||||
|
||||
// our data index is further down. add the min difference and try again.
|
||||
// the iterator will add 1 on the next loop so account for that.
|
||||
// also, add the (spread - 1) of the cell we found at this index to skip it.
|
||||
var spreadCurr = heightCache[rangeCache[i]].normalizedSpread;
|
||||
|
||||
int jmp = dataIndex - rangeCache[i] - 1;
|
||||
jmp += spreadCurr - 2;
|
||||
i = (jmp < 1 ? i : i + jmp);
|
||||
}
|
||||
}
|
||||
@ -263,7 +262,7 @@ namespace UnityExplorer.UI.Widgets
|
||||
{
|
||||
//start at 1 because 0's start pos is always 0
|
||||
for (int i = 1; i < heightCache.Count; i++)
|
||||
SetIndex(i, heightCache[i].height);
|
||||
SetIndex(i, heightCache[i].height, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,6 @@ namespace UnityExplorer.UI.Widgets
|
||||
public interface IPoolDataSource<T> where T : ICell
|
||||
{
|
||||
int ItemCount { get; }
|
||||
int GetRealIndexOfTempIndex(int tempIndex);
|
||||
|
||||
void OnCellBorrowed(T cell);
|
||||
void OnCellReturned(T cell);
|
||||
|
@ -100,7 +100,7 @@ namespace UnityExplorer.UI.Widgets
|
||||
private bool writingLocked;
|
||||
private float timeofLastWriteLock;
|
||||
|
||||
private float prevContentHeight;
|
||||
private float prevContentHeight = 1.0f;
|
||||
|
||||
public void SetUninitialized()
|
||||
{
|
||||
@ -115,8 +115,10 @@ namespace UnityExplorer.UI.Widgets
|
||||
if (writingLocked && timeofLastWriteLock < Time.time)
|
||||
writingLocked = false;
|
||||
|
||||
if (prevContentHeight == 0.0f && Content?.rect.height != 0.0f)
|
||||
if (prevContentHeight <= 1f && Content?.rect.height > 1f)
|
||||
{
|
||||
prevContentHeight = Content.rect.height;
|
||||
}
|
||||
else if (Content.rect.height != prevContentHeight)
|
||||
{
|
||||
prevContentHeight = Content.rect.height;
|
||||
@ -240,21 +242,13 @@ namespace UnityExplorer.UI.Widgets
|
||||
{
|
||||
bottomPoolIndex++;
|
||||
|
||||
////Instantiate and add to Pool
|
||||
//RectTransform rect = GameObject.Instantiate(PrototypeCell.gameObject).GetComponent<RectTransform>();
|
||||
//rect.gameObject.SetActive(true);
|
||||
//rect.name = $"Cell_{CellPool.Count}";
|
||||
//var cell = DataSource.CreateCell(rect);
|
||||
//CellPool.Add(cell);
|
||||
//rect.SetParent(ScrollRect.content, false);
|
||||
|
||||
var cell = Pool<T>.Borrow();
|
||||
DataSource.OnCellBorrowed(cell);
|
||||
var rect = cell.Rect;
|
||||
//var rect = cell.Rect;
|
||||
CellPool.Add(cell);
|
||||
rect.SetParent(ScrollRect.content, false);
|
||||
cell.Rect.SetParent(ScrollRect.content, false);
|
||||
|
||||
currentPoolCoverage += rect.rect.height;
|
||||
currentPoolCoverage += PrototypeHeight;
|
||||
}
|
||||
|
||||
if (andResetDataIndex)
|
||||
@ -289,30 +283,29 @@ namespace UnityExplorer.UI.Widgets
|
||||
|
||||
var requiredCoverage = Math.Abs(RecycleViewBounds.y - RecycleViewBounds.x);
|
||||
var currentCoverage = CellPool.Count * PrototypeHeight;
|
||||
int cellsRequired = (int)Math.Ceiling((decimal)(requiredCoverage - currentCoverage) / (decimal)PrototypeHeight);
|
||||
int cellsRequired = (int)Math.Floor((decimal)(requiredCoverage - currentCoverage) / (decimal)PrototypeHeight);
|
||||
if (cellsRequired > 0 || forceRecreate)
|
||||
{
|
||||
WritingLocked = true;
|
||||
|
||||
//// Disable cells so DataSource can handle its content if need be
|
||||
//var enumerator = GetPoolEnumerator();
|
||||
//while (enumerator.MoveNext())
|
||||
//{
|
||||
// var curr = enumerator.Current;
|
||||
// DataSource.DisableCell(CellPool[curr.cellIndex], curr.dataIndex);
|
||||
//}
|
||||
|
||||
bottomDataIndex += cellsRequired;
|
||||
int maxDataIndex = Math.Max(CellPool.Count + cellsRequired - 1, DataSource.ItemCount - 1);
|
||||
if (bottomDataIndex > maxDataIndex)
|
||||
bottomDataIndex = maxDataIndex;
|
||||
|
||||
// CreateCellPool will destroy existing cells and recreate list.
|
||||
float curAnchor = Content.localPosition.y;
|
||||
float curHeight = Content.rect.height;
|
||||
|
||||
CreateCellPool(resetDataIndex);
|
||||
|
||||
LayoutRebuilder.ForceRebuildLayoutImmediate(Content);
|
||||
// fix slight jumping when resizing panel and size increases
|
||||
|
||||
if (Content.rect.height != curHeight)
|
||||
{
|
||||
var diff = Content.rect.height - curHeight;
|
||||
Content.localPosition = new Vector3(Content.localPosition.x, Content.localPosition.y + (diff * 0.5f));
|
||||
}
|
||||
|
||||
//Content.anchoredPosition = new Vector2(0, pos);
|
||||
ScrollRect.UpdatePrevData();
|
||||
|
||||
SetScrollBounds();
|
||||
@ -437,10 +430,7 @@ namespace UnityExplorer.UI.Widgets
|
||||
|
||||
SetRecycleViewBounds(true);
|
||||
|
||||
//if (!SetRecycleViewBounds(true))
|
||||
// RefreshCells(false);
|
||||
|
||||
float yChange = (ScrollRect.content.anchoredPosition - prevAnchoredPos).y;
|
||||
float yChange = ((Vector2)ScrollRect.content.localPosition - prevAnchoredPos).y;
|
||||
float adjust = 0f;
|
||||
|
||||
if (yChange > 0) // Scrolling down
|
||||
@ -642,7 +632,6 @@ namespace UnityExplorer.UI.Widgets
|
||||
{
|
||||
if (TopDataIndex > poolStartIndex && TopDataIndex < desiredBottomIndex)
|
||||
{
|
||||
//ExplorerCore.Log("Scroll bottom to top");
|
||||
// top cell falls within the new range, rotate around that
|
||||
int rotate = TopDataIndex - poolStartIndex;
|
||||
for (int i = 0; i < rotate; i++)
|
||||
@ -659,7 +648,6 @@ namespace UnityExplorer.UI.Widgets
|
||||
}
|
||||
else if (bottomDataIndex > poolStartIndex && bottomDataIndex < desiredBottomIndex)
|
||||
{
|
||||
//ExplorerCore.Log("Scroll top to bottom");
|
||||
// bottom cells falls within the new range, rotate around that
|
||||
int rotate = desiredBottomIndex - bottomDataIndex;
|
||||
for (int i = 0; i < rotate; i++)
|
||||
@ -676,9 +664,6 @@ namespace UnityExplorer.UI.Widgets
|
||||
}
|
||||
else
|
||||
{
|
||||
// new cells are completely different, set all cells
|
||||
//ExplorerCore.Log("Scroll jump");
|
||||
|
||||
bottomDataIndex = desiredBottomIndex;
|
||||
var enumerator = GetPoolEnumerator();
|
||||
while (enumerator.MoveNext())
|
||||
@ -692,17 +677,6 @@ namespace UnityExplorer.UI.Widgets
|
||||
|
||||
SetRecycleViewBounds(true);
|
||||
|
||||
//CheckDataSourceCountChange(out bool jumpToBottom);
|
||||
|
||||
//// force check recycles
|
||||
//if (andReloadFromDataSource)
|
||||
//{
|
||||
// RecycleBottomToTop();
|
||||
// RecycleTopToBottom();
|
||||
//}
|
||||
|
||||
//LayoutRebuilder.ForceRebuildLayoutImmediate(Content);
|
||||
|
||||
SetScrollBounds();
|
||||
ScrollRect.UpdatePrevData();
|
||||
|
||||
|
@ -55,8 +55,6 @@ namespace UnityExplorer.UI.Widgets
|
||||
ScrollPool.Initialize(this);
|
||||
}
|
||||
|
||||
public int GetRealIndexOfTempIndex(int index) => -1;// not needed
|
||||
|
||||
public void DisableCell(TransformCell cell, int index) => cell.Disable();
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user