Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans Goudey <h.goudey@me.com>2021-08-27 16:27:24 +0300
committerHans Goudey <h.goudey@me.com>2021-08-27 16:27:24 +0300
commit8b9a3b94fc148d197b137aa892855c60db2783e6 (patch)
tree0afd1a3538e76a029c0d6365f5bc06d681da92d9
parent3f5e0f7d91e0e13870d98fe721a871e6be210489 (diff)
Refactor IDProperty UI data storage
The storage of IDProperty UI data (min, max, default value, etc) is quite complicated. For every property, retrieving a single one of these values involves three string lookups. First for the "_RNA_UI" group property, then another for a group with the property's name, then for the data value name. Not only is this inefficient, it's hard to reason about, unintuitive, and not at all self-explanatory. This commit replaces that system with a UI data struct directly in the IDProperty. If it's not used, the only cost is of a NULL pointer. Beyond storing the description, name, and RNA subtype, derived structs are used to store type specific UI data like min and max. Note that this means that addons using (abusing) the `_RNA_UI` custom property will have to be changed. A few places in the addons repository will be changed after this commit with D9919. **Before** Before, first the _RNA_UI subgroup is retrieved the _RNA_UI group, then the subgroup for the original property, then specific UI data is accessed like any other IDProperty. ``` prop = rna_idprop_ui_prop_get(idproperties_owner, "prop_name", create=True) prop["min"] = 1.0 ``` **After** After, the `id_properties_ui` function for RNA structs returns a python object specifically for managing an IDProperty's UI data. ``` ui_data = idproperties_owner.id_properties_ui("prop_name") ui_data.update(min=1.0) ``` In addition to `update`, there are now other functions: - `as_dict`: Returns a dictionary of the property's UI data. - `clear`: Removes the property's UI data. - `update_from`: Copy UI data between properties, even if they have different owners. Differential Revision: https://developer.blender.org/D9697
-rw-r--r--release/scripts/modules/rna_prop_ui.py118
-rw-r--r--release/scripts/startup/bl_operators/object.py2
-rw-r--r--release/scripts/startup/bl_operators/wm.py83
-rw-r--r--release/scripts/startup/keyingsets_builtins.py4
-rw-r--r--source/blender/blenkernel/BKE_idprop.h27
-rw-r--r--source/blender/blenkernel/intern/idprop.c336
-rw-r--r--source/blender/blenloader/intern/versioning_300.c241
-rw-r--r--source/blender/io/alembic/exporter/abc_custom_props.cc3
-rw-r--r--source/blender/makesdna/DNA_ID.h54
-rw-r--r--source/blender/makesrna/RNA_access.h6
-rw-r--r--source/blender/makesrna/intern/rna_access.c443
-rw-r--r--source/blender/makesrna/intern/rna_internal_types.h3
-rw-r--r--source/blender/modifiers/intern/MOD_nodes.cc517
-rw-r--r--source/blender/python/generic/CMakeLists.txt3
-rw-r--r--source/blender/python/generic/idprop_py_api.c2
-rw-r--r--source/blender/python/generic/idprop_py_ui_api.c734
-rw-r--r--source/blender/python/generic/idprop_py_ui_api.h33
-rw-r--r--source/blender/python/intern/bpy.c2
-rw-r--r--source/blender/python/intern/bpy_rna.c50
-rw-r--r--tests/python/bl_pyapi_idprop.py63
20 files changed, 1925 insertions, 799 deletions
diff --git a/release/scripts/modules/rna_prop_ui.py b/release/scripts/modules/rna_prop_ui.py
index bafa2b28bbf..26a2f9ad89b 100644
--- a/release/scripts/modules/rna_prop_ui.py
+++ b/release/scripts/modules/rna_prop_ui.py
@@ -30,24 +30,6 @@ ARRAY_TYPES = (list, tuple, IDPropertyArray, Vector)
MAX_DISPLAY_ROWS = 4
-def rna_idprop_ui_get(item, *, create=True):
- try:
- return item['_RNA_UI']
- except:
- if create:
- item['_RNA_UI'] = {}
- return item['_RNA_UI']
- else:
- return None
-
-
-def rna_idprop_ui_del(item):
- try:
- del item['_RNA_UI']
- except KeyError:
- pass
-
-
def rna_idprop_quote_path(prop):
return "[\"%s\"]" % bpy.utils.escape_identifier(prop)
@@ -59,32 +41,9 @@ def rna_idprop_ui_prop_update(item, prop):
prop_rna.update()
-def rna_idprop_ui_prop_get(item, prop, *, create=True):
-
- rna_ui = rna_idprop_ui_get(item, create=create)
-
- if rna_ui is None:
- return None
-
- try:
- return rna_ui[prop]
- except:
- rna_ui[prop] = {}
- return rna_ui[prop]
-
-
-def rna_idprop_ui_prop_clear(item, prop, *, remove=True):
- rna_ui = rna_idprop_ui_get(item, create=False)
-
- if rna_ui is None:
- return
-
- try:
- del rna_ui[prop]
- except KeyError:
- pass
- if remove and len(item.keys()) == 1:
- rna_idprop_ui_del(item)
+def rna_idprop_ui_prop_clear(item, prop):
+ ui_data = item.id_properties_ui(prop)
+ ui_data.clear()
def rna_idprop_context_value(context, context_member, property_type):
@@ -106,8 +65,7 @@ def rna_idprop_context_value(context, context_member, property_type):
def rna_idprop_has_properties(rna_item):
keys = rna_item.keys()
- nbr_props = len(keys)
- return (nbr_props > 1) or (nbr_props and '_RNA_UI' not in keys)
+ return bool(keys)
def rna_idprop_value_to_python(value):
@@ -126,31 +84,8 @@ def rna_idprop_value_item_type(value):
def rna_idprop_ui_prop_default_set(item, prop, value):
- defvalue = None
- try:
- prop_type, is_array = rna_idprop_value_item_type(item[prop])
-
- if prop_type in {int, float, str}:
- if is_array and isinstance(value, ARRAY_TYPES):
- value = [prop_type(item) for item in value]
- if any(value):
- defvalue = value
- else:
- defvalue = prop_type(value)
- except KeyError:
- pass
- except ValueError:
- pass
-
- if defvalue:
- rna_ui = rna_idprop_ui_prop_get(item, prop, create=True)
- rna_ui["default"] = defvalue
- else:
- rna_ui = rna_idprop_ui_prop_get(item, prop)
- if rna_ui:
- rna_ui.pop("default", None)
-
- return defvalue
+ ui_data = item.id_properties_ui(prop)
+ ui_data.update(default=value)
def rna_idprop_ui_create(
@@ -163,7 +98,7 @@ def rna_idprop_ui_create(
):
"""Create and initialize a custom property with limits, defaults and other settings."""
- proptype, is_array = rna_idprop_value_item_type(default)
+ proptype, _ = rna_idprop_value_item_type(default)
# Sanitize limits
if proptype is bool:
@@ -180,35 +115,22 @@ def rna_idprop_ui_create(
rna_idprop_ui_prop_update(item, prop)
- # Clear the UI settings
- rna_ui_group = rna_idprop_ui_get(item, create=True)
- rna_ui_group[prop] = {}
- rna_ui = rna_ui_group[prop]
-
- # Assign limits and default
- if proptype in {int, float, bool}:
- # The type must be exactly the same
- rna_ui["min"] = proptype(min)
- rna_ui["soft_min"] = proptype(soft_min)
- rna_ui["max"] = proptype(max)
- rna_ui["soft_max"] = proptype(soft_max)
-
- if default and (not is_array or any(default)):
- rna_ui["default"] = default
-
- if is_array and subtype and subtype != 'NONE':
- rna_ui["subtype"] = subtype
-
- # Assign other settings
- if description is not None:
- rna_ui["description"] = description
+ # Update the UI settings.
+ ui_data = item.id_properties_ui(prop)
+ ui_data.update(
+ subtype=subtype,
+ min=min,
+ max=max,
+ soft_min=soft_min,
+ soft_max=soft_max,
+ description=description,
+ default=default,
+ )
prop_path = rna_idprop_quote_path(prop)
item.property_overridable_library_set(prop_path, overridable)
- return rna_ui
-
def draw(layout, context, context_member, property_type, *, use_edit=True):
@@ -254,10 +176,6 @@ def draw(layout, context, context_member, property_type, *, use_edit=True):
flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=True)
for key, val in items:
-
- if key == '_RNA_UI':
- continue
-
is_rna = (key in rna_properties)
# only show API defined for developers
diff --git a/release/scripts/startup/bl_operators/object.py b/release/scripts/startup/bl_operators/object.py
index d61bed71cab..df37db40102 100644
--- a/release/scripts/startup/bl_operators/object.py
+++ b/release/scripts/startup/bl_operators/object.py
@@ -970,7 +970,7 @@ class OBJECT_OT_assign_property_defaults(Operator):
def assign_defaults(obj):
from rna_prop_ui import rna_idprop_ui_prop_default_set
- rna_properties = {'_RNA_UI'} | {prop.identifier for prop in obj.bl_rna.properties if prop.is_runtime}
+ rna_properties = {prop.identifier for prop in obj.bl_rna.properties if prop.is_runtime}
for prop, value in obj.items():
if prop not in rna_properties:
diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py
index 6a3830ad1e4..989c067efec 100644
--- a/release/scripts/startup/bl_operators/wm.py
+++ b/release/scripts/startup/bl_operators/wm.py
@@ -1388,10 +1388,8 @@ class WM_OT_properties_edit(Operator):
def execute(self, context):
from rna_prop_ui import (
- rna_idprop_ui_prop_get,
rna_idprop_ui_prop_clear,
rna_idprop_ui_prop_update,
- rna_idprop_ui_prop_default_set,
rna_idprop_value_item_type,
)
@@ -1431,27 +1429,35 @@ class WM_OT_properties_edit(Operator):
prop_type_new = type(prop_value)
prop_type, is_array = rna_idprop_value_item_type(prop_value)
- prop_ui = rna_idprop_ui_prop_get(item, prop)
-
- if prop_type in {float, int}:
- prop_ui["min"] = prop_type(self.min)
- prop_ui["max"] = prop_type(self.max)
-
- if self.use_soft_limits:
- prop_ui["soft_min"] = prop_type(self.soft_min)
- prop_ui["soft_max"] = prop_type(self.soft_max)
- else:
- prop_ui["soft_min"] = prop_type(self.min)
- prop_ui["soft_max"] = prop_type(self.max)
-
- if prop_type == float and is_array and self.subtype != 'NONE':
- prop_ui["subtype"] = self.subtype
- else:
- prop_ui.pop("subtype", None)
-
- prop_ui["description"] = self.description
-
- rna_idprop_ui_prop_default_set(item, prop, default_eval)
+ ui_data = item.id_properties_ui(prop)
+ ui_data.update(subtype=self.subtype, description=self.description)
+
+ if prop_type == int:
+ if type(default_eval) == str:
+ self.report({'WARNING'}, "Could not evaluate number from default value")
+ default_eval = None
+ elif hasattr(default_eval, "__len__"):
+ default_eval = [int(round(value)) for value in default_eval]
+ ui_data.update(
+ min=int(round(self.min)),
+ max=int(round(self.max)),
+ soft_min=int(round(self.soft_min)),
+ soft_max=int(round(self.soft_max)),
+ default=default_eval,
+ )
+ elif prop_type == float:
+ if type(default_eval) == str:
+ self.report({'WARNING'}, "Could not evaluate number from default value")
+ default_eval = None
+ ui_data.update(
+ min=self.min,
+ max=self.max,
+ soft_min=self.soft_min,
+ soft_max=self.soft_max,
+ default=default_eval,
+ )
+ elif prop_type == str:
+ ui_data.update(default=self.default)
# If we have changed the type of the property, update its potential anim curves!
if prop_type_old != prop_type_new:
@@ -1492,7 +1498,6 @@ class WM_OT_properties_edit(Operator):
def invoke(self, context, _event):
from rna_prop_ui import (
- rna_idprop_ui_prop_get,
rna_idprop_value_to_python,
rna_idprop_value_item_type
)
@@ -1526,28 +1531,22 @@ class WM_OT_properties_edit(Operator):
self.default = ""
# setup defaults
- prop_ui = rna_idprop_ui_prop_get(item, prop, create=False)
- if prop_ui:
- self.min = prop_ui.get("min", -1000000000)
- self.max = prop_ui.get("max", 1000000000)
- self.description = prop_ui.get("description", "")
-
- defval = prop_ui.get("default", None)
- if defval is not None:
- self.default = str(rna_idprop_value_to_python(defval))
-
- self.soft_min = prop_ui.get("soft_min", self.min)
- self.soft_max = prop_ui.get("soft_max", self.max)
+ ui_data = item.id_properties_ui(prop)
+ rna_data = ui_data.as_dict()
+ self.subtype = rna_data["subtype"]
+ if prop_type in {int, float}:
+ self.min = rna_data["min"]
+ self.max = rna_data["max"]
+ self.soft_min = rna_data["soft_min"]
+ self.soft_max = rna_data["soft_max"]
self.use_soft_limits = (
self.min != self.soft_min or
self.max != self.soft_max
)
+ if prop_type in {int, float, str}:
+ self.default = str(rna_data["default"])
- subtype = prop_ui.get("subtype", None)
- else:
- subtype = None
-
- self._init_subtype(prop_type, is_array, subtype)
+ self._init_subtype(prop_type, is_array, self.subtype)
# store for comparison
self._cmp_props = self._cmp_props_get()
@@ -1688,7 +1687,6 @@ class WM_OT_properties_remove(Operator):
def execute(self, context):
from rna_prop_ui import (
- rna_idprop_ui_prop_clear,
rna_idprop_ui_prop_update,
)
data_path = self.data_path
@@ -1701,7 +1699,6 @@ class WM_OT_properties_remove(Operator):
prop = self.property
rna_idprop_ui_prop_update(item, prop)
del item[prop]
- rna_idprop_ui_prop_clear(item, prop)
return {'FINISHED'}
diff --git a/release/scripts/startup/keyingsets_builtins.py b/release/scripts/startup/keyingsets_builtins.py
index ceffeaaff6c..d9811b6c0ed 100644
--- a/release/scripts/startup/keyingsets_builtins.py
+++ b/release/scripts/startup/keyingsets_builtins.py
@@ -523,10 +523,6 @@ class WholeCharacterMixin:
# go over all custom properties for bone
for prop in bone.keys():
- # ignore special "_RNA_UI" used for UI editing
- if prop == "_RNA_UI":
- continue
-
# for now, just add all of 'em
prop_rna = type(bone).bl_rna.properties.get(prop, None)
if prop_rna is None:
diff --git a/source/blender/blenkernel/BKE_idprop.h b/source/blender/blenkernel/BKE_idprop.h
index a5cb6489194..c28ac63388b 100644
--- a/source/blender/blenkernel/BKE_idprop.h
+++ b/source/blender/blenkernel/BKE_idprop.h
@@ -32,6 +32,7 @@ struct BlendLibReader;
struct BlendWriter;
struct ID;
struct IDProperty;
+struct IDPropertyUIData;
typedef union IDPropertyTemplate {
int i;
@@ -183,6 +184,10 @@ void IDP_Reset(struct IDProperty *prop, const struct IDProperty *reference);
# define IDP_Id(prop) ((ID *)(prop)->data.pointer)
#endif
+int IDP_coerce_to_int_or_zero(const struct IDProperty *prop);
+float IDP_coerce_to_float_or_zero(const struct IDProperty *prop);
+double IDP_coerce_to_double_or_zero(const struct IDProperty *prop);
+
/**
* Call a callback for each idproperty in the hierarchy under given root one (included).
*
@@ -209,6 +214,28 @@ void IDP_BlendReadData_impl(struct BlendDataReader *reader,
void IDP_BlendReadLib(struct BlendLibReader *reader, struct IDProperty *prop);
void IDP_BlendReadExpand(struct BlendExpander *expander, struct IDProperty *prop);
+typedef enum eIDPropertyUIDataType {
+ /** Other properties types that don't support RNA UI data. */
+ IDP_UI_DATA_TYPE_UNSUPPORTED = -1,
+ /** IDP_INT or IDP_ARRAY with subtype IDP_INT. */
+ IDP_UI_DATA_TYPE_INT = 0,
+ /** IDP_FLOAT and IDP_DOUBLE or IDP_ARRAY properties with a float or double subtypes. */
+ IDP_UI_DATA_TYPE_FLOAT = 1,
+ /** IDP_STRING properties. */
+ IDP_UI_DATA_TYPE_STRING = 2,
+ /** IDP_ID. */
+ IDP_UI_DATA_TYPE_ID = 3,
+} eIDPropertyUIDataType;
+
+bool IDP_ui_data_supported(const struct IDProperty *prop);
+eIDPropertyUIDataType IDP_ui_data_type(const struct IDProperty *prop);
+void IDP_ui_data_free(struct IDProperty *prop);
+void IDP_ui_data_free_unique_contents(struct IDPropertyUIData *ui_data,
+ eIDPropertyUIDataType type,
+ const struct IDPropertyUIData *other);
+struct IDPropertyUIData *IDP_ui_data_ensure(struct IDProperty *prop);
+struct IDPropertyUIData *IDP_ui_data_copy(const struct IDProperty *prop);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/intern/idprop.c b/source/blender/blenkernel/intern/idprop.c
index 7a88a00c44f..786df14756a 100644
--- a/source/blender/blenkernel/intern/idprop.c
+++ b/source/blender/blenkernel/intern/idprop.c
@@ -21,6 +21,7 @@
* \ingroup bke
*/
+#include <limits.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
@@ -274,6 +275,43 @@ void IDP_FreeArray(IDProperty *prop)
}
}
+IDPropertyUIData *IDP_ui_data_copy(const IDProperty *prop)
+{
+ IDPropertyUIData *dst_ui_data = MEM_dupallocN(prop->ui_data);
+
+ /* Copy extra type specific data. */
+ switch (IDP_ui_data_type(prop)) {
+ case IDP_UI_DATA_TYPE_STRING: {
+ const IDPropertyUIDataString *src = (const IDPropertyUIDataString *)prop->ui_data;
+ IDPropertyUIDataString *dst = (IDPropertyUIDataString *)dst_ui_data;
+ dst->default_value = MEM_dupallocN(src->default_value);
+ break;
+ }
+ case IDP_UI_DATA_TYPE_ID: {
+ break;
+ }
+ case IDP_UI_DATA_TYPE_INT: {
+ const IDPropertyUIDataInt *src = (const IDPropertyUIDataInt *)prop->ui_data;
+ IDPropertyUIDataInt *dst = (IDPropertyUIDataInt *)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;
+ dst->default_array = MEM_dupallocN(src->default_array);
+ break;
+ }
+ case IDP_UI_DATA_TYPE_UNSUPPORTED: {
+ break;
+ }
+ }
+
+ dst_ui_data->description = MEM_dupallocN(prop->ui_data->description);
+
+ return dst_ui_data;
+}
+
static IDProperty *idp_generic_copy(const IDProperty *prop, const int UNUSED(flag))
{
IDProperty *newp = MEM_callocN(sizeof(IDProperty), __func__);
@@ -284,6 +322,10 @@ static IDProperty *idp_generic_copy(const IDProperty *prop, const int UNUSED(fla
newp->data.val = prop->data.val;
newp->data.val2 = prop->data.val2;
+ if (prop->ui_data != NULL) {
+ newp->ui_data = IDP_ui_data_copy(prop);
+ }
+
return newp;
}
@@ -679,6 +721,7 @@ bool IDP_InsertToGroup(IDProperty *group, IDProperty *previous, IDProperty *pnew
void IDP_RemoveFromGroup(IDProperty *group, IDProperty *prop)
{
BLI_assert(group->type == IDP_GROUP);
+ BLI_assert(BLI_findindex(&group->data.group, prop) != -1);
group->len--;
BLI_remlink(&group->data.group, prop);
@@ -725,6 +768,60 @@ static void IDP_FreeGroup(IDProperty *prop, const bool do_id_user)
/** \name Main Functions (IDProperty Main API)
* \{ */
+/**
+ * Return an int from an IDProperty with a compatible type. This should be avoided, but
+ * it's sometimes necessary, for example when legacy files have incorrect property types.
+ */
+int IDP_coerce_to_int_or_zero(const IDProperty *prop)
+{
+ switch (prop->type) {
+ case IDP_INT:
+ return IDP_Int(prop);
+ case IDP_DOUBLE:
+ return (int)IDP_Double(prop);
+ case IDP_FLOAT:
+ return (int)IDP_Float(prop);
+ default:
+ return 0;
+ }
+}
+
+/**
+ * Return a double from an IDProperty with a compatible type. This should be avoided, but
+ * it's sometimes necessary, for example when legacy files have incorrect property types.
+ */
+double IDP_coerce_to_double_or_zero(const IDProperty *prop)
+{
+ switch (prop->type) {
+ case IDP_DOUBLE:
+ return IDP_Double(prop);
+ case IDP_FLOAT:
+ return (double)IDP_Float(prop);
+ case IDP_INT:
+ return (double)IDP_Int(prop);
+ default:
+ return 0.0;
+ }
+}
+
+/**
+ * Return a float from an IDProperty with a compatible type. This should be avoided, but
+ * it's sometimes necessary, for example when legacy files have incorrect property types.
+ */
+float IDP_coerce_to_float_or_zero(const IDProperty *prop)
+{
+ switch (prop->type) {
+ case IDP_FLOAT:
+ return IDP_Float(prop);
+ case IDP_DOUBLE:
+ return (float)IDP_Double(prop);
+ case IDP_INT:
+ return (float)IDP_Int(prop);
+ default:
+ return 0.0f;
+ }
+}
+
IDProperty *IDP_CopyProperty_ex(const IDProperty *prop, const int flag)
{
switch (prop->type) {
@@ -1000,6 +1097,85 @@ IDProperty *IDP_New(const char type, const IDPropertyTemplate *val, const char *
}
/**
+ * Free allocated pointers in the UI data that isn't shared with the UI data in the #other
+ * argument. Useful for returning early on failure when updating UI data in place, or when
+ * replacing a subset of the UI data's allocated pointers.
+ */
+void IDP_ui_data_free_unique_contents(IDPropertyUIData *ui_data,
+ const eIDPropertyUIDataType type,
+ const IDPropertyUIData *other)
+{
+ if (ui_data->description != other->description) {
+ MEM_SAFE_FREE(ui_data->description);
+ }
+
+ switch (type) {
+ case IDP_UI_DATA_TYPE_STRING: {
+ const IDPropertyUIDataString *other_string = (const IDPropertyUIDataString *)other;
+ IDPropertyUIDataString *ui_data_string = (IDPropertyUIDataString *)ui_data;
+ if (ui_data_string->default_value != other_string->default_value) {
+ MEM_SAFE_FREE(ui_data_string->default_value);
+ }
+ break;
+ }
+ case IDP_UI_DATA_TYPE_ID: {
+ break;
+ }
+ case IDP_UI_DATA_TYPE_INT: {
+ const IDPropertyUIDataInt *other_int = (const IDPropertyUIDataInt *)other;
+ IDPropertyUIDataInt *ui_data_int = (IDPropertyUIDataInt *)ui_data;
+ if (ui_data_int->default_array != other_int->default_array) {
+ MEM_SAFE_FREE(ui_data_int->default_array);
+ }
+ break;
+ }
+ case IDP_UI_DATA_TYPE_FLOAT: {
+ const IDPropertyUIDataFloat *other_float = (const IDPropertyUIDataFloat *)other;
+ IDPropertyUIDataFloat *ui_data_float = (IDPropertyUIDataFloat *)ui_data;
+ if (ui_data_float->default_array != other_float->default_array) {
+ MEM_SAFE_FREE(ui_data_float->default_array);
+ }
+ break;
+ }
+ case IDP_UI_DATA_TYPE_UNSUPPORTED: {
+ break;
+ }
+ }
+}
+
+void IDP_ui_data_free(IDProperty *prop)
+{
+ switch (IDP_ui_data_type(prop)) {
+ case IDP_UI_DATA_TYPE_STRING: {
+ IDPropertyUIDataString *ui_data_string = (IDPropertyUIDataString *)prop->ui_data;
+ MEM_SAFE_FREE(ui_data_string->default_value);
+ break;
+ }
+ case IDP_UI_DATA_TYPE_ID: {
+ break;
+ }
+ case IDP_UI_DATA_TYPE_INT: {
+ IDPropertyUIDataInt *ui_data_int = (IDPropertyUIDataInt *)prop->ui_data;
+ MEM_SAFE_FREE(ui_data_int->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);
+ break;
+ }
+ case IDP_UI_DATA_TYPE_UNSUPPORTED: {
+ break;
+ }
+ }
+
+ MEM_SAFE_FREE(prop->ui_data->description);
+
+ MEM_freeN(prop->ui_data);
+ prop->ui_data = NULL;
+}
+
+/**
* \note This will free allocated data, all child properties of arrays and groups, and unlink IDs!
* But it does not free the actual IDProperty struct itself.
*/
@@ -1024,6 +1200,10 @@ void IDP_FreePropertyContent_ex(IDProperty *prop, const bool do_id_user)
}
break;
}
+
+ if (prop->ui_data != NULL) {
+ IDP_ui_data_free(prop);
+ }
}
void IDP_FreePropertyContent(IDProperty *prop)
@@ -1104,6 +1284,48 @@ void IDP_foreach_property(IDProperty *id_property_root,
void IDP_WriteProperty_OnlyData(const IDProperty *prop, BlendWriter *writer);
+static void write_ui_data(const IDProperty *prop, BlendWriter *writer)
+{
+ IDPropertyUIData *ui_data = prop->ui_data;
+
+ BLO_write_string(writer, ui_data->description);
+
+ switch (IDP_ui_data_type(prop)) {
+ case IDP_UI_DATA_TYPE_STRING: {
+ IDPropertyUIDataString *ui_data_string = (IDPropertyUIDataString *)ui_data;
+ BLO_write_string(writer, ui_data_string->default_value);
+ BLO_write_struct(writer, IDPropertyUIDataString, ui_data);
+ break;
+ }
+ case IDP_UI_DATA_TYPE_ID: {
+ BLO_write_struct(writer, IDPropertyUIDataID, ui_data);
+ break;
+ }
+ case IDP_UI_DATA_TYPE_INT: {
+ IDPropertyUIDataInt *ui_data_int = (IDPropertyUIDataInt *)ui_data;
+ if (prop->type == IDP_ARRAY) {
+ BLO_write_int32_array(
+ writer, (uint)ui_data_int->default_array_len, (int32_t *)ui_data_int->default_array);
+ }
+ BLO_write_struct(writer, IDPropertyUIDataInt, ui_data);
+ break;
+ }
+ case IDP_UI_DATA_TYPE_FLOAT: {
+ IDPropertyUIDataFloat *ui_data_float = (IDPropertyUIDataFloat *)ui_data;
+ if (prop->type == IDP_ARRAY) {
+ BLO_write_double_array(
+ writer, (uint)ui_data_float->default_array_len, ui_data_float->default_array);
+ }
+ BLO_write_struct(writer, IDPropertyUIDataFloat, ui_data);
+ break;
+ }
+ case IDP_UI_DATA_TYPE_UNSUPPORTED: {
+ BLI_assert_unreachable();
+ break;
+ }
+ }
+}
+
static void IDP_WriteArray(const IDProperty *prop, BlendWriter *writer)
{
/* Remember to set #IDProperty.totallen to len in the linking code! */
@@ -1165,6 +1387,9 @@ void IDP_WriteProperty_OnlyData(const IDProperty *prop, BlendWriter *writer)
IDP_WriteIDPArray(prop, writer);
break;
}
+ if (prop->ui_data != NULL) {
+ write_ui_data(prop, writer);
+ }
}
void IDP_BlendWrite(BlendWriter *writer, const IDProperty *prop)
@@ -1175,6 +1400,43 @@ void IDP_BlendWrite(BlendWriter *writer, const IDProperty *prop)
static void IDP_DirectLinkProperty(IDProperty *prop, BlendDataReader *reader);
+static void read_ui_data(IDProperty *prop, BlendDataReader *reader)
+{
+ BLO_read_data_address(reader, &prop->ui_data);
+ BLO_read_data_address(reader, &prop->ui_data->description);
+
+ switch (IDP_ui_data_type(prop)) {
+ case IDP_UI_DATA_TYPE_STRING: {
+ IDPropertyUIDataString *ui_data_string = (IDPropertyUIDataString *)prop->ui_data;
+ BLO_read_data_address(reader, &ui_data_string->default_value);
+ break;
+ }
+ case IDP_UI_DATA_TYPE_ID: {
+ break;
+ }
+ case IDP_UI_DATA_TYPE_INT: {
+ IDPropertyUIDataInt *ui_data_int = (IDPropertyUIDataInt *)prop->ui_data;
+ if (prop->type == IDP_ARRAY) {
+ BLO_read_int32_array(
+ reader, ui_data_int->default_array_len, (int **)&ui_data_int->default_array);
+ }
+ break;
+ }
+ case IDP_UI_DATA_TYPE_FLOAT: {
+ IDPropertyUIDataFloat *ui_data_float = (IDPropertyUIDataFloat *)prop->ui_data;
+ if (prop->type == IDP_ARRAY) {
+ BLO_read_double_array(
+ reader, ui_data_float->default_array_len, (double **)&ui_data_float->default_array);
+ }
+ break;
+ }
+ case IDP_UI_DATA_TYPE_UNSUPPORTED: {
+ BLI_assert_unreachable();
+ break;
+ }
+ }
+}
+
static void IDP_DirectLinkIDPArray(IDProperty *prop, BlendDataReader *reader)
{
/* since we didn't save the extra buffer, set totallen to len */
@@ -1279,6 +1541,10 @@ static void IDP_DirectLinkProperty(IDProperty *prop, BlendDataReader *reader)
prop->subtype = 0;
IDP_Int(prop) = 0;
}
+
+ if (prop->ui_data != NULL) {
+ read_ui_data(prop, reader);
+ }
}
void IDP_BlendReadData_impl(BlendDataReader *reader, IDProperty **prop, const char *caller_func_id)
@@ -1358,4 +1624,74 @@ void IDP_BlendReadExpand(struct BlendExpander *expander, IDProperty *prop)
}
}
+eIDPropertyUIDataType IDP_ui_data_type(const IDProperty *prop)
+{
+ if (prop->type == IDP_STRING) {
+ return IDP_UI_DATA_TYPE_STRING;
+ }
+ if (prop->type == IDP_ID) {
+ return IDP_UI_DATA_TYPE_ID;
+ }
+ if (prop->type == IDP_INT || (prop->type == IDP_ARRAY && prop->subtype == IDP_INT)) {
+ return IDP_UI_DATA_TYPE_INT;
+ }
+ if (ELEM(prop->type, IDP_FLOAT, IDP_DOUBLE) ||
+ (prop->type == IDP_ARRAY && ELEM(prop->subtype, IDP_FLOAT, IDP_DOUBLE))) {
+ return IDP_UI_DATA_TYPE_FLOAT;
+ }
+ return IDP_UI_DATA_TYPE_UNSUPPORTED;
+}
+
+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)
+{
+ if (prop->ui_data != NULL) {
+ return prop->ui_data;
+ }
+
+ switch (IDP_ui_data_type(prop)) {
+ case IDP_UI_DATA_TYPE_STRING: {
+ prop->ui_data = MEM_callocN(sizeof(IDPropertyUIDataString), __func__);
+ break;
+ }
+ case IDP_UI_DATA_TYPE_ID: {
+ IDPropertyUIDataID *ui_data = MEM_callocN(sizeof(IDPropertyUIDataID), __func__);
+ prop->ui_data = (IDPropertyUIData *)ui_data;
+ break;
+ }
+ case IDP_UI_DATA_TYPE_INT: {
+ IDPropertyUIDataInt *ui_data = MEM_callocN(sizeof(IDPropertyUIDataInt), __func__);
+ ui_data->min = -INT_MAX;
+ ui_data->max = INT_MAX;
+ ui_data->soft_min = -INT_MAX;
+ ui_data->soft_max = INT_MAX;
+ ui_data->step = 1;
+ 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;
+ ui_data->max = FLT_MAX;
+ ui_data->soft_min = -FLT_MAX;
+ ui_data->soft_max = FLT_MAX;
+ ui_data->step = 1.0f;
+ ui_data->precision = 3;
+ prop->ui_data = (IDPropertyUIData *)ui_data;
+ break;
+ }
+ case IDP_UI_DATA_TYPE_UNSUPPORTED: {
+ /* UI data not supported for remaining types, this shouldn't be called in those cases. */
+ BLI_assert_unreachable();
+ break;
+ }
+ }
+
+ return prop->ui_data;
+}
+
/** \} */
diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c
index aee6d798799..0253316dc3a 100644
--- a/source/blender/blenloader/intern/versioning_300.c
+++ b/source/blender/blenloader/intern/versioning_300.c
@@ -20,6 +20,10 @@
/* allow readfile to use deprecated functionality */
#define DNA_DEPRECATED_ALLOW
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
#include "BLI_listbase.h"
#include "BLI_math_vector.h"
#include "BLI_path_util.h"
@@ -46,10 +50,14 @@
#include "BKE_deform.h"
#include "BKE_fcurve.h"
#include "BKE_fcurve_driver.h"
+#include "BKE_idprop.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_node.h"
+#include "RNA_access.h"
+#include "RNA_enum_types.h"
+
#include "BLO_readfile.h"
#include "MEM_guardedalloc.h"
#include "readfile.h"
@@ -60,6 +68,238 @@
#include "versioning_common.h"
+static IDProperty *idproperty_find_ui_container(IDProperty *idprop_group)
+{
+ LISTBASE_FOREACH (IDProperty *, prop, &idprop_group->data.group) {
+ if (prop->type == IDP_GROUP && STREQ(prop->name, "_RNA_UI")) {
+ return prop;
+ }
+ }
+ return NULL;
+}
+
+static void version_idproperty_move_data_int(IDPropertyUIDataInt *ui_data,
+ const IDProperty *prop_ui_data)
+{
+ IDProperty *min = IDP_GetPropertyFromGroup(prop_ui_data, "min");
+ if (min != NULL) {
+ ui_data->min = ui_data->soft_min = IDP_coerce_to_int_or_zero(min);
+ }
+ IDProperty *max = IDP_GetPropertyFromGroup(prop_ui_data, "max");
+ if (max != NULL) {
+ ui_data->max = ui_data->soft_max = IDP_coerce_to_int_or_zero(max);
+ }
+ IDProperty *soft_min = IDP_GetPropertyFromGroup(prop_ui_data, "soft_min");
+ if (soft_min != NULL) {
+ ui_data->soft_min = IDP_coerce_to_int_or_zero(soft_min);
+ ui_data->soft_min = MIN2(ui_data->soft_min, ui_data->min);
+ }
+ IDProperty *soft_max = IDP_GetPropertyFromGroup(prop_ui_data, "soft_max");
+ if (soft_max != NULL) {
+ ui_data->soft_max = IDP_coerce_to_int_or_zero(soft_max);
+ ui_data->soft_max = MAX2(ui_data->soft_max, ui_data->max);
+ }
+ IDProperty *step = IDP_GetPropertyFromGroup(prop_ui_data, "step");
+ if (step != NULL) {
+ ui_data->step = IDP_coerce_to_int_or_zero(soft_max);
+ }
+ IDProperty *default_value = IDP_GetPropertyFromGroup(prop_ui_data, "default");
+ if (default_value != NULL) {
+ if (default_value->type == IDP_ARRAY) {
+ if (default_value->subtype == IDP_INT) {
+ ui_data->default_array = MEM_dupallocN(IDP_Array(default_value));
+ ui_data->default_array_len = default_value->len;
+ }
+ }
+ else if (default_value->type == IDP_INT) {
+ ui_data->default_value = IDP_coerce_to_int_or_zero(default_value);
+ }
+ }
+}
+
+static void version_idproperty_move_data_float(IDPropertyUIDataFloat *ui_data,
+ const IDProperty *prop_ui_data)
+{
+ IDProperty *min = IDP_GetPropertyFromGroup(prop_ui_data, "min");
+ if (min != NULL) {
+ ui_data->min = ui_data->soft_min = IDP_coerce_to_double_or_zero(min);
+ }
+ IDProperty *max = IDP_GetPropertyFromGroup(prop_ui_data, "max");
+ if (max != NULL) {
+ ui_data->max = ui_data->soft_max = IDP_coerce_to_double_or_zero(min);
+ }
+ IDProperty *soft_min = IDP_GetPropertyFromGroup(prop_ui_data, "soft_min");
+ if (soft_min != NULL) {
+ ui_data->soft_min = IDP_coerce_to_double_or_zero(soft_min);
+ ui_data->soft_min = MAX2(ui_data->soft_min, ui_data->min);
+ }
+ IDProperty *soft_max = IDP_GetPropertyFromGroup(prop_ui_data, "soft_max");
+ if (soft_max != NULL) {
+ ui_data->soft_max = IDP_coerce_to_double_or_zero(soft_max);
+ ui_data->soft_max = MIN2(ui_data->soft_max, ui_data->max);
+ }
+ IDProperty *step = IDP_GetPropertyFromGroup(prop_ui_data, "step");
+ if (step != NULL) {
+ ui_data->step = IDP_coerce_to_float_or_zero(step);
+ }
+ IDProperty *precision = IDP_GetPropertyFromGroup(prop_ui_data, "precision");
+ if (precision != NULL) {
+ ui_data->precision = IDP_coerce_to_int_or_zero(precision);
+ }
+ IDProperty *default_value = IDP_GetPropertyFromGroup(prop_ui_data, "default");
+ if (default_value != NULL) {
+ if (default_value->type == IDP_ARRAY) {
+ if (ELEM(default_value->subtype, IDP_FLOAT, IDP_DOUBLE)) {
+ ui_data->default_array = MEM_dupallocN(IDP_Array(default_value));
+ ui_data->default_array_len = default_value->len;
+ }
+ }
+ else if (ELEM(default_value->type, IDP_DOUBLE, IDP_FLOAT)) {
+ ui_data->default_value = IDP_coerce_to_double_or_zero(default_value);
+ }
+ }
+}
+
+static void version_idproperty_move_data_string(IDPropertyUIDataString *ui_data,
+ const IDProperty *prop_ui_data)
+{
+ IDProperty *default_value = IDP_GetPropertyFromGroup(prop_ui_data, "default");
+ if (default_value != NULL && default_value->type == IDP_STRING) {
+ ui_data->default_value = BLI_strdup(IDP_String(default_value));
+ }
+}
+
+static void version_idproperty_ui_data(IDProperty *idprop_group)
+{
+ if (idprop_group == NULL) { /* NULL check here to reduce verbosity of calls to this function. */
+ return;
+ }
+
+ IDProperty *ui_container = idproperty_find_ui_container(idprop_group);
+ if (ui_container == NULL) {
+ return;
+ }
+
+ LISTBASE_FOREACH (IDProperty *, prop, &idprop_group->data.group) {
+ IDProperty *prop_ui_data = IDP_GetPropertyFromGroup(ui_container, prop->name);
+ if (prop_ui_data == NULL) {
+ continue;
+ }
+
+ if (!IDP_ui_data_supported(prop)) {
+ continue;
+ }
+
+ IDPropertyUIData *ui_data = IDP_ui_data_ensure(prop);
+
+ IDProperty *subtype = IDP_GetPropertyFromGroup(prop_ui_data, "subtype");
+ if (subtype != NULL && subtype->type == IDP_STRING) {
+ const char *subtype_string = IDP_String(subtype);
+ int result = PROP_NONE;
+ RNA_enum_value_from_id(rna_enum_property_subtype_items, subtype_string, &result);
+ ui_data->rna_subtype = result;
+ }
+
+ IDProperty *description = IDP_GetPropertyFromGroup(prop_ui_data, "description");
+ if (description != NULL && description->type == IDP_STRING) {
+ ui_data->description = BLI_strdup(IDP_String(description));
+ }
+
+ /* Type specific data. */
+ switch (IDP_ui_data_type(prop)) {
+ case IDP_UI_DATA_TYPE_STRING:
+ version_idproperty_move_data_string((IDPropertyUIDataString *)ui_data, prop_ui_data);
+ break;
+ case IDP_UI_DATA_TYPE_ID:
+ break;
+ case IDP_UI_DATA_TYPE_INT:
+ version_idproperty_move_data_int((IDPropertyUIDataInt *)ui_data, prop_ui_data);
+ break;
+ case IDP_UI_DATA_TYPE_FLOAT:
+ version_idproperty_move_data_float((IDPropertyUIDataFloat *)ui_data, prop_ui_data);
+ break;
+ case IDP_UI_DATA_TYPE_UNSUPPORTED:
+ BLI_assert_unreachable();
+ break;
+ }
+
+ IDP_FreeFromGroup(ui_container, prop_ui_data);
+ }
+
+ IDP_FreeFromGroup(idprop_group, ui_container);
+}
+
+static void do_versions_idproperty_bones_recursive(Bone *bone)
+{
+ version_idproperty_ui_data(bone->prop);
+ LISTBASE_FOREACH (Bone *, child_bone, &bone->childbase) {
+ do_versions_idproperty_bones_recursive(child_bone);
+ }
+}
+
+/**
+ * For every data block that supports them, initialize the new IDProperty UI data struct based on
+ * the old more complicated storage. Assumes only the top level of IDProperties below the parent
+ * group had UI data in a "_RNA_UI" group.
+ *
+ * \note The following IDProperty groups in DNA aren't exposed in the UI or are runtime-only, so
+ * they don't have UI data: wmOperator, bAddon, bUserMenuItem_Op, wmKeyMapItem, wmKeyConfigPref,
+ * uiList, FFMpegCodecData, View3DShading, bToolRef, TimeMarker, ViewLayer, bPoseChannel.
+ */
+static void do_versions_idproperty_ui_data(Main *bmain)
+{
+ /* ID data. */
+ ID *id;
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ IDProperty *idprop_group = IDP_GetProperties(id, false);
+ if (idprop_group == NULL) {
+ continue;
+ }
+ version_idproperty_ui_data(idprop_group);
+ }
+ FOREACH_MAIN_ID_END;
+
+ /* Bones. */
+ LISTBASE_FOREACH (bArmature *, armature, &bmain->armatures) {
+ LISTBASE_FOREACH (Bone *, bone, &armature->bonebase) {
+ do_versions_idproperty_bones_recursive(bone);
+ }
+ }
+
+ /* Nodes and node sockets. */
+ LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
+ version_idproperty_ui_data(node->prop);
+ }
+ LISTBASE_FOREACH (bNodeSocket *, socket, &ntree->inputs) {
+ version_idproperty_ui_data(socket->prop);
+ }
+ LISTBASE_FOREACH (bNodeSocket *, socket, &ntree->outputs) {
+ version_idproperty_ui_data(socket->prop);
+ }
+ }
+
+ /* The UI data from exposed node modifier properties is just copied from the corresponding node
+ * group, but the copying only runs when necessary, so we still need to version UI data here. */
+ LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
+ LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
+ if (md->type == eModifierType_Nodes) {
+ NodesModifierData *nmd = (NodesModifierData *)md;
+ version_idproperty_ui_data(nmd->settings.properties);
+ }
+ }
+ }
+
+ /* Sequences. */
+ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
+ if (scene->ed != NULL) {
+ LISTBASE_FOREACH (Sequence *, seq, &scene->ed->seqbase) {
+ version_idproperty_ui_data(seq->prop);
+ }
+ }
+ }
+}
+
static void sort_linked_ids(Main *bmain)
{
ListBase *lb;
@@ -252,6 +492,7 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports))
*/
{
/* Keep this block, even when empty. */
+ do_versions_idproperty_ui_data(bmain);
}
}
diff --git a/source/blender/io/alembic/exporter/abc_custom_props.cc b/source/blender/io/alembic/exporter/abc_custom_props.cc
index 4ea2fd03fff..e27a9012dab 100644
--- a/source/blender/io/alembic/exporter/abc_custom_props.cc
+++ b/source/blender/io/alembic/exporter/abc_custom_props.cc
@@ -60,9 +60,6 @@ void CustomPropertiesExporter::write_all(const IDProperty *group)
/* Loop over the properties, just like IDP_foreach_property() does, but without the recursion. */
LISTBASE_FOREACH (IDProperty *, id_property, &group->data.group) {
- if (STREQ(id_property->name, "_RNA_UI")) {
- continue;
- }
write(id_property);
}
}
diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h
index 10a5a0f1c47..5b7c99f2545 100644
--- a/source/blender/makesdna/DNA_ID.h
+++ b/source/blender/makesdna/DNA_ID.h
@@ -59,6 +59,58 @@ typedef struct DrawDataList {
struct DrawData *first, *last;
} DrawDataList;
+typedef struct IDPropertyUIData {
+ /** Tooltip / property description pointer. Owned by the IDProperty. */
+ char *description;
+ /** RNA subtype, used for every type except string properties (PropertySubType). */
+ int rna_subtype;
+
+ char _pad[4];
+} IDPropertyUIData;
+
+/* IDP_UI_DATA_TYPE_INT */
+typedef struct IDPropertyUIDataInt {
+ IDPropertyUIData base;
+ int *default_array; /* Only for array properties. */
+ int default_array_len;
+ char _pad[4];
+
+ int min;
+ int max;
+ int soft_min;
+ int soft_max;
+ int step;
+ int default_value;
+} IDPropertyUIDataInt;
+
+/* IDP_UI_DATA_TYPE_FLOAT */
+typedef struct IDPropertyUIDataFloat {
+ IDPropertyUIData base;
+ double *default_array; /* Only for array properties. */
+ int default_array_len;
+ char _pad[4];
+
+ float step;
+ int precision;
+
+ double min;
+ double max;
+ double soft_min;
+ double soft_max;
+ double default_value;
+} IDPropertyUIDataFloat;
+
+/* IDP_UI_DATA_TYPE_STRING */
+typedef struct IDPropertyUIDataString {
+ IDPropertyUIData base;
+ char *default_value;
+} IDPropertyUIDataString;
+
+/* IDP_UI_DATA_TYPE_ID */
+typedef struct IDPropertyUIDataID {
+ IDPropertyUIData base;
+} IDPropertyUIDataID;
+
typedef struct IDPropertyData {
void *pointer;
ListBase group;
@@ -87,6 +139,8 @@ typedef struct IDProperty {
/* totallen is total length of allocated array/string, including a buffer.
* Note that the buffering is mild; the code comes from python's list implementation. */
int totallen;
+
+ IDPropertyUIData *ui_data;
} IDProperty;
#define MAX_IDPROP_NAME 64
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index b943a8fad5a..abbe609d0ef 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -1006,7 +1006,7 @@ int RNA_property_int_get_index(PointerRNA *ptr, PropertyRNA *prop, int index);
void RNA_property_int_set_array(PointerRNA *ptr, PropertyRNA *prop, const int *values);
void RNA_property_int_set_index(PointerRNA *ptr, PropertyRNA *prop, int index, int value);
int RNA_property_int_get_default(PointerRNA *ptr, PropertyRNA *prop);
-bool RNA_property_int_set_default(PointerRNA *ptr, PropertyRNA *prop, int value);
+bool RNA_property_int_set_default(PropertyRNA *prop, int value);
void RNA_property_int_get_default_array(PointerRNA *ptr, PropertyRNA *prop, int *values);
int RNA_property_int_get_default_index(PointerRNA *ptr, PropertyRNA *prop, int index);
@@ -1018,7 +1018,7 @@ float RNA_property_float_get_index(PointerRNA *ptr, PropertyRNA *prop, int index
void RNA_property_float_set_array(PointerRNA *ptr, PropertyRNA *prop, const float *values);
void RNA_property_float_set_index(PointerRNA *ptr, PropertyRNA *prop, int index, float value);
float RNA_property_float_get_default(PointerRNA *ptr, PropertyRNA *prop);
-bool RNA_property_float_set_default(PointerRNA *ptr, PropertyRNA *prop, float value);
+bool RNA_property_float_set_default(PropertyRNA *prop, float value);
void RNA_property_float_get_default_array(PointerRNA *ptr, PropertyRNA *prop, float *values);
float RNA_property_float_get_default_index(PointerRNA *ptr, PropertyRNA *prop, int index);
@@ -1028,7 +1028,7 @@ char *RNA_property_string_get_alloc(
void RNA_property_string_set(PointerRNA *ptr, PropertyRNA *prop, const char *value);
void RNA_property_string_set_bytes(PointerRNA *ptr, PropertyRNA *prop, const char *value, int len);
int RNA_property_string_length(PointerRNA *ptr, PropertyRNA *prop);
-void RNA_property_string_get_default(PointerRNA *ptr, PropertyRNA *prop, char *value);
+void RNA_property_string_get_default(PropertyRNA *prop, char *value, int max_len);
char *RNA_property_string_get_default_alloc(
PointerRNA *ptr, PropertyRNA *prop, char *fixedbuf, int fixedlen, int *r_len);
int RNA_property_string_default_length(PointerRNA *ptr, PropertyRNA *prop);
diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c
index 8fa2d0ed849..2d6d5408aa0 100644
--- a/source/blender/makesrna/intern/rna_access.c
+++ b/source/blender/makesrna/intern/rna_access.c
@@ -246,130 +246,6 @@ void rna_idproperty_touch(IDProperty *idprop)
idprop->flag &= ~IDP_FLAG_GHOST;
}
-static IDProperty *rna_idproperty_ui_container(PropertyRNA *prop)
-{
- IDProperty *idprop;
-
- for (idprop = ((IDProperty *)prop)->prev; idprop; idprop = idprop->prev) {
- if (STREQ(RNA_IDP_UI, idprop->name)) {
- break;
- }
- }
-
- if (idprop == NULL) {
- for (idprop = ((IDProperty *)prop)->next; idprop; idprop = idprop->next) {
- if (STREQ(RNA_IDP_UI, idprop->name)) {
- break;
- }
- }
- }
-
- return idprop;
-}
-
-/* return a UI local ID prop definition for this prop */
-static const IDProperty *rna_idproperty_ui(const PropertyRNA *prop)
-{
- IDProperty *idprop = rna_idproperty_ui_container((PropertyRNA *)prop);
-
- if (idprop) {
- return IDP_GetPropertyTypeFromGroup(idprop, ((IDProperty *)prop)->name, IDP_GROUP);
- }
-
- return NULL;
-}
-
-/* return or create a UI local ID prop definition for this prop */
-static IDProperty *rna_idproperty_ui_ensure(PointerRNA *ptr, PropertyRNA *prop, bool create)
-{
- IDProperty *idprop = rna_idproperty_ui_container(prop);
- IDPropertyTemplate dummy = {0};
-
- if (idprop == NULL && create) {
- IDProperty *props = RNA_struct_idprops(ptr, false);
-
- /* Sanity check: props is the actual container of this property. */
- if (props != NULL && BLI_findindex(&props->data.group, prop) >= 0) {
- idprop = IDP_New(IDP_GROUP, &dummy, RNA_IDP_UI);
-
- if (!IDP_AddToGroup(props, idprop)) {
- IDP_FreePropertyContent(idprop);
- return NULL;
- }
- }
- }
-
- if (idprop) {
- const char *name = ((IDProperty *)prop)->name;
- IDProperty *rv = IDP_GetPropertyTypeFromGroup(idprop, name, IDP_GROUP);
-
- if (rv == NULL && create) {
- rv = IDP_New(IDP_GROUP, &dummy, name);
-
- if (!IDP_AddToGroup(idprop, rv)) {
- IDP_FreePropertyContent(rv);
- return NULL;
- }
- }
-
- return rv;
- }
-
- return NULL;
-}
-
-static bool rna_idproperty_ui_set_default(PointerRNA *ptr,
- PropertyRNA *prop,
- const char type,
- IDPropertyTemplate *value)
-{
- BLI_assert(ELEM(type, IDP_INT, IDP_DOUBLE));
-
- if (prop->magic == RNA_MAGIC) {
- return false;
- }
-
- /* attempt to get the local ID values */
- IDProperty *idp_ui = rna_idproperty_ui_ensure(ptr, prop, value != NULL);
-
- if (idp_ui == NULL) {
- return (value == NULL);
- }
-
- IDProperty *item = IDP_GetPropertyTypeFromGroup(idp_ui, "default", type);
-
- if (value == NULL) {
- if (item != NULL) {
- IDP_RemoveFromGroup(idp_ui, item);
- }
- }
- else {
- if (item != NULL) {
- switch (type) {
- case IDP_INT:
- IDP_Int(item) = value->i;
- break;
- case IDP_DOUBLE:
- IDP_Double(item) = value->d;
- break;
- default:
- BLI_assert(false);
- return false;
- }
- }
- else {
- item = IDP_New(type, value, "default");
-
- if (!IDP_AddToGroup(idp_ui, item)) {
- IDP_FreePropertyContent(item);
- return false;
- }
- }
- }
-
- return true;
-}
-
IDProperty **RNA_struct_idprops_p(PointerRNA *ptr)
{
StructRNA *type = ptr->type;
@@ -693,28 +569,17 @@ static const char *rna_ensure_property_identifier(const PropertyRNA *prop)
static const char *rna_ensure_property_description(const PropertyRNA *prop)
{
- const char *description = NULL;
-
if (prop->magic == RNA_MAGIC) {
- description = prop->description;
+ return prop->description;
}
- else {
- /* attempt to get the local ID values */
- const IDProperty *idp_ui = rna_idproperty_ui(prop);
-
- if (idp_ui) {
- IDProperty *item = IDP_GetPropertyTypeFromGroup(idp_ui, "description", IDP_STRING);
- if (item) {
- description = IDP_String(item);
- }
- }
- if (description == NULL) {
- description = ((IDProperty *)prop)->name; /* XXX: not correct. */
- }
+ const IDProperty *idprop = (const IDProperty *)prop;
+ if (idprop->ui_data) {
+ const IDPropertyUIData *ui_data = idprop->ui_data;
+ return ui_data->description;
}
- return description;
+ return "";
}
static const char *rna_ensure_property_name(const PropertyRNA *prop)
@@ -1196,19 +1061,9 @@ PropertySubType RNA_property_subtype(PropertyRNA *prop)
if (prop->magic != RNA_MAGIC) {
IDProperty *idprop = (IDProperty *)prop;
- if (ELEM(idprop->type, IDP_INT, IDP_FLOAT, IDP_DOUBLE) ||
- ((idprop->type == IDP_ARRAY) && ELEM(idprop->subtype, IDP_INT, IDP_FLOAT, IDP_DOUBLE))) {
- const IDProperty *idp_ui = rna_idproperty_ui(prop);
-
- if (idp_ui) {
- IDProperty *item = IDP_GetPropertyTypeFromGroup(idp_ui, "subtype", IDP_STRING);
-
- if (item) {
- int result = PROP_NONE;
- RNA_enum_value_from_id(rna_enum_property_subtype_items, IDP_String(item), &result);
- return (PropertySubType)result;
- }
- }
+ if (idprop->ui_data) {
+ IDPropertyUIData *ui_data = idprop->ui_data;
+ return (PropertySubType)ui_data->rna_subtype;
}
}
@@ -1387,20 +1242,17 @@ void RNA_property_int_range(PointerRNA *ptr, PropertyRNA *prop, int *hardmin, in
int softmin, softmax;
if (prop->magic != RNA_MAGIC) {
- /* attempt to get the local ID values */
- const IDProperty *idp_ui = rna_idproperty_ui(prop);
-
- if (idp_ui) {
- IDProperty *item;
-
- item = IDP_GetPropertyTypeFromGroup(idp_ui, "min", IDP_INT);
- *hardmin = item ? IDP_Int(item) : INT_MIN;
-
- item = IDP_GetPropertyTypeFromGroup(idp_ui, "max", IDP_INT);
- *hardmax = item ? IDP_Int(item) : INT_MAX;
-
- return;
+ const IDProperty *idprop = (IDProperty *)prop;
+ if (idprop->ui_data) {
+ IDPropertyUIDataInt *ui_data = (IDPropertyUIDataInt *)idprop->ui_data;
+ *hardmin = ui_data->min;
+ *hardmax = ui_data->max;
+ }
+ else {
+ *hardmin = INT_MIN;
+ *hardmax = INT_MAX;
}
+ return;
}
if (iprop->range) {
@@ -1428,23 +1280,19 @@ void RNA_property_int_ui_range(
int hardmin, hardmax;
if (prop->magic != RNA_MAGIC) {
- /* attempt to get the local ID values */
- const IDProperty *idp_ui = rna_idproperty_ui(prop);
-
- if (idp_ui) {
- IDProperty *item;
-
- item = IDP_GetPropertyTypeFromGroup(idp_ui, "soft_min", IDP_INT);
- *softmin = item ? IDP_Int(item) : INT_MIN;
-
- item = IDP_GetPropertyTypeFromGroup(idp_ui, "soft_max", IDP_INT);
- *softmax = item ? IDP_Int(item) : INT_MAX;
-
- item = IDP_GetPropertyTypeFromGroup(idp_ui, "step", IDP_INT);
- *step = item ? IDP_Int(item) : 1;
-
- return;
+ const IDProperty *idprop = (IDProperty *)prop;
+ if (idprop->ui_data) {
+ IDPropertyUIDataInt *ui_data_int = (IDPropertyUIDataInt *)idprop->ui_data;
+ *softmin = ui_data_int->soft_min;
+ *softmax = ui_data_int->soft_max;
+ *step = ui_data_int->step;
}
+ else {
+ *softmin = INT_MIN;
+ *softmax = INT_MAX;
+ *step = 1;
+ }
+ return;
}
*softmin = iprop->softmin;
@@ -1478,20 +1326,17 @@ void RNA_property_float_range(PointerRNA *ptr, PropertyRNA *prop, float *hardmin
float softmin, softmax;
if (prop->magic != RNA_MAGIC) {
- /* attempt to get the local ID values */
- const IDProperty *idp_ui = rna_idproperty_ui(prop);
-
- if (idp_ui) {
- IDProperty *item;
-
- item = IDP_GetPropertyTypeFromGroup(idp_ui, "min", IDP_DOUBLE);
- *hardmin = item ? (float)IDP_Double(item) : -FLT_MAX;
-
- item = IDP_GetPropertyTypeFromGroup(idp_ui, "max", IDP_DOUBLE);
- *hardmax = item ? (float)IDP_Double(item) : FLT_MAX;
-
- return;
+ const IDProperty *idprop = (IDProperty *)prop;
+ if (idprop->ui_data) {
+ IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)idprop->ui_data;
+ *hardmin = (float)ui_data->min;
+ *hardmax = (float)ui_data->max;
+ }
+ else {
+ *hardmin = FLT_MIN;
+ *hardmax = FLT_MAX;
}
+ return;
}
if (fprop->range) {
@@ -1523,26 +1368,21 @@ void RNA_property_float_ui_range(PointerRNA *ptr,
float hardmin, hardmax;
if (prop->magic != RNA_MAGIC) {
- /* attempt to get the local ID values */
- const IDProperty *idp_ui = rna_idproperty_ui(prop);
-
- if (idp_ui) {
- IDProperty *item;
-
- item = IDP_GetPropertyTypeFromGroup(idp_ui, "soft_min", IDP_DOUBLE);
- *softmin = item ? (float)IDP_Double(item) : -FLT_MAX;
-
- item = IDP_GetPropertyTypeFromGroup(idp_ui, "soft_max", IDP_DOUBLE);
- *softmax = item ? (float)IDP_Double(item) : FLT_MAX;
-
- item = IDP_GetPropertyTypeFromGroup(idp_ui, "step", IDP_DOUBLE);
- *step = item ? (float)IDP_Double(item) : 1.0f;
-
- item = IDP_GetPropertyTypeFromGroup(idp_ui, "precision", IDP_DOUBLE);
- *precision = item ? (float)IDP_Double(item) : 3.0f;
-
- return;
+ const IDProperty *idprop = (IDProperty *)prop;
+ if (idprop->ui_data) {
+ IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)idprop->ui_data;
+ *softmin = (float)ui_data->soft_min;
+ *softmax = (float)ui_data->soft_max;
+ *step = ui_data->step;
+ *precision = (float)ui_data->precision;
+ }
+ else {
+ *softmin = FLT_MIN;
+ *softmax = FLT_MAX;
+ *step = 1.0f;
+ *precision = 3.0f;
}
+ return;
}
*softmin = fprop->softmin;
@@ -2905,29 +2745,28 @@ int RNA_property_int_get_default(PointerRNA *UNUSED(ptr), PropertyRNA *prop)
IntPropertyRNA *iprop = (IntPropertyRNA *)rna_ensure_property(prop);
if (prop->magic != RNA_MAGIC) {
- /* attempt to get the local ID values */
- const IDProperty *idp_ui = rna_idproperty_ui(prop);
-
- if (idp_ui) {
- IDProperty *item;
-
- item = IDP_GetPropertyTypeFromGroup(idp_ui, "default", IDP_INT);
- return item ? IDP_Int(item) : iprop->defaultvalue;
+ const IDProperty *idprop = (const IDProperty *)prop;
+ if (idprop->ui_data) {
+ const IDPropertyUIDataInt *ui_data = (const IDPropertyUIDataInt *)idprop->ui_data;
+ return ui_data->default_value;
}
}
return iprop->defaultvalue;
}
-bool RNA_property_int_set_default(PointerRNA *ptr, PropertyRNA *prop, int value)
+bool RNA_property_int_set_default(PropertyRNA *prop, int value)
{
- if (value != 0) {
- IDPropertyTemplate val = {
- .i = value,
- };
- return rna_idproperty_ui_set_default(ptr, prop, IDP_INT, &val);
+ if (prop->magic == RNA_MAGIC) {
+ return false;
}
- return rna_idproperty_ui_set_default(ptr, prop, IDP_INT, NULL);
+
+ IDProperty *idprop = (IDProperty *)prop;
+ BLI_assert(idprop->type == IDP_INT);
+
+ IDPropertyUIDataInt *ui_data = (IDPropertyUIDataInt *)IDP_ui_data_ensure(idprop);
+ ui_data->default_value = value;
+ return true;
}
void RNA_property_int_get_default_array(PointerRNA *ptr, PropertyRNA *prop, int *values)
@@ -2940,17 +2779,22 @@ void RNA_property_int_get_default_array(PointerRNA *ptr, PropertyRNA *prop, int
if (prop->magic != RNA_MAGIC) {
int length = rna_ensure_property_array_length(ptr, prop);
- const IDProperty *idp_ui = rna_idproperty_ui(prop);
- IDProperty *item = idp_ui ? IDP_GetPropertyFromGroup(idp_ui, "default") : NULL;
-
- int defval = (item && item->type == IDP_INT) ? IDP_Int(item) : iprop->defaultvalue;
-
- if (item && item->type == IDP_ARRAY && item->subtype == IDP_INT) {
- rna_property_int_fill_default_array_values(
- IDP_Array(item), item->len, defval, length, values);
- }
- else {
- rna_property_int_fill_default_array_values(NULL, 0, defval, length, values);
+ const IDProperty *idprop = (const IDProperty *)prop;
+ if (idprop->ui_data) {
+ BLI_assert(idprop->type == IDP_ARRAY);
+ BLI_assert(idprop->subtype == IDP_INT);
+ const IDPropertyUIDataInt *ui_data = (const IDPropertyUIDataInt *)idprop->ui_data;
+ if (ui_data->default_array) {
+ rna_property_int_fill_default_array_values(ui_data->default_array,
+ ui_data->default_array_len,
+ ui_data->default_value,
+ length,
+ values);
+ }
+ else {
+ rna_property_int_fill_default_array_values(
+ NULL, 0, ui_data->default_value, length, values);
+ }
}
}
else if (prop->arraydimension == 0) {
@@ -3066,6 +2910,26 @@ static void rna_property_float_fill_default_array_values(
}
}
+/**
+ * The same logic as #rna_property_float_fill_default_array_values for a double array.
+ */
+static void rna_property_float_fill_default_array_values_double(const double *default_array,
+ const int default_array_len,
+ const double default_value,
+ const int out_length,
+ float *r_values)
+{
+ const int array_copy_len = MIN2(out_length, default_array_len);
+
+ for (int i = 0; i < array_copy_len; i++) {
+ r_values[i] = (float)default_array[i];
+ }
+
+ for (int i = array_copy_len; i < out_length; i++) {
+ r_values[i] = (float)default_value;
+ }
+}
+
static void rna_property_float_get_default_array_values(PointerRNA *ptr,
FloatPropertyRNA *fprop,
float *r_values)
@@ -3268,29 +3132,29 @@ float RNA_property_float_get_default(PointerRNA *UNUSED(ptr), PropertyRNA *prop)
BLI_assert(RNA_property_array_check(prop) == false);
if (prop->magic != RNA_MAGIC) {
- /* attempt to get the local ID values */
- const IDProperty *idp_ui = rna_idproperty_ui(prop);
-
- if (idp_ui) {
- IDProperty *item;
-
- item = IDP_GetPropertyTypeFromGroup(idp_ui, "default", IDP_DOUBLE);
- return item ? IDP_Double(item) : fprop->defaultvalue;
+ const IDProperty *idprop = (const IDProperty *)prop;
+ if (idprop->ui_data) {
+ BLI_assert(ELEM(idprop->type, IDP_FLOAT, IDP_DOUBLE));
+ const IDPropertyUIDataFloat *ui_data = (const IDPropertyUIDataFloat *)idprop->ui_data;
+ return (float)ui_data->default_value;
}
}
return fprop->defaultvalue;
}
-bool RNA_property_float_set_default(PointerRNA *ptr, PropertyRNA *prop, float value)
+bool RNA_property_float_set_default(PropertyRNA *prop, float value)
{
- if (value != 0) {
- IDPropertyTemplate val = {
- .d = value,
- };
- return rna_idproperty_ui_set_default(ptr, prop, IDP_DOUBLE, &val);
+ if (prop->magic == RNA_MAGIC) {
+ return false;
}
- return rna_idproperty_ui_set_default(ptr, prop, IDP_DOUBLE, NULL);
+
+ IDProperty *idprop = (IDProperty *)prop;
+ BLI_assert(idprop->type == IDP_FLOAT);
+
+ IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(idprop);
+ ui_data->default_value = (double)value;
+ return true;
}
void RNA_property_float_get_default_array(PointerRNA *ptr, PropertyRNA *prop, float *values)
@@ -3303,23 +3167,16 @@ void RNA_property_float_get_default_array(PointerRNA *ptr, PropertyRNA *prop, fl
if (prop->magic != RNA_MAGIC) {
int length = rna_ensure_property_array_length(ptr, prop);
- const IDProperty *idp_ui = rna_idproperty_ui(prop);
- IDProperty *item = idp_ui ? IDP_GetPropertyFromGroup(idp_ui, "default") : NULL;
-
- float defval = (item && item->type == IDP_DOUBLE) ? IDP_Double(item) : fprop->defaultvalue;
-
- if (item && item->type == IDP_ARRAY && item->subtype == IDP_DOUBLE) {
- double *defarr = IDP_Array(item);
- for (int i = 0; i < length; i++) {
- values[i] = (i < item->len) ? (float)defarr[i] : defval;
- }
- }
- else if (item && item->type == IDP_ARRAY && item->subtype == IDP_FLOAT) {
- rna_property_float_fill_default_array_values(
- IDP_Array(item), item->len, defval, length, values);
- }
- else {
- rna_property_float_fill_default_array_values(NULL, 0, defval, length, values);
+ const IDProperty *idprop = (const IDProperty *)prop;
+ if (idprop->ui_data) {
+ BLI_assert(idprop->type == IDP_ARRAY);
+ BLI_assert(ELEM(idprop->subtype, IDP_FLOAT, IDP_DOUBLE));
+ const IDPropertyUIDataFloat *ui_data = (const IDPropertyUIDataFloat *)idprop->ui_data;
+ rna_property_float_fill_default_array_values_double(ui_data->default_array,
+ ui_data->default_array_len,
+ ui_data->default_value,
+ length,
+ values);
}
}
else if (prop->arraydimension == 0) {
@@ -3510,22 +3367,17 @@ void RNA_property_string_set_bytes(PointerRNA *ptr, PropertyRNA *prop, const cha
}
}
-void RNA_property_string_get_default(PointerRNA *UNUSED(ptr), PropertyRNA *prop, char *value)
+void RNA_property_string_get_default(PropertyRNA *prop, char *value, const int max_len)
{
StringPropertyRNA *sprop = (StringPropertyRNA *)rna_ensure_property(prop);
if (prop->magic != RNA_MAGIC) {
- /* attempt to get the local ID values */
- const IDProperty *idp_ui = rna_idproperty_ui(prop);
-
- if (idp_ui) {
- IDProperty *item;
-
- item = IDP_GetPropertyTypeFromGroup(idp_ui, "default", IDP_STRING);
- if (item) {
- strcpy(value, IDP_String(item));
- return;
- }
+ const IDProperty *idprop = (const IDProperty *)prop;
+ if (idprop->ui_data) {
+ BLI_assert(idprop->type == IDP_STRING);
+ const IDPropertyUIDataString *ui_data = (const IDPropertyUIDataString *)idprop->ui_data;
+ BLI_strncpy(value, ui_data->default_value, max_len);
+ return;
}
strcpy(value, "");
@@ -3554,7 +3406,7 @@ char *RNA_property_string_get_default_alloc(
buf = MEM_callocN(sizeof(char) * (length + 1), __func__);
}
- RNA_property_string_get_default(ptr, prop, buf);
+ RNA_property_string_get_default(prop, buf, length + 1);
if (r_len) {
*r_len = length;
@@ -3569,15 +3421,12 @@ int RNA_property_string_default_length(PointerRNA *UNUSED(ptr), PropertyRNA *pro
StringPropertyRNA *sprop = (StringPropertyRNA *)rna_ensure_property(prop);
if (prop->magic != RNA_MAGIC) {
- /* attempt to get the local ID values */
- const IDProperty *idp_ui = rna_idproperty_ui(prop);
-
- if (idp_ui) {
- IDProperty *item;
-
- item = IDP_GetPropertyTypeFromGroup(idp_ui, "default", IDP_STRING);
- if (item) {
- return strlen(IDP_String(item));
+ const IDProperty *idprop = (const IDProperty *)prop;
+ if (idprop->ui_data) {
+ BLI_assert(idprop->type == IDP_STRING);
+ const IDPropertyUIDataString *ui_data = (const IDPropertyUIDataString *)idprop->ui_data;
+ if (ui_data->default_value != NULL) {
+ return strlen(ui_data->default_value);
}
}
@@ -8200,12 +8049,12 @@ bool RNA_property_assign_default(PointerRNA *ptr, PropertyRNA *prop)
switch (RNA_property_type(prop)) {
case PROP_INT: {
int value = RNA_property_int_get(ptr, prop);
- return RNA_property_int_set_default(ptr, prop, value);
+ return RNA_property_int_set_default(prop, value);
}
case PROP_FLOAT: {
float value = RNA_property_float_get(ptr, prop);
- return RNA_property_float_set_default(ptr, prop, value);
+ return RNA_property_float_set_default(prop, value);
}
default:
diff --git a/source/blender/makesrna/intern/rna_internal_types.h b/source/blender/makesrna/intern/rna_internal_types.h
index 479306e8c06..22a9b9d930a 100644
--- a/source/blender/makesrna/intern/rna_internal_types.h
+++ b/source/blender/makesrna/intern/rna_internal_types.h
@@ -42,9 +42,6 @@ struct bContext;
typedef struct IDProperty IDProperty;
-/* store local properties here */
-#define RNA_IDP_UI "_RNA_UI"
-
/* Function Callbacks */
typedef void (*UpdateFunc)(struct Main *main, struct Scene *scene, struct PointerRNA *ptr);
diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc
index 620c7ef438a..9d8630b21e7 100644
--- a/source/blender/modifiers/intern/MOD_nodes.cc
+++ b/source/blender/modifiers/intern/MOD_nodes.cc
@@ -289,348 +289,178 @@ static bool logging_enabled(const ModifierEvalContext *ctx)
return true;
}
-/**
- * This code is responsible for creating the new property and also creating the group of
- * properties in the prop_ui_container group for the UI info, the mapping for which is
- * scattered about in RNA_access.c.
- *
- * TODO(Hans): Codify this with some sort of table or refactor IDProperty use in RNA_access.c.
- */
-struct SocketPropertyType {
- /* Create the actual property used to store the data for the modifier. */
- IDProperty *(*create_prop)(const bNodeSocket &socket, const char *name);
- /* Reused to build the "soft_min" property too. */
- IDProperty *(*create_min_ui_prop)(const bNodeSocket &socket, const char *name);
- /* Reused to build the "soft_max" property too. */
- IDProperty *(*create_max_ui_prop)(const bNodeSocket &socket, const char *name);
- /* This uses the same values as #create_prop, but sometimes the type is different, so it can't
- * be the same function. */
- IDProperty *(*create_default_ui_prop)(const bNodeSocket &socket, const char *name);
- PropertyType (*rna_subtype_get)(const bNodeSocket &socket);
- bool (*is_correct_type)(const IDProperty &property);
- void (*init_cpp_value)(const IDProperty &property, void *r_value);
-};
-
-static IDProperty *socket_add_property(IDProperty *settings_prop_group,
- IDProperty *ui_container,
- const SocketPropertyType &property_type,
- const bNodeSocket &socket)
+static IDProperty *id_property_create_from_socket(const bNodeSocket &socket)
{
- const char *new_prop_name = socket.identifier;
- /* Add the property actually storing the data to the modifier's group. */
- IDProperty *prop = property_type.create_prop(socket, new_prop_name);
- IDP_AddToGroup(settings_prop_group, prop);
-
- prop->flag |= IDP_FLAG_OVERRIDABLE_LIBRARY;
-
- /* Make the group in the UI container group to hold the property's UI settings. */
- IDProperty *prop_ui_group;
- {
- IDPropertyTemplate idprop = {0};
- prop_ui_group = IDP_New(IDP_GROUP, &idprop, new_prop_name);
- IDP_AddToGroup(ui_container, prop_ui_group);
- }
-
- /* Set property description (tooltip). */
- IDPropertyTemplate property_description_template;
- property_description_template.string.str = socket.description;
- property_description_template.string.len = BLI_strnlen(socket.description, MAX_NAME) + 1;
- property_description_template.string.subtype = IDP_STRING_SUB_UTF8;
- IDProperty *description = IDP_New(IDP_STRING, &property_description_template, "description");
- IDP_AddToGroup(prop_ui_group, description);
-
- /* Create the properties for the socket's UI settings. */
- if (property_type.create_min_ui_prop != nullptr) {
- IDP_AddToGroup(prop_ui_group, property_type.create_min_ui_prop(socket, "min"));
- IDP_AddToGroup(prop_ui_group, property_type.create_min_ui_prop(socket, "soft_min"));
- }
- if (property_type.create_max_ui_prop != nullptr) {
- IDP_AddToGroup(prop_ui_group, property_type.create_max_ui_prop(socket, "max"));
- IDP_AddToGroup(prop_ui_group, property_type.create_max_ui_prop(socket, "soft_max"));
- }
- if (property_type.create_default_ui_prop != nullptr) {
- IDP_AddToGroup(prop_ui_group, property_type.create_default_ui_prop(socket, "default"));
- }
- if (property_type.rna_subtype_get != nullptr) {
- const char *subtype_identifier = nullptr;
- RNA_enum_identifier(rna_enum_property_subtype_items,
- property_type.rna_subtype_get(socket),
- &subtype_identifier);
-
- if (subtype_identifier != nullptr) {
+ switch (socket.type) {
+ case SOCK_FLOAT: {
+ bNodeSocketValueFloat *value = (bNodeSocketValueFloat *)socket.default_value;
+ IDPropertyTemplate idprop = {0};
+ idprop.f = value->value;
+ IDProperty *property = IDP_New(IDP_FLOAT, &idprop, socket.identifier);
+ IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property);
+ ui_data->base.rna_subtype = value->subtype;
+ ui_data->min = ui_data->soft_min = (double)value->min;
+ ui_data->max = ui_data->soft_max = (double)value->max;
+ ui_data->default_value = value->value;
+ return property;
+ }
+ case SOCK_INT: {
+ bNodeSocketValueInt *value = (bNodeSocketValueInt *)socket.default_value;
+ IDPropertyTemplate idprop = {0};
+ idprop.i = value->value;
+ IDProperty *property = IDP_New(IDP_INT, &idprop, socket.identifier);
+ IDPropertyUIDataInt *ui_data = (IDPropertyUIDataInt *)IDP_ui_data_ensure(property);
+ ui_data->base.rna_subtype = value->subtype;
+ ui_data->min = ui_data->soft_min = value->min;
+ ui_data->max = ui_data->soft_max = value->max;
+ ui_data->default_value = value->value;
+ return property;
+ }
+ case SOCK_VECTOR: {
+ bNodeSocketValueVector *value = (bNodeSocketValueVector *)socket.default_value;
+ IDPropertyTemplate idprop = {0};
+ idprop.array.len = 3;
+ idprop.array.type = IDP_FLOAT;
+ IDProperty *property = IDP_New(IDP_ARRAY, &idprop, socket.identifier);
+ copy_v3_v3((float *)IDP_Array(property), value->value);
+ IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property);
+ ui_data->base.rna_subtype = value->subtype;
+ ui_data->min = ui_data->soft_min = (double)value->min;
+ ui_data->max = ui_data->soft_max = (double)value->max;
+ ui_data->default_array = (double *)MEM_mallocN(sizeof(double[3]), "mod_prop_default");
+ ui_data->default_array_len = 3;
+ for (int i = 3; i < 3; i++) {
+ ui_data->default_array[i] = (double)value->value[i];
+ }
+ return property;
+ }
+ case SOCK_BOOLEAN: {
+ bNodeSocketValueBoolean *value = (bNodeSocketValueBoolean *)socket.default_value;
IDPropertyTemplate idprop = {0};
- idprop.string.str = subtype_identifier;
- idprop.string.len = BLI_strnlen(subtype_identifier, MAX_NAME) + 1;
- IDP_AddToGroup(prop_ui_group, IDP_New(IDP_STRING, &idprop, "subtype"));
+ idprop.i = value->value != 0;
+ IDProperty *property = IDP_New(IDP_INT, &idprop, socket.identifier);
+ IDPropertyUIDataInt *ui_data = (IDPropertyUIDataInt *)IDP_ui_data_ensure(property);
+ ui_data->min = ui_data->soft_min = 0;
+ ui_data->max = ui_data->soft_max = 1;
+ ui_data->default_value = value->value != 0;
+ return property;
+ }
+ case SOCK_STRING: {
+ bNodeSocketValueString *value = (bNodeSocketValueString *)socket.default_value;
+ IDProperty *property = IDP_NewString(
+ value->value, socket.identifier, BLI_strnlen(value->value, sizeof(value->value)) + 1);
+ IDPropertyUIDataString *ui_data = (IDPropertyUIDataString *)IDP_ui_data_ensure(property);
+ ui_data->default_value = BLI_strdup(value->value);
+ return property;
+ }
+ case SOCK_OBJECT: {
+ bNodeSocketValueObject *value = (bNodeSocketValueObject *)socket.default_value;
+ IDPropertyTemplate idprop = {0};
+ idprop.id = (ID *)value->value;
+ return IDP_New(IDP_ID, &idprop, socket.identifier);
+ }
+ case SOCK_COLLECTION: {
+ bNodeSocketValueCollection *value = (bNodeSocketValueCollection *)socket.default_value;
+ IDPropertyTemplate idprop = {0};
+ idprop.id = (ID *)value->value;
+ return IDP_New(IDP_ID, &idprop, socket.identifier);
+ }
+ case SOCK_TEXTURE: {
+ bNodeSocketValueTexture *value = (bNodeSocketValueTexture *)socket.default_value;
+ IDPropertyTemplate idprop = {0};
+ idprop.id = (ID *)value->value;
+ return IDP_New(IDP_ID, &idprop, socket.identifier);
+ }
+ case SOCK_MATERIAL: {
+ bNodeSocketValueMaterial *value = (bNodeSocketValueMaterial *)socket.default_value;
+ IDPropertyTemplate idprop = {0};
+ idprop.id = (ID *)value->value;
+ return IDP_New(IDP_ID, &idprop, socket.identifier);
}
}
+ return nullptr;
+}
- return prop;
+static bool id_property_type_matches_socket(const bNodeSocket &socket, const IDProperty &property)
+{
+ switch (socket.type) {
+ case SOCK_FLOAT:
+ return ELEM(property.type, IDP_FLOAT, IDP_DOUBLE);
+ case SOCK_INT:
+ return property.type == IDP_INT;
+ case SOCK_VECTOR:
+ return property.type == IDP_ARRAY && property.subtype == IDP_FLOAT && property.len == 3;
+ case SOCK_BOOLEAN:
+ return property.type == IDP_INT;
+ case SOCK_STRING:
+ return property.type == IDP_STRING;
+ case SOCK_OBJECT:
+ case SOCK_COLLECTION:
+ case SOCK_TEXTURE:
+ case SOCK_MATERIAL:
+ return property.type == IDP_ID;
+ }
+ BLI_assert_unreachable();
+ return false;
}
-static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bsocket)
+static void init_socket_cpp_value_from_property(const IDProperty &property,
+ const eNodeSocketDatatype socket_value_type,
+ void *r_value)
{
- switch (bsocket.type) {
+ switch (socket_value_type) {
case SOCK_FLOAT: {
- static const SocketPropertyType float_type = {
- [](const bNodeSocket &socket, const char *name) {
- bNodeSocketValueFloat *value = (bNodeSocketValueFloat *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.f = value->value;
- return IDP_New(IDP_FLOAT, &idprop, name);
- },
- [](const bNodeSocket &socket, const char *name) {
- bNodeSocketValueFloat *value = (bNodeSocketValueFloat *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.d = value->min;
- return IDP_New(IDP_DOUBLE, &idprop, name);
- },
- [](const bNodeSocket &socket, const char *name) {
- bNodeSocketValueFloat *value = (bNodeSocketValueFloat *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.d = value->max;
- return IDP_New(IDP_DOUBLE, &idprop, name);
- },
- [](const bNodeSocket &socket, const char *name) {
- bNodeSocketValueFloat *value = (bNodeSocketValueFloat *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.d = value->value;
- return IDP_New(IDP_DOUBLE, &idprop, name);
- },
- [](const bNodeSocket &socket) {
- return (PropertyType)((bNodeSocketValueFloat *)socket.default_value)->subtype;
- },
- [](const IDProperty &property) { return ELEM(property.type, IDP_FLOAT, IDP_DOUBLE); },
- [](const IDProperty &property, void *r_value) {
- if (property.type == IDP_FLOAT) {
- *(float *)r_value = IDP_Float(&property);
- }
- else if (property.type == IDP_DOUBLE) {
- *(float *)r_value = (float)IDP_Double(&property);
- }
- },
- };
- return &float_type;
+ if (property.type == IDP_FLOAT) {
+ *(float *)r_value = IDP_Float(&property);
+ }
+ else if (property.type == IDP_DOUBLE) {
+ *(float *)r_value = (float)IDP_Double(&property);
+ }
+ break;
}
case SOCK_INT: {
- static const SocketPropertyType int_type = {
- [](const bNodeSocket &socket, const char *name) {
- bNodeSocketValueInt *value = (bNodeSocketValueInt *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.i = value->value;
- return IDP_New(IDP_INT, &idprop, name);
- },
- [](const bNodeSocket &socket, const char *name) {
- bNodeSocketValueInt *value = (bNodeSocketValueInt *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.i = value->min;
- return IDP_New(IDP_INT, &idprop, name);
- },
- [](const bNodeSocket &socket, const char *name) {
- bNodeSocketValueInt *value = (bNodeSocketValueInt *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.i = value->max;
- return IDP_New(IDP_INT, &idprop, name);
- },
- [](const bNodeSocket &socket, const char *name) {
- bNodeSocketValueInt *value = (bNodeSocketValueInt *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.i = value->value;
- return IDP_New(IDP_INT, &idprop, name);
- },
- [](const bNodeSocket &socket) {
- return (PropertyType)((bNodeSocketValueInt *)socket.default_value)->subtype;
- },
- [](const IDProperty &property) { return property.type == IDP_INT; },
- [](const IDProperty &property, void *r_value) { *(int *)r_value = IDP_Int(&property); },
- };
- return &int_type;
+ *(int *)r_value = IDP_Int(&property);
+ break;
}
case SOCK_VECTOR: {
- static const SocketPropertyType vector_type = {
- [](const bNodeSocket &socket, const char *name) {
- bNodeSocketValueVector *value = (bNodeSocketValueVector *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.array.len = 3;
- idprop.array.type = IDP_FLOAT;
- IDProperty *property = IDP_New(IDP_ARRAY, &idprop, name);
- copy_v3_v3((float *)IDP_Array(property), value->value);
- return property;
- },
- [](const bNodeSocket &socket, const char *name) {
- bNodeSocketValueVector *value = (bNodeSocketValueVector *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.d = value->min;
- return IDP_New(IDP_DOUBLE, &idprop, name);
- },
- [](const bNodeSocket &socket, const char *name) {
- bNodeSocketValueVector *value = (bNodeSocketValueVector *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.d = value->max;
- return IDP_New(IDP_DOUBLE, &idprop, name);
- },
- [](const bNodeSocket &socket, const char *name) {
- bNodeSocketValueVector *value = (bNodeSocketValueVector *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.array.len = 3;
- idprop.array.type = IDP_FLOAT;
- IDProperty *property = IDP_New(IDP_ARRAY, &idprop, name);
- copy_v3_v3((float *)IDP_Array(property), value->value);
- return property;
- },
- [](const bNodeSocket &socket) {
- return (PropertyType)((bNodeSocketValueVector *)socket.default_value)->subtype;
- },
- [](const IDProperty &property) {
- return property.type == IDP_ARRAY && property.subtype == IDP_FLOAT &&
- property.len == 3;
- },
- [](const IDProperty &property, void *r_value) {
- copy_v3_v3((float *)r_value, (const float *)IDP_Array(&property));
- },
- };
- return &vector_type;
+ copy_v3_v3((float *)r_value, (const float *)IDP_Array(&property));
+ break;
}
case SOCK_BOOLEAN: {
- static const SocketPropertyType boolean_type = {
- [](const bNodeSocket &socket, const char *name) {
- bNodeSocketValueBoolean *value = (bNodeSocketValueBoolean *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.i = value->value != 0;
- return IDP_New(IDP_INT, &idprop, name);
- },
- [](const bNodeSocket &UNUSED(socket), const char *name) {
- IDPropertyTemplate idprop = {0};
- idprop.i = 0;
- return IDP_New(IDP_INT, &idprop, name);
- },
- [](const bNodeSocket &UNUSED(socket), const char *name) {
- IDPropertyTemplate idprop = {0};
- idprop.i = 1;
- return IDP_New(IDP_INT, &idprop, name);
- },
- [](const bNodeSocket &socket, const char *name) {
- bNodeSocketValueBoolean *value = (bNodeSocketValueBoolean *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.i = value->value != 0;
- return IDP_New(IDP_INT, &idprop, name);
- },
- nullptr,
- [](const IDProperty &property) { return property.type == IDP_INT; },
- [](const IDProperty &property, void *r_value) {
- *(bool *)r_value = IDP_Int(&property) != 0;
- },
- };
- return &boolean_type;
+ *(bool *)r_value = IDP_Int(&property) != 0;
+ break;
}
case SOCK_STRING: {
- static const SocketPropertyType string_type = {
- [](const bNodeSocket &socket, const char *name) {
- bNodeSocketValueString *value = (bNodeSocketValueString *)socket.default_value;
- return IDP_NewString(
- value->value, name, BLI_strnlen(value->value, sizeof(value->value)) + 1);
- },
- nullptr,
- nullptr,
- [](const bNodeSocket &socket, const char *name) {
- bNodeSocketValueString *value = (bNodeSocketValueString *)socket.default_value;
- return IDP_NewString(
- value->value, name, BLI_strnlen(value->value, sizeof(value->value)) + 1);
- },
- nullptr,
- [](const IDProperty &property) { return property.type == IDP_STRING; },
- [](const IDProperty &property, void *r_value) {
- new (r_value) std::string(IDP_String(&property));
- },
- };
- return &string_type;
+ new (r_value) std::string(IDP_String(&property));
+ break;
}
case SOCK_OBJECT: {
- static const SocketPropertyType object_type = {
- [](const bNodeSocket &socket, const char *name) {
- bNodeSocketValueObject *value = (bNodeSocketValueObject *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.id = (ID *)value->value;
- return IDP_New(IDP_ID, &idprop, name);
- },
- nullptr,
- nullptr,
- nullptr,
- nullptr,
- [](const IDProperty &property) { return property.type == IDP_ID; },
- [](const IDProperty &property, void *r_value) {
- ID *id = IDP_Id(&property);
- Object *object = (id && GS(id->name) == ID_OB) ? (Object *)id : nullptr;
- *(Object **)r_value = object;
- },
- };
- return &object_type;
+ ID *id = IDP_Id(&property);
+ Object *object = (id && GS(id->name) == ID_OB) ? (Object *)id : nullptr;
+ *(Object **)r_value = object;
+ break;
}
case SOCK_COLLECTION: {
- static const SocketPropertyType collection_type = {
- [](const bNodeSocket &socket, const char *name) {
- bNodeSocketValueCollection *value = (bNodeSocketValueCollection *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.id = (ID *)value->value;
- return IDP_New(IDP_ID, &idprop, name);
- },
- nullptr,
- nullptr,
- nullptr,
- nullptr,
- [](const IDProperty &property) { return property.type == IDP_ID; },
- [](const IDProperty &property, void *r_value) {
- ID *id = IDP_Id(&property);
- Collection *collection = (id && GS(id->name) == ID_GR) ? (Collection *)id : nullptr;
- *(Collection **)r_value = collection;
- },
- };
- return &collection_type;
+ ID *id = IDP_Id(&property);
+ Collection *collection = (id && GS(id->name) == ID_GR) ? (Collection *)id : nullptr;
+ *(Collection **)r_value = collection;
+ break;
}
case SOCK_TEXTURE: {
- static const SocketPropertyType texture_type = {
- [](const bNodeSocket &socket, const char *name) {
- bNodeSocketValueTexture *value = (bNodeSocketValueTexture *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.id = (ID *)value->value;
- return IDP_New(IDP_ID, &idprop, name);
- },
- nullptr,
- nullptr,
- nullptr,
- nullptr,
- [](const IDProperty &property) { return property.type == IDP_ID; },
- [](const IDProperty &property, void *r_value) {
- ID *id = IDP_Id(&property);
- Tex *texture = (id && GS(id->name) == ID_TE) ? (Tex *)id : nullptr;
- *(Tex **)r_value = texture;
- },
- };
- return &texture_type;
+ ID *id = IDP_Id(&property);
+ Tex *texture = (id && GS(id->name) == ID_TE) ? (Tex *)id : nullptr;
+ *(Tex **)r_value = texture;
+ break;
}
case SOCK_MATERIAL: {
- static const SocketPropertyType material_type = {
- [](const bNodeSocket &socket, const char *name) {
- bNodeSocketValueMaterial *value = (bNodeSocketValueMaterial *)socket.default_value;
- IDPropertyTemplate idprop = {0};
- idprop.id = (ID *)value->value;
- return IDP_New(IDP_ID, &idprop, name);
- },
- nullptr,
- nullptr,
- nullptr,
- nullptr,
- [](const IDProperty &property) { return property.type == IDP_ID; },
- [](const IDProperty &property, void *r_value) {
- ID *id = IDP_Id(&property);
- Material *material = (id && GS(id->name) == ID_MA) ? (Material *)id : nullptr;
- *(Material **)r_value = material;
- },
- };
- return &material_type;
+ ID *id = IDP_Id(&property);
+ Material *material = (id && GS(id->name) == ID_MA) ? (Material *)id : nullptr;
+ *(Material **)r_value = material;
+ break;
}
default: {
- return nullptr;
+ BLI_assert_unreachable();
+ break;
}
}
}
@@ -647,32 +477,39 @@ void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd)
}
IDProperty *old_properties = nmd->settings.properties;
-
{
IDPropertyTemplate idprop = {0};
nmd->settings.properties = IDP_New(IDP_GROUP, &idprop, "Nodes Modifier Settings");
}
- IDProperty *ui_container_group;
- {
- IDPropertyTemplate idprop = {0};
- ui_container_group = IDP_New(IDP_GROUP, &idprop, "_RNA_UI");
- IDP_AddToGroup(nmd->settings.properties, ui_container_group);
- }
-
LISTBASE_FOREACH (bNodeSocket *, socket, &nmd->node_group->inputs) {
- const SocketPropertyType *property_type = get_socket_property_type(*socket);
- if (property_type == nullptr) {
+ IDProperty *new_prop = id_property_create_from_socket(*socket);
+ if (new_prop == nullptr) {
+ /* Out of the set of supported input sockets, only
+ * geometry sockets aren't added to the modifier. */
+ BLI_assert(socket->type == SOCK_GEOMETRY);
continue;
}
- IDProperty *new_prop = socket_add_property(
- nmd->settings.properties, ui_container_group, *property_type, *socket);
+ new_prop->flag |= IDP_FLAG_OVERRIDABLE_LIBRARY;
+ if (socket->description[0] != '\0') {
+ IDPropertyUIData *ui_data = IDP_ui_data_ensure(new_prop);
+ ui_data->description = BLI_strdup(socket->description);
+ }
+ IDP_AddToGroup(nmd->settings.properties, new_prop);
if (old_properties != nullptr) {
IDProperty *old_prop = IDP_GetPropertyFromGroup(old_properties, socket->identifier);
- if (old_prop != nullptr && property_type->is_correct_type(*old_prop)) {
+ if (old_prop != nullptr && id_property_type_matches_socket(*socket, *old_prop)) {
+ /* #IDP_CopyPropertyContent replaces the UI data as well, which we don't (we only
+ * want to replace the values). So release it temporarily and replace it after. */
+ IDPropertyUIData *ui_data = new_prop->ui_data;
+ new_prop->ui_data = nullptr;
IDP_CopyPropertyContent(new_prop, old_prop);
+ if (new_prop->ui_data != nullptr) {
+ IDP_ui_data_free(new_prop);
+ }
+ new_prop->ui_data = ui_data;
}
}
}
@@ -713,14 +550,8 @@ void MOD_nodes_init(Main *bmain, NodesModifierData *nmd)
static void initialize_group_input(NodesModifierData &nmd,
const bNodeSocket &socket,
- const CPPType &cpp_type,
void *r_value)
{
- const SocketPropertyType *property_type = get_socket_property_type(socket);
- if (property_type == nullptr) {
- cpp_type.copy_construct(cpp_type.default_value(), r_value);
- return;
- }
if (nmd.settings.properties == nullptr) {
socket.typeinfo->get_geometry_nodes_cpp_value(socket, r_value);
return;
@@ -731,11 +562,13 @@ static void initialize_group_input(NodesModifierData &nmd,
socket.typeinfo->get_geometry_nodes_cpp_value(socket, r_value);
return;
}
- if (!property_type->is_correct_type(*property)) {
+ if (!id_property_type_matches_socket(socket, *property)) {
socket.typeinfo->get_geometry_nodes_cpp_value(socket, r_value);
return;
}
- property_type->init_cpp_value(*property, r_value);
+
+ init_socket_cpp_value_from_property(
+ *property, static_cast<eNodeSocketDatatype>(socket.type), r_value);
}
static Vector<SpaceSpreadsheet *> find_spreadsheet_editors(Main *bmain)
@@ -886,7 +719,7 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree,
for (const OutputSocketRef *socket : remaining_input_sockets) {
const CPPType &cpp_type = *socket->typeinfo()->get_geometry_nodes_cpp_type();
void *value_in = allocator.allocate(cpp_type.size(), cpp_type.alignment());
- initialize_group_input(*nmd, *socket->bsocket(), cpp_type, value_in);
+ initialize_group_input(*nmd, *socket->bsocket(), value_in);
group_inputs.add_new({root_context, socket}, {cpp_type, value_in});
}
}
@@ -954,8 +787,7 @@ static void check_property_socket_sync(const Object *ob, ModifierData *md)
continue;
}
- const SocketPropertyType *property_type = get_socket_property_type(*socket);
- if (!property_type->is_correct_type(*property)) {
+ if (!id_property_type_matches_socket(*socket, *property)) {
BKE_modifier_set_error(
ob, md, "Property type does not match input socket \"(%s)\"", socket->name);
continue;
@@ -1051,17 +883,12 @@ static void draw_property_for_socket(uiLayout *layout,
const IDProperty *modifier_props,
const bNodeSocket &socket)
{
- const SocketPropertyType *property_type = get_socket_property_type(socket);
- if (property_type == nullptr) {
- return;
- }
-
/* The property should be created in #MOD_nodes_update_interface with the correct type. */
IDProperty *property = IDP_GetPropertyFromGroup(modifier_props, socket.identifier);
/* IDProperties can be removed with python, so there could be a situation where
* there isn't a property for a socket or it doesn't have the correct type. */
- if (property == nullptr || !property_type->is_correct_type(*property)) {
+ if (property == nullptr || !id_property_type_matches_socket(socket, *property)) {
return;
}
diff --git a/source/blender/python/generic/CMakeLists.txt b/source/blender/python/generic/CMakeLists.txt
index dce6a7e1c91..10cc1e8b113 100644
--- a/source/blender/python/generic/CMakeLists.txt
+++ b/source/blender/python/generic/CMakeLists.txt
@@ -21,6 +21,7 @@ set(INC
../../blenlib
../../gpu
../../makesdna
+ ../../makesrna
../../../../intern/glew-mx
../../../../intern/guardedalloc
)
@@ -36,6 +37,7 @@ set(SRC
blf_py_api.c
bpy_threads.c
idprop_py_api.c
+ idprop_py_ui_api.c
imbuf_py_api.c
py_capi_utils.c
@@ -43,6 +45,7 @@ set(SRC
bl_math_py_api.h
blf_py_api.h
idprop_py_api.h
+ idprop_py_ui_api.h
imbuf_py_api.h
py_capi_utils.h
diff --git a/source/blender/python/generic/idprop_py_api.c b/source/blender/python/generic/idprop_py_api.c
index 024900db691..58a947943b1 100644
--- a/source/blender/python/generic/idprop_py_api.c
+++ b/source/blender/python/generic/idprop_py_api.c
@@ -25,6 +25,7 @@
#include "BLI_utildefines.h"
#include "idprop_py_api.h"
+#include "idprop_py_ui_api.h"
#include "BKE_idprop.h"
@@ -2135,6 +2136,7 @@ static PyObject *BPyInit_idprop_types(void)
submodule = PyModule_Create(&IDProp_types_module_def);
IDProp_Init_Types();
+ IDPropertyUIData_Init_Types();
/* bmesh_py_types.c */
PyModule_AddType(submodule, &BPy_IDGroup_Type);
diff --git a/source/blender/python/generic/idprop_py_ui_api.c b/source/blender/python/generic/idprop_py_ui_api.c
new file mode 100644
index 00000000000..92cc3b8e1dd
--- /dev/null
+++ b/source/blender/python/generic/idprop_py_ui_api.c
@@ -0,0 +1,734 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup pygen
+ */
+
+#include <Python.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_string.h"
+#include "BLI_utildefines.h"
+
+#include "idprop_py_ui_api.h"
+
+#include "BKE_idprop.h"
+
+#include "DNA_ID.h"
+
+#include "RNA_access.h"
+#include "RNA_enum_types.h"
+
+#include "../intern/bpy_rna.h"
+
+#define USE_STRING_COERCE
+
+#ifdef USE_STRING_COERCE
+# include "py_capi_utils.h"
+#endif
+
+#include "python_utildefines.h"
+
+/* -------------------------------------------------------------------- */
+/** \name UI Data Update
+ * \{ */
+
+static bool args_contain_key(PyObject *kwargs, const char *name)
+{
+ PyObject *py_key = PyUnicode_FromString(name);
+ const bool result = PyDict_Contains(kwargs, py_key) == 1;
+ Py_DECREF(py_key);
+ return result;
+}
+
+/**
+ * \return False when parsing fails, in which case caller should return NULL.
+ */
+static bool idprop_ui_data_update_base(IDPropertyUIData *ui_data,
+ const char *rna_subtype,
+ const char *description)
+{
+ if (rna_subtype != NULL) {
+ if (pyrna_enum_value_from_id(rna_enum_property_subtype_items,
+ rna_subtype,
+ &ui_data->rna_subtype,
+ "IDPropertyUIManager.update") == -1) {
+ return false;
+ }
+ }
+
+ if (description != NULL) {
+ ui_data->description = BLI_strdup(description);
+ }
+
+ 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_int_default(IDProperty *idprop,
+ IDPropertyUIDataInt *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);
+ int *new_default_array = (int *)MEM_malloc_arrayN(len, sizeof(int), __func__);
+ if (PyC_AsArray(
+ new_default_array, sizeof(int), default_value, len, &PyLong_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_AsI32(default_value);
+ if ((value == -1) && PyErr_Occurred()) {
+ PyErr_SetString(PyExc_ValueError, "Error converting \"default\" argument to integer");
+ return false;
+ }
+ ui_data->default_value = value;
+ }
+
+ return true;
+}
+
+/**
+ * \return False when parsing fails, in which case caller should return NULL.
+ */
+static bool idprop_ui_data_update_int(IDProperty *idprop, PyObject *args, PyObject *kwargs)
+{
+ const char *rna_subtype = NULL;
+ const char *description = NULL;
+ int min, max, soft_min, soft_max, step;
+ PyObject *default_value = NULL;
+ const char *kwlist[] = {
+ "min", "max", "soft_min", "soft_max", "step", "default", "subtype", "description", NULL};
+ if (!PyArg_ParseTupleAndKeywords(args,
+ kwargs,
+ "|$iiiiiOzz:update",
+ (char **)kwlist,
+ &min,
+ &max,
+ &soft_min,
+ &soft_max,
+ &step,
+ &default_value,
+ &rna_subtype,
+ &description)) {
+ return false;
+ }
+
+ /* Write to a temporary copy of the UI data in case some part of the parsing fails. */
+ IDPropertyUIDataInt *ui_data_orig = (IDPropertyUIDataInt *)idprop->ui_data;
+ IDPropertyUIDataInt 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 (args_contain_key(kwargs, "min")) {
+ ui_data.min = min;
+ ui_data.soft_min = MAX2(ui_data.soft_min, ui_data.min);
+ ui_data.max = MAX2(ui_data.min, ui_data.max);
+ }
+ if (args_contain_key(kwargs, "max")) {
+ ui_data.max = max;
+ ui_data.soft_max = MIN2(ui_data.soft_max, ui_data.max);
+ ui_data.min = MIN2(ui_data.min, ui_data.max);
+ }
+ if (args_contain_key(kwargs, "soft_min")) {
+ ui_data.soft_min = soft_min;
+ ui_data.soft_min = MAX2(ui_data.soft_min, ui_data.min);
+ ui_data.soft_max = MAX2(ui_data.soft_min, ui_data.soft_max);
+ }
+ if (args_contain_key(kwargs, "soft_max")) {
+ ui_data.soft_max = soft_max;
+ ui_data.soft_max = MIN2(ui_data.soft_max, ui_data.max);
+ ui_data.soft_min = MIN2(ui_data.soft_min, ui_data.soft_max);
+ }
+ if (args_contain_key(kwargs, "step")) {
+ ui_data.step = step;
+ }
+
+ if (!ELEM(default_value, NULL, Py_None)) {
+ if (!idprop_ui_data_update_int_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 proeprty'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.
+ */
+static bool idprop_ui_data_update_float_default(IDProperty *idprop,
+ IDPropertyUIDataFloat *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);
+ double *new_default_array = (double *)MEM_malloc_arrayN(len, sizeof(double), __func__);
+ if (PyC_AsArray(new_default_array,
+ sizeof(double),
+ default_value,
+ len,
+ &PyFloat_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 double value = PyFloat_AsDouble(default_value);
+ if ((value == -1.0) && PyErr_Occurred()) {
+ PyErr_SetString(PyExc_ValueError, "Error converting \"default\" argument to double");
+ return false;
+ }
+ ui_data->default_value = value;
+ }
+
+ return true;
+}
+
+/**
+ * \return False when parsing fails, in which case caller should return NULL.
+ */
+static bool idprop_ui_data_update_float(IDProperty *idprop, PyObject *args, PyObject *kwargs)
+{
+ const char *rna_subtype = NULL;
+ const char *description = NULL;
+ int precision;
+ double min, max, soft_min, soft_max, step;
+ PyObject *default_value = NULL;
+ const char *kwlist[] = {"min",
+ "max",
+ "soft_min",
+ "soft_max",
+ "step",
+ "precision",
+ "default",
+ "subtype",
+ "description",
+ NULL};
+ if (!PyArg_ParseTupleAndKeywords(args,
+ kwargs,
+ "|$dddddiOzz:update",
+ (char **)kwlist,
+ &min,
+ &max,
+ &soft_min,
+ &soft_max,
+ &step,
+ &precision,
+ &default_value,
+ &rna_subtype,
+ &description)) {
+ return false;
+ }
+
+ /* Write to a temporary copy of the UI data in case some part of the parsing fails. */
+ IDPropertyUIDataFloat *ui_data_orig = (IDPropertyUIDataFloat *)idprop->ui_data;
+ IDPropertyUIDataFloat 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 (args_contain_key(kwargs, "min")) {
+ ui_data.min = min;
+ ui_data.soft_min = MAX2(ui_data.soft_min, ui_data.min);
+ ui_data.max = MAX2(ui_data.min, ui_data.max);
+ }
+ if (args_contain_key(kwargs, "max")) {
+ ui_data.max = max;
+ ui_data.soft_max = MIN2(ui_data.soft_max, ui_data.max);
+ ui_data.min = MIN2(ui_data.min, ui_data.max);
+ }
+ if (args_contain_key(kwargs, "soft_min")) {
+ ui_data.soft_min = soft_min;
+ ui_data.soft_min = MAX2(ui_data.soft_min, ui_data.min);
+ ui_data.soft_max = MAX2(ui_data.soft_min, ui_data.soft_max);
+ }
+ if (args_contain_key(kwargs, "soft_max")) {
+ ui_data.soft_max = soft_max;
+ ui_data.soft_max = MIN2(ui_data.soft_max, ui_data.max);
+ ui_data.soft_min = MIN2(ui_data.soft_min, ui_data.soft_max);
+ }
+ if (args_contain_key(kwargs, "step")) {
+ ui_data.step = (float)step;
+ }
+ if (args_contain_key(kwargs, "precision")) {
+ ui_data.precision = precision;
+ }
+
+ if (!ELEM(default_value, NULL, Py_None)) {
+ if (!idprop_ui_data_update_float_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 proeprty'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;
+}
+
+/**
+ * \return False when parsing fails, in which case caller should return NULL.
+ */
+static bool idprop_ui_data_update_string(IDProperty *idprop, PyObject *args, PyObject *kwargs)
+{
+ const char *rna_subtype = NULL;
+ const char *description = NULL;
+ const char *default_value;
+ const char *kwlist[] = {"default", "subtype", "description", NULL};
+ if (!PyArg_ParseTupleAndKeywords(args,
+ kwargs,
+ "|$zzz: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. */
+ IDPropertyUIDataString *ui_data_orig = (IDPropertyUIDataString *)idprop->ui_data;
+ IDPropertyUIDataString 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 (default_value != NULL) {
+ ui_data.default_value = BLI_strdup(default_value);
+ }
+
+ /* Write back to the proeprty'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;
+}
+
+/**
+ * \return False when parsing fails, in which case caller should return NULL.
+ */
+static bool idprop_ui_data_update_id(IDProperty *idprop, PyObject *args, PyObject *kwargs)
+{
+ const char *rna_subtype = NULL;
+ const char *description = NULL;
+ const char *kwlist[] = {"subtype", "description", NULL};
+ if (!PyArg_ParseTupleAndKeywords(
+ args, kwargs, "|$zz:update", (char **)kwlist, &rna_subtype, &description)) {
+ return false;
+ }
+
+ /* Write to a temporary copy of the UI data in case some part of the parsing fails. */
+ IDPropertyUIDataID *ui_data_orig = (IDPropertyUIDataID *)idprop->ui_data;
+ IDPropertyUIDataID 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;
+ }
+
+ /* Write back to the proeprty'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;
+}
+
+PyDoc_STRVAR(BPy_IDPropertyUIManager_update_doc,
+ ".. method:: update( "
+ "subtype=None, "
+ "min=None, "
+ "max=None, "
+ "soft_min=None, "
+ "soft_max=None, "
+ "precision=None, "
+ "step=None, "
+ "default=None, "
+ "description=None)\n"
+ "\n"
+ " Update the RNA information of the IDProperty used for interaction and\n"
+ " display in the user interface. The required types for many of the keyword\n"
+ " arguments depend on the type of the property.\n ");
+static PyObject *BPy_IDPropertyUIManager_update(BPy_IDPropertyUIManager *self,
+ PyObject *args,
+ PyObject *kwargs)
+{
+ IDProperty *property = self->property;
+ BLI_assert(IDP_ui_data_supported(property));
+
+ switch (IDP_ui_data_type(property)) {
+ case IDP_UI_DATA_TYPE_INT:
+ IDP_ui_data_ensure(property);
+ if (!idprop_ui_data_update_int(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)) {
+ return NULL;
+ }
+ Py_RETURN_NONE;
+ case IDP_UI_DATA_TYPE_STRING:
+ IDP_ui_data_ensure(property);
+ if (!idprop_ui_data_update_string(property, args, kwargs)) {
+ return NULL;
+ }
+ Py_RETURN_NONE;
+ case IDP_UI_DATA_TYPE_ID:
+ IDP_ui_data_ensure(property);
+ if (!idprop_ui_data_update_id(property, args, kwargs)) {
+ return NULL;
+ }
+ Py_RETURN_NONE;
+ case IDP_UI_DATA_TYPE_UNSUPPORTED:
+ PyErr_Format(PyExc_TypeError, "IDProperty \"%s\" does not support RNA data", property->name);
+ return NULL;
+ }
+
+ BLI_assert_unreachable();
+ Py_RETURN_NONE;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name UI Data As Dictionary
+ * \{ */
+
+static void idprop_ui_data_to_dict_int(IDProperty *property, PyObject *dict)
+{
+ IDPropertyUIDataInt *ui_data = (IDPropertyUIDataInt *)property->ui_data;
+ PyObject *item;
+
+ PyDict_SetItemString(dict, "min", item = PyLong_FromLong(ui_data->min));
+ Py_DECREF(item);
+ PyDict_SetItemString(dict, "max", item = PyLong_FromLong(ui_data->max));
+ Py_DECREF(item);
+ PyDict_SetItemString(dict, "soft_min", item = PyLong_FromLong(ui_data->soft_min));
+ Py_DECREF(item);
+ PyDict_SetItemString(dict, "soft_max", item = PyLong_FromLong(ui_data->soft_max));
+ Py_DECREF(item);
+ PyDict_SetItemString(dict, "step", item = PyLong_FromLong(ui_data->step));
+ Py_DECREF(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, PyLong_FromLong(ui_data->default_array[i]));
+ }
+ PyDict_SetItemString(dict, "default", list);
+ Py_DECREF(list);
+ }
+ else {
+ PyDict_SetItemString(dict, "default", item = PyLong_FromLong(ui_data->step));
+ Py_DECREF(item);
+ }
+}
+
+static void idprop_ui_data_to_dict_float(IDProperty *property, PyObject *dict)
+{
+ IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)property->ui_data;
+ PyObject *item;
+
+ PyDict_SetItemString(dict, "min", item = PyFloat_FromDouble(ui_data->min));
+ Py_DECREF(item);
+ PyDict_SetItemString(dict, "max", item = PyFloat_FromDouble(ui_data->max));
+ Py_DECREF(item);
+ PyDict_SetItemString(dict, "soft_min", item = PyFloat_FromDouble(ui_data->soft_min));
+ Py_DECREF(item);
+ PyDict_SetItemString(dict, "soft_max", item = PyFloat_FromDouble(ui_data->soft_max));
+ Py_DECREF(item);
+ PyDict_SetItemString(dict, "step", item = PyFloat_FromDouble((double)ui_data->step));
+ Py_DECREF(item);
+ PyDict_SetItemString(dict, "precision", item = PyFloat_FromDouble((double)ui_data->precision));
+ Py_DECREF(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, PyFloat_FromDouble(ui_data->default_array[i]));
+ }
+ PyDict_SetItemString(dict, "default", list);
+ Py_DECREF(list);
+ }
+ else {
+ PyDict_SetItemString(dict, "default", item = PyFloat_FromDouble(ui_data->step));
+ Py_DECREF(item);
+ }
+}
+
+static void idprop_ui_data_to_dict_string(IDProperty *property, PyObject *dict)
+{
+ IDPropertyUIDataString *ui_data = (IDPropertyUIDataString *)property->ui_data;
+ PyObject *item;
+
+ const char *default_value = (ui_data->default_value == NULL) ? "" : ui_data->default_value;
+
+ PyDict_SetItemString(dict, "default", item = PyUnicode_FromString(default_value));
+ Py_DECREF(item);
+}
+
+PyDoc_STRVAR(BPy_IDPropertyUIManager_as_dict_doc,
+ ".. method:: as_dict()\n"
+ "\n"
+ " Return a dictionary of the property's RNA UI data. The fields in the\n"
+ " returned dictionary and their types will depend on the property's type.\n");
+static PyObject *BPy_IDIDPropertyUIManager_as_dict(BPy_IDPropertyUIManager *self)
+{
+ IDProperty *property = self->property;
+ BLI_assert(IDP_ui_data_supported(property));
+
+ IDPropertyUIData *ui_data = IDP_ui_data_ensure(property);
+
+ PyObject *dict = PyDict_New();
+
+ /* RNA subtype. */
+ {
+ const char *subtype_id = NULL;
+ RNA_enum_identifier(rna_enum_property_subtype_items, ui_data->rna_subtype, &subtype_id);
+ PyObject *item = PyUnicode_FromString(subtype_id);
+ PyDict_SetItemString(dict, "subtype", item);
+ Py_DECREF(item);
+ }
+
+ /* Description. */
+ if (ui_data->description != NULL) {
+ PyObject *item = PyUnicode_FromString(ui_data->description);
+ PyDict_SetItemString(dict, "description", item);
+ Py_DECREF(item);
+ }
+
+ /* Type specific data. */
+ switch (IDP_ui_data_type(property)) {
+ case IDP_UI_DATA_TYPE_STRING:
+ idprop_ui_data_to_dict_string(property, dict);
+ break;
+ case IDP_UI_DATA_TYPE_ID:
+ break;
+ case IDP_UI_DATA_TYPE_INT:
+ idprop_ui_data_to_dict_int(property, dict);
+ break;
+ case IDP_UI_DATA_TYPE_FLOAT:
+ idprop_ui_data_to_dict_float(property, dict);
+ break;
+ case IDP_UI_DATA_TYPE_UNSUPPORTED:
+ BLI_assert_unreachable();
+ break;
+ }
+
+ return dict;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name UI Data Clear
+ * \{ */
+
+PyDoc_STRVAR(BPy_IDPropertyUIManager_clear_doc,
+ ".. method:: clear()\n"
+ "\n"
+ " Remove the RNA UI data from this IDProperty.\n");
+static PyObject *BPy_IDPropertyUIManager_clear(BPy_IDPropertyUIManager *self)
+{
+ IDProperty *property = self->property;
+ BLI_assert(IDP_ui_data_supported(property));
+
+ if (property == NULL) {
+ PyErr_SetString(PyExc_RuntimeError, "IDPropertyUIManager missing property");
+ BLI_assert_unreachable();
+ return NULL;
+ }
+
+ if (property->ui_data != NULL) {
+ IDP_ui_data_free(property);
+ }
+
+ Py_RETURN_NONE;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name UI Data Copying
+ * \{ */
+
+PyDoc_STRVAR(
+ BPy_IDPropertyUIManager_update_from_doc,
+ ".. method:: update_from(ui_manager_source)\n"
+ "\n"
+ " Copy UI data from an IDProperty in the source group to a property in this group.\n "
+ " If the source property has no UI data, the target UI data will be reset if it exists.\n"
+ "\n"
+ " :raises TypeError: If the types of the two properties don't match.\n");
+static PyObject *BPy_IDPropertyUIManager_update_from(BPy_IDPropertyUIManager *self, PyObject *args)
+{
+ IDProperty *property = self->property;
+ BLI_assert(IDP_ui_data_supported(property));
+
+ BPy_IDPropertyUIManager *ui_manager_src;
+ if (!PyArg_ParseTuple(args, "O!:update_from", &BPy_IDPropertyUIManager_Type, &ui_manager_src)) {
+ return NULL;
+ }
+
+ if (property->ui_data != NULL) {
+ IDP_ui_data_free(property);
+ }
+
+ property->ui_data = IDP_ui_data_copy(ui_manager_src->property);
+
+ Py_RETURN_NONE;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name UI Data Manager Definition
+ * \{ */
+
+static struct PyMethodDef BPy_IDPropertyUIManager_methods[] = {
+ {"update",
+ (PyCFunction)BPy_IDPropertyUIManager_update,
+ METH_VARARGS | METH_KEYWORDS,
+ BPy_IDPropertyUIManager_update_doc},
+ {"as_dict",
+ (PyCFunction)BPy_IDIDPropertyUIManager_as_dict,
+ METH_NOARGS,
+ BPy_IDPropertyUIManager_as_dict_doc},
+ {"clear",
+ (PyCFunction)BPy_IDPropertyUIManager_clear,
+ METH_NOARGS,
+ BPy_IDPropertyUIManager_clear_doc},
+ {"update_from",
+ (PyCFunction)BPy_IDPropertyUIManager_update_from,
+ METH_VARARGS,
+ BPy_IDPropertyUIManager_update_from_doc},
+ {NULL, NULL, 0, NULL},
+};
+
+static PyObject *BPy_IDPropertyUIManager_repr(BPy_IDPropertyUIManager *self)
+{
+ return PyUnicode_FromFormat(
+ "<bpy id prop ui manager: name=\"%s\", address=%p>", self->property->name, self->property);
+}
+
+static Py_hash_t BPy_IDPropertyUIManager_hash(BPy_IDPropertyUIManager *self)
+{
+ return _Py_HashPointer(self->property);
+}
+
+PyTypeObject BPy_IDPropertyUIManager_Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ /* For printing, in format "<module>.<name>" */
+ "IDPropertyUIManager", /* char *tp_name; */
+ sizeof(BPy_IDPropertyUIManager), /* int tp_basicsize; */
+ 0, /* tp_itemsize; For allocation */
+
+ /* Methods to implement standard operations */
+
+ NULL, /* destructor tp_dealloc; */
+ 0, /* tp_vectorcall_offset */
+ NULL, /* getattrfunc tp_getattr; */
+ NULL, /* setattrfunc tp_setattr; */
+ NULL, /* cmpfunc tp_compare; */
+ (reprfunc)BPy_IDPropertyUIManager_repr, /* reprfunc tp_repr; */
+
+ /* Method suites for standard classes */
+
+ NULL, /* PyNumberMethods *tp_as_number; */
+ NULL, /* PySequenceMethods *tp_as_sequence; */
+ NULL, /* PyMappingMethods *tp_as_mapping; */
+
+ /* More standard operations (here for binary compatibility) */
+
+ (hashfunc)BPy_IDPropertyUIManager_hash, /* hashfunc tp_hash; */
+ NULL, /* ternaryfunc tp_call; */
+ NULL, /* reprfunc tp_str; */
+ NULL, /* getattrofunc tp_getattro; */
+ NULL, /* setattrofunc tp_setattro; */
+
+ /* Functions to access object as input/output buffer */
+ NULL, /* PyBufferProcs *tp_as_buffer; */
+
+ /*** Flags to define presence of optional/expanded features ***/
+ Py_TPFLAGS_DEFAULT, /* long tp_flags; */
+
+ NULL, /* char *tp_doc; Documentation string */
+ /*** Assigned meaning in release 2.0 ***/
+ /* call function for all accessible objects */
+ NULL, /* traverseproc tp_traverse; */
+
+ /* delete references to contained objects */
+ NULL, /* inquiry tp_clear; */
+
+ /*** Assigned meaning in release 2.1 ***/
+ /*** rich comparisons ***/
+ NULL, /* richcmpfunc tp_richcompare; */
+
+ /*** weak reference enabler ***/
+ 0, /* long tp_weaklistoffset; */
+
+ /*** Added in release 2.2 ***/
+ /* Iterators */
+ NULL, /* getiterfunc tp_iter; */
+ NULL, /* iternextfunc tp_iternext; */
+ /*** Attribute descriptor and subclassing stuff ***/
+ BPy_IDPropertyUIManager_methods, /* struct PyMethodDef *tp_methods; */
+ NULL, /* struct PyMemberDef *tp_members; */
+ NULL, /* struct PyGetSetDef *tp_getset; */
+};
+
+void IDPropertyUIData_Init_Types()
+{
+ PyType_Ready(&BPy_IDPropertyUIManager_Type);
+}
+
+/** \} */
diff --git a/source/blender/python/generic/idprop_py_ui_api.h b/source/blender/python/generic/idprop_py_ui_api.h
new file mode 100644
index 00000000000..f678e5ae9c1
--- /dev/null
+++ b/source/blender/python/generic/idprop_py_ui_api.h
@@ -0,0 +1,33 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+/** \file
+ * \ingroup pygen
+ */
+
+#pragma once
+
+struct ID;
+struct IDProperty;
+
+extern PyTypeObject BPy_IDPropertyUIManager_Type;
+
+typedef struct BPy_IDPropertyUIManager {
+ PyObject_VAR_HEAD
+ struct IDProperty *property;
+} BPy_IDPropertyUIManager;
+
+void IDPropertyUIData_Init_Types(void);
diff --git a/source/blender/python/intern/bpy.c b/source/blender/python/intern/bpy.c
index 0e0f431cb19..7646109c1b0 100644
--- a/source/blender/python/intern/bpy.c
+++ b/source/blender/python/intern/bpy.c
@@ -56,6 +56,7 @@
/* external util modules */
#include "../generic/idprop_py_api.h"
+#include "../generic/idprop_py_ui_api.h"
#include "bpy_msgbus.h"
#ifdef WITH_FREESTYLE
@@ -407,6 +408,7 @@ void BPy_init_modules(struct bContext *C)
}
/* stand alone utility modules not related to blender directly */
IDProp_Init_Types(); /* not actually a submodule, just types */
+ IDPropertyUIData_Init_Types();
#ifdef WITH_FREESTYLE
Freestyle_Init();
#endif
diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c
index e9d5ae278bb..ac1fdeb2bad 100644
--- a/source/blender/python/intern/bpy_rna.c
+++ b/source/blender/python/intern/bpy_rna.c
@@ -73,6 +73,7 @@
#include "DEG_depsgraph_query.h"
#include "../generic/idprop_py_api.h" /* For IDprop lookups. */
+#include "../generic/idprop_py_ui_api.h"
#include "../generic/py_capi_utils.h"
#include "../generic/python_utildefines.h"
@@ -4309,6 +4310,51 @@ static PyObject *pyrna_struct_id_properties_ensure(BPy_StructRNA *self)
return (PyObject *)group;
}
+PyDoc_STRVAR(pyrna_struct_id_properties_ui_doc,
+ ".. method:: id_properties_ui(key)\n"
+ "\n"
+ " :return: Return an object used to manage an IDProperty's UI data.\n"
+ " :arg key: String name of the property.\n"
+ " :rtype: :class:`bpy.types.IDPropertyUIManager`\n");
+static PyObject *pyrna_struct_id_properties_ui(BPy_StructRNA *self, PyObject *args)
+{
+ PYRNA_STRUCT_CHECK_OBJ(self);
+
+ if (RNA_struct_idprops_check(self->ptr.type) == 0) {
+ PyErr_SetString(PyExc_TypeError, "This type doesn't support IDProperties");
+ return NULL;
+ }
+
+ const char *key;
+ if (!PyArg_ParseTuple(args, "s:ui_data", &key)) {
+ return NULL;
+ }
+
+ IDProperty *parent_group = RNA_struct_idprops(&self->ptr, true);
+
+ /* This is a paranoid check that theoretically might not be necessary.
+ * It allows the possibility that some structs can't ensure IDProperties. */
+ if (parent_group == NULL) {
+ return Py_None;
+ }
+
+ IDProperty *property = IDP_GetPropertyFromGroup(parent_group, key);
+ if (property == NULL) {
+ PyErr_SetString(PyExc_KeyError, "Property not found in IDProperty group");
+ return NULL;
+ }
+
+ if (!IDP_ui_data_supported(property)) {
+ PyErr_Format(PyExc_TypeError, "IDProperty \"%s\" does not support UI data", property->name);
+ return NULL;
+ }
+
+ BPy_IDPropertyUIManager *ui_manager = PyObject_New(BPy_IDPropertyUIManager,
+ &BPy_IDPropertyUIManager_Type);
+ ui_manager->property = property;
+ return (PyObject *)ui_manager;
+}
+
PyDoc_STRVAR(pyrna_struct_id_properties_clear_doc,
".. method:: id_properties_clear()\n\n"
" :return: Remove the parent group for an RNA struct's custom IDProperties.\n");
@@ -5829,6 +5875,10 @@ static struct PyMethodDef pyrna_struct_methods[] = {
(PyCFunction)pyrna_struct_id_properties_clear,
METH_NOARGS,
pyrna_struct_id_properties_clear_doc},
+ {"id_properties_ui",
+ (PyCFunction)pyrna_struct_id_properties_ui,
+ METH_VARARGS,
+ pyrna_struct_id_properties_ui_doc},
/* experimental */
/* unused for now */
diff --git a/tests/python/bl_pyapi_idprop.py b/tests/python/bl_pyapi_idprop.py
index 38cd9d04a6b..bdf625c1c2e 100644
--- a/tests/python/bl_pyapi_idprop.py
+++ b/tests/python/bl_pyapi_idprop.py
@@ -246,6 +246,69 @@ class TestBufferProtocol(TestHelper, unittest.TestCase):
self.assertEqual(list(view1), list(view2))
self.assertEqual(view1.tobytes(), view2.tobytes())
+class TestRNAData(TestHelper, unittest.TestCase):
+
+ def test_custom_properties_none(self):
+ bpy.data.objects.new("test", None)
+ test_object = bpy.data.objects["test"]
+
+ # Access default RNA data values.
+ test_object.id_properties_clear()
+ test_object["test_prop"] = 0.5
+ ui_data_test_prop = test_object.id_properties_ui("test_prop")
+
+ rna_data = ui_data_test_prop.as_dict()
+ self.assertTrue("min" in rna_data)
+ self.assertLess(rna_data["min"], -10000.0)
+ self.assertEqual(rna_data["subtype"], "NONE")
+ self.assertGreater(rna_data["soft_max"], 10000.0)
+
+ # Change RNA data values.
+ ui_data_test_prop.update(subtype="TEMPERATURE", min=0, soft_min=0.1)
+ rna_data = ui_data_test_prop.as_dict()
+ self.assertEqual(rna_data["min"], 0)
+ self.assertEqual(rna_data["soft_min"], 0.1)
+ self.assertEqual(rna_data["subtype"], "TEMPERATURE")
+
+ # Copy RNA data values from one property to another.
+ test_object["test_prop_2"] = 11.7
+ ui_data_test_prop_2 = test_object.id_properties_ui("test_prop_2")
+ ui_data_test_prop_2.update_from(ui_data_test_prop)
+ rna_data = ui_data_test_prop_2.as_dict()
+ self.assertEqual(rna_data["min"], 0)
+ self.assertEqual(rna_data["soft_min"], 0.1)
+ self.assertEqual(rna_data["subtype"], "TEMPERATURE")
+ self.assertGreater(rna_data["soft_max"], 10000.0)
+
+ # Copy RNA data values to another object's property.
+ bpy.data.objects.new("test_2", None)
+ test_object_2 = bpy.data.objects["test_2"]
+ test_object_2["test_prop_3"] = 20.1
+ ui_data_test_prop_3 = test_object_2.id_properties_ui("test_prop_3")
+ ui_data_test_prop_3.update_from(ui_data_test_prop_2)
+ rna_data = ui_data_test_prop_3.as_dict()
+ self.assertEqual(rna_data["min"], 0)
+ self.assertEqual(rna_data["soft_min"], 0.1)
+ self.assertEqual(rna_data["subtype"], "TEMPERATURE")
+ self.assertGreater(rna_data["soft_max"], 10000.0)
+
+ # Test RNA data for string property.
+ test_object.id_properties_clear()
+ test_object["test_string_prop"] = "Hello there!"
+ ui_data_test_prop_string = test_object.id_properties_ui("test_string_prop")
+ ui_data_test_prop_string.update(default="Goodbye where?")
+ rna_data = ui_data_test_prop_string.as_dict()
+ self.assertEqual(rna_data["default"], "Goodbye where?")
+
+ # Test RNA data for array property.
+ test_object.id_properties_clear()
+ test_object["test_array_prop"] = [1, 2, 3]
+ ui_data_test_prop_array = test_object.id_properties_ui("test_array_prop")
+ ui_data_test_prop_array.update(default=[1, 2])
+ rna_data = ui_data_test_prop_array.as_dict()
+ self.assertEqual(rna_data["default"], [1, 2])
+
+
if __name__ == '__main__':
import sys