Files
ichni_Creator_Studio/Assets/Scripts/DynamicUI/DynamicUIElements/Simple/DynamicUISlider.cs
SoulliesOfficial 545bfae0db 修复Slider
2026-06-14 03:48:05 -04:00

233 lines
7.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)
{
// [对象池安全] 重置可交互状态
if (slider != null) slider.interactable = true;
if (valueInputField != null) valueInputField.interactable = true;
if (minInputField != null) minInputField.interactable = true;
if (maxInputField != null) maxInputField.interactable = true;
// [对象池安全] 精准解绑业务代理 — 必须在修改 slider range 之前完成,
// 否则 Unity Slider 在 maxValue < 旧 value 时会自动触发 onValueChanged
// 导致旧监听器用旧的 parameterName 将错误值写回对象。
slider.onValueChanged.RemoveListener(OnSliderValueChanged);
valueInputField?.onEndEdit.RemoveListener(OnValueInputEndEdit);
minInputField?.onEndEdit.RemoveListener(OnMinInputEndEdit);
maxInputField?.onEndEdit.RemoveListener(OnMaxInputEndEdit);
customAction = null;
slider.minValue = min;
slider.maxValue = max;
slider.wholeNumbers = wholeNumbers;
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);
valueInputField?.onEndEdit.AddListener(OnValueInputEndEdit);
minInputField?.onEndEdit.AddListener(OnMinInputEndEdit);
maxInputField?.onEndEdit.AddListener(OnMaxInputEndEdit);
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));
}
#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
}
}