ReactivePanel.cs
#if SANDBOX
using System.Diagnostics;
using Sandbox.Reactivity.Internals;
using Sandbox.UI;
using static Sandbox.Reactivity.Reactive;
#if JETBRAINS_ANNOTATIONS
#endif

namespace Sandbox.Reactivity;

/// <summary>
/// The reactive counterpart to <see cref="Panel" /> that allows usage of reactive properties.
/// </summary>
/// <remarks>
/// Make sure you set up an effect root using <see cref="PanelRoot" /> at the top of your razor markup:
/// <code>
/// @{ using var _ = PanelRoot(); }
/// </code>
/// Engine limitations prevent this from being done automatically.
/// </remarks>
#if JETBRAINS_ANNOTATIONS
#endif
public class ReactivePanel : Panel, IReactivePropertyContainer, IReactivePanel
{
	private Effect? _effectRoot;

	private Effect? _renderEffectRoot;

	private int _version;

	public ReactivePanel()
	{
		var parent = Runtime.CurrentEffect;

		_effectRoot = new Effect([StackTraceHidden] [DebuggerStepThrough]() =>
			{
				OnActivate();
				return null;
			},
			parent,
			false);

		_effectRoot.SetDebugInfo(DisplayInfo.For(this).Name,
			DisplayInfo.For(this).Icon,
			new CallLocation(GetType(), nameof(OnActivate)),
			parent ?? (object?)this);

		_effectRoot.Run();
	}

	Effect? IReactivePanel.RenderEffectRoot
	{
		get => _renderEffectRoot;
		set => _renderEffectRoot = value;
	}

	int IReactivePanel.Version
	{
		get => _version;
		set => _version = value;
	}

	Dictionary<int, IProducer> IReactivePropertyContainer.Producers { get; } = [];

	protected ReactivePanelScope PanelRoot()
	{
		return new ReactivePanelScope(this);
	}

	public sealed override void Delete(bool immediate = false)
	{
		_renderEffectRoot?.Dispose();
		_renderEffectRoot = null;

		_effectRoot?.Dispose();
		_effectRoot = null;

		base.Delete(immediate);
	}

	protected sealed override int BuildHash()
	{
		return _version;
	}

	/// <summary>
	/// Called inside an effect root when this panel is instantiated, allowing for effects to be created. When this
	/// panel is deleted, the effect root (and all of its descendants) are disposed.
	/// </summary>
	protected virtual void OnActivate()
	{
	}
}
#endif