Fix #91256, #112646: IDProperty UI data lost when changing type #106161

Merged
Hans Goudey merged 1 commits from HooglyBoogly/blender:fix-idprop-set-ui-data-reset into blender-v4.1-release 2024-03-12 15:49:33 +01:00
4 changed files with 251 additions and 32 deletions

View File

@ -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:

View File

@ -367,6 +367,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
}

View File

@ -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<IDPropertyUIData *>(
return static_cast<IDPropertyUIData *>(
MEM_callocN(sizeof(IDPropertyUIDataString), __func__));
break;
}
case IDP_UI_DATA_TYPE_ID: {
IDPropertyUIDataID *ui_data = static_cast<IDPropertyUIDataID *>(
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<IDPropertyUIDataInt *>(
@ -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<IDPropertyUIDataBool *>(
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<IDPropertyUIDataFloat *>(
@ -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<IDPropertyUIDataInt *>(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<IDPropertyUIDataBool *>(
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<int8_t *>(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<IDPropertyUIDataFloat *>(
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<double *>(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<IDPropertyUIDataBool *>(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<IDPropertyUIDataInt *>(
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<int *>(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<IDPropertyUIDataFloat *>(
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<double *>(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<IDPropertyUIDataFloat *>(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<double>(value, INT_MIN, INT_MAX));
};
IDPropertyUIDataInt *dst = reinterpret_cast<IDPropertyUIDataInt *>(
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<int *>(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<IDPropertyUIDataBool *>(
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<int8_t *>(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;
}
/** \} */

View File

@ -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 {