新增参数窗口按钮

新增参数窗口按钮
This commit is contained in:
zhang hongbo 2026-04-14 14:58:35 +08:00
parent cf352f2a22
commit 0fca77ea9f
2 changed files with 216 additions and 24 deletions

View File

@ -933,16 +933,69 @@ namespace MeowmentDebugTool
} }
else else
{ {
Dictionary<string, List<Action<string>>> linkedFieldCallbacks = new Dictionary<string, List<Action<string>>>();
List<KeyValuePair<string, string>> initialLinkedValues = new List<KeyValuePair<string, string>>();
foreach (ParameterInfo parameter in parameters) foreach (ParameterInfo parameter in parameters)
{ {
Func<InputDialogValueResult> reader = CreateInputDialogField(parameter); DebugInputParameterAttribute paramAttr = parameter.GetCustomAttribute<DebugInputParameterAttribute>();
if (reader == null)
{
Debug.LogWarning($"[MeowmentDebugTool] 暂不支持参数类型: {parameter.ParameterType.Name}");
return;
}
readers.Add(reader); if (paramAttr != null && paramAttr.ReadOnly)
{
string readOnlyLabel = !string.IsNullOrEmpty(paramAttr.Label) ? paramAttr.Label : parameter.Name;
Func<string, string> displayProvider = ResolveDisplayValueProvider(parameter.Member.DeclaringType, paramAttr.DisplayValueProvider);
Func<InputDialogValueResult> readOnlyReader;
Action<string> readOnlyUpdater;
CreateReadOnlyDisplayField(readOnlyLabel, string.Empty, out readOnlyReader, out readOnlyUpdater);
if (!string.IsNullOrEmpty(paramAttr.LinkedTo))
{
string linkedTo = paramAttr.LinkedTo;
if (!linkedFieldCallbacks.ContainsKey(linkedTo))
linkedFieldCallbacks[linkedTo] = new List<Action<string>>();
linkedFieldCallbacks[linkedTo].Add(selectedValue =>
{
string displayValue = displayProvider != null ? displayProvider(selectedValue) : selectedValue;
readOnlyUpdater(displayValue);
});
}
readers.Add(readOnlyReader);
}
else
{
string paramName = parameter.Name;
Action<string> onSelectionChanged = selectedValue =>
{
if (linkedFieldCallbacks.TryGetValue(paramName, out List<Action<string>> callbacks))
foreach (Action<string> cb in callbacks)
cb(selectedValue);
};
Func<InputDialogValueResult> reader = CreateInputDialogField(parameter, onSelectionChanged);
if (reader == null)
{
Debug.LogWarning($"[MeowmentDebugTool] 暂不支持参数类型: {parameter.ParameterType.Name}");
return;
}
readers.Add(reader);
object defaultVal = GetParameterDefaultValue(parameter, paramAttr, out bool hasDefault);
if (hasDefault && defaultVal != null)
{
initialLinkedValues.Add(new KeyValuePair<string, string>(paramName, defaultVal.ToString()));
}
}
}
foreach (KeyValuePair<string, string> kvp in initialLinkedValues)
{
if (linkedFieldCallbacks.TryGetValue(kvp.Key, out List<Action<string>> callbacks))
foreach (Action<string> cb in callbacks)
cb(kvp.Value);
} }
} }
@ -1020,16 +1073,72 @@ namespace MeowmentDebugTool
List<RuntimeDebugInputParameterDefinition> parameters = definition.Parameters ?? new List<RuntimeDebugInputParameterDefinition>(); List<RuntimeDebugInputParameterDefinition> parameters = definition.Parameters ?? new List<RuntimeDebugInputParameterDefinition>();
List<Func<InputDialogValueResult>> readers = new List<Func<InputDialogValueResult>>(); List<Func<InputDialogValueResult>> readers = new List<Func<InputDialogValueResult>>();
Dictionary<string, List<Action<string>>> linkedFieldCallbacks = new Dictionary<string, List<Action<string>>>();
List<KeyValuePair<string, string>> initialLinkedValues = new List<KeyValuePair<string, string>>();
foreach (RuntimeDebugInputParameterDefinition parameter in parameters) foreach (RuntimeDebugInputParameterDefinition parameter in parameters)
{ {
Func<InputDialogValueResult> reader = CreateRuntimeInputDialogField(parameter); if (parameter != null && parameter.ReadOnly)
if (reader == null)
{ {
Debug.LogWarning($"[MeowmentDebugTool] 暂不支持运行时输入参数类型: {parameter?.ParameterType?.Name}"); string readOnlyLabel = string.IsNullOrEmpty(parameter.Label)
return; ? (string.IsNullOrEmpty(parameter.Key) ? "display" : parameter.Key)
} : parameter.Label;
Func<string, string> displayProvider = parameter.DisplayValueProvider;
readers.Add(reader); Func<InputDialogValueResult> readOnlyReader;
Action<string> readOnlyUpdater;
CreateReadOnlyDisplayField(readOnlyLabel, string.Empty, out readOnlyReader, out readOnlyUpdater);
if (!string.IsNullOrEmpty(parameter.LinkedTo))
{
string linkedTo = parameter.LinkedTo;
if (!linkedFieldCallbacks.ContainsKey(linkedTo))
linkedFieldCallbacks[linkedTo] = new List<Action<string>>();
linkedFieldCallbacks[linkedTo].Add(selectedValue =>
{
string displayValue = displayProvider != null ? displayProvider(selectedValue) : selectedValue;
readOnlyUpdater(displayValue);
});
}
readers.Add(readOnlyReader);
}
else
{
string paramKey = parameter != null && !string.IsNullOrEmpty(parameter.Key) ? parameter.Key : $"param{readers.Count}";
Action<string> onSelectionChanged = selectedValue =>
{
if (linkedFieldCallbacks.TryGetValue(paramKey, out List<Action<string>> callbacks))
foreach (Action<string> cb in callbacks)
cb(selectedValue);
};
Func<InputDialogValueResult> reader = CreateRuntimeInputDialogField(parameter, onSelectionChanged);
if (reader == null)
{
Debug.LogWarning($"[MeowmentDebugTool] 暂不支持运行时输入参数类型: {parameter?.ParameterType?.Name}");
return;
}
readers.Add(reader);
if (parameter != null)
{
object defaultVal = GetRuntimeParameterDefaultValue(parameter, out bool hasDefault);
if (hasDefault && defaultVal != null)
{
initialLinkedValues.Add(new KeyValuePair<string, string>(paramKey, defaultVal.ToString()));
}
}
}
}
foreach (KeyValuePair<string, string> kvp in initialLinkedValues)
{
if (linkedFieldCallbacks.TryGetValue(kvp.Key, out List<Action<string>> callbacks))
foreach (Action<string> cb in callbacks)
cb(kvp.Value);
} }
inputDialogConfirmBtn.onClick.RemoveAllListeners(); inputDialogConfirmBtn.onClick.RemoveAllListeners();
@ -1096,7 +1205,7 @@ namespace MeowmentDebugTool
inputDialogGeneratedObjects.Clear(); inputDialogGeneratedObjects.Clear();
} }
private Func<InputDialogValueResult> CreateInputDialogField(ParameterInfo parameter) private Func<InputDialogValueResult> CreateInputDialogField(ParameterInfo parameter, Action<string> onSelectionChanged = null)
{ {
Type parameterType = parameter.ParameterType; Type parameterType = parameter.ParameterType;
DebugInputParameterAttribute parameterAttribute = parameter.GetCustomAttribute<DebugInputParameterAttribute>(); DebugInputParameterAttribute parameterAttribute = parameter.GetCustomAttribute<DebugInputParameterAttribute>();
@ -1120,7 +1229,7 @@ namespace MeowmentDebugTool
return () => CreateInputError(label, $"{label} 当前没有可选项"); return () => CreateInputError(label, $"{label} 当前没有可选项");
} }
return CreateDynamicOptionField(fieldRoot.transform, parameterType, label, parameterAttribute, dynamicOptions, defaultValue, hasDefaultValue); return CreateDynamicOptionField(fieldRoot.transform, parameterType, label, parameterAttribute, dynamicOptions, defaultValue, hasDefaultValue, onSelectionChanged);
} }
if (parameterType == typeof(string)) if (parameterType == typeof(string))
@ -1172,7 +1281,7 @@ namespace MeowmentDebugTool
return CreateFlagsEnumField(fieldRoot.transform, parameterType, label, parameterAttribute, defaultValue, hasDefaultValue); return CreateFlagsEnumField(fieldRoot.transform, parameterType, label, parameterAttribute, defaultValue, hasDefaultValue);
} }
return CreateEnumField(fieldRoot.transform, parameterType, label, parameterAttribute, defaultValue, hasDefaultValue); return CreateEnumField(fieldRoot.transform, parameterType, label, parameterAttribute, defaultValue, hasDefaultValue, onSelectionChanged);
} }
GameObject unsupported = CreateDialogHint(fieldRoot.transform, $"暂不支持类型: {parameterType.Name}"); GameObject unsupported = CreateDialogHint(fieldRoot.transform, $"暂不支持类型: {parameterType.Name}");
@ -1180,7 +1289,7 @@ namespace MeowmentDebugTool
return null; return null;
} }
private Func<InputDialogValueResult> CreateRuntimeInputDialogField(RuntimeDebugInputParameterDefinition parameterDefinition) private Func<InputDialogValueResult> CreateRuntimeInputDialogField(RuntimeDebugInputParameterDefinition parameterDefinition, Action<string> onSelectionChanged = null)
{ {
if (parameterDefinition == null || parameterDefinition.ParameterType == null) if (parameterDefinition == null || parameterDefinition.ParameterType == null)
{ {
@ -1208,7 +1317,7 @@ namespace MeowmentDebugTool
return () => CreateInputError(label, $"{label} 当前没有可选项"); return () => CreateInputError(label, $"{label} 当前没有可选项");
} }
return CreateDynamicOptionField(fieldRoot.transform, parameterType, label, parameterDefinition.Required, dynamicOptions, defaultValue, hasDefaultValue); return CreateDynamicOptionField(fieldRoot.transform, parameterType, label, parameterDefinition.Required, dynamicOptions, defaultValue, hasDefaultValue, onSelectionChanged);
} }
if (parameterType == typeof(string)) if (parameterType == typeof(string))
@ -1260,7 +1369,7 @@ namespace MeowmentDebugTool
return CreateFlagsEnumField(fieldRoot.transform, parameterType, label, parameterDefinition.Required, defaultValue, hasDefaultValue); return CreateFlagsEnumField(fieldRoot.transform, parameterType, label, parameterDefinition.Required, defaultValue, hasDefaultValue);
} }
return CreateEnumField(fieldRoot.transform, parameterType, label, parameterDefinition.Required, defaultValue, hasDefaultValue); return CreateEnumField(fieldRoot.transform, parameterType, label, parameterDefinition.Required, defaultValue, hasDefaultValue, onSelectionChanged);
} }
GameObject unsupported = CreateDialogHint(fieldRoot.transform, $"暂不支持类型: {parameterType.Name}"); GameObject unsupported = CreateDialogHint(fieldRoot.transform, $"暂不支持类型: {parameterType.Name}");
@ -1500,7 +1609,7 @@ namespace MeowmentDebugTool
} }
private Func<InputDialogValueResult> CreateDynamicOptionField(Transform parent, Type parameterType, string label, private Func<InputDialogValueResult> CreateDynamicOptionField(Transform parent, Type parameterType, string label,
DebugInputParameterAttribute parameterAttribute, List<InputDialogDynamicOption> options, object defaultValue, bool hasDefaultValue) DebugInputParameterAttribute parameterAttribute, List<InputDialogDynamicOption> options, object defaultValue, bool hasDefaultValue, Action<string> onSelectionChanged = null)
{ {
GameObject optionContainer = new GameObject("DynamicOptions"); GameObject optionContainer = new GameObject("DynamicOptions");
optionContainer.transform.SetParent(parent, false); optionContainer.transform.SetParent(parent, false);
@ -1530,6 +1639,7 @@ namespace MeowmentDebugTool
{ {
selectedIndex = currentIndex; selectedIndex = currentIndex;
UpdateEnumOptionButtonStyles(buttons, selectedIndex); UpdateEnumOptionButtonStyles(buttons, selectedIndex);
onSelectionChanged?.Invoke(currentOption.Value?.ToString() ?? string.Empty);
}); });
buttons.Add(optionButton); buttons.Add(optionButton);
} }
@ -1558,7 +1668,7 @@ namespace MeowmentDebugTool
} }
private Func<InputDialogValueResult> CreateDynamicOptionField(Transform parent, Type parameterType, string label, private Func<InputDialogValueResult> CreateDynamicOptionField(Transform parent, Type parameterType, string label,
bool required, List<InputDialogDynamicOption> options, object defaultValue, bool hasDefaultValue) bool required, List<InputDialogDynamicOption> options, object defaultValue, bool hasDefaultValue, Action<string> onSelectionChanged = null)
{ {
GameObject optionContainer = new GameObject("DynamicOptions"); GameObject optionContainer = new GameObject("DynamicOptions");
optionContainer.transform.SetParent(parent, false); optionContainer.transform.SetParent(parent, false);
@ -1588,6 +1698,7 @@ namespace MeowmentDebugTool
{ {
selectedIndex = currentIndex; selectedIndex = currentIndex;
UpdateEnumOptionButtonStyles(buttons, selectedIndex); UpdateEnumOptionButtonStyles(buttons, selectedIndex);
onSelectionChanged?.Invoke(currentOption.Value?.ToString() ?? string.Empty);
}); });
buttons.Add(optionButton); buttons.Add(optionButton);
} }
@ -1631,7 +1742,7 @@ namespace MeowmentDebugTool
} }
private Func<InputDialogValueResult> CreateEnumField(Transform parent, Type enumType, string label, private Func<InputDialogValueResult> CreateEnumField(Transform parent, Type enumType, string label,
DebugInputParameterAttribute parameterAttribute, object defaultValue, bool hasDefaultValue) DebugInputParameterAttribute parameterAttribute, object defaultValue, bool hasDefaultValue, Action<string> onSelectionChanged = null)
{ {
GameObject optionContainer = new GameObject("Options"); GameObject optionContainer = new GameObject("Options");
optionContainer.transform.SetParent(parent, false); optionContainer.transform.SetParent(parent, false);
@ -1658,6 +1769,7 @@ namespace MeowmentDebugTool
{ {
selectedIndex = currentIndex; selectedIndex = currentIndex;
UpdateEnumOptionButtonStyles(buttons, selectedIndex); UpdateEnumOptionButtonStyles(buttons, selectedIndex);
onSelectionChanged?.Invoke(optionValue.ToString());
}); });
buttons.Add(optionButton); buttons.Add(optionButton);
} }
@ -1685,7 +1797,7 @@ namespace MeowmentDebugTool
} }
private Func<InputDialogValueResult> CreateEnumField(Transform parent, Type enumType, string label, private Func<InputDialogValueResult> CreateEnumField(Transform parent, Type enumType, string label,
bool required, object defaultValue, bool hasDefaultValue) bool required, object defaultValue, bool hasDefaultValue, Action<string> onSelectionChanged = null)
{ {
GameObject optionContainer = new GameObject("Options"); GameObject optionContainer = new GameObject("Options");
optionContainer.transform.SetParent(parent, false); optionContainer.transform.SetParent(parent, false);
@ -1712,6 +1824,7 @@ namespace MeowmentDebugTool
{ {
selectedIndex = currentIndex; selectedIndex = currentIndex;
UpdateEnumOptionButtonStyles(buttons, selectedIndex); UpdateEnumOptionButtonStyles(buttons, selectedIndex);
onSelectionChanged?.Invoke(optionValue.ToString());
}); });
buttons.Add(optionButton); buttons.Add(optionButton);
} }
@ -2004,6 +2117,76 @@ namespace MeowmentDebugTool
}; };
} }
private void CreateReadOnlyDisplayField(string label, string initialValue, out Func<InputDialogValueResult> reader, out Action<string> updater)
{
GameObject fieldRoot = CreateInputFieldRoot(label, false);
inputDialogGeneratedObjects.Add(fieldRoot);
GameObject displayObj = new GameObject("ReadOnlyDisplay");
displayObj.transform.SetParent(fieldRoot.transform, false);
Image displayBg = displayObj.AddComponent<Image>();
displayBg.color = new Color(0.14f, 0.14f, 0.14f, 1f);
LayoutElement layoutEl = displayObj.AddComponent<LayoutElement>();
layoutEl.preferredHeight = 64f;
layoutEl.flexibleWidth = 1f;
GameObject textContainer = new GameObject("TextContainer");
textContainer.transform.SetParent(displayObj.transform, false);
RectTransform containerRect = textContainer.AddComponent<RectTransform>();
containerRect.anchorMin = Vector2.zero;
containerRect.anchorMax = Vector2.one;
containerRect.offsetMin = new Vector2(20, 0);
containerRect.offsetMax = new Vector2(-20, 0);
TextMeshProUGUI displayText = textContainer.AddComponent<TextMeshProUGUI>();
displayText.text = string.IsNullOrEmpty(initialValue) ? "—" : initialValue;
displayText.fontSize = 28;
displayText.color = new Color(1f, 1f, 1f, 0.6f);
displayText.alignment = TextAlignmentOptions.MidlineLeft;
ApplySavedFont(displayText);
string currentValue = initialValue ?? string.Empty;
reader = () => new InputDialogValueResult { Success = true, Value = currentValue };
updater = value =>
{
currentValue = value ?? string.Empty;
displayText.text = string.IsNullOrEmpty(value) ? "—" : value;
};
}
private Func<string, string> ResolveDisplayValueProvider(Type declaringType, string methodName)
{
if (declaringType == null || string.IsNullOrEmpty(methodName))
{
return null;
}
MethodInfo method = declaringType.GetMethod(
methodName,
BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic,
null,
new Type[] { typeof(string) },
null);
if (method == null)
{
Debug.LogWarning($"[MeowmentDebugTool] 未找到显示值提供方法: {declaringType.Name}.{methodName}(string)");
return null;
}
if (method.ReturnType != typeof(string))
{
Debug.LogWarning($"[MeowmentDebugTool] 显示值提供方法 {methodName} 必须返回 string");
return null;
}
return selectedValue => method.Invoke(null, new object[] { selectedValue }) as string ?? string.Empty;
}
private GameObject CreateInputFieldRoot(string label, bool required) private GameObject CreateInputFieldRoot(string label, bool required)
{ {
GameObject fieldRoot = new GameObject($"Field_{label}"); GameObject fieldRoot = new GameObject($"Field_{label}");
@ -2634,6 +2817,9 @@ public class DebugInputParameterAttribute : Attribute
public bool Required { get; set; } public bool Required { get; set; }
public float Min { get; set; } = float.NaN; public float Min { get; set; } = float.NaN;
public float Max { get; set; } = float.NaN; public float Max { get; set; } = float.NaN;
public bool ReadOnly { get; set; }
public string LinkedTo { get; set; }
public string DisplayValueProvider { get; set; }
public DebugInputParameterAttribute(string label = "") public DebugInputParameterAttribute(string label = "")
{ {
@ -2642,6 +2828,8 @@ public class DebugInputParameterAttribute : Attribute
DefaultValue = string.Empty; DefaultValue = string.Empty;
OptionsProvider = string.Empty; OptionsProvider = string.Empty;
Required = false; Required = false;
LinkedTo = string.Empty;
DisplayValueProvider = string.Empty;
} }
} }
@ -2694,6 +2882,9 @@ public class RuntimeDebugInputParameterDefinition
public float Min { get; set; } = float.NaN; public float Min { get; set; } = float.NaN;
public float Max { get; set; } = float.NaN; public float Max { get; set; } = float.NaN;
public Func<IEnumerable> OptionsProvider { get; set; } public Func<IEnumerable> OptionsProvider { get; set; }
public bool ReadOnly { get; set; }
public string LinkedTo { get; set; }
public Func<string, string> DisplayValueProvider { get; set; }
public RuntimeDebugInputParameterDefinition(string key, Type parameterType, string label = "") public RuntimeDebugInputParameterDefinition(string key, Type parameterType, string label = "")
{ {
@ -2703,6 +2894,7 @@ public class RuntimeDebugInputParameterDefinition
Placeholder = string.Empty; Placeholder = string.Empty;
DefaultValue = string.Empty; DefaultValue = string.Empty;
Required = false; Required = false;
LinkedTo = string.Empty;
} }
} }

