Compare commits
176 Commits
temp-ghost
...
refactor-i
Author | SHA1 | Date | |
---|---|---|---|
a0f754e774 | |||
137a5e162c | |||
aad18a0050 | |||
8a9d764544 | |||
414ad257ec | |||
07b2aaa8c7 | |||
14d333cd31 | |||
2263e16a57 | |||
1681d09919 | |||
1bbc4ccbd2 | |||
6b944e8dc0 | |||
8473bee909 | |||
983e84322a | |||
c85397ad51 | |||
ed351f2784 | |||
fb4b5c7235 | |||
cc00567061 | |||
95538552a8 | |||
442236d9ea | |||
ecd8bdafbb | |||
71164a826d | |||
11d7619e22 | |||
c41c378428 | |||
1cf574dc39 | |||
6a5e97e379 | |||
a207c1560f | |||
40e3841939 | |||
4877b0f742 | |||
3bbeb8543b | |||
02fd6dda35 | |||
638c2f0c3f | |||
c492f26549 | |||
34fb3a19df | |||
3b39186afa | |||
73012ab1f7 | |||
57317c4f83 | |||
6f258744b1 | |||
c577e6598d | |||
e1fa608c1b | |||
6903142975 | |||
5b98e14f26 | |||
79fd9d8883 | |||
5f17a525b9 | |||
07154dada0 | |||
cee6e9dcc0 | |||
45c1135999 | |||
ae74e88990 | |||
e93214ab23 | |||
de3c9dfabd | |||
212fde45ac | |||
232e3f83bf | |||
278bead415 | |||
5d58f177f7 | |||
47fee34e9f | |||
29a9eaaee3 | |||
9520290dbb | |||
4b67d8302e | |||
158e353ece | |||
b4b8be0d2e | |||
30c424481c | |||
a9484fa797 | |||
7576dcfaa4 | |||
3480934ff4 | |||
dca2303328 | |||
7849e80632 | |||
5810fa80d4 | |||
e1bcb248ef | |||
72974a60b4 | |||
4d330d97ed | |||
a24dc46d0a | |||
60c156c9c8 | |||
e513178bc4 | |||
013bb7e9fd | |||
35a2b13709 | |||
433585f906 | |||
ad3cdcc437 | |||
596aebcc28 | |||
2ebdf5b7a7 | |||
4e96d8da88 | |||
0d1f6b518b | |||
49a8b6bc4e | |||
d1e4f05734 | |||
eea8793205 | |||
a1cf09a202 | |||
dafd70d8a2 | |||
bba8ec2367 | |||
d785ee3a98 | |||
927bec3fba | |||
6d51bb8efe | |||
6c596cde06 | |||
5849aba1e3 | |||
b608a0195f | |||
66350caeec | |||
80a8cdbab0 | |||
39e3bd87a0 | |||
a4f5e84c33 | |||
1047713bbe | |||
4c406f7c45 | |||
9a52428ebe | |||
704c9b25dd | |||
2e199fc2c5 | |||
378b19233e | |||
74c77c887b | |||
334a31fe19 | |||
6c981f2d73 | |||
a8c8400342 | |||
113ac52701 | |||
4f4fdc11cd | |||
d5a51c1376 | |||
40f0b2d5ef | |||
7af42f70f9 | |||
f7cec43e7d | |||
61bda1952c | |||
08ea54f42e | |||
7832003229 | |||
e841acfa79 | |||
81a293b984 | |||
54eef5a9b0 | |||
8b9fd2a417 | |||
1a489fce83 | |||
872f5a0b2a | |||
257fd605e2 | |||
799422bb1c | |||
fb324f6370 | |||
897fdb9b58 | |||
ea2387e7cb | |||
e860c129a8 | |||
351fd68e20 | |||
63a0d6ab3d | |||
b30a0ce808 | |||
90d2fe54b8 | |||
9aa7d11146 | |||
21c240c1e7 | |||
10888eb8ad | |||
36b04e04ad | |||
aacc976522 | |||
6ec08c5f3a | |||
3fcb015454 | |||
f57d8e7239 | |||
336593f340 | |||
4c323d6e9d | |||
06abe408e7 | |||
53ca2fa43b | |||
d92bc84e3a | |||
2ce0cf0c57 | |||
20b7ba360d | |||
e8b1e5776a | |||
e5598b75d4 | |||
db662b2565 | |||
d96ec8ff1c | |||
786e12da25 | |||
b67fd20dcb | |||
467f737b7a | |||
6c90c8bded | |||
f75921b9c9 | |||
a97e3beac3 | |||
72329b7018 | |||
9ac87b653c | |||
4a34ed3aff | |||
f12810906e | |||
87dfb93f4b | |||
9e5fbaef47 | |||
26c5ad06d3 | |||
054417e378 | |||
b7db5c4692 | |||
6f1653a6de | |||
d3d1d9bd5e | |||
3b614abec9 | |||
ad2fc4c569 | |||
210250a91a | |||
3fe37136ff | |||
498685b295 | |||
e2e57307e9 | |||
da9b541261 | |||
4ab2b3c205 | |||
ad72114ad9 |
@@ -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
|
||||
|
@@ -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:
|
||||
|
@@ -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)
|
||||
ui_data = item.id_properties_ui(prop)
|
||||
ui_data.update(subtype=self.subtype, description=self.description)
|
||||
|
||||
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)
|
||||
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'}
|
||||
|
||||
|
@@ -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:
|
||||
|
@@ -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
|
||||
|
@@ -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) {
|
||||
@@ -999,6 +1096,85 @@ IDProperty *IDP_New(const char type, const IDPropertyTemplate *val, const char *
|
||||
return prop;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -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
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
}
|
||||
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. */
|
||||
}
|
||||
return prop->description;
|
||||
}
|
||||
|
||||
return description;
|
||||
const IDProperty *idprop = (const IDProperty *)prop;
|
||||
if (idprop->ui_data) {
|
||||
const IDPropertyUIData *ui_data = idprop->ui_data;
|
||||
return ui_data->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:
|
||||
|
@@ -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);
|
||||
|
@@ -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) {
|
||||
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"));
|
||||
}
|
||||
}
|
||||
|
||||
return prop;
|
||||
}
|
||||
|
||||
static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bsocket)
|
||||
{
|
||||
switch (bsocket.type) {
|
||||
switch (socket.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;
|
||||
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: {
|
||||
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;
|
||||
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: {
|
||||
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;
|
||||
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: {
|
||||
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;
|
||||
bNodeSocketValueBoolean *value = (bNodeSocketValueBoolean *)socket.default_value;
|
||||
IDPropertyTemplate idprop = {0};
|
||||
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: {
|
||||
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;
|
||||
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: {
|
||||
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;
|
||||
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: {
|
||||
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;
|
||||
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: {
|
||||
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;
|
||||
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: {
|
||||
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;
|
||||
bNodeSocketValueMaterial *value = (bNodeSocketValueMaterial *)socket.default_value;
|
||||
IDPropertyTemplate idprop = {0};
|
||||
idprop.id = (ID *)value->value;
|
||||
return IDP_New(IDP_ID, &idprop, socket.identifier);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
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 void init_socket_cpp_value_from_property(const IDProperty &property,
|
||||
const eNodeSocketDatatype socket_value_type,
|
||||
void *r_value)
|
||||
{
|
||||
switch (socket_value_type) {
|
||||
case SOCK_FLOAT: {
|
||||
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: {
|
||||
*(int *)r_value = IDP_Int(&property);
|
||||
break;
|
||||
}
|
||||
case SOCK_VECTOR: {
|
||||
copy_v3_v3((float *)r_value, (const float *)IDP_Array(&property));
|
||||
break;
|
||||
}
|
||||
case SOCK_BOOLEAN: {
|
||||
*(bool *)r_value = IDP_Int(&property) != 0;
|
||||
break;
|
||||
}
|
||||
case SOCK_STRING: {
|
||||
new (r_value) std::string(IDP_String(&property));
|
||||
break;
|
||||
}
|
||||
case SOCK_OBJECT: {
|
||||
ID *id = IDP_Id(&property);
|
||||
Object *object = (id && GS(id->name) == ID_OB) ? (Object *)id : nullptr;
|
||||
*(Object **)r_value = object;
|
||||
break;
|
||||
}
|
||||
case SOCK_COLLECTION: {
|
||||
ID *id = IDP_Id(&property);
|
||||
Collection *collection = (id && GS(id->name) == ID_GR) ? (Collection *)id : nullptr;
|
||||
*(Collection **)r_value = collection;
|
||||
break;
|
||||
}
|
||||
case SOCK_TEXTURE: {
|
||||
ID *id = IDP_Id(&property);
|
||||
Tex *texture = (id && GS(id->name) == ID_TE) ? (Tex *)id : nullptr;
|
||||
*(Tex **)r_value = texture;
|
||||
break;
|
||||
}
|
||||
case SOCK_MATERIAL: {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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);
|
||||
|
734
source/blender/python/generic/idprop_py_ui_api.c
Normal file
734
source/blender/python/generic/idprop_py_ui_api.c
Normal file
@@ -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);
|
||||
}
|
||||
|
||||
/** \} */
|
33
source/blender/python/generic/idprop_py_ui_api.h
Normal file
33
source/blender/python/generic/idprop_py_ui_api.h
Normal file
@@ -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);
|
@@ -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
|
||||
|
@@ -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 */
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user