(Feat) Add Remmaping
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Ashwild.Settings
|
||||
{
|
||||
/// <summary>
|
||||
/// A "◄ value ►" control that cycles a list of preset values. Pure UI: it knows nothing about
|
||||
/// which setting it drives. The hosting panel calls <see cref="Init"/> once with the presets
|
||||
/// (each a value plus its display label) and the action to run when the user steps; afterwards
|
||||
/// the panel pushes the current value through <see cref="SetValue"/>. Because each preset
|
||||
/// carries its own label, a value like -1 can read as "Unlimited" rather than a number.
|
||||
/// </summary>
|
||||
[DisallowMultipleComponent]
|
||||
public class SettingStepperWidget : MonoBehaviour
|
||||
{
|
||||
#region Serialized Fields
|
||||
|
||||
[Header("References")]
|
||||
[SerializeField] private Button previousButton;
|
||||
[SerializeField] private Button nextButton;
|
||||
[SerializeField] private TMP_Text valueLabel;
|
||||
|
||||
#endregion
|
||||
|
||||
#region State
|
||||
|
||||
private IReadOnlyList<(int value, string label)> presets;
|
||||
private Action<int> onChanged;
|
||||
private bool wrap;
|
||||
private int currentIndex;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public API
|
||||
|
||||
/// <summary>
|
||||
/// Configures the stepper with its presets and wrap behaviour, and registers the action to
|
||||
/// run when the user steps. Call once (the action is not invoked by <see cref="SetValue"/>).
|
||||
/// </summary>
|
||||
public void Init(IReadOnlyList<(int value, string label)> presets, bool wrap, Action<int> onChanged)
|
||||
{
|
||||
if (presets == null || presets.Count == 0)
|
||||
{
|
||||
Debug.LogError($"[SettingStepperWidget] '{name}' was initialized with no presets.", this);
|
||||
return;
|
||||
}
|
||||
|
||||
this.presets = presets;
|
||||
this.wrap = wrap;
|
||||
this.onChanged = onChanged;
|
||||
|
||||
if (previousButton != null)
|
||||
{
|
||||
previousButton.onClick.RemoveListener(StepDown);
|
||||
previousButton.onClick.AddListener(StepDown);
|
||||
}
|
||||
if (nextButton != null)
|
||||
{
|
||||
nextButton.onClick.RemoveListener(StepUp);
|
||||
nextButton.onClick.AddListener(StepUp);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Selects the preset matching the given value (falling back to the first) and shows its
|
||||
/// label, without invoking the apply action.
|
||||
/// </summary>
|
||||
public void SetValue(int value)
|
||||
{
|
||||
if (presets == null) return;
|
||||
currentIndex = IndexOfValue(value);
|
||||
UpdateLabel();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
private void StepDown() => Step(-1);
|
||||
private void StepUp() => Step(+1);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Internal Helpers
|
||||
|
||||
/// <summary>
|
||||
/// Moves the selection by one step (clamped or wrapped), shows it and runs the apply action.
|
||||
/// </summary>
|
||||
private void Step(int direction)
|
||||
{
|
||||
if (presets == null || presets.Count == 0) return;
|
||||
|
||||
int count = presets.Count;
|
||||
int next = currentIndex + direction;
|
||||
if (wrap)
|
||||
next = (next % count + count) % count;
|
||||
else
|
||||
next = Mathf.Clamp(next, 0, count - 1);
|
||||
|
||||
if (next == currentIndex) return;
|
||||
|
||||
currentIndex = next;
|
||||
UpdateLabel();
|
||||
onChanged?.Invoke(presets[currentIndex].value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the preset index whose value equals the given one, or 0 when none matches.
|
||||
/// </summary>
|
||||
private int IndexOfValue(int value)
|
||||
{
|
||||
for (int i = 0; i < presets.Count; i++)
|
||||
if (presets[i].value == value) return i;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows the current preset's label and disables arrows that would do nothing.
|
||||
/// </summary>
|
||||
private void UpdateLabel()
|
||||
{
|
||||
if (valueLabel != null) valueLabel.text = presets[currentIndex].label;
|
||||
|
||||
if (!wrap)
|
||||
{
|
||||
if (previousButton != null) previousButton.interactable = currentIndex > 0;
|
||||
if (nextButton != null) nextButton.interactable = currentIndex < presets.Count - 1;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user