using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using UnityEngine.Assertions;
using UnityEngine.Rendering.UI;
using UnityEngine.UI;
namespace UnityEngine.Rendering
{
using UnityObject = UnityEngine.Object;
///
/// IDebugData interface.
///
public interface IDebugData
{
/// Get the reset callback for this DebugData
/// The reset callback
Action GetReset();
//Action GetLoad();
//Action GetSave();
}
///
/// Manager class for the Debug Window.
///
public sealed partial class DebugManager
{
static readonly Lazy s_Instance = new Lazy(() => new DebugManager());
///
/// Global instance of the DebugManager.
///
public static DebugManager instance => s_Instance.Value;
ReadOnlyCollection m_ReadOnlyPanels;
readonly List m_Panels = new List();
void UpdateReadOnlyCollection()
{
m_Panels.Sort();
m_ReadOnlyPanels = m_Panels.AsReadOnly();
}
///
/// List of currently registered debug panels.
///
public ReadOnlyCollection panels
{
get
{
if (m_ReadOnlyPanels == null)
UpdateReadOnlyCollection();
return m_ReadOnlyPanels;
}
}
///
/// Callback called when the runtime UI changed.
///
public event Action onDisplayRuntimeUIChanged = delegate { };
///
/// Callback called when the debug window is dirty.
///
public event Action onSetDirty = delegate { };
event Action resetData;
///
/// Force an editor request.
///
public bool refreshEditorRequested;
int? m_RequestedPanelIndex;
GameObject m_Root;
DebugUIHandlerCanvas m_RootUICanvas;
GameObject m_PersistentRoot;
DebugUIHandlerPersistentCanvas m_RootUIPersistentCanvas;
// Knowing if the DebugWindows is open, is done by event as it is in another assembly.
// The DebugWindows is responsible to link its event to ToggleEditorUI.
bool m_EditorOpen = false;
///
/// Is the debug editor window open.
///
public bool displayEditorUI => m_EditorOpen;
///
/// Toggle the debug window.
///
/// State of the debug window.
public void ToggleEditorUI(bool open) => m_EditorOpen = open;
private bool m_EnableRuntimeUI = true;
///
/// Controls whether runtime UI can be enabled. When this is set to false, there will be no overhead
/// from debug GameObjects or runtime initialization.
///
public bool enableRuntimeUI
{
get => m_EnableRuntimeUI;
set
{
if (value != m_EnableRuntimeUI)
{
m_EnableRuntimeUI = value;
DebugUpdater.SetEnabled(value);
}
}
}
///
/// Displays the runtime version of the debug window.
///
public bool displayRuntimeUI
{
get => m_Root != null && m_Root.activeInHierarchy;
set
{
if (value)
{
m_Root = UnityObject.Instantiate(Resources.Load("DebugUICanvas")).gameObject;
m_Root.name = "[Debug Canvas]";
m_Root.transform.localPosition = Vector3.zero;
m_RootUICanvas = m_Root.GetComponent();
#if UNITY_ANDROID || UNITY_IPHONE || UNITY_TVOS || UNITY_SWITCH
var canvasScaler = m_Root.GetComponent();
canvasScaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
#endif
m_Root.SetActive(true);
}
else
{
CoreUtils.Destroy(m_Root);
m_Root = null;
m_RootUICanvas = null;
}
onDisplayRuntimeUIChanged(value);
DebugUpdater.HandleInternalEventSystemComponents(value);
}
}
///
/// Displays the persistent runtime debug window.
///
public bool displayPersistentRuntimeUI
{
get => m_RootUIPersistentCanvas != null && m_PersistentRoot.activeInHierarchy;
set
{
if (value)
{
EnsurePersistentCanvas();
}
else
{
CoreUtils.Destroy(m_PersistentRoot);
m_PersistentRoot = null;
m_RootUIPersistentCanvas = null;
}
}
}
DebugManager()
{
#if DEVELOPMENT_BUILD || UNITY_EDITOR
RegisterInputs();
RegisterActions();
#endif
}
///
/// Refresh the debug window.
///
public void RefreshEditor()
{
refreshEditorRequested = true;
}
///
/// Reset the debug window.
///
public void Reset()
{
resetData?.Invoke();
ReDrawOnScreenDebug();
}
///
/// Request the runtime debug UI be redrawn on the next update.
///
public void ReDrawOnScreenDebug()
{
if (displayRuntimeUI)
m_RootUICanvas?.RequestHierarchyReset();
}
///
/// Register debug data.
///
/// Data to be registered.
public void RegisterData(IDebugData data) => resetData += data.GetReset();
///
/// Register debug data.
///
/// Data to be registered.
public void UnregisterData(IDebugData data) => resetData -= data.GetReset();
///
/// Get hashcode state of the Debug Window.
///
///
public int GetState()
{
int hash = 17;
foreach (var panel in m_Panels)
hash = hash * 23 + panel.GetHashCode();
return hash;
}
internal void RegisterRootCanvas(DebugUIHandlerCanvas root)
{
Assert.IsNotNull(root);
m_Root = root.gameObject;
m_RootUICanvas = root;
}
internal void ChangeSelection(DebugUIHandlerWidget widget, bool fromNext)
{
m_RootUICanvas.ChangeSelection(widget, fromNext);
}
internal void SetScrollTarget(DebugUIHandlerWidget widget)
{
if (m_RootUICanvas != null)
m_RootUICanvas.SetScrollTarget(widget);
}
void EnsurePersistentCanvas()
{
if (m_RootUIPersistentCanvas == null)
{
var uiManager = UnityObject.FindObjectOfType();
if (uiManager == null)
{
m_PersistentRoot = UnityObject.Instantiate(Resources.Load("DebugUIPersistentCanvas")).gameObject;
m_PersistentRoot.name = "[Debug Canvas - Persistent]";
m_PersistentRoot.transform.localPosition = Vector3.zero;
}
else
{
m_PersistentRoot = uiManager.gameObject;
}
m_RootUIPersistentCanvas = m_PersistentRoot.GetComponent();
}
}
internal void TogglePersistent(DebugUI.Widget widget)
{
if (widget == null)
return;
var valueWidget = widget as DebugUI.Value;
if (valueWidget == null)
{
Debug.Log("Only DebugUI.Value items can be made persistent.");
return;
}
EnsurePersistentCanvas();
m_RootUIPersistentCanvas.Toggle(valueWidget);
}
void OnPanelDirty(DebugUI.Panel panel)
{
onSetDirty();
}
///
/// Request DebugWindow to open the specified panel.
///
/// Index of the debug window panel to activate.
public void RequestEditorWindowPanelIndex(int index)
{
// Similar to RefreshEditor(), this function is required to bypass a dependency problem where DebugWindow
// cannot be accessed from the Core.Runtime assembly. Should there be a better way to allow editor-dependent
// features in DebugUI?
m_RequestedPanelIndex = index;
}
internal int? GetRequestedEditorWindowPanelIndex()
{
int? requestedIndex = m_RequestedPanelIndex;
m_RequestedPanelIndex = null;
return requestedIndex;
}
// TODO: Optimally we should use a query path here instead of a display name
///
/// Returns a debug panel.
///
/// Name of the debug panel.
/// Create the panel if it does not exists.
/// Group index.
/// Replace an existing panel.
///
public DebugUI.Panel GetPanel(string displayName, bool createIfNull = false, int groupIndex = 0, bool overrideIfExist = false)
{
DebugUI.Panel p = null;
foreach (var panel in m_Panels)
{
if (panel.displayName == displayName)
{
p = panel;
break;
}
}
if (p != null)
{
if (overrideIfExist)
{
p.onSetDirty -= OnPanelDirty;
RemovePanel(p);
p = null;
}
else
return p;
}
if (createIfNull)
{
p = new DebugUI.Panel { displayName = displayName, groupIndex = groupIndex };
p.onSetDirty += OnPanelDirty;
m_Panels.Add(p);
UpdateReadOnlyCollection();
}
return p;
}
// TODO: Use a query path here as well instead of a display name
///
/// Remove a debug panel.
///
/// Name of the debug panel to remove.
public void RemovePanel(string displayName)
{
DebugUI.Panel panel = null;
foreach (var p in m_Panels)
{
if (p.displayName == displayName)
{
p.onSetDirty -= OnPanelDirty;
panel = p;
break;
}
}
RemovePanel(panel);
}
///
/// Remove a debug panel.
///
/// Reference to the debug panel to remove.
public void RemovePanel(DebugUI.Panel panel)
{
if (panel == null)
return;
m_Panels.Remove(panel);
UpdateReadOnlyCollection();
}
///
/// Get a Debug Item.
///
/// Path of the debug item.
/// Reference to the requested debug item.
public DebugUI.Widget GetItem(string queryPath)
{
foreach (var panel in m_Panels)
{
var w = GetItem(queryPath, panel);
if (w != null)
return w;
}
return null;
}
///
/// Get a debug item from a specific container.
///
/// Path of the debug item.
/// Container to query.
/// Reference to the requested debug item.
DebugUI.Widget GetItem(string queryPath, DebugUI.IContainer container)
{
foreach (var child in container.children)
{
if (child.queryPath == queryPath)
return child;
var containerChild = child as DebugUI.IContainer;
if (containerChild != null)
{
var w = GetItem(queryPath, containerChild);
if (w != null)
return w;
}
}
return null;
}
}
}