using System;
using System.Collections.Generic;
using RosettaUI.Reactive;
using UnityEngine.Assertions;
namespace RosettaUI
{
///
/// RosettaUI basic unit.
/// - UI implementation-independent
/// - Builder creates implementation-dependent UI entities based on Element
/// - Application accesses UI via Element, does not touch entities
///
public abstract class Element
{
private readonly List _children = new();
public IReadOnlyList Children => _children;
public readonly ReactiveProperty enableRx = new(true);
public readonly ReactiveProperty interactableRx = new(true);
public bool UpdateWhileDisabled { get; set; }
public bool HasBuilt => ViewBridge.HasBuilt;
public event Action onUpdate;
public event Action onViewValueChanged;
public event Action onDetachView;
private ElementViewBridge _viewBridge;
public bool Enable
{
get => enableRx.Value;
set => enableRx.Value = value;
}
public bool Interactable
{
get => interactableRx.Value;
set => interactableRx.Value = value;
}
public Style Style { get; } = new();
public Element Parent { get; private set; }
public Element()
{
// 可視化したとき表示前にUpdate()が呼ばれていることを保証する
// onUpdateとかでサイズ調整しているやつを考慮
enableRx.Subscribe(flag =>
{
if (flag && HasBuilt) Update();
});
}
private void SetParent(Element element)
{
this.ValidateSingleParent();
Parent = element;
}
public void DetachParent()
{
Assert.IsFalse(HasBuilt, $"{GetType()} has already built. Call DestroyView() to reuse this element");
Parent?.RemoveChild(this, false);
}
protected void AddChild(Element element)
{
_children.Add(element);
element.SetParent(this);
}
protected bool RemoveChild(Element element, bool destroyView = true)
{
if (!_children.Remove(element)) return false;
element.Parent = null;
element.DetachView(destroyView);
return true;
}
public virtual void Update()
{
if (Enable || UpdateWhileDisabled) UpdateInternal();
}
protected virtual void UpdateInternal()
{
onUpdate?.Invoke(this);
foreach(var e in _children) e.Update();
}
public void DetachView(bool destroyView = true)
{
onDetachView?.Invoke(this, destroyView);
DetachViewChildren();
if (destroyView)
{
DetachParent();
}
}
private void DetachViewChildren()
{
foreach(var child in _children)
{
child.DetachView(false);
}
}
public void NotifyViewValueChanged()
{
onViewValueChanged?.Invoke();
Parent?.NotifyViewValueChanged();
}
///
/// Interface for UI library
///
internal ElementViewBridge ViewBridge => _viewBridge ??= CreateViewBridge();
protected virtual ElementViewBridge CreateViewBridge() => new(this);
public class ElementViewBridge
{
public event Action onUnsubscribe;
protected readonly Element element;
public bool HasBuilt => onUnsubscribe != null;
public ElementViewBridge(Element element) => this.element = element;
public virtual void UnsubscribeAll()
{
onUnsubscribe?.Invoke();
onUnsubscribe = null;
}
}
}
public static partial class ElementViewBridgeExtensions
{
public static Element.ElementViewBridge GetViewBridge(this Element element) => element.ViewBridge;
}
}