View File

@ -1,7 +1,7 @@
{ {
"name": "com.bywaystudios.meowmentdebugtool", "name": "com.bywaystudios.meowmentdebugtool",
"displayName": "MeowmentDebugTool", "displayName": "MeowmentDebugTool",
"version": "0.5.2", "version": "0.5.3",
"description": "\u8c03\u8bd5\u5de5\u5177\uff0c\u96c6\u6210\u4e86\u5ba2\u6237\u7aef\u6d4b\u8bd5\u65f6\u7684\u5e38\u7528\u529f\u80fd\uff0c\u652f\u6301\u6269\u5c55\u81ea\u5b9a\u4e49\u6309\u94ae\uff0c\u65b9\u4fbf\u5f00\u53d1\u8005\u8c03\u8bd5\u548c\u6d4b\u8bd5\u3002", "description": "\u8c03\u8bd5\u5de5\u5177\uff0c\u96c6\u6210\u4e86\u5ba2\u6237\u7aef\u6d4b\u8bd5\u65f6\u7684\u5e38\u7528\u529f\u80fd\uff0c\u652f\u6301\u6269\u5c55\u81ea\u5b9a\u4e49\u6309\u94ae\uff0c\u65b9\u4fbf\u5f00\u53d1\u8005\u8c03\u8bd5\u548c\u6d4b\u8bd5\u3002",
"samples": [ "samples": [
{ {