Enhance default value field (#1125)

* feat: enhance default value field input

* fix: format default value when update type or value on parameters inspector

* fix: don't accept NaN and Infinity for default value setting
This commit is contained in:
nekobako 2024-09-15 09:53:00 +09:00 committed by GitHub
parent f4d80b857d
commit faa8d210f2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 87 additions and 102 deletions

View File

@ -1,6 +1,4 @@
using System.Globalization;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;
@ -15,95 +13,104 @@ namespace nadena.dev.modular_avatar.core.editor
private const string V_None = " ";
private const string V_True = "ON";
private const string V_False = "OFF";
private readonly TextField _visibleField;
private readonly FloatField _defaultValueField;
private readonly Toggle _hasExplicitDefaultValueField;
private readonly TextField _numberField;
private readonly DropdownField _boolField;
private readonly Toggle _hasExplicitDefaultSetField;
private ParameterSyncType _syncType;
public DefaultValueField()
{
// Hidden binding elements
_defaultValueField = new FloatField();
_hasExplicitDefaultSetField = new Toggle();
_boolField = new DropdownField();
_defaultValueField.style.display = DisplayStyle.None;
_defaultValueField.bindingPath = nameof(ParameterConfig.defaultValue);
_defaultValueField.RegisterValueChangedCallback(evt => UpdateVisibleField(evt.newValue, _hasExplicitDefaultValueField.value));
_hasExplicitDefaultValueField = new Toggle();
_hasExplicitDefaultValueField.style.display = DisplayStyle.None;
_hasExplicitDefaultValueField.bindingPath = nameof(ParameterConfig.hasExplicitDefaultValue);
_hasExplicitDefaultValueField.RegisterValueChangedCallback(evt => UpdateVisibleField(_defaultValueField.value, evt.newValue));
// Visible elements for input
_numberField = new TextField();
_numberField.isDelayed = true;
_numberField.RegisterValueChangedCallback(evt => OnUpdateNumberValue(evt.newValue));
_boolField = new DropdownField();
_boolField.choices.Add(V_None);
_boolField.choices.Add(V_True);
_boolField.choices.Add(V_False);
_boolField.RegisterValueChangedCallback(evt => OnUpdateBoolValue(evt.newValue));
_defaultValueField.RegisterValueChangedCallback(
evt => UpdateVisibleField(evt.newValue, _hasExplicitDefaultSetField.value));
_defaultValueField.bindingPath = nameof(ParameterConfig.defaultValue);
_hasExplicitDefaultSetField.RegisterValueChangedCallback(
evt => UpdateVisibleField(_defaultValueField.value, evt.newValue));
_hasExplicitDefaultSetField.bindingPath = nameof(ParameterConfig.hasExplicitDefaultValue);
_visibleField = new TextField();
_visibleField.RegisterValueChangedCallback(evt =>
{
if (string.IsNullOrWhiteSpace(evt.newValue))
{
_hasExplicitDefaultSetField.value = false;
_defaultValueField.value = 0;
}
else
{
_hasExplicitDefaultSetField.value = true;
_defaultValueField.value = float.Parse(evt.newValue, CultureInfo.InvariantCulture);
}
});
_defaultValueField.style.width = 0;
_defaultValueField.SetEnabled(false);
_hasExplicitDefaultSetField.style.width = 0;
_hasExplicitDefaultSetField.SetEnabled(false);
_boolField.RegisterValueChangedCallback(evt =>
{
if (evt.newValue == V_True)
_defaultValueField.value = 1;
else
_defaultValueField.value = 0;
_hasExplicitDefaultSetField.value = evt.newValue != V_None;
});
style.flexDirection = FlexDirection.Row;
Add(_visibleField);
Add(_boolField);
Add(_defaultValueField);
Add(_hasExplicitDefaultSetField);
Add(_hasExplicitDefaultValueField);
Add(_numberField);
Add(_boolField);
}
public void ManualBindProperty(SerializedProperty property)
public void OnUpdateSyncType(ParameterSyncType syncType)
{
_defaultValueField.BindProperty(property);
_hasExplicitDefaultSetField.BindProperty(property);
_syncType = syncType;
if (syncType != ParameterSyncType.Bool)
{
_numberField.style.display = DisplayStyle.Flex;
_boolField.style.display = DisplayStyle.None;
OnUpdateNumberValue(_numberField.value);
}
else
{
_numberField.style.display = DisplayStyle.None;
_boolField.style.display = DisplayStyle.Flex;
OnUpdateBoolValue(_boolField.value);
}
}
private void OnUpdateNumberValue(string value)
{
if (string.IsNullOrWhiteSpace(value))
{
_defaultValueField.value = 0;
_hasExplicitDefaultValueField.value = false;
}
else if (float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var parsed)
&& !float.IsNaN(parsed)
&& !float.IsInfinity(parsed))
{
_defaultValueField.value = _syncType switch
{
ParameterSyncType.Int => Mathf.FloorToInt(Mathf.Clamp(parsed, 0, 255)),
ParameterSyncType.Float => Mathf.Clamp(parsed, -1, 1),
ParameterSyncType.Bool => parsed != 0 ? 1 : 0,
_ => parsed,
};
_hasExplicitDefaultValueField.value = true;
}
UpdateVisibleField(_defaultValueField.value, _hasExplicitDefaultValueField.value);
}
private void OnUpdateBoolValue(string value)
{
_defaultValueField.value = value == V_True ? 1 : 0;
_hasExplicitDefaultValueField.value = value != V_None;
UpdateVisibleField(_defaultValueField.value, _hasExplicitDefaultValueField.value);
}
private void UpdateVisibleField(float value, bool hasExplicitValue)
{
if (Mathf.Abs(value) > 0.0000001)
if (hasExplicitValue || Mathf.Abs(value) > 0.0000001)
{
hasExplicitValue = true;
_numberField.SetValueWithoutNotify(value.ToString(CultureInfo.InvariantCulture));
_boolField.SetValueWithoutNotify(value != 0 ? V_True : V_False);
}
var str = hasExplicitValue ? value.ToString(CultureInfo.InvariantCulture) : "";
_visibleField.SetValueWithoutNotify(str);
string boolStr;
if (!hasExplicitValue)
boolStr = V_None;
else if (value > 0.5)
boolStr = V_True;
else
boolStr = V_False;
_boolField.SetValueWithoutNotify(boolStr);
{
_numberField.SetValueWithoutNotify(string.Empty);
_boolField.SetValueWithoutNotify(V_None);
}
}
}
}
}

