diff --git a/scripts/startup/bl_operators/object_quick_effects.py b/scripts/startup/bl_operators/object_quick_effects.py index 487765aee20..d7ec7d37628 100644 --- a/scripts/startup/bl_operators/object_quick_effects.py +++ b/scripts/startup/bl_operators/object_quick_effects.py @@ -190,10 +190,6 @@ class QuickFur(ObjectModeOperator, Operator): curves_object.modifiers.move(0, len(curves_object.modifiers) - 1) - # Workaround for #105965: Rebuild UI data of modifier input properties. - for modifier in curves_object.modifiers: - modifier.node_group = modifier.node_group - if mesh_with_zero_area: self.report({'WARNING'}, "Mesh has no face area") if mesh_missing_uv_map: diff --git a/source/blender/blenkernel/BKE_idprop.h b/source/blender/blenkernel/BKE_idprop.h index 48edea8f7ed..f4d24235edb 100644 --- a/source/blender/blenkernel/BKE_idprop.h +++ b/source/blender/blenkernel/BKE_idprop.h @@ -365,6 +365,13 @@ void IDP_ui_data_free_unique_contents(struct IDPropertyUIData *ui_data, const struct IDPropertyUIData *other); struct IDPropertyUIData *IDP_ui_data_ensure(struct IDProperty *prop); struct IDPropertyUIData *IDP_ui_data_copy(const struct IDProperty *prop); +/** + * Convert UI data like default arrays from the old type to the new type as possible. + * Takes ownership of the input data; it can return it directly if the types match. + */ +struct IDPropertyUIData *IDP_TryConvertUIData(struct IDPropertyUIData *src, + eIDPropertyUIDataType src_type, + eIDPropertyUIDataType dst_type); #ifdef __cplusplus } diff --git a/source/blender/blenkernel/intern/idprop.cc b/source/blender/blenkernel/intern/idprop.cc index c8479708d3e..8a972e2d18f 100644 --- a/source/blender/blenkernel/intern/idprop.cc +++ b/source/blender/blenkernel/intern/idprop.cc @@ -1128,11 +1128,11 @@ void IDP_ui_data_free_unique_contents(IDPropertyUIData *ui_data, } } -void IDP_ui_data_free(IDProperty *prop) +static void ui_data_free(IDPropertyUIData *ui_data, const eIDPropertyUIDataType type) { - switch (IDP_ui_data_type(prop)) { + switch (type) { case IDP_UI_DATA_TYPE_STRING: { - IDPropertyUIDataString *ui_data_string = (IDPropertyUIDataString *)prop->ui_data; + IDPropertyUIDataString *ui_data_string = (IDPropertyUIDataString *)ui_data; MEM_SAFE_FREE(ui_data_string->default_value); break; } @@ -1140,18 +1140,18 @@ void IDP_ui_data_free(IDProperty *prop) break; } case IDP_UI_DATA_TYPE_INT: { - IDPropertyUIDataInt *ui_data_int = (IDPropertyUIDataInt *)prop->ui_data; + IDPropertyUIDataInt *ui_data_int = (IDPropertyUIDataInt *)ui_data; MEM_SAFE_FREE(ui_data_int->default_array); IDP_int_ui_data_free_enum_items(ui_data_int); break; } case IDP_UI_DATA_TYPE_BOOLEAN: { - IDPropertyUIDataBool *ui_data_bool = (IDPropertyUIDataBool *)prop->ui_data; + IDPropertyUIDataBool *ui_data_bool = (IDPropertyUIDataBool *)ui_data; MEM_SAFE_FREE(ui_data_bool->default_array); break; } case IDP_UI_DATA_TYPE_FLOAT: { - IDPropertyUIDataFloat *ui_data_float = (IDPropertyUIDataFloat *)prop->ui_data; + IDPropertyUIDataFloat *ui_data_float = (IDPropertyUIDataFloat *)ui_data; MEM_SAFE_FREE(ui_data_float->default_array); break; } @@ -1160,9 +1160,14 @@ void IDP_ui_data_free(IDProperty *prop) } } - MEM_SAFE_FREE(prop->ui_data->description); + MEM_SAFE_FREE(ui_data->description); - MEM_freeN(prop->ui_data); + MEM_freeN(ui_data); +} + +void IDP_ui_data_free(IDProperty *prop) +{ + ui_data_free(prop->ui_data, IDP_ui_data_type(prop)); prop->ui_data = nullptr; } @@ -1610,23 +1615,17 @@ bool IDP_ui_data_supported(const IDProperty *prop) return IDP_ui_data_type(prop) != IDP_UI_DATA_TYPE_UNSUPPORTED; } -IDPropertyUIData *IDP_ui_data_ensure(IDProperty *prop) +static IDPropertyUIData *ui_data_alloc(const eIDPropertyUIDataType type) { - if (prop->ui_data != nullptr) { - return prop->ui_data; - } - - switch (IDP_ui_data_type(prop)) { + switch (type) { case IDP_UI_DATA_TYPE_STRING: { - prop->ui_data = static_cast( + return static_cast( MEM_callocN(sizeof(IDPropertyUIDataString), __func__)); - break; } case IDP_UI_DATA_TYPE_ID: { IDPropertyUIDataID *ui_data = static_cast( MEM_callocN(sizeof(IDPropertyUIDataID), __func__)); - prop->ui_data = (IDPropertyUIData *)ui_data; - break; + return &ui_data->base; } case IDP_UI_DATA_TYPE_INT: { IDPropertyUIDataInt *ui_data = static_cast( @@ -1636,14 +1635,12 @@ IDPropertyUIData *IDP_ui_data_ensure(IDProperty *prop) ui_data->soft_min = INT_MIN; ui_data->soft_max = INT_MAX; ui_data->step = 1; - prop->ui_data = (IDPropertyUIData *)ui_data; - break; + return &ui_data->base; } case IDP_UI_DATA_TYPE_BOOLEAN: { IDPropertyUIDataBool *ui_data = static_cast( MEM_callocN(sizeof(IDPropertyUIDataBool), __func__)); - prop->ui_data = (IDPropertyUIData *)ui_data; - break; + return &ui_data->base; } case IDP_UI_DATA_TYPE_FLOAT: { IDPropertyUIDataFloat *ui_data = static_cast( @@ -1654,8 +1651,7 @@ IDPropertyUIData *IDP_ui_data_ensure(IDProperty *prop) ui_data->soft_max = FLT_MAX; ui_data->step = 1.0f; ui_data->precision = 3; - prop->ui_data = (IDPropertyUIData *)ui_data; - break; + return &ui_data->base; } case IDP_UI_DATA_TYPE_UNSUPPORTED: { /* UI data not supported for remaining types, this shouldn't be called in those cases. */ @@ -1663,8 +1659,222 @@ IDPropertyUIData *IDP_ui_data_ensure(IDProperty *prop) break; } } + return nullptr; +} +IDPropertyUIData *IDP_ui_data_ensure(IDProperty *prop) +{ + if (prop->ui_data != nullptr) { + return prop->ui_data; + } + prop->ui_data = ui_data_alloc(IDP_ui_data_type(prop)); return prop->ui_data; } +static IDPropertyUIData *convert_base_ui_data(IDPropertyUIData *src, + const eIDPropertyUIDataType dst_type) +{ + IDPropertyUIData *dst = ui_data_alloc(dst_type); + *dst = *src; + src->description = nullptr; + return dst; +} + +IDPropertyUIData *IDP_TryConvertUIData(IDPropertyUIData *src, + const eIDPropertyUIDataType src_type, + const eIDPropertyUIDataType dst_type) +{ + switch (src_type) { + case IDP_UI_DATA_TYPE_STRING: { + switch (dst_type) { + case IDP_UI_DATA_TYPE_STRING: + return src; + case IDP_UI_DATA_TYPE_INT: + case IDP_UI_DATA_TYPE_BOOLEAN: + case IDP_UI_DATA_TYPE_FLOAT: + case IDP_UI_DATA_TYPE_ID: { + IDPropertyUIData *dst = convert_base_ui_data(src, dst_type); + ui_data_free(src, src_type); + return dst; + } + case IDP_UI_DATA_TYPE_UNSUPPORTED: + break; + } + break; + } + case IDP_UI_DATA_TYPE_ID: { + switch (dst_type) { + case IDP_UI_DATA_TYPE_ID: + return src; + case IDP_UI_DATA_TYPE_STRING: + case IDP_UI_DATA_TYPE_INT: + case IDP_UI_DATA_TYPE_BOOLEAN: + case IDP_UI_DATA_TYPE_FLOAT: { + IDPropertyUIData *dst = convert_base_ui_data(src, dst_type); + ui_data_free(src, src_type); + return dst; + } + case IDP_UI_DATA_TYPE_UNSUPPORTED: + break; + } + break; + } + case IDP_UI_DATA_TYPE_INT: { + IDPropertyUIDataInt *src_int = reinterpret_cast(src); + switch (dst_type) { + case IDP_UI_DATA_TYPE_INT: + return src; + case IDP_UI_DATA_TYPE_ID: + case IDP_UI_DATA_TYPE_STRING: { + IDPropertyUIData *dst = convert_base_ui_data(src, dst_type); + ui_data_free(src, src_type); + return dst; + } + case IDP_UI_DATA_TYPE_BOOLEAN: { + IDPropertyUIDataBool *dst = reinterpret_cast( + convert_base_ui_data(src, dst_type)); + dst->default_value = src_int->default_value != 0; + if (src_int->default_array) { + dst->default_array = static_cast(MEM_malloc_arrayN( + size_t(src_int->default_array_len), sizeof(*dst->default_array), __func__)); + for (int i = 0; i < src_int->default_array_len; i++) { + dst->default_array[i] = src_int->default_array[i] != 0; + } + } + ui_data_free(src, src_type); + return &dst->base; + } + case IDP_UI_DATA_TYPE_FLOAT: { + IDPropertyUIDataFloat *dst = reinterpret_cast( + convert_base_ui_data(src, dst_type)); + dst->min = double(src_int->min); + dst->max = double(src_int->max); + dst->soft_min = double(src_int->soft_min); + dst->soft_max = double(src_int->soft_max); + dst->step = float(src_int->step); + dst->default_value = double(src_int->default_value); + if (src_int->default_array) { + dst->default_array = static_cast(MEM_malloc_arrayN( + size_t(src_int->default_array_len), sizeof(*dst->default_array), __func__)); + for (int i = 0; i < src_int->default_array_len; i++) { + dst->default_array[i] = double(src_int->default_array[i]); + } + } + ui_data_free(src, src_type); + return &dst->base; + } + case IDP_UI_DATA_TYPE_UNSUPPORTED: + break; + } + break; + } + case IDP_UI_DATA_TYPE_BOOLEAN: { + IDPropertyUIDataBool *src_bool = reinterpret_cast(src); + switch (dst_type) { + case IDP_UI_DATA_TYPE_BOOLEAN: + return src; + case IDP_UI_DATA_TYPE_ID: + case IDP_UI_DATA_TYPE_STRING: { + IDPropertyUIData *dst = convert_base_ui_data(src, dst_type); + ui_data_free(src, src_type); + return dst; + } + case IDP_UI_DATA_TYPE_INT: { + IDPropertyUIDataInt *dst = reinterpret_cast( + convert_base_ui_data(src, dst_type)); + dst->min = 0; + dst->max = 1; + dst->soft_min = 0; + dst->soft_max = 1; + dst->step = 1; + dst->default_value = int(src_bool->default_value); + if (src_bool->default_array) { + dst->default_array = static_cast(MEM_malloc_arrayN( + size_t(src_bool->default_array_len), sizeof(*dst->default_array), __func__)); + for (int i = 0; i < src_bool->default_array_len; i++) { + dst->default_array[i] = int(src_bool->default_array[i]); + } + } + ui_data_free(src, src_type); + return &dst->base; + } + case IDP_UI_DATA_TYPE_FLOAT: { + IDPropertyUIDataFloat *dst = reinterpret_cast( + convert_base_ui_data(src, dst_type)); + dst->min = 0.0; + dst->max = 1.0; + dst->soft_min = 0.0; + dst->soft_max = 1.0; + dst->step = 1.0; + if (src_bool->default_array) { + dst->default_array = static_cast(MEM_malloc_arrayN( + size_t(src_bool->default_array_len), sizeof(*dst->default_array), __func__)); + for (int i = 0; i < src_bool->default_array_len; i++) { + dst->default_array[i] = src_bool->default_array[i] == 0 ? 0.0 : 1.0; + } + } + ui_data_free(src, src_type); + return &dst->base; + } + case IDP_UI_DATA_TYPE_UNSUPPORTED: + break; + } + break; + } + case IDP_UI_DATA_TYPE_FLOAT: { + IDPropertyUIDataFloat *src_float = reinterpret_cast(src); + switch (dst_type) { + case IDP_UI_DATA_TYPE_FLOAT: + return src; + case IDP_UI_DATA_TYPE_ID: + case IDP_UI_DATA_TYPE_STRING: + return convert_base_ui_data(src, dst_type); + case IDP_UI_DATA_TYPE_INT: { + auto clamp_double_to_int = [](const double value) { + return int(std::clamp(value, INT_MIN, INT_MAX)); + }; + IDPropertyUIDataInt *dst = reinterpret_cast( + convert_base_ui_data(src, dst_type)); + dst->min = clamp_double_to_int(src_float->min); + dst->max = clamp_double_to_int(src_float->max); + dst->soft_min = clamp_double_to_int(src_float->soft_min); + dst->soft_max = clamp_double_to_int(src_float->soft_max); + dst->step = clamp_double_to_int(src_float->step); + dst->default_value = clamp_double_to_int(src_float->default_value); + if (src_float->default_array) { + dst->default_array = static_cast(MEM_malloc_arrayN( + size_t(src_float->default_array_len), sizeof(*dst->default_array), __func__)); + for (int i = 0; i < src_float->default_array_len; i++) { + dst->default_array[i] = clamp_double_to_int(src_float->default_array[i]); + } + } + ui_data_free(src, src_type); + return &dst->base; + } + case IDP_UI_DATA_TYPE_BOOLEAN: { + IDPropertyUIDataBool *dst = reinterpret_cast( + convert_base_ui_data(src, dst_type)); + dst->default_value = src_float->default_value > 0.0f; + if (src_float->default_array) { + dst->default_array = static_cast(MEM_malloc_arrayN( + size_t(src_float->default_array_len), sizeof(*dst->default_array), __func__)); + for (int i = 0; i < src_float->default_array_len; i++) { + dst->default_array[i] = src_float->default_array[i] > 0.0f; + } + } + ui_data_free(src, src_type); + return &dst->base; + } + case IDP_UI_DATA_TYPE_UNSUPPORTED: + break; + } + break; + } + case IDP_UI_DATA_TYPE_UNSUPPORTED: + break; + } + ui_data_free(src, src_type); + return nullptr; +} + /** \} */ diff --git a/source/blender/python/generic/idprop_py_api.cc b/source/blender/python/generic/idprop_py_api.cc index 85d98cecbd0..669de2e5d1c 100644 --- a/source/blender/python/generic/idprop_py_api.cc +++ b/source/blender/python/generic/idprop_py_api.cc @@ -742,6 +742,16 @@ bool BPy_IDProperty_Map_ValidateAndCreate(PyObject *name_obj, IDProperty *group, /* avoid freeing when types match in case they are referenced by the UI, see: #37073 * obviously this isn't a complete solution, but helps for common cases. */ prop_exist = IDP_GetPropertyFromGroup(group, prop->name); + + if (prop_exist && prop_exist->ui_data) { + /* Take ownership of the existing property's UI data. */ + const eIDPropertyUIDataType src_type = IDP_ui_data_type(prop_exist); + IDPropertyUIData *ui_data = prop_exist->ui_data; + prop_exist->ui_data = nullptr; + + prop->ui_data = IDP_TryConvertUIData(ui_data, src_type, IDP_ui_data_type(prop)); + } + if ((prop_exist != nullptr) && (prop_exist->type == prop->type) && (prop_exist->subtype == prop->subtype)) { @@ -750,12 +760,8 @@ bool BPy_IDProperty_Map_ValidateAndCreate(PyObject *name_obj, IDProperty *group, prop->next = prop_exist->next; prop->flag = prop_exist->flag; - /* Don't free and reset the existing property's UI data, since this only assigns a value. */ - IDPropertyUIData *ui_data = prop_exist->ui_data; - prop_exist->ui_data = nullptr; IDP_FreePropertyContent(prop_exist); *prop_exist = *prop; - prop_exist->ui_data = ui_data; MEM_freeN(prop); } else {