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:
-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 72572b05ef6..5b9cdc8c44f 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