View File

@ -20,14 +20,14 @@ namespace nadena.dev.modular_avatar.core.editor.Parameters
Localization.UI.Localize(root);
root.styleSheets.Add(uss);
var type_field = root.Q<DropdownField>("f-type");
var f_sync_type = root.Q<VisualElement>("f-sync-type");
var f_type = root.Q<DropdownField>("f-type");
var f_sync_type = root.Q<DropdownField>("f-sync-type");
var f_is_prefix = root.Q<VisualElement>("f-is-prefix");
SetupPairedDropdownField(
root,
type_field,
f_type,
f_sync_type,
root.Q<VisualElement>("f-is-prefix"),
f_is_prefix,
("Bool", "False", "params.syncmode.Bool"),
("Float", "False", "params.syncmode.Float"),
("Int", "False", "params.syncmode.Int"),
@ -35,15 +35,9 @@ namespace nadena.dev.modular_avatar.core.editor.Parameters
(null, "True", "params.syncmode.PhysBonesPrefix")
);
f_sync_type.Q<DropdownField>().RegisterValueChangedCallback(evt =>
{
var is_anim_only = evt.newValue == "Not Synced";
if (is_anim_only)
root.AddToClassList("st-anim-only");
else
root.RemoveFromClassList("st-anim-only");
});
var f_default = root.Q<DefaultValueField>();
f_default.OnUpdateSyncType((ParameterSyncType)f_sync_type.index);
f_sync_type.RegisterValueChangedCallback(evt => f_default.OnUpdateSyncType((ParameterSyncType)f_sync_type.index));
var f_synced = root.Q<Toggle>("f-synced");
var f_local_only = root.Q<Toggle>("f-local-only");

View File

@ -30,7 +30,7 @@
display: none;
}
.st-anim-only .st-anim-only__hide {
.st-ty-Not-Synced .st-anim-only__hide {
display: none;
}
@ -79,28 +79,12 @@
margin: 0;
}
DefaultValueField TextField {
display: flex;
DefaultValueField > * {
width: 60px;
height: 100%;
margin: 0;
}
DefaultValueField DropdownField {
display: none;
width: 60px;
height: 100%;
margin: 0;
}
.st-ty-Bool DefaultValueField TextField {
display: none;
}
.st-ty-Bool DefaultValueField DropdownField {
display: flex;
}
#f-local-only {
display: none;
}