Examples/Cards/DissolvePanelShowcase.razor
@using Sandbox;
@using Sandbox.UI;
@using PostprocessPanel;
@inherits PanelComponent
@attribute [Category("Postprocessable Examples")]
@namespace Sandbox

<root>
	<div class="panels">
		<PostprocessablePanel @ref="PostprocessPanel">
			<Body> 
				<Card></Card>
			</Body>
			<Display>
				<div class="placeholder animate">
				</div>
			</Display>
		</PostprocessablePanel>
		<Card class="animate"></Card>
	</div>
	<div class="controls">
		<div class="group">
			<label>Compute Type</label>
			<DropDown @ref="ComputeDropdown"></DropDown>
		</div>
		<div class="group" Tooltip="Modifies strength of an effect ( crt, dissolve )" style="width:300px;">
			<label>Value</label>
			@if ((string)ComputeDropdown.Selected.Value == "example shaders/dissolve.shader")
			{
				<SliderControl Value:bind=@Value Min=@(0) Max=@(1) Step=@(0.05f)>></SliderControl>
			}
			else if ((string)ComputeDropdown.Selected.Value == "example shaders/crtwarp.shader")
			{
				<SliderControl Value:bind=@Value Min=@(0.1f) Max=@(7) Step=@(0.05f)>></SliderControl>
			}
		</div>
		<div class="group" style="width:300px;">
			<label>Texture padding (x then y)</label>
			<SliderControl Tooltip="Chance texture padding along x axis" Value:bind=@TexturePaddingX Min=@(0) Max=@(100) Step=@(1)>></SliderControl>
			<SliderControl Tooltip="Chance texture padding along y axis" Value:bind=@TexturePaddingY Min=@(0) Max=@(100) Step=@(1)>></SliderControl>
		</div>
	</div>
</root>

@code
{
	[Property]
	[Range(0f, 7f), Step(0.05f)]
	public float Value { get; set; } = 1f;

	[Property]
	public Vector2Int TexturePadding { get; set; }

	[Property]
	public Texture NoiseTexture { get; set; }

	private int TexturePaddingX
	{
		get => TexturePadding.x;
		set
		{
			TexturePadding = TexturePadding.WithX(value);
		}
	}

	private int TexturePaddingY
	{
		get => TexturePadding.y;
		set
		{
			TexturePadding = TexturePadding.WithY(value);
		}
	}

	private const string SHADERS_PATH = "example shaders";

	private DropDown ComputeDropdown;

	PostprocessablePanel PostprocessPanel { get; set; }
	ComputeShader Compute { get; set; }

	protected override void OnTreeFirstBuilt()
	{
		SubscribeCallbacks();
	}

	protected override void OnDisabled()
	{
		UnsubscribeCallbacks();
	}

	private void UnsubscribeCallbacks()
	{
		if (PostprocessPanel.IsReady())
			PostprocessPanel.OnRendering -= OnRendering;

		if (ComputeDropdown.IsValid())
			ComputeDropdown.ValueChanged -= OnOptionSelected;
	}

	private void SubscribeCallbacks()
	{
		if (PostprocessPanel.IsReady())
			PostprocessPanel.OnRendering += OnRendering;

		if (ComputeDropdown.IsValid())
		{
			ComputeDropdown.ValueChanged += OnOptionSelected;
			BuildOptions();
		}
	}

	private void OnRendering()
	{
		if (PostprocessPanel.IsReady is false || Compute is null)
			return;

		if (Components.TryGet<ScreenPanel>(out var screenPanel))
		{
			PostprocessPanel.UpdateRootSettingsFrom(screenPanel);
		}

		PostprocessPanel.TexturePadding = TexturePadding;

		RenderAttributes attributes = PostprocessPanel.Attributes;

		attributes.Set("NoiseTexture", NoiseTexture);
		attributes.Set("DissolveStrength", Value);
		attributes.Set("Curvature", Value);

		PostprocessPanel.DispatchCompute(Compute);
	}

	private void BuildOptions()
	{
		if (FileSystem.Mounted.DirectoryExists(SHADERS_PATH) is false)
		{
			Log.Warning("Example shader directory does not exist!");
			return;
		}

		foreach (var shader_name in FileSystem.Mounted.FindFile(SHADERS_PATH))
		{
			if (shader_name.EndsWith("_c"))
				continue;

			int extension = shader_name.LastIndexOf(".shader");
			string name = shader_name.Remove(extension, shader_name.Length - extension);

			string path = SHADERS_PATH + "/" + shader_name;

			ComputeDropdown.Options.Add(new(name, path));

			Log.Info($"Added {name} with path: {path.QuoteSafe()}");
		}

		ComputeDropdown.Selected = ComputeDropdown.Options[1];
	}

	private void OnOptionSelected(string value)
	{
		if (FileSystem.Mounted.FileExists(value) is false)
		{
			Log.Warning($"{value} does not exist!");
			return;
		}

		Compute = new(value);
	}

	/// <summary>
	/// the hash determines if the system should be rebuilt. If it changes, it will be rebuilt
	/// </summary>
	protected override int BuildHash() => System.HashCode.Combine(Value, TexturePadding, NoiseTexture, ComputeDropdown?.Selected ?? null);
}