diff --git a/release/scripts/modules/rna_prop_ui.py b/release/scripts/modules/rna_prop_ui.py index 5f4a6b8cf38..f6c01bde9cd 100644 --- a/release/scripts/modules/rna_prop_ui.py +++ b/release/scripts/modules/rna_prop_ui.py @@ -81,25 +81,26 @@ def rna_idprop_ui_create( ): """Create and initialize a custom property with limits, defaults and other settings.""" + # Assign the value + item[prop] = default + + rna_idprop_ui_prop_update(item, prop) + ui_data = item.id_properties_ui(prop) proptype, _ = rna_idprop_value_item_type(default) - # Sanitize limits if proptype is bool: - min = soft_min = False - max = soft_max = True + ui_data = item.id_properties_ui(prop) + ui_data.update( + description=description, + default=default, + ) + return if soft_min is None: soft_min = min if soft_max is None: soft_max = max - # Assign the value - item[prop] = default - - rna_idprop_ui_prop_update(item, prop) - - # Update the UI settings. - ui_data = item.id_properties_ui(prop) ui_data.update( subtype=subtype, min=min, diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py index 39789cba0c4..fe43051281d 100644 --- a/release/scripts/startup/bl_operators/wm.py +++ b/release/scripts/startup/bl_operators/wm.py @@ -14,6 +14,7 @@ from bpy.props import ( FloatProperty, IntProperty, StringProperty, + BoolVectorProperty, IntVectorProperty, FloatVectorProperty, ) @@ -1325,6 +1326,8 @@ rna_custom_property_type_items = ( ('FLOAT_ARRAY', "Float Array", "An array of floating-point values"), ('INT', "Integer", "A single integer"), ('INT_ARRAY', "Integer Array", "An array of integers"), + ('BOOL', "Boolean", "A true or false value"), + ('BOOL_ARRAY', "Boolean Array", "An array of true or false values"), ('STRING', "String", "A string value"), ('PYTHON', "Python", "Edit a python value directly, for unsupported property types"), ) @@ -1410,6 +1413,14 @@ class WM_OT_properties_edit(Operator): default=1, ) + # Boolean properties. + + # This property stores values for both array and non-array properties. + default_bool: BoolVectorProperty( + name="Default Value", + size=32, + ) + # Float properties. # This property stores values for both array and non-array properties. @@ -1520,6 +1531,10 @@ class WM_OT_properties_edit(Operator): if is_array: return 'FLOAT_ARRAY' return 'FLOAT' + elif prop_type == bool: + if is_array: + return 'BOOL_ARRAY' + return 'BOOL' elif prop_type == str: if is_array: return 'PYTHON' @@ -1571,8 +1586,10 @@ class WM_OT_properties_edit(Operator): self.default_int = self._convert_new_value_array(rna_data["default"], int, 32) elif self.property_type == 'STRING': self.default_string = rna_data["default"] + elif self.property_type in {'BOOL', 'BOOL_ARRAY'}: + self.default_int = self._convert_new_value_array(rna_data["default"], bool, 32) - if self.property_type in {'FLOAT_ARRAY', 'INT_ARRAY'}: + if self.property_type in {'FLOAT_ARRAY', 'INT_ARRAY', 'BOOL_ARRAY'}: self.array_length = len(item[name]) # The dictionary does not contain the description if it was empty. @@ -1591,16 +1608,26 @@ class WM_OT_properties_edit(Operator): if prop_type_new == 'FLOAT': return self._convert_new_value_single(item[name_old], float) + if prop_type_new == 'BOOL': + return self._convert_new_value_single(item[name_old], bool) + if prop_type_new == 'INT_ARRAY': prop_type_old = self.get_property_type(item, name_old) - if prop_type_old in {'INT', 'FLOAT', 'INT_ARRAY', 'FLOAT_ARRAY'}: + if prop_type_old in {'INT', 'FLOAT', 'INT_ARRAY', 'FLOAT_ARRAY', 'BOOL_ARRAY'}: return self._convert_new_value_array(item[name_old], int, self.array_length) if prop_type_new == 'FLOAT_ARRAY': prop_type_old = self.get_property_type(item, name_old) - if prop_type_old in {'INT', 'FLOAT', 'FLOAT_ARRAY', 'INT_ARRAY'}: + if prop_type_old in {'INT', 'FLOAT', 'FLOAT_ARRAY', 'INT_ARRAY', 'BOOL_ARRAY'}: return self._convert_new_value_array(item[name_old], float, self.array_length) + if prop_type_new == 'BOOL_ARRAY': + prop_type_old = self.get_property_type(item, name_old) + if prop_type_old in {'INT', 'FLOAT', 'FLOAT_ARRAY', 'INT_ARRAY'}: + return self._convert_new_value_array(item[name_old], bool, self.array_length) + else: + return [False] * self.array_length + if prop_type_new == 'STRING': return self.convert_custom_property_to_string(item, name_old) @@ -1622,6 +1649,9 @@ class WM_OT_properties_edit(Operator): self.soft_min_float = float(self.soft_min_int) self.soft_max_float = float(self.soft_max_int) self.default_float = self._convert_new_value_array(self.default_int, float, 32) + elif prop_type_new in {'BOOL', 'BOOL_ARRAY'} and prop_type_old in {'INT', 'INT_ARRAY'}: + self.default_bool = self._convert_new_value_array(self.default_int, bool, 32) + # Don't convert between string and float/int defaults here, it's not expected like the other conversions. # Fill the property's UI data with the values chosen in the operator. @@ -1637,6 +1667,12 @@ class WM_OT_properties_edit(Operator): default=self.default_int[0] if prop_type_new == 'INT' else self.default_int[:self.array_length], description=self.description, ) + if prop_type_new in {'BOOL', 'BOOL_ARRAY'}: + ui_data = item.id_properties_ui(name) + ui_data.update( + default=self.default_bool[0] if prop_type_new == 'BOOL' else self.default_bool[:self.array_length], + description=self.description, + ) elif prop_type_new in {'FLOAT', 'FLOAT_ARRAY'}: ui_data = item.id_properties_ui(name) ui_data.update( @@ -1879,6 +1915,15 @@ class WM_OT_properties_edit(Operator): col.prop(self, "soft_max_int", text="Max") layout.prop(self, "step_int") + elif self.property_type in {'BOOL', 'BOOL_ARRAY'}: + if self.property_type == 'BOOL_ARRAY': + layout.prop(self, "array_length") + col = layout.column(align=True) + col.prop(self, "default_bool", index=0, text="Default") + for i in range(1, self.array_length): + col.prop(self, "default_bool", index=i, text=" ") + else: + layout.prop(self, "default_bool", index=0) elif self.property_type == 'STRING': layout.prop(self, "default_string") diff --git a/source/blender/blenkernel/BKE_idprop.h b/source/blender/blenkernel/BKE_idprop.h index 9ac4b4e4619..84412fd139f 100644 --- a/source/blender/blenkernel/BKE_idprop.h +++ b/source/blender/blenkernel/BKE_idprop.h @@ -243,6 +243,7 @@ void IDP_ClearProperty(struct IDProperty *prop); void IDP_Reset(struct IDProperty *prop, const struct IDProperty *reference); #define IDP_Int(prop) ((prop)->data.val) +#define IDP_Bool(prop) ((prop)->data.val) #define IDP_Array(prop) ((prop)->data.pointer) /* C11 const correctness for casts */ #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) @@ -334,6 +335,8 @@ typedef enum eIDPropertyUIDataType { IDP_UI_DATA_TYPE_STRING = 2, /** IDP_ID. */ IDP_UI_DATA_TYPE_ID = 3, + /** IDP_BOOLEAN or IDP_ARRAY with subtype IDP_BOOLEAN. */ + IDP_UI_DATA_TYPE_BOOLEAN = 4, } eIDPropertyUIDataType; bool IDP_ui_data_supported(const struct IDProperty *prop); diff --git a/source/blender/blenkernel/intern/idprop.c b/source/blender/blenkernel/intern/idprop.c index 00e7425c8af..b8f0db0699d 100644 --- a/source/blender/blenkernel/intern/idprop.c +++ b/source/blender/blenkernel/intern/idprop.c @@ -50,6 +50,8 @@ static size_t idp_size_table[] = { sizeof(ListBase), /* Group type. */ sizeof(void *), sizeof(double), + 0, + sizeof(int8_t), /* Boolean type. */ }; /* -------------------------------------------------------------------- */ @@ -272,6 +274,12 @@ IDPropertyUIData *IDP_ui_data_copy(const IDProperty *prop) dst->default_array = MEM_dupallocN(src->default_array); break; } + case IDP_UI_DATA_TYPE_BOOLEAN: { + const IDPropertyUIDataBool *src = (const IDPropertyUIDataBool *)prop->ui_data; + IDPropertyUIDataBool *dst = (IDPropertyUIDataBool *)dst_ui_data; + dst->default_array = MEM_dupallocN(src->default_array); + break; + } case IDP_UI_DATA_TYPE_FLOAT: { const IDPropertyUIDataFloat *src = (const IDPropertyUIDataFloat *)prop->ui_data; IDPropertyUIDataFloat *dst = (IDPropertyUIDataFloat *)dst_ui_data; @@ -497,6 +505,7 @@ void IDP_SyncGroupValues(IDProperty *dest, const IDProperty *src) case IDP_INT: case IDP_FLOAT: case IDP_DOUBLE: + case IDP_BOOLEAN: other->data = prop->data; break; case IDP_GROUP: @@ -708,6 +717,8 @@ int IDP_coerce_to_int_or_zero(const IDProperty *prop) return (int)IDP_Double(prop); case IDP_FLOAT: return (int)IDP_Float(prop); + case IDP_BOOLEAN: + return (int)IDP_Bool(prop); default: return 0; } @@ -722,6 +733,8 @@ double IDP_coerce_to_double_or_zero(const IDProperty *prop) return (double)IDP_Float(prop); case IDP_INT: return (double)IDP_Int(prop); + case IDP_BOOLEAN: + return (double)IDP_Bool(prop); default: return 0.0; } @@ -736,6 +749,8 @@ float IDP_coerce_to_float_or_zero(const IDProperty *prop) return (float)IDP_Double(prop); case IDP_INT: return (float)IDP_Int(prop); + case IDP_BOOLEAN: + return (float)IDP_Bool(prop); default: return 0.0f; } @@ -826,6 +841,8 @@ bool IDP_EqualsProperties_ex(IDProperty *prop1, IDProperty *prop2, const bool is return (IDP_Float(prop1) == IDP_Float(prop2)); case IDP_DOUBLE: return (IDP_Double(prop1) == IDP_Double(prop2)); + case IDP_BOOLEAN: + return (IDP_Bool(prop1) == IDP_Bool(prop2)); case IDP_STRING: { return ((prop1->len == prop2->len) && STREQLEN(IDP_String(prop1), IDP_String(prop2), (size_t)prop1->len)); @@ -899,9 +916,12 @@ IDProperty *IDP_New(const char type, const IDPropertyTemplate *val, const char * prop = MEM_callocN(sizeof(IDProperty), "IDProperty double"); *(double *)&prop->data.val = val->d; break; + case IDP_BOOLEAN: + prop = MEM_callocN(sizeof(IDProperty), "IDProperty boolean"); + prop->data.val = (bool)val->i; + break; case IDP_ARRAY: { - /* for now, we only support float and int and double arrays */ - if (ELEM(val->array.type, IDP_FLOAT, IDP_INT, IDP_DOUBLE, IDP_GROUP)) { + if (ELEM(val->array.type, IDP_FLOAT, IDP_INT, IDP_DOUBLE, IDP_GROUP, IDP_BOOLEAN)) { prop = MEM_callocN(sizeof(IDProperty), "IDProperty array"); prop->subtype = val->array.type; if (val->array.len) { @@ -1004,6 +1024,14 @@ void IDP_ui_data_free_unique_contents(IDPropertyUIData *ui_data, } break; } + case IDP_UI_DATA_TYPE_BOOLEAN: { + const IDPropertyUIDataBool *other_bool = (const IDPropertyUIDataBool *)other; + IDPropertyUIDataBool *ui_data_bool = (IDPropertyUIDataBool *)ui_data; + if (ui_data_bool->default_array != other_bool->default_array) { + MEM_SAFE_FREE(ui_data_bool->default_array); + } + break; + } case IDP_UI_DATA_TYPE_FLOAT: { const IDPropertyUIDataFloat *other_float = (const IDPropertyUIDataFloat *)other; IDPropertyUIDataFloat *ui_data_float = (IDPropertyUIDataFloat *)ui_data; @@ -1034,6 +1062,11 @@ void IDP_ui_data_free(IDProperty *prop) MEM_SAFE_FREE(ui_data_int->default_array); break; } + case IDP_UI_DATA_TYPE_BOOLEAN: { + IDPropertyUIDataBool *ui_data_bool = (IDPropertyUIDataBool *)prop->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; MEM_SAFE_FREE(ui_data_float->default_array); @@ -1173,6 +1206,16 @@ static void write_ui_data(const IDProperty *prop, BlendWriter *writer) BLO_write_struct(writer, IDPropertyUIDataInt, ui_data); break; } + case IDP_UI_DATA_TYPE_BOOLEAN: { + IDPropertyUIDataBool *ui_data_bool = (IDPropertyUIDataBool *)ui_data; + if (prop->type == IDP_ARRAY) { + BLO_write_int8_array(writer, + (uint)ui_data_bool->default_array_len, + (const int8_t *)ui_data_bool->default_array); + } + BLO_write_struct(writer, IDPropertyUIDataBool, ui_data); + break; + } case IDP_UI_DATA_TYPE_FLOAT: { IDPropertyUIDataFloat *ui_data_float = (IDPropertyUIDataFloat *)ui_data; if (prop->type == IDP_ARRAY) { @@ -1285,6 +1328,14 @@ static void read_ui_data(IDProperty *prop, BlendDataReader *reader) } break; } + case IDP_UI_DATA_TYPE_BOOLEAN: { + IDPropertyUIDataBool *ui_data_bool = (IDPropertyUIDataBool *)prop->ui_data; + if (prop->type == IDP_ARRAY) { + BLO_read_int8_array( + reader, ui_data_bool->default_array_len, (int8_t **)&ui_data_bool->default_array); + } + break; + } case IDP_UI_DATA_TYPE_FLOAT: { IDPropertyUIDataFloat *ui_data_float = (IDPropertyUIDataFloat *)prop->ui_data; if (prop->type == IDP_ARRAY) { @@ -1336,10 +1387,13 @@ static void IDP_DirectLinkArray(IDProperty *prop, BlendDataReader *reader) else if (prop->subtype == IDP_DOUBLE) { BLO_read_double_array(reader, prop->len, (double **)&prop->data.pointer); } - else { + else if (ELEM(prop->subtype, IDP_INT, IDP_FLOAT)) { /* also used for floats */ BLO_read_int32_array(reader, prop->len, (int **)&prop->data.pointer); } + else if (prop->subtype == IDP_BOOLEAN) { + BLO_read_int8_array(reader, prop->len, (int8_t **)&prop->data.pointer); + } } static void IDP_DirectLinkString(IDProperty *prop, BlendDataReader *reader) @@ -1392,6 +1446,7 @@ static void IDP_DirectLinkProperty(IDProperty *prop, BlendDataReader *reader) break; case IDP_INT: case IDP_FLOAT: + case IDP_BOOLEAN: case IDP_ID: break; /* Nothing special to do here. */ default: @@ -1502,6 +1557,9 @@ eIDPropertyUIDataType IDP_ui_data_type(const IDProperty *prop) (prop->type == IDP_ARRAY && ELEM(prop->subtype, IDP_FLOAT, IDP_DOUBLE))) { return IDP_UI_DATA_TYPE_FLOAT; } + if (prop->type == IDP_BOOLEAN || (prop->type == IDP_ARRAY && prop->subtype == IDP_BOOLEAN)) { + return IDP_UI_DATA_TYPE_BOOLEAN; + } return IDP_UI_DATA_TYPE_UNSUPPORTED; } @@ -1536,6 +1594,11 @@ IDPropertyUIData *IDP_ui_data_ensure(IDProperty *prop) prop->ui_data = (IDPropertyUIData *)ui_data; break; } + case IDP_UI_DATA_TYPE_BOOLEAN: { + IDPropertyUIDataBool *ui_data = MEM_callocN(sizeof(IDPropertyUIDataBool), __func__); + prop->ui_data = (IDPropertyUIData *)ui_data; + break; + } case IDP_UI_DATA_TYPE_FLOAT: { IDPropertyUIDataFloat *ui_data = MEM_callocN(sizeof(IDPropertyUIDataFloat), __func__); ui_data->min = -FLT_MAX; diff --git a/source/blender/blenkernel/intern/idprop_utils.c b/source/blender/blenkernel/intern/idprop_utils.c index 5b7484ab422..0ade0aa654e 100644 --- a/source/blender/blenkernel/intern/idprop_utils.c +++ b/source/blender/blenkernel/intern/idprop_utils.c @@ -111,6 +111,10 @@ static void idp_repr_fn_recursive(struct ReprState *state, const IDProperty *pro STR_APPEND_FMT("%g", IDP_Double(prop)); break; } + case IDP_BOOLEAN: { + STR_APPEND_FMT("%s", IDP_Bool(prop) ? "True" : "False"); + break; + } case IDP_ARRAY: { STR_APPEND_STR("["); switch (prop->subtype) { @@ -138,6 +142,14 @@ static void idp_repr_fn_recursive(struct ReprState *state, const IDProperty *pro STR_APPEND_FMT("%g", *v); } break; + case IDP_BOOLEAN: + for (const double *v = prop->data.pointer, *v_end = v + prop->len; v != v_end; v++) { + if (v != prop->data.pointer) { + STR_APPEND_STR(", "); + } + STR_APPEND_FMT("%s", IDP_Bool(prop) ? "True" : "False"); + } + break; } STR_APPEND_STR("]"); break; diff --git a/source/blender/blenloader/BLO_read_write.h b/source/blender/blenloader/BLO_read_write.h index 6f39670a226..56b0cc81598 100644 --- a/source/blender/blenloader/BLO_read_write.h +++ b/source/blender/blenloader/BLO_read_write.h @@ -162,6 +162,7 @@ void blo_write_id_struct(BlendWriter *writer, * Write raw data. */ void BLO_write_raw(BlendWriter *writer, size_t size_in_bytes, const void *data_ptr); +void BLO_write_int8_array(BlendWriter *writer, uint num, const int8_t *data_ptr); void BLO_write_int32_array(BlendWriter *writer, uint num, const int32_t *data_ptr); void BLO_write_uint32_array(BlendWriter *writer, uint num, const uint32_t *data_ptr); void BLO_write_float_array(BlendWriter *writer, uint num, const float *data_ptr); @@ -228,6 +229,7 @@ void BLO_read_list(BlendDataReader *reader, struct ListBase *list); /* Update data pointers and correct byte-order if necessary. */ +void BLO_read_int8_array(BlendDataReader *reader, int array_size, int8_t **ptr_p); void BLO_read_int32_array(BlendDataReader *reader, int array_size, int32_t **ptr_p); void BLO_read_uint32_array(BlendDataReader *reader, int array_size, uint32_t **ptr_p); void BLO_read_float_array(BlendDataReader *reader, int array_size, float **ptr_p); diff --git a/source/blender/blenloader/intern/readfile.cc b/source/blender/blenloader/intern/readfile.cc index 91a72fb50c4..f4a5b6dd2fc 100644 --- a/source/blender/blenloader/intern/readfile.cc +++ b/source/blender/blenloader/intern/readfile.cc @@ -4976,6 +4976,11 @@ void BLO_read_int32_array(BlendDataReader *reader, int array_size, int32_t **ptr } } +void BLO_read_int8_array(BlendDataReader *reader, int /*array_size*/, int8_t **ptr_p) +{ + BLO_read_data_address(reader, ptr_p); +} + void BLO_read_uint32_array(BlendDataReader *reader, int array_size, uint32_t **ptr_p) { BLO_read_data_address(reader, ptr_p); diff --git a/source/blender/blenloader/intern/versioning_300.cc b/source/blender/blenloader/intern/versioning_300.cc index 0a68a526d0f..ff7c7f4480a 100644 --- a/source/blender/blenloader/intern/versioning_300.cc +++ b/source/blender/blenloader/intern/versioning_300.cc @@ -248,6 +248,7 @@ static void version_idproperty_ui_data(IDProperty *idprop_group) case IDP_UI_DATA_TYPE_FLOAT: version_idproperty_move_data_float((IDPropertyUIDataFloat *)ui_data, prop_ui_data); break; + case IDP_UI_DATA_TYPE_BOOLEAN: case IDP_UI_DATA_TYPE_UNSUPPORTED: BLI_assert_unreachable(); break; diff --git a/source/blender/blenloader/intern/writefile.cc b/source/blender/blenloader/intern/writefile.cc index 0e3c1645fb4..57ac9d650d9 100644 --- a/source/blender/blenloader/intern/writefile.cc +++ b/source/blender/blenloader/intern/writefile.cc @@ -1639,6 +1639,11 @@ int BLO_get_struct_id_by_name(BlendWriter *writer, const char *struct_name) return struct_id; } +void BLO_write_int8_array(BlendWriter *writer, uint num, const int8_t *data_ptr) +{ + BLO_write_raw(writer, sizeof(int8_t) * size_t(num), data_ptr); +} + void BLO_write_int32_array(BlendWriter *writer, uint num, const int32_t *data_ptr) { BLO_write_raw(writer, sizeof(int32_t) * size_t(num), data_ptr); diff --git a/source/blender/io/alembic/exporter/abc_custom_props.cc b/source/blender/io/alembic/exporter/abc_custom_props.cc index c5cc4631e18..ad7df45dca0 100644 --- a/source/blender/io/alembic/exporter/abc_custom_props.cc +++ b/source/blender/io/alembic/exporter/abc_custom_props.cc @@ -70,6 +70,9 @@ void CustomPropertiesExporter::write(const IDProperty *id_property) set_scalar_property(id_property->name, IDP_Double(id_property)); break; + case IDP_BOOLEAN: + set_scalar_property(id_property->name, IDP_Bool(id_property)); + break; case IDP_ARRAY: write_array(id_property); break; @@ -100,6 +103,11 @@ void CustomPropertiesExporter::write_array(const IDProperty *id_property) set_array_property(id_property->name, array, id_property->len); break; } + case IDP_BOOLEAN: { + const int8_t *array = static_cast(IDP_Array(id_property)); + set_array_property(id_property->name, array, id_property->len); + break; + } } } @@ -165,7 +173,7 @@ void CustomPropertiesExporter::write_idparray_of_numbers(const IDProperty *idp_a BLI_assert(idp_rows[0].type == IDP_ARRAY); const int subtype = idp_rows[0].subtype; - if (!ELEM(subtype, IDP_INT, IDP_FLOAT, IDP_DOUBLE)) { + if (!ELEM(subtype, IDP_INT, IDP_FLOAT, IDP_DOUBLE, IDP_BOOLEAN)) { /* Non-numerical types are not supported. */ return; } @@ -181,6 +189,9 @@ void CustomPropertiesExporter::write_idparray_of_numbers(const IDProperty *idp_a case IDP_DOUBLE: write_idparray_flattened_typed(idp_array); break; + case IDP_BOOLEAN: + write_idparray_flattened_typed(idp_array); + break; } } @@ -192,7 +203,7 @@ void CustomPropertiesExporter::write_idparray_flattened_typed(const IDProperty * const IDProperty *idp_rows = (IDProperty *)IDP_Array(idp_array); BLI_assert(idp_rows[0].type == IDP_ARRAY); - BLI_assert(ELEM(idp_rows[0].subtype, IDP_INT, IDP_FLOAT, IDP_DOUBLE)); + BLI_assert(ELEM(idp_rows[0].subtype, IDP_INT, IDP_FLOAT, IDP_DOUBLE, IDP_BOOLEAN)); const uint64_t num_rows = idp_array->len; std::vector matrix_values; diff --git a/source/blender/io/collada/collada_utils.cpp b/source/blender/io/collada/collada_utils.cpp index 8a093e0c5e1..ae6bb9a541d 100644 --- a/source/blender/io/collada/collada_utils.cpp +++ b/source/blender/io/collada/collada_utils.cpp @@ -718,6 +718,9 @@ float bc_get_property(Bone *bone, std::string key, float def) case IDP_DOUBLE: result = float(IDP_Double(property)); break; + case IDP_BOOLEAN: + result = (float)(IDP_Bool(property)); + break; default: result = def; } diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index 8b3a8793883..4e56fbe3dd0 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -68,6 +68,16 @@ typedef struct IDPropertyUIDataInt { int default_value; } IDPropertyUIDataInt; +/* IDP_UI_DATA_TYPE_BOOLEAN Use "int8_t" because DNA does not support "bool". */ +typedef struct IDPropertyUIDataBool { + IDPropertyUIData base; + int8_t *default_array; /* Only for array properties. */ + int default_array_len; + char _pad[3]; + + int8_t default_value; +} IDPropertyUIDataBool; + /* IDP_UI_DATA_TYPE_FLOAT */ typedef struct IDPropertyUIDataFloat { IDPropertyUIData base; @@ -142,8 +152,13 @@ typedef enum eIDPropertyType { IDP_ID = 7, IDP_DOUBLE = 8, IDP_IDPARRAY = 9, + /** + * True or false value, backed by an `int8_t` underlying type for arrays. Values are expected to + * be 0 or 1. + */ + IDP_BOOLEAN = 10, } eIDPropertyType; -#define IDP_NUMTYPES 10 +#define IDP_NUMTYPES 11 /** Used by some IDP utils, keep values in sync with type enum above. */ enum { @@ -155,6 +170,7 @@ enum { IDP_TYPE_FILTER_ID = 1 << 7, IDP_TYPE_FILTER_DOUBLE = 1 << 8, IDP_TYPE_FILTER_IDPARRAY = 1 << 9, + IDP_TYPE_FILTER_BOOLEAN = 1 << 10, }; /*->subtype */ diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index 5e9318b65dd..49c388a6cbf 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -1485,6 +1485,15 @@ static void rna_def_ID_properties(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_IDPROPERTY); RNA_def_property_array(prop, 1); + /* IDP_BOOLEAN */ + prop = RNA_def_property(srna, "bool", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_flag(prop, PROP_IDPROPERTY); + RNA_def_property_array(prop, 1); + + prop = RNA_def_property(srna, "bool_array", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_flag(prop, PROP_IDPROPERTY); + RNA_def_property_array(prop, 1); + /* IDP_GROUP */ prop = RNA_def_property(srna, "group", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_IDPROPERTY); diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index a1faab65089..e7187b2822b 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -369,6 +369,9 @@ static bool rna_idproperty_verify_valid(PointerRNA *ptr, PropertyRNA *prop, IDPr if (idprop->subtype == IDP_FLOAT && prop->type != PROP_FLOAT) { return false; } + if (idprop->subtype == IDP_BOOLEAN && prop->type != PROP_BOOLEAN) { + return false; + } if (idprop->subtype == IDP_INT && !ELEM(prop->type, PROP_BOOLEAN, PROP_INT, PROP_ENUM)) { return false; } @@ -379,6 +382,11 @@ static bool rna_idproperty_verify_valid(PointerRNA *ptr, PropertyRNA *prop, IDPr return false; } break; + case IDP_BOOLEAN: + if (prop->type != PROP_BOOLEAN) { + return false; + } + break; case IDP_FLOAT: case IDP_DOUBLE: if (prop->type != PROP_FLOAT) { @@ -414,6 +422,7 @@ static PropertyRNA *typemap[IDP_NUMTYPES] = { &rna_PropertyGroupItem_id, &rna_PropertyGroupItem_double, &rna_PropertyGroupItem_idp_array, + &rna_PropertyGroupItem_bool, }; static PropertyRNA *arraytypemap[IDP_NUMTYPES] = { @@ -426,6 +435,8 @@ static PropertyRNA *arraytypemap[IDP_NUMTYPES] = { &rna_PropertyGroupItem_collection, NULL, &rna_PropertyGroupItem_double_array, + NULL, + (PropertyRNA *)&rna_PropertyGroupItem_bool_array, }; void rna_property_rna_or_id_get(PropertyRNA *prop, @@ -2160,7 +2171,7 @@ bool RNA_property_boolean_get(PointerRNA *ptr, PropertyRNA *prop) BLI_assert(RNA_property_array_check(prop) == false); if ((idprop = rna_idproperty_check(&prop, ptr))) { - value = IDP_Int(idprop) != 0; + value = IDP_Bool(idprop); } else if (bprop->get) { value = bprop->get(ptr); @@ -2190,7 +2201,7 @@ void RNA_property_boolean_set(PointerRNA *ptr, PropertyRNA *prop, bool value) BLI_assert(ELEM(value, true, false)); if ((idprop = rna_idproperty_check(&prop, ptr))) { - IDP_Int(idprop) = (int)value; + IDP_Bool(idprop) = value; rna_idproperty_touch(idprop); } else if (bprop->set) { @@ -2207,7 +2218,7 @@ void RNA_property_boolean_set(PointerRNA *ptr, PropertyRNA *prop, bool value) group = RNA_struct_idprops(ptr, 1); if (group) { - IDP_AddToGroup(group, IDP_New(IDP_INT, &val, prop->identifier)); + IDP_AddToGroup(group, IDP_New(IDP_BOOLEAN, &val, prop->identifier)); } } } @@ -2251,12 +2262,20 @@ void RNA_property_boolean_get_array(PointerRNA *ptr, PropertyRNA *prop, bool *va if (prop->arraydimension == 0) { values[0] = RNA_property_boolean_get(ptr, prop); } - else { + else if (idprop->subtype == IDP_INT) { + /* Some boolean IDProperty arrays might be saved in files as an integer + * array property, since the boolean IDProperty type was added later. */ int *values_src = IDP_Array(idprop); for (uint i = 0; i < idprop->len; i++) { values[i] = (bool)values_src[i]; } } + else if (idprop->subtype == IDP_BOOLEAN) { + bool *values_src = IDP_Array(idprop); + for (int i = 0; i < idprop->len; i++) { + values[i] = values_src[i]; + } + } } else if (prop->arraydimension == 0) { values[0] = RNA_property_boolean_get(ptr, prop); @@ -2314,9 +2333,17 @@ void RNA_property_boolean_set_array(PointerRNA *ptr, PropertyRNA *prop, const bo IDP_Int(idprop) = values[0]; } else { - int *values_dst = IDP_Array(idprop); - for (uint i = 0; i < idprop->len; i++) { - values_dst[i] = (int)values[i]; + BLI_assert(idprop->type = IDP_ARRAY); + if (idprop->subtype == IDP_BOOLEAN) { + memcpy(IDP_Array(idprop), values, sizeof(int8_t) * idprop->len); + } + else if (idprop->subtype == IDP_INT) { + /* Support writing to integer and boolean IDProperties, since boolean + RNA properties used to be stored with integer IDProperties. */ + int *values_dst = IDP_Array(idprop); + for (uint i = 0; i < idprop->len; i++) { + values_dst[i] = (int)values[i]; + } } } rna_idproperty_touch(idprop); @@ -2335,15 +2362,15 @@ void RNA_property_boolean_set_array(PointerRNA *ptr, PropertyRNA *prop, const bo IDProperty *group; val.array.len = prop->totarraylength; - val.array.type = IDP_INT; + val.array.type = IDP_BOOLEAN; group = RNA_struct_idprops(ptr, 1); if (group) { idprop = IDP_New(IDP_ARRAY, &val, prop->identifier); IDP_AddToGroup(group, idprop); - int *values_dst = IDP_Array(idprop); + bool *values_dst = IDP_Array(idprop); for (uint i = 0; i < idprop->len; i++) { - values_dst[i] = (int)values[i]; + values_dst[i] = values[i]; } } } @@ -2378,12 +2405,23 @@ void RNA_property_boolean_set_index(PointerRNA *ptr, PropertyRNA *prop, int inde bool RNA_property_boolean_get_default(PointerRNA *UNUSED(ptr), PropertyRNA *prop) { + /* TODO: Make defaults work for IDProperties. */ BoolPropertyRNA *bprop = (BoolPropertyRNA *)rna_ensure_property(prop); BLI_assert(RNA_property_type(prop) == PROP_BOOLEAN); BLI_assert(RNA_property_array_check(prop) == false); BLI_assert(ELEM(bprop->defaultvalue, false, true)); + if (prop->magic != RNA_MAGIC) { + const IDProperty *idprop = (const IDProperty *)prop; + BLI_assert(idprop->type == IDP_BOOLEAN); + if (idprop->ui_data) { + const IDPropertyUIDataBool *ui_data = (const IDPropertyUIDataBool *)idprop->ui_data; + return ui_data->default_value; + } + return false; + } + return bprop->defaultvalue; } @@ -2394,7 +2432,26 @@ void RNA_property_boolean_get_default_array(PointerRNA *ptr, PropertyRNA *prop, BLI_assert(RNA_property_type(prop) == PROP_BOOLEAN); BLI_assert(RNA_property_array_check(prop) != false); - if (prop->arraydimension == 0) { + if (prop->magic != RNA_MAGIC) { + const IDProperty *idprop = (const IDProperty *)prop; + if (idprop->ui_data) { + BLI_assert(idprop->type == IDP_ARRAY); + BLI_assert(idprop->subtype == IDP_BOOLEAN); + const IDPropertyUIDataBool *ui_data = (const IDPropertyUIDataBool *)idprop->ui_data; + if (ui_data->default_array) { + rna_property_boolean_fill_default_array_values((bool *)ui_data->default_array, + ui_data->default_array_len, + ui_data->default_value, + idprop->len, + values); + } + else { + rna_property_boolean_fill_default_array_values( + NULL, 0, ui_data->default_value, idprop->len, values); + } + } + } + else if (prop->arraydimension == 0) { values[0] = bprop->defaultvalue; } else { diff --git a/source/blender/python/generic/idprop_py_api.c b/source/blender/python/generic/idprop_py_api.c index 9a6568355af..08cdbe10233 100644 --- a/source/blender/python/generic/idprop_py_api.c +++ b/source/blender/python/generic/idprop_py_api.c @@ -75,6 +75,11 @@ static PyObject *idprop_py_from_idp_double(const IDProperty *prop) return PyFloat_FromDouble(IDP_Double(prop)); } +static PyObject *idprop_py_from_idp_bool(const IDProperty *prop) +{ + return PyBool_FromLong(IDP_Bool(prop)); +} + static PyObject *idprop_py_from_idp_group(ID *id, IDProperty *prop, IDProperty *parent) { BPy_IDProperty *group = PyObject_New(BPy_IDProperty, &BPy_IDGroup_Type); @@ -155,6 +160,8 @@ PyObject *BPy_IDGroup_WrapData(ID *id, IDProperty *prop, IDProperty *parent) return idprop_py_from_idp_float(prop); case IDP_DOUBLE: return idprop_py_from_idp_double(prop); + case IDP_BOOLEAN: + return idprop_py_from_idp_bool(prop); case IDP_GROUP: return idprop_py_from_idp_group(id, prop, parent); case IDP_ARRAY: @@ -333,6 +340,12 @@ static char idp_sequence_type(PyObject *seq_fast) } type = IDP_DOUBLE; } + else if (PyBool_Check(item)) { + if (i != 0 && (type != IDP_BOOLEAN)) { + return -1; + } + type = IDP_BOOLEAN; + } else if (PyLong_Check(item)) { if (type == IDP_IDPARRAY) { /* mixed dict/int */ return -1; @@ -396,6 +409,13 @@ static IDProperty *idp_from_PyFloat(const char *name, PyObject *ob) return IDP_New(IDP_DOUBLE, &val, name); } +static IDProperty *idp_from_PyBool(const char *name, PyObject *ob) +{ + IDPropertyTemplate val = {0}; + val.i = PyC_Long_AsBool(ob); + return IDP_New(IDP_BOOLEAN, &val, name); +} + static IDProperty *idp_from_PyLong(const char *name, PyObject *ob) { IDPropertyTemplate val = {0}; @@ -466,6 +486,9 @@ static const char *idp_format_from_array_type(int type) if (type == IDP_DOUBLE) { return "d"; } + if (type == IDP_BOOLEAN) { + return "b"; + } return NULL; } @@ -549,6 +572,20 @@ static IDProperty *idp_from_PySequence_Fast(const char *name, PyObject *ob) } break; } + case IDP_BOOLEAN: { + prop = IDP_New(IDP_ARRAY, &val, name); + bool *prop_data = IDP_Array(prop); + for (i = 0; i < val.array.len; i++) { + item = ob_seq_fast_items[i]; + const int value = PyC_Long_AsBool(item); + if ((value == -1) && PyErr_Occurred()) { + IDP_FreeProperty(prop); + return NULL; + } + prop_data[i] = (value != 0); + } + break; + } default: /* should never happen */ PyErr_SetString(PyExc_RuntimeError, "internal error with idp array.type"); @@ -642,6 +679,9 @@ static IDProperty *idp_from_PyObject(PyObject *name_obj, PyObject *ob) if (PyFloat_Check(ob)) { return idp_from_PyFloat(name, ob); } + if (PyBool_Check(ob)) { + return idp_from_PyBool(name, ob); + } if (PyLong_Check(ob)) { return idp_from_PyLong(name, ob); } @@ -779,6 +819,8 @@ PyObject *BPy_IDGroup_MapDataToPy(IDProperty *prop) return idprop_py_from_idp_float(prop); case IDP_DOUBLE: return idprop_py_from_idp_double(prop); + case IDP_BOOLEAN: + return idprop_py_from_idp_bool(prop); case IDP_ID: return idprop_py_from_idp_id(prop); case IDP_ARRAY: { @@ -813,6 +855,13 @@ PyObject *BPy_IDGroup_MapDataToPy(IDProperty *prop) } break; } + case IDP_BOOLEAN: { + const int8_t *array = (const int8_t *)IDP_Array(prop); + for (i = 0; i < prop->len; i++) { + PyList_SET_ITEM(seq, i, PyBool_FromLong(array[i])); + } + break; + } default: PyErr_Format( PyExc_RuntimeError, "%s: invalid/corrupt array type '%d'!", __func__, prop->subtype); @@ -1629,20 +1678,23 @@ PyTypeObject BPy_IDGroup_Type = { /** \name ID Array Methods * \{ */ -static PyTypeObject *idp_array_py_type(BPy_IDArray *self, bool *r_is_double) +static PyTypeObject *idp_array_py_type(BPy_IDArray *self, size_t *elem_size) { switch (self->prop->subtype) { case IDP_FLOAT: - *r_is_double = false; + *elem_size = sizeof(float); return &PyFloat_Type; case IDP_DOUBLE: - *r_is_double = true; + *elem_size = sizeof(double); return &PyFloat_Type; + case IDP_BOOLEAN: + *elem_size = sizeof(int8_t); + return &PyBool_Type; case IDP_INT: - *r_is_double = false; + *elem_size = sizeof(int); return &PyLong_Type; default: - *r_is_double = false; + *elem_size = 0; return NULL; } } @@ -1653,7 +1705,7 @@ static PyObject *BPy_IDArray_repr(BPy_IDArray *self) } PyDoc_STRVAR(BPy_IDArray_get_typecode_doc, - "The type of the data in the array {'f': float, 'd': double, 'i': int}."); + "The type of the data in the array {'f': float, 'd': double, 'i': int, 'b': bool}."); static PyObject *BPy_IDArray_get_typecode(BPy_IDArray *self) { switch (self->prop->subtype) { @@ -1663,6 +1715,8 @@ static PyObject *BPy_IDArray_get_typecode(BPy_IDArray *self) return PyUnicode_FromString("d"); case IDP_INT: return PyUnicode_FromString("i"); + case IDP_BOOLEAN: + return PyUnicode_FromString("b"); } PyErr_Format( @@ -1714,6 +1768,8 @@ static PyObject *BPy_IDArray_GetItem(BPy_IDArray *self, Py_ssize_t index) return PyFloat_FromDouble(((double *)IDP_Array(self->prop))[index]); case IDP_INT: return PyLong_FromLong((long)((int *)IDP_Array(self->prop))[index]); + case IDP_BOOLEAN: + return PyBool_FromLong((long)((int8_t *)IDP_Array(self->prop))[index]); } PyErr_Format( @@ -1755,6 +1811,15 @@ static int BPy_IDArray_SetItem(BPy_IDArray *self, Py_ssize_t index, PyObject *va ((int *)IDP_Array(self->prop))[index] = i; break; } + case IDP_BOOLEAN: { + const int i = PyC_Long_AsBool(value); + if (i == -1 && PyErr_Occurred()) { + return -1; + } + + ((int8_t *)IDP_Array(self->prop))[index] = i; + break; + } } return 0; } @@ -1810,6 +1875,13 @@ static PyObject *BPy_IDArray_slice(BPy_IDArray *self, int begin, int end) } break; } + case IDP_BOOLEAN: { + const int8_t *array = (const int8_t *)IDP_Array(prop); + for (count = begin; count < end; count++) { + PyTuple_SET_ITEM(tuple, count - begin, PyBool_FromLong((long)array[count])); + } + break; + } } return tuple; @@ -1818,9 +1890,8 @@ static PyObject *BPy_IDArray_slice(BPy_IDArray *self, int begin, int end) static int BPy_IDArray_ass_slice(BPy_IDArray *self, int begin, int end, PyObject *seq) { IDProperty *prop = self->prop; - bool is_double; - const PyTypeObject *py_type = idp_array_py_type(self, &is_double); - const size_t elem_size = is_double ? sizeof(double) : sizeof(float); + size_t elem_size; + const PyTypeObject *py_type = idp_array_py_type(self, &elem_size); size_t alloc_len; size_t size; void *vec; @@ -1933,6 +2004,9 @@ static int itemsize_by_idarray_type(int array_type) if (array_type == IDP_DOUBLE) { return sizeof(double); } + if (array_type == IDP_BOOLEAN) { + return sizeof(bool); + } return -1; /* should never happen */ } diff --git a/source/blender/python/generic/idprop_py_ui_api.c b/source/blender/python/generic/idprop_py_ui_api.c index 4487a885a6c..78a328ab772 100644 --- a/source/blender/python/generic/idprop_py_ui_api.c +++ b/source/blender/python/generic/idprop_py_ui_api.c @@ -181,6 +181,89 @@ static bool idprop_ui_data_update_int(IDProperty *idprop, PyObject *args, PyObje return true; } +/** + * \note The default value needs special handling because for array IDProperties it can + * be a single value or an array, but for non-array properties it can only be a value. + */ +static bool idprop_ui_data_update_bool_default(IDProperty *idprop, + IDPropertyUIDataBool *ui_data, + PyObject *default_value) +{ + if (PySequence_Check(default_value)) { + if (idprop->type != IDP_ARRAY) { + PyErr_SetString(PyExc_TypeError, "Only array properties can have array default values"); + return false; + } + + Py_ssize_t len = PySequence_Size(default_value); + int8_t *new_default_array = (int8_t *)MEM_malloc_arrayN(len, sizeof(int8_t), __func__); + if (PyC_AsArray(new_default_array, + sizeof(int8_t), + default_value, + len, + &PyBool_Type, + "ui_data_update") == -1) { + MEM_freeN(new_default_array); + return false; + } + + ui_data->default_array_len = len; + ui_data->default_array = new_default_array; + } + else { + const int value = PyC_Long_AsBool(default_value); + if ((value == -1) && PyErr_Occurred()) { + PyErr_SetString(PyExc_ValueError, "Error converting \"default\" argument to integer"); + return false; + } + ui_data->default_value = (value != 0); + } + + return true; +} + +/** + * \return False when parsing fails, in which case caller should return NULL. + */ +static bool idprop_ui_data_update_bool(IDProperty *idprop, PyObject *args, PyObject *kwargs) +{ + const char *rna_subtype = NULL; + const char *description = NULL; + PyObject *default_value = NULL; + const char *kwlist[] = {"default", "subtype", "description", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, + kwargs, + "|$Ozz:update", + (char **)kwlist, + &default_value, + &rna_subtype, + &description)) { + return false; + } + + /* Write to a temporary copy of the UI data in case some part of the parsing fails. */ + IDPropertyUIDataBool *ui_data_orig = (IDPropertyUIDataBool *)idprop->ui_data; + IDPropertyUIDataBool ui_data = *ui_data_orig; + + if (!idprop_ui_data_update_base(&ui_data.base, rna_subtype, description)) { + IDP_ui_data_free_unique_contents(&ui_data.base, IDP_ui_data_type(idprop), &ui_data_orig->base); + return false; + } + + if (!ELEM(default_value, NULL, Py_None)) { + if (!idprop_ui_data_update_bool_default(idprop, &ui_data, default_value)) { + IDP_ui_data_free_unique_contents( + &ui_data.base, IDP_ui_data_type(idprop), &ui_data_orig->base); + return false; + } + } + + /* Write back to the property's UI data. */ + IDP_ui_data_free_unique_contents(&ui_data_orig->base, IDP_ui_data_type(idprop), &ui_data.base); + *ui_data_orig = ui_data; + return true; +} + /** * \note The default value needs special handling because for array IDProperties it can * be a single value or an array, but for non-array properties it can only be a value. @@ -403,6 +486,12 @@ static PyObject *BPy_IDPropertyUIManager_update(BPy_IDPropertyUIManager *self, return NULL; } Py_RETURN_NONE; + case IDP_UI_DATA_TYPE_BOOLEAN: + IDP_ui_data_ensure(property); + if (!idprop_ui_data_update_bool(property, args, kwargs)) { + return NULL; + } + Py_RETURN_NONE; case IDP_UI_DATA_TYPE_FLOAT: IDP_ui_data_ensure(property); if (!idprop_ui_data_update_float(property, args, kwargs)) { @@ -465,6 +554,25 @@ static void idprop_ui_data_to_dict_int(IDProperty *property, PyObject *dict) } } +static void idprop_ui_data_to_dict_bool(IDProperty *property, PyObject *dict) +{ + IDPropertyUIDataBool *ui_data = (IDPropertyUIDataBool *)property->ui_data; + PyObject *item; + + if (property->type == IDP_ARRAY) { + PyObject *list = PyList_New(ui_data->default_array_len); + for (int i = 0; i < ui_data->default_array_len; i++) { + PyList_SET_ITEM(list, i, PyBool_FromLong(ui_data->default_array[i])); + } + PyDict_SetItemString(dict, "default", list); + Py_DECREF(list); + } + else { + PyDict_SetItemString(dict, "default", item = PyBool_FromLong(ui_data->default_value)); + Py_DECREF(item); + } +} + static void idprop_ui_data_to_dict_float(IDProperty *property, PyObject *dict) { IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)property->ui_data; @@ -547,6 +655,9 @@ static PyObject *BPy_IDIDPropertyUIManager_as_dict(BPy_IDPropertyUIManager *self case IDP_UI_DATA_TYPE_INT: idprop_ui_data_to_dict_int(property, dict); break; + case IDP_UI_DATA_TYPE_BOOLEAN: + idprop_ui_data_to_dict_bool(property, dict); + break; case IDP_UI_DATA_TYPE_FLOAT: idprop_ui_data_to_dict_float(property, dict); break;