using Uigenx.Binding;
using UnityEngine;
using UnityEngine.UIElements;
namespace Uigenx
{
///
/// Drives one generated screen on one . Use it for a screen that is
/// simply present in a scene; use when screens are pushed and
/// popped at runtime.
///
[AddComponentMenu("UI Creator/Document")]
[RequireComponent(typeof(UIDocument))]
[DisallowMultipleComponent]
public sealed class UgxDocument : MonoBehaviour
{
[Tooltip("Screen key as exported by UI Creator, for example `Hud`.")]
[SerializeField] string screenKey;
[Tooltip("Object implementing IUgxDataSource. Optional; the screen binds to nothing without it.")]
[SerializeField] UnityEngine.Object dataSourceObject;
[SerializeField] bool autoShow = true;
[SerializeField] UgxScreenRegistry registryOverride;
UIDocument _document;
UgxScreen _screen;
VisualElement _container;
bool _ownsContainer;
// A plain C# source cannot live in the serialized field, so it is held here and wins.
IUgxDataSource _runtimeDataSource;
public UgxScreen Screen => _screen;
public string ScreenKey
{
get => screenKey;
set
{
if (screenKey == value) return;
screenKey = value;
if (isActiveAndEnabled) Reopen();
}
}
public IUgxDataSource DataSource
{
get => _runtimeDataSource ?? dataSourceObject as IUgxDataSource;
set
{
_runtimeDataSource = value;
dataSourceObject = value as UnityEngine.Object;
if (isActiveAndEnabled) Reopen();
}
}
void OnEnable() => Open();
void OnDisable() => Close();
public void Reopen()
{
Close();
Open();
}
void Open()
{
if (string.IsNullOrEmpty(screenKey)) return;
_document = GetComponent();
var registry = registryOverride != null ? registryOverride : UgxScreenRegistry.Active;
if (registry == null)
{
Debug.LogError("[ugx] no screen registry is available for '" + screenKey + "'", this);
return;
}
if (!registry.TryGet(screenKey, out var entry))
{
Debug.LogError("[ugx] screen '" + screenKey + "' is not in the registry. Re-export it from " +
"UI Creator.", this);
return;
}
_screen = UgxScreens.Create(screenKey);
if (_screen == null)
{
Debug.LogError("[ugx] no generated factory can create screen '" + screenKey +
"'. Check that UgxScreenFactory.g.cs compiled.", this);
return;
}
_container = ResolveContainer(entry);
if (_container == null) return;
ApplyStyles(entry, _container);
_screen.Attach(_container, DataSource);
if (autoShow) _screen.Show();
else _container.style.display = DisplayStyle.None;
}
void Close()
{
_screen?.Detach();
_screen = null;
if (_ownsContainer) _container?.RemoveFromHierarchy();
_container = null;
_ownsContainer = false;
}
VisualElement ResolveContainer(UgxScreenEntry entry)
{
if (entry.Uxml == null)
{
Debug.LogError("[ugx] screen '" + screenKey + "' has no VisualTreeAsset in the registry", this);
return null;
}
// Assigning the asset makes UIDocument rebuild its tree straight away, so the clone is
// available on the next line.
_document.visualTreeAsset = entry.Uxml;
var root = _document.rootVisualElement;
if (root == null) return null;
if (root.childCount > 0)
{
_ownsContainer = false;
return root[0];
}
var container = entry.Uxml.Instantiate();
container.style.flexGrow = 1f;
root.Add(container);
_ownsContainer = true;
return container;
}
static void ApplyStyles(UgxScreenEntry entry, VisualElement container)
{
var styles = entry.Styles;
if (styles == null) return;
foreach (var sheet in styles)
if (sheet != null && !container.styleSheets.Contains(sheet))
container.styleSheets.Add(sheet);
}
}
}