226 lines
6.5 KiB
C#
226 lines
6.5 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using Ichni.RhythmGame;
|
|
using TMPro;
|
|
using UnityEngine;
|
|
using UnityEngine.Events;
|
|
using UnityEngine.UI;
|
|
|
|
namespace Ichni.Editor
|
|
{
|
|
public class DynamicUISlider : DynamicUIElement
|
|
{
|
|
public Slider slider;
|
|
public TMP_InputField valueInputField;
|
|
public TMP_InputField minInputField;
|
|
public TMP_InputField maxInputField;
|
|
|
|
private UnityAction<float> customAction;
|
|
private bool updatingInternally; // 防止输入框和滑块互相递归更新
|
|
|
|
#region [初始化] Initialize
|
|
|
|
public override void Initialize(IBaseElement baseElement, string title, string parameterName)
|
|
{
|
|
Initialize(baseElement, title, parameterName, 0f, 1f, false);
|
|
}
|
|
|
|
public void Initialize(IBaseElement baseElement, string title, string parameterName,
|
|
float min, float max, bool wholeNumbers = false)
|
|
{
|
|
slider.minValue = min;
|
|
slider.maxValue = max;
|
|
slider.wholeNumbers = wholeNumbers;
|
|
|
|
// [对象池安全] 精准解绑业务代理
|
|
slider.onValueChanged.RemoveListener(OnSliderValueChanged);
|
|
valueInputField?.onEndEdit.RemoveListener(OnValueInputEndEdit);
|
|
minInputField?.onEndEdit.RemoveListener(OnMinInputEndEdit);
|
|
maxInputField?.onEndEdit.RemoveListener(OnMaxInputEndEdit);
|
|
customAction = null;
|
|
|
|
base.Initialize(baseElement, title, parameterName);
|
|
|
|
if (parameterName != string.Empty)
|
|
{
|
|
var val = ReflectionHelper.GetDeepValue(connectedBaseElement, parameterName);
|
|
if (val != null)
|
|
{
|
|
slider.SetValueWithoutNotify(System.Convert.ToSingle(val));
|
|
}
|
|
else
|
|
{
|
|
Debug.LogWarning($"[DynamicUI] 尝试绑定 {title} ({parameterName}) 失败,由于其值或路径无效。");
|
|
}
|
|
slider.onValueChanged.AddListener(OnSliderValueChanged);
|
|
}
|
|
else
|
|
{
|
|
slider.onValueChanged.AddListener(OnSliderValueChanged);
|
|
}
|
|
|
|
SyncAllInputFields();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region [滑块变化回调] Slider Callback
|
|
|
|
private void OnSliderValueChanged(float value)
|
|
{
|
|
if (updatingInternally) return;
|
|
updatingInternally = true;
|
|
|
|
UpdateValueInputText(value);
|
|
if (parameterName != string.Empty)
|
|
{
|
|
ApplyParameters(value);
|
|
}
|
|
customAction?.Invoke(value);
|
|
|
|
updatingInternally = false;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region [输入框回调] Input Field Callbacks
|
|
|
|
private void OnValueInputEndEdit(string text)
|
|
{
|
|
if (updatingInternally) return;
|
|
if (!float.TryParse(text, out float val)) return;
|
|
|
|
val = Mathf.Clamp(val, slider.minValue, slider.maxValue);
|
|
|
|
updatingInternally = true;
|
|
|
|
slider.SetValueWithoutNotify(val);
|
|
UpdateValueInputText(val);
|
|
if (parameterName != string.Empty)
|
|
{
|
|
ApplyParameters(val);
|
|
}
|
|
customAction?.Invoke(val);
|
|
|
|
updatingInternally = false;
|
|
}
|
|
|
|
private void OnMinInputEndEdit(string text)
|
|
{
|
|
if (updatingInternally) return;
|
|
if (!float.TryParse(text, out float val)) return;
|
|
|
|
if (val >= slider.maxValue) val = slider.maxValue - 0.0001f;
|
|
slider.minValue = val;
|
|
|
|
if (slider.value < val)
|
|
{
|
|
slider.value = val;
|
|
}
|
|
|
|
UpdateMinMaxInputTexts();
|
|
UpdateValueInputText(slider.value);
|
|
}
|
|
|
|
private void OnMaxInputEndEdit(string text)
|
|
{
|
|
if (updatingInternally) return;
|
|
if (!float.TryParse(text, out float val)) return;
|
|
|
|
if (val <= slider.minValue) val = slider.minValue + 0.0001f;
|
|
slider.maxValue = val;
|
|
|
|
if (slider.value > val)
|
|
{
|
|
slider.value = val;
|
|
}
|
|
|
|
UpdateMinMaxInputTexts();
|
|
UpdateValueInputText(slider.value);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region [参数写入] Apply Parameters
|
|
|
|
private void ApplyParameters(float value)
|
|
{
|
|
object convertedValue;
|
|
if (slider.wholeNumbers)
|
|
{
|
|
convertedValue = Mathf.RoundToInt(value);
|
|
}
|
|
else
|
|
{
|
|
convertedValue = value;
|
|
}
|
|
Ichni.Editor.Commands.CommandManager.ExecuteCommand(
|
|
new Ichni.Editor.Commands.ChangeValueCommand(connectedBaseElement, parameterName, convertedValue));
|
|
connectedBaseElement.Refresh();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region [UI 同步] UI Sync Helpers
|
|
|
|
private void SyncAllInputFields()
|
|
{
|
|
UpdateValueInputText(slider.value);
|
|
UpdateMinMaxInputTexts();
|
|
}
|
|
|
|
private void UpdateValueInputText(float value)
|
|
{
|
|
if (valueInputField != null)
|
|
{
|
|
valueInputField.SetTextWithoutNotify(slider.wholeNumbers
|
|
? Mathf.RoundToInt(value).ToString()
|
|
: value.ToString("F3"));
|
|
}
|
|
}
|
|
|
|
private void UpdateMinMaxInputTexts()
|
|
{
|
|
string FormatRangeVal(float v)
|
|
{
|
|
return slider.wholeNumbers ? Mathf.RoundToInt(v).ToString() : v.ToString("F3");
|
|
}
|
|
|
|
if (minInputField != null)
|
|
{
|
|
minInputField.SetTextWithoutNotify(FormatRangeVal(slider.minValue));
|
|
}
|
|
if (maxInputField != null)
|
|
{
|
|
maxInputField.SetTextWithoutNotify(FormatRangeVal(slider.maxValue));
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region [Public API]
|
|
|
|
public void SetRange(float min, float max)
|
|
{
|
|
slider.minValue = min;
|
|
slider.maxValue = max;
|
|
UpdateMinMaxInputTexts();
|
|
UpdateValueInputText(slider.value);
|
|
}
|
|
|
|
public void SetWholeNumbers(bool wholeNumbers)
|
|
{
|
|
slider.wholeNumbers = wholeNumbers;
|
|
SyncAllInputFields();
|
|
}
|
|
|
|
public override DynamicUIElement AddListenerFunction(UnityAction action)
|
|
{
|
|
customAction += _ => action();
|
|
return this;